mmdevapi: Implement ActivateAudioInterfaceAsync.

Signed-off-by: Andrew Eikum <aeikum@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Andrew Eikum 2020-10-06 07:23:26 -05:00 committed by Alexandre Julliard
parent d58919968d
commit 2b69540e74
4 changed files with 393 additions and 0 deletions

View File

@ -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;
}

View File

@ -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 )

View File

@ -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();
}

View File

@ -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)