[PATCH] mmdevapi: Implement ActivateAudioInterfaceAsync

Andrew Eikum aeikum at codeweavers.com
Tue Oct 6 07:23:26 CDT 2020


Signed-off-by: Andrew Eikum <aeikum at 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)
-- 
2.28.0




More information about the wine-devel mailing list