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