У меня 2 вопроса: 1. Как можно отследить (получить уведомление) изменение аудиоустройства по умолчанию или списка аудиоустройств (добавилось, отключилось)? Может, какое-то broadcast-сообщение посылается или ещё что-то? 2. Во время записи при добавлении буфера в callback-функции функция waveInAddBuffer зависает, если было изменено аудиоустройство по умолчанию или удалено текущее устройство. Стандартная программа "Звукозапись" же, к примеру, спокойно переживает изменение аудиоустройства по умолчанию и даже переключает устройство на новое (т.е. продолжает запись с нового устройства). Как отслеживать подобные ситуации, чтобы прога не висла? Может, какую-то дополнительную проверку нужно делать через вызовом waveInAddBuffer?
1. IMMDeviceEnumerator::RegisterEndpointNotificationCallback 2. Покажи код. Проверил у себя - ничего не зависает.
1. Спасибо, а как это будет выглядеть на Asm (т.е. как использовать сие)? Там интерфейс же... И какие параметры подставлять. 2. Короче, когда используется CALLBACK_WINDOW, то всё ок, а виснет при CALLBACK_FUNCTION. Ты какой использовал? У меня код на Delphi, большой довольно, см. аттач (это тестовый код для изучения разного рода штук с записью).
1. Создаешь COM объект который реализует IMMNotificationClient интерфейс и передаешь его в RegisterEndpointNotificationCallback. Потом система будет вызывать методы этого объекта в зависимости от событий. Учти что методы вызываются из разных потоков, также нужно исключить дедлоки, для этого нужно следовать правилам в описании интерфейса IMMNotificationClient. 2. CALLBACK_WINDOW. Вообще в описании сказано что нельзя вызывать эту функцию в обработчике waveInProc иначе будут дедлок - как раз твой случай.
Код (C++): #include "stdafx.h" #include <CGuid.h> #include <InitGuid.h> #include <Mmdeviceapi.h> #include <Functiondiscoverykeys_devpkey.h> #include <iostream> #include <atlbase.h> #define SAFE_RELEASE(punk) \ if ((punk) != NULL) \ { (punk)->Release(); (punk) = NULL; } class CMMNotificationClient : public IMMNotificationClient { LONG _cRef; IMMDeviceEnumerator *_pEnumerator; // Private function to print device-friendly name HRESULT _PrintDeviceName(LPCWSTR pwstrId); public: CMMNotificationClient() : _cRef(1), _pEnumerator(NULL) { } ~CMMNotificationClient() { SAFE_RELEASE(_pEnumerator) } // IUnknown methods -- AddRef, Release, and QueryInterface ULONG STDMETHODCALLTYPE AddRef() { return InterlockedIncrement(&_cRef); } ULONG STDMETHODCALLTYPE Release() { ULONG ulRef = InterlockedDecrement(&_cRef); if (0 == ulRef) { delete this; } return ulRef; } HRESULT STDMETHODCALLTYPE QueryInterface( REFIID riid, VOID **ppvInterface) { if (IID_IUnknown == riid) { AddRef(); *ppvInterface = (IUnknown*)this; } else if (__uuidof(IMMNotificationClient) == riid) { AddRef(); *ppvInterface = (IMMNotificationClient*)this; } else { *ppvInterface = NULL; return E_NOINTERFACE; } return S_OK; } // Callback methods for device-event notifications. HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged( EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId) { char *pszFlow = "?????"; char *pszRole = "?????"; _PrintDeviceName(pwstrDeviceId); switch (flow) { case eRender: pszFlow = "eRender"; break; case eCapture: pszFlow = "eCapture"; break; } switch (role) { case eConsole: pszRole = "eConsole"; break; case eMultimedia: pszRole = "eMultimedia"; break; case eCommunications: pszRole = "eCommunications"; break; } printf(" -->New default device: flow = %s, role = %s\n", pszFlow, pszRole); return S_OK; } HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId) { _PrintDeviceName(pwstrDeviceId); printf(" -->Added device\n"); return S_OK; }; HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId) { _PrintDeviceName(pwstrDeviceId); printf(" -->Removed device\n"); return S_OK; } HRESULT STDMETHODCALLTYPE OnDeviceStateChanged( LPCWSTR pwstrDeviceId, DWORD dwNewState) { char *pszState = "?????"; _PrintDeviceName(pwstrDeviceId); switch (dwNewState) { case DEVICE_STATE_ACTIVE: pszState = "ACTIVE"; break; case DEVICE_STATE_DISABLED: pszState = "DISABLED"; break; case DEVICE_STATE_NOTPRESENT: pszState = "NOTPRESENT"; break; case DEVICE_STATE_UNPLUGGED: pszState = "UNPLUGGED"; break; } printf(" -->New device state is DEVICE_STATE_%s (0x%8.8x)\n", pszState, dwNewState); return S_OK; } HRESULT STDMETHODCALLTYPE OnPropertyValueChanged( LPCWSTR pwstrDeviceId, const PROPERTYKEY key) { _PrintDeviceName(pwstrDeviceId); printf(" -->Changed device property " "{%8.8x-%4.4x-%4.4x-%2.2x%2.2x-%2.2x%2.2x%2.2x%2.2x%2.2x%2.2x}#%d\n", key.fmtid.Data1, key.fmtid.Data2, key.fmtid.Data3, key.fmtid.Data4[0], key.fmtid.Data4[1], key.fmtid.Data4[2], key.fmtid.Data4[3], key.fmtid.Data4[4], key.fmtid.Data4[5], key.fmtid.Data4[6], key.fmtid.Data4[7], key.pid); return S_OK; } }; // Given an endpoint ID string, print the friendly device name. HRESULT CMMNotificationClient::_PrintDeviceName(LPCWSTR pwstrId) { HRESULT hr = S_OK; IMMDevice *pDevice = NULL; IPropertyStore *pProps = NULL; PROPVARIANT varString; CoInitialize(NULL); PropVariantInit(&varString); if (_pEnumerator == NULL) { // Get enumerator for audio endpoint devices. hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_INPROC_SERVER, __uuidof(IMMDeviceEnumerator), (void**)&_pEnumerator); } if (hr == S_OK) { hr = _pEnumerator->GetDevice(pwstrId, &pDevice); } if (hr == S_OK) { hr = pDevice->OpenPropertyStore(STGM_READ, &pProps); } if (hr == S_OK) { // Get the endpoint device's friendly-name property. hr = pProps->GetValue(PKEY_Device_FriendlyName, &varString); } printf("----------------------\nDevice name: \"%S\"\n" " Endpoint ID string: \"%S\"\n", (hr == S_OK) ? varString.pwszVal : L"null device", (pwstrId != NULL) ? pwstrId : L"null ID"); PropVariantClear(&varString); SAFE_RELEASE(pProps) SAFE_RELEASE(pDevice) CoUninitialize(); return hr; } int main() { CoInitialize(NULL); setlocale(LC_ALL, ""); { CComPtr <IMMDeviceEnumerator> pEnumerator; CComPtr <IMMNotificationClient> pClient; HRESULT hr = S_OK; CMMNotificationClient cClient; if (FAILED(hr = cClient.QueryInterface(__uuidof(IMMNotificationClient), (void**)&pClient))) { printf("QueryInterface failed %x", hr); goto Exit; } if (FAILED(hr = pEnumerator.CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL))) { printf("CoCreateInstance failed %x", hr); goto Exit; } if (FAILED(hr = pEnumerator->RegisterEndpointNotificationCallback(pClient.p))) { printf("RegisterEndpointNotificationCallback failed %x", hr); goto Exit; } getchar(); if (FAILED(hr = pEnumerator->UnregisterEndpointNotificationCallback(pClient.p))) { printf("UnregisterEndpointNotificationCallback failed %x", hr); goto Exit; } } Exit: CoUninitialize(); return 0; }