Signed-off-by: Andrew Eikum aeikum@codeweavers.com --- dlls/mmdevapi/main.c | 165 +++++++++++++++++++++++++++ dlls/mmdevapi/mmdevapi.spec | 2 + dlls/mmdevapi/tests/mmdevenum.c | 190 ++++++++++++++++++++++++++++++++ include/mmdeviceapi.idl | 36 ++++++ 4 files changed, 393 insertions(+)
diff --git a/dlls/mmdevapi/main.c b/dlls/mmdevapi/main.c index 7680b451381..8e9127a7507 100644 --- a/dlls/mmdevapi/main.c +++ b/dlls/mmdevapi/main.c @@ -319,3 +319,168 @@ HRESULT WINAPI DllUnregisterServer(void) { return __wine_unregister_resources( instance ); } + +struct activate_async_op { + IActivateAudioInterfaceAsyncOperation IActivateAudioInterfaceAsyncOperation_iface; + LONG ref; + + IActivateAudioInterfaceCompletionHandler *callback; + HRESULT result_hr; + IUnknown *result_iface; +}; + +static struct activate_async_op *impl_from_IActivateAudioInterfaceAsyncOperation(IActivateAudioInterfaceAsyncOperation *iface) +{ + return CONTAINING_RECORD(iface, struct activate_async_op, IActivateAudioInterfaceAsyncOperation_iface); +} + +static HRESULT WINAPI activate_async_op_QueryInterface(IActivateAudioInterfaceAsyncOperation *iface, + REFIID riid, void **ppv) +{ + struct activate_async_op *This = impl_from_IActivateAudioInterfaceAsyncOperation(iface); + + TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv); + + if (!ppv) + return E_POINTER; + + if (IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_IActivateAudioInterfaceAsyncOperation)) { + *ppv = &This->IActivateAudioInterfaceAsyncOperation_iface; + } else { + *ppv = NULL; + return E_NOINTERFACE; + } + + IUnknown_AddRef((IUnknown*)*ppv); + return S_OK; +} + +static ULONG WINAPI activate_async_op_AddRef(IActivateAudioInterfaceAsyncOperation *iface) +{ + struct activate_async_op *This = impl_from_IActivateAudioInterfaceAsyncOperation(iface); + LONG ref = InterlockedIncrement(&This->ref); + TRACE("(%p) refcount now %i\n", This, ref); + return ref; +} + +static ULONG WINAPI activate_async_op_Release(IActivateAudioInterfaceAsyncOperation *iface) +{ + struct activate_async_op *This = impl_from_IActivateAudioInterfaceAsyncOperation(iface); + LONG ref = InterlockedDecrement(&This->ref); + TRACE("(%p) refcount now %i\n", This, ref); + if (!ref) { + if(This->result_iface) + IUnknown_Release(This->result_iface); + IActivateAudioInterfaceCompletionHandler_Release(This->callback); + HeapFree(GetProcessHeap(), 0, This); + } + return ref; +} + +static HRESULT WINAPI activate_async_op_GetActivateResult(IActivateAudioInterfaceAsyncOperation *iface, + HRESULT *result_hr, IUnknown **result_iface) +{ + struct activate_async_op *This = impl_from_IActivateAudioInterfaceAsyncOperation(iface); + + TRACE("(%p)->(%p, %p)\n", This, result_hr, result_iface); + + *result_hr = This->result_hr; + + if(This->result_hr == S_OK){ + *result_iface = This->result_iface; + IUnknown_AddRef(*result_iface); + } + + return S_OK; +} + +static IActivateAudioInterfaceAsyncOperationVtbl IActivateAudioInterfaceAsyncOperation_vtbl = { + activate_async_op_QueryInterface, + activate_async_op_AddRef, + activate_async_op_Release, + activate_async_op_GetActivateResult, +}; + +static DWORD WINAPI activate_async_threadproc(void *user) +{ + struct activate_async_op *op = user; + + IActivateAudioInterfaceCompletionHandler_ActivateCompleted(op->callback, &op->IActivateAudioInterfaceAsyncOperation_iface); + + IActivateAudioInterfaceAsyncOperation_Release(&op->IActivateAudioInterfaceAsyncOperation_iface); + + return 0; +} + +static HRESULT get_mmdevice_by_activatepath(const WCHAR *path, IMMDevice **mmdev) +{ + IMMDeviceEnumerator *devenum; + HRESULT hr; + + static const WCHAR DEVINTERFACE_AUDIO_RENDER_WSTR[] = L"{E6327CAD-DCEC-4949-AE8A-991E976A79D2}"; + static const WCHAR DEVINTERFACE_AUDIO_CAPTURE_WSTR[] = L"{2EEF81BE-33FA-4800-9670-1CD474972C3F}"; + + hr = MMDevEnum_Create(&IID_IMMDeviceEnumerator, (void**)&devenum); + if (FAILED(hr)) { + WARN("Failed to create MMDeviceEnumerator: %08x\n", hr); + return hr; + } + + if (!lstrcmpiW(path, DEVINTERFACE_AUDIO_RENDER_WSTR)){ + hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, eRender, eMultimedia, mmdev); + } else if (!lstrcmpiW(path, DEVINTERFACE_AUDIO_CAPTURE_WSTR)){ + hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint(devenum, eCapture, eMultimedia, mmdev); + } else { + FIXME("How to map path to device id? %s\n", debugstr_w(path)); + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + } + + if (FAILED(hr)) { + WARN("Failed to get requested device (%s): %08x\n", debugstr_w(path), hr); + *mmdev = NULL; + hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + } + + IMMDeviceEnumerator_Release(devenum); + + return hr; +} + +/*********************************************************************** + * ActivateAudioInterfaceAsync (MMDEVAPI.17) + */ +HRESULT WINAPI ActivateAudioInterfaceAsync(const WCHAR *path, REFIID riid, + PROPVARIANT *params, IActivateAudioInterfaceCompletionHandler *done_handler, + IActivateAudioInterfaceAsyncOperation **op_out) +{ + struct activate_async_op *op; + HANDLE ht; + IMMDevice *mmdev; + + TRACE("(%s, %s, %p, %p, %p)\n", debugstr_w(path), debugstr_guid(riid), + params, done_handler, op_out); + + op = HeapAlloc(GetProcessHeap(), 0, sizeof(*op)); + if (!op) + return E_OUTOFMEMORY; + + op->ref = 2; /* returned ref and threadproc ref */ + op->IActivateAudioInterfaceAsyncOperation_iface.lpVtbl = &IActivateAudioInterfaceAsyncOperation_vtbl; + op->callback = done_handler; + IActivateAudioInterfaceCompletionHandler_AddRef(done_handler); + + op->result_hr = get_mmdevice_by_activatepath(path, &mmdev); + if (SUCCEEDED(op->result_hr)) { + op->result_hr = IMMDevice_Activate(mmdev, riid, CLSCTX_INPROC_SERVER, params, (void**)&op->result_iface); + IMMDevice_Release(mmdev); + }else + op->result_iface = NULL; + + ht = CreateThread(NULL, 0, &activate_async_threadproc, op, 0, NULL); + CloseHandle(ht); + + *op_out = &op->IActivateAudioInterfaceAsyncOperation_iface; + + return S_OK; +} diff --git a/dlls/mmdevapi/mmdevapi.spec b/dlls/mmdevapi/mmdevapi.spec index 88aaf7c30c9..90ecaf569bc 100644 --- a/dlls/mmdevapi/mmdevapi.spec +++ b/dlls/mmdevapi/mmdevapi.spec @@ -12,6 +12,8 @@ 13 stub @ 14 stub @ 15 stub @ +16 stub @ +17 stdcall ActivateAudioInterfaceAsync( wstr ptr ptr ptr ptr )
@ stdcall -private DllCanUnloadNow() @ stdcall -private DllGetClassObject( ptr ptr ptr ) diff --git a/dlls/mmdevapi/tests/mmdevenum.c b/dlls/mmdevapi/tests/mmdevenum.c index 4f8c848380d..11df1f0229b 100644 --- a/dlls/mmdevapi/tests/mmdevenum.c +++ b/dlls/mmdevapi/tests/mmdevenum.c @@ -31,6 +31,9 @@
DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0);
+static UINT g_num_mmdevs; +static WCHAR g_device_path[MAX_PATH]; + /* Some of the QueryInterface tests are really just to check if I got the IIDs right :) */
/* IMMDeviceCollection appears to have no QueryInterface method and instead forwards to mme */ @@ -85,6 +88,8 @@ static void test_collection(IMMDeviceEnumerator *mme, IMMDeviceCollection *col) ok(hr == E_INVALIDARG, "Asking for too high device returned 0x%08x\n", hr); ok(dev == NULL, "Returned non-null device\n");
+ g_num_mmdevs = numdev; + if (numdev) { hr = IMMDeviceCollection_Item(col, 0, NULL); @@ -101,6 +106,7 @@ static void test_collection(IMMDeviceEnumerator *mme, IMMDeviceCollection *col) { IMMDevice *dev2;
+ lstrcpyW(g_device_path, id); temp[sizeof(temp)-1] = 0; WideCharToMultiByte(CP_ACP, 0, id, -1, temp, sizeof(temp)-1, NULL, NULL); trace("Device found: %s\n", temp); @@ -119,6 +125,188 @@ static void test_collection(IMMDeviceEnumerator *mme, IMMDeviceCollection *col) IMMDeviceCollection_Release(col); }
+static struct { + LONG ref; + HANDLE evt; + CRITICAL_SECTION lock; + IActivateAudioInterfaceAsyncOperation *op; + DWORD main_tid; + char msg_pfx[128]; + IUnknown *result_iface; + HRESULT result_hr; +} async_activate_test; + +static HRESULT WINAPI async_activate_QueryInterface( + IActivateAudioInterfaceCompletionHandler *iface, + REFIID riid, + void **ppvObject) +{ + if(IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_IAgileObject) || + IsEqualIID(riid, &IID_IActivateAudioInterfaceCompletionHandler)){ + *ppvObject = iface; + IUnknown_AddRef((IUnknown*)*ppvObject); + return S_OK; + } + + *ppvObject = NULL; + return E_NOINTERFACE; +} + +static ULONG WINAPI async_activate_AddRef( + IActivateAudioInterfaceCompletionHandler *iface) +{ + return InterlockedIncrement(&async_activate_test.ref); +} + +static ULONG WINAPI async_activate_Release( + IActivateAudioInterfaceCompletionHandler *iface) +{ + ULONG ref = InterlockedDecrement(&async_activate_test.ref); + if(ref == 1) + SetEvent(async_activate_test.evt); + return ref; +} + +static HRESULT WINAPI async_activate_ActivateCompleted( + IActivateAudioInterfaceCompletionHandler *iface, + IActivateAudioInterfaceAsyncOperation *op) +{ + HRESULT hr; + + EnterCriticalSection(&async_activate_test.lock); + ok(op == async_activate_test.op, + "%s: Got different completion operation\n", + async_activate_test.msg_pfx); + LeaveCriticalSection(&async_activate_test.lock); + + ok(GetCurrentThreadId() != async_activate_test.main_tid, + "%s: Expected callback on worker thread\n", + async_activate_test.msg_pfx); + + hr = IActivateAudioInterfaceAsyncOperation_GetActivateResult(op, + &async_activate_test.result_hr, &async_activate_test.result_iface); + ok(hr == S_OK, + "%s: GetActivateResult failed: %08x\n", + async_activate_test.msg_pfx, hr); + + return S_OK; +} + +static IActivateAudioInterfaceCompletionHandlerVtbl async_activate_vtbl = { + async_activate_QueryInterface, + async_activate_AddRef, + async_activate_Release, + async_activate_ActivateCompleted, +}; + +static IActivateAudioInterfaceCompletionHandler async_activate_done = { + &async_activate_vtbl +}; + +static void test_ActivateAudioInterfaceAsync(void) +{ + HRESULT (* WINAPI pActivateAudioInterfaceAsync)(const WCHAR *path, + REFIID riid, PROPVARIANT *params, + IActivateAudioInterfaceCompletionHandler *done_handler, + IActivateAudioInterfaceAsyncOperation **op); + HANDLE h_mmdev; + HRESULT hr; + LPOLESTR path; + DWORD dr; + IAudioClient3 *ac3; + + h_mmdev = LoadLibraryA("mmdevapi.dll"); + + /* some applications look this up by ordinal */ + pActivateAudioInterfaceAsync = (void*)GetProcAddress(h_mmdev, (char *)17); + ok(pActivateAudioInterfaceAsync != NULL, "mmdevapi.ActivateAudioInterfaceAsync missing!\n"); + + async_activate_test.ref = 1; + async_activate_test.evt = CreateEventW(NULL, FALSE, FALSE, NULL); + InitializeCriticalSection(&async_activate_test.lock); + async_activate_test.op = NULL; + async_activate_test.main_tid = GetCurrentThreadId(); + async_activate_test.result_iface = NULL; + async_activate_test.result_hr = 0; + + + /* try invalid device path */ + strcpy(async_activate_test.msg_pfx, "invalid_path"); + + EnterCriticalSection(&async_activate_test.lock); + hr = pActivateAudioInterfaceAsync(L"winetest_bogus", &IID_IAudioClient3, NULL, &async_activate_done, &async_activate_test.op); + ok(hr == S_OK, "ActivateAudioInterfaceAsync failed: %08x\n", hr); + LeaveCriticalSection(&async_activate_test.lock); + + IActivateAudioInterfaceAsyncOperation_Release(async_activate_test.op); + + dr = WaitForSingleObject(async_activate_test.evt, 1000); /* wait for all refs other than our own to be released */ + ok(dr == WAIT_OBJECT_0, "Timed out waiting for async activate to complete\n"); + ok(async_activate_test.ref == 1, "ActivateAudioInterfaceAsync leaked a handler ref: %u\n", async_activate_test.ref); + ok(async_activate_test.result_hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), + "mmdevice activation gave wrong result: %08x\n", async_activate_test.result_hr); + ok(async_activate_test.result_iface == NULL, "Got non-NULL iface pointer: %p\n", async_activate_test.result_iface); + + + /* device id from IMMDevice does not work */ + if(g_num_mmdevs > 0){ + strcpy(async_activate_test.msg_pfx, "mmdevice_id"); + + EnterCriticalSection(&async_activate_test.lock); + hr = pActivateAudioInterfaceAsync(g_device_path, &IID_IAudioClient3, NULL, &async_activate_done, &async_activate_test.op); + ok(hr == S_OK, "ActivateAudioInterfaceAsync failed: %08x\n", hr); + LeaveCriticalSection(&async_activate_test.lock); + + IActivateAudioInterfaceAsyncOperation_Release(async_activate_test.op); + + dr = WaitForSingleObject(async_activate_test.evt, 1000); + ok(dr == WAIT_OBJECT_0, "Timed out waiting for async activate to complete\n"); + ok(async_activate_test.ref == 1, "ActivateAudioInterfaceAsync leaked a handler ref: %u\n", async_activate_test.ref); + ok(async_activate_test.result_hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), + "mmdevice activation gave wrong result: %08x\n", async_activate_test.result_hr); + ok(async_activate_test.result_iface == NULL, "Got non-NULL iface pointer: %p\n", async_activate_test.result_iface); + } + + + /* try DEVINTERFACE_AUDIO_RENDER */ + strcpy(async_activate_test.msg_pfx, "audio_render"); + StringFromIID(&DEVINTERFACE_AUDIO_RENDER, &path); + + EnterCriticalSection(&async_activate_test.lock); + hr = pActivateAudioInterfaceAsync(path, &IID_IAudioClient3, NULL, &async_activate_done, &async_activate_test.op); + ok(hr == S_OK, "ActivateAudioInterfaceAsync failed: %08x\n", hr); + LeaveCriticalSection(&async_activate_test.lock); + + IActivateAudioInterfaceAsyncOperation_Release(async_activate_test.op); + + dr = WaitForSingleObject(async_activate_test.evt, 1000); + ok(dr == WAIT_OBJECT_0, "Timed out waiting for async activate to complete\n"); + ok(async_activate_test.ref == 1, "ActivateAudioInterfaceAsync leaked a handler ref\n"); + ok(async_activate_test.result_hr == S_OK || + (g_num_mmdevs == 0 && async_activate_test.result_hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) || /* no devices */ + broken(async_activate_test.result_hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)), /* win8 doesn't support DEVINTERFACE_AUDIO_RENDER */ + "mmdevice activation gave wrong result: %08x\n", async_activate_test.result_hr); + + if(async_activate_test.result_hr == S_OK){ + ok(async_activate_test.result_iface != NULL, "Got NULL iface pointer on success?\n"); + + /* returned iface should be the IID we requested */ + hr = IUnknown_QueryInterface(async_activate_test.result_iface, &IID_IAudioClient3, (void**)&ac3); + ok(hr == S_OK, "Failed to query IAudioClient3: %08x\n", hr); + ok(async_activate_test.result_iface == (IUnknown*)ac3, + "Activated interface other than IAudioClient3!\n"); + IAudioClient3_Release(ac3); + + IUnknown_Release(async_activate_test.result_iface); + } + + CoTaskMemFree(path); + + CloseHandle(async_activate_test.evt); + DeleteCriticalSection(&async_activate_test.lock); +} + static HRESULT WINAPI notif_QueryInterface(IMMNotificationClient *iface, const GUID *riid, void **obj) { @@ -285,4 +473,6 @@ START_TEST(mmdevenum) ok(hr == E_NOTFOUND, "UnregisterEndpointNotificationCallback failed: %08x\n", hr);
IMMDeviceEnumerator_Release(mme); + + test_ActivateAudioInterfaceAsync(); } diff --git a/include/mmdeviceapi.idl b/include/mmdeviceapi.idl index d1fb6ae74ff..1c2bb18c33c 100644 --- a/include/mmdeviceapi.idl +++ b/include/mmdeviceapi.idl @@ -26,6 +26,8 @@ cpp_quote("#ifndef E_UNSUPPORTED_TYPE") cpp_quote("#define E_UNSUPPORTED_TYPE HRESULT_FROM_WIN32(ERROR_UNSUPPORTED_TYPE)") cpp_quote("#endif")
+cpp_quote("DEFINE_GUID(DEVINTERFACE_AUDIO_RENDER, 0xe6327cad,0xdcec,0x4949,0xae,0x8a,0x99,0x1e,0x97,0x6a,0x79,0xd2);") +cpp_quote("DEFINE_GUID(DEVINTERFACE_AUDIO_CAPTURE, 0x2eef81be,0x33fa,0x4800,0x96,0x70,0x1c,0xd4,0x74,0x97,0x2c,0x3f);")
cpp_quote("#define DEVICE_STATE_ACTIVE 0x1") cpp_quote("#define DEVICE_STATE_DISABLED 0x2") @@ -237,6 +239,40 @@ typedef struct _AudioExtensionParams IMMDevice *pPnpDevnode; } AudioExtensionParams;
+[ + object, + local, + uuid(72a22d78-cde4-431d-b8cc-843a71199b6d), + nonextensible, + pointer_default(unique) +] +interface IActivateAudioInterfaceAsyncOperation : IUnknown +{ + HRESULT GetActivateResult( + [out] HRESULT *result, + [out] IUnknown **iface + ); +} + +[ + object, + local, + uuid(41d949ab-9862-444a-80f6-c261334da5eb), + nonextensible, + pointer_default(unique) +] +interface IActivateAudioInterfaceCompletionHandler : IUnknown +{ + HRESULT ActivateCompleted( + [in] IActivateAudioInterfaceAsyncOperation *op + ); +} + +cpp_quote("HRESULT WINAPI ActivateAudioInterfaceAsync(") +cpp_quote(" const WCHAR *path, REFIID riid, PROPVARIANT *params,") +cpp_quote(" IActivateAudioInterfaceCompletionHandler *done_handler,") +cpp_quote(" IActivateAudioInterfaceAsyncOperation **op);") + [ uuid(2fdaafa3-7523-4f66-9957-9d5e7fe698f6), version(1.0)