2 * Copyright 2009 Maarten Lankhorst
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19 #define NONAMELESSUNION
30 #include "wine/debug.h"
31 #include "wine/unicode.h"
34 #include "mmdeviceapi.h"
37 #include "audioclient.h"
38 #include "endpointvolume.h"
39 #include "audiopolicy.h"
44 WINE_DEFAULT_DEBUG_CHANNEL(mmdevapi);
46 static const WCHAR software_mmdevapi[] =
47 { 'S','o','f','t','w','a','r','e','\\',
48 'M','i','c','r','o','s','o','f','t','\\',
49 'W','i','n','d','o','w','s','\\',
50 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
51 'M','M','D','e','v','i','c','e','s','\\',
52 'A','u','d','i','o',0};
53 static const WCHAR reg_render[] =
54 { 'R','e','n','d','e','r',0 };
55 static const WCHAR reg_capture[] =
56 { 'C','a','p','t','u','r','e',0 };
57 static const WCHAR reg_devicestate[] =
58 { 'D','e','v','i','c','e','S','t','a','t','e',0 };
59 static const WCHAR reg_properties[] =
60 { 'P','r','o','p','e','r','t','i','e','s',0 };
62 static HKEY key_render;
63 static HKEY key_capture;
65 typedef struct MMDevEnumImpl
67 const IMMDeviceEnumeratorVtbl *lpVtbl;
71 static MMDevEnumImpl *MMDevEnumerator;
72 static MMDevice **MMDevice_head;
73 static MMDevice *MMDevice_def_rec, *MMDevice_def_play;
74 static DWORD MMDevice_count;
75 static const IMMDeviceEnumeratorVtbl MMDevEnumVtbl;
76 static const IMMDeviceCollectionVtbl MMDevColVtbl;
77 static const IMMDeviceVtbl MMDeviceVtbl;
79 typedef struct MMDevColImpl
81 const IMMDeviceCollectionVtbl *lpVtbl;
87 /* Creates or updates the state of a device
88 * If GUID is null, a random guid will be assigned
89 * and the device will be created
91 static void MMDevice_Create(WCHAR *name, GUID *id, EDataFlow flow, DWORD state, BOOL setdefault)
98 for (i = 0; i < MMDevice_count; ++i)
100 cur = MMDevice_head[i];
101 if (cur->flow == flow && !lstrcmpW(cur->alname, name))
104 /* Same device, update state */
106 StringFromGUID2(&cur->devguid, guidstr, sizeof(guidstr)/sizeof(*guidstr));
107 ret = RegOpenKeyExW(flow == eRender ? key_render : key_capture, guidstr, 0, KEY_WRITE, &key);
108 if (ret == ERROR_SUCCESS)
110 RegSetValueExW(key, reg_devicestate, 0, REG_DWORD, (const BYTE*)&state, sizeof(DWORD));
117 /* No device found, allocate new one */
118 cur = HeapAlloc(GetProcessHeap(), 0, sizeof(*cur));
121 cur->alname = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(name)+1)*sizeof(WCHAR));
124 HeapFree(GetProcessHeap(), 0, cur);
127 lstrcpyW(cur->alname, name);
128 cur->lpVtbl = &MMDeviceVtbl;
130 InitializeCriticalSection(&cur->crst);
131 cur->crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": MMDevice.crst");
140 StringFromGUID2(id, guidstr, sizeof(guidstr)/sizeof(*guidstr));
145 if (!RegCreateKeyExW(root, guidstr, 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &key, NULL))
148 RegSetValueExW(key, reg_devicestate, 0, REG_DWORD, (const BYTE*)&state, sizeof(DWORD));
149 if (!RegCreateKeyExW(key, reg_properties, 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &keyprop, NULL))
151 RegCloseKey(keyprop);
156 MMDevice_head = HeapAlloc(GetProcessHeap(), 0, sizeof(*MMDevice_head));
158 MMDevice_head = HeapReAlloc(GetProcessHeap(), 0, MMDevice_head, sizeof(*MMDevice_head)*(1+MMDevice_count));
159 MMDevice_head[MMDevice_count++] = cur;
165 MMDevice_def_play = cur;
167 MMDevice_def_rec = cur;
171 static void MMDevice_Destroy(MMDevice *This)
174 TRACE("Freeing %s\n", debugstr_w(This->alname));
175 /* Since this function is called at destruction time, reordering of the list is unimportant */
176 for (i = 0; i < MMDevice_count; ++i)
178 if (MMDevice_head[i] == This)
180 MMDevice_head[i] = MMDevice_head[--MMDevice_count];
184 This->crst.DebugInfo->Spare[0] = 0;
185 DeleteCriticalSection(&This->crst);
186 HeapFree(GetProcessHeap(), 0, This->alname);
187 HeapFree(GetProcessHeap(), 0, This);
190 static HRESULT WINAPI MMDevice_QueryInterface(IMMDevice *iface, REFIID riid, void **ppv)
192 MMDevice *This = (MMDevice *)iface;
193 TRACE("(%p)->(%s,%p)\n", iface, debugstr_guid(riid), ppv);
198 if (IsEqualIID(riid, &IID_IUnknown)
199 || IsEqualIID(riid, &IID_IMMDevice))
203 IUnknown_AddRef((IUnknown*)*ppv);
206 WARN("Unknown interface %s\n", debugstr_guid(riid));
207 return E_NOINTERFACE;
210 static ULONG WINAPI MMDevice_AddRef(IMMDevice *iface)
212 MMDevice *This = (MMDevice *)iface;
215 ref = InterlockedIncrement(&This->ref);
216 TRACE("Refcount now %i\n", ref);
220 static ULONG WINAPI MMDevice_Release(IMMDevice *iface)
222 MMDevice *This = (MMDevice *)iface;
225 ref = InterlockedDecrement(&This->ref);
226 TRACE("Refcount now %i\n", ref);
230 static HRESULT WINAPI MMDevice_Activate(IMMDevice *iface, REFIID riid, DWORD clsctx, PROPVARIANT *params, void **ppv)
232 MMDevice *This = (MMDevice *)iface;
233 HRESULT hr = E_NOINTERFACE;
234 TRACE("(%p)->(%p,%x,%p,%p)\n", This, riid, clsctx, params, ppv);
241 TRACE("Returning %08x\n", hr);
245 static HRESULT WINAPI MMDevice_OpenPropertyStore(IMMDevice *iface, DWORD access, IPropertyStore **ppv)
247 MMDevice *This = (MMDevice *)iface;
248 TRACE("(%p)->(%x,%p)\n", This, access, ppv);
256 static HRESULT WINAPI MMDevice_GetId(IMMDevice *iface, WCHAR **itemid)
258 MMDevice *This = (MMDevice *)iface;
260 TRACE("(%p)->(%p)\n", This, itemid);
268 static HRESULT WINAPI MMDevice_GetState(IMMDevice *iface, DWORD *state)
270 MMDevice *This = (MMDevice *)iface;
271 TRACE("(%p)->(%p)\n", iface, state);
275 *state = This->state;
279 static const IMMDeviceVtbl MMDeviceVtbl =
281 MMDevice_QueryInterface,
285 MMDevice_OpenPropertyStore,
290 static HRESULT MMDevCol_Create(IMMDeviceCollection **ppv, EDataFlow flow, DWORD state)
294 This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
297 return E_OUTOFMEMORY;
298 This->lpVtbl = &MMDevColVtbl;
302 *ppv = (IMMDeviceCollection*)This;
306 static void MMDevCol_Destroy(MMDevColImpl *This)
308 HeapFree(GetProcessHeap(), 0, This);
311 static HRESULT WINAPI MMDevCol_QueryInterface(IMMDeviceCollection *iface, REFIID riid, void **ppv)
313 MMDevColImpl *This = (MMDevColImpl*)iface;
317 if (IsEqualIID(riid, &IID_IUnknown)
318 || IsEqualIID(riid, &IID_IMMDeviceCollection))
323 return E_NOINTERFACE;
324 IUnknown_AddRef((IUnknown*)*ppv);
328 static ULONG WINAPI MMDevCol_AddRef(IMMDeviceCollection *iface)
330 MMDevColImpl *This = (MMDevColImpl*)iface;
331 LONG ref = InterlockedIncrement(&This->ref);
332 TRACE("Refcount now %i\n", ref);
336 static ULONG WINAPI MMDevCol_Release(IMMDeviceCollection *iface)
338 MMDevColImpl *This = (MMDevColImpl*)iface;
339 LONG ref = InterlockedDecrement(&This->ref);
340 TRACE("Refcount now %i\n", ref);
342 MMDevCol_Destroy(This);
346 static HRESULT WINAPI MMDevCol_GetCount(IMMDeviceCollection *iface, UINT *numdevs)
348 MMDevColImpl *This = (MMDevColImpl*)iface;
350 TRACE("(%p)->(%p)\n", This, numdevs);
357 static HRESULT WINAPI MMDevCol_Item(IMMDeviceCollection *iface, UINT i, IMMDevice **dev)
359 MMDevColImpl *This = (MMDevColImpl*)iface;
360 TRACE("(%p)->(%u, %p)\n", This, i, dev);
367 static const IMMDeviceCollectionVtbl MMDevColVtbl =
369 MMDevCol_QueryInterface,
376 HRESULT MMDevEnum_Create(REFIID riid, void **ppv)
378 MMDevEnumImpl *This = MMDevEnumerator;
387 This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
390 return E_OUTOFMEMORY;
392 This->lpVtbl = &MMDevEnumVtbl;
393 MMDevEnumerator = This;
395 ret = RegCreateKeyExW(HKEY_LOCAL_MACHINE, software_mmdevapi, 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &root, NULL);
396 if (ret == ERROR_SUCCESS)
397 ret = RegCreateKeyExW(root, reg_capture, 0, NULL, 0, KEY_READ|KEY_WRITE, NULL, &key_capture, NULL);
398 if (ret == ERROR_SUCCESS)
399 ret = RegCreateKeyExW(root, reg_render, 0, NULL, 0, KEY_READ|KEY_WRITE, NULL, &key_render, NULL);
403 if (ret != ERROR_SUCCESS)
405 RegCloseKey(key_capture);
406 key_render = key_capture = NULL;
407 WARN("Couldn't create key: %u\n", ret);
415 len = sizeof(guidvalue);
416 ret = RegEnumKeyExW(cur, i++, guidvalue, &len, NULL, NULL, NULL, NULL);
417 if (ret == ERROR_NO_MORE_ITEMS)
419 if (cur == key_capture)
428 if (ret != ERROR_SUCCESS)
430 if (SUCCEEDED(CLSIDFromString(guidvalue, &guid)))
431 /* Using guid as friendly name till property store works */
432 MMDevice_Create(guidvalue, &guid, curflow,
433 DEVICE_STATE_NOTPRESENT, FALSE);
436 return IUnknown_QueryInterface((IUnknown*)This, riid, ppv);
439 void MMDevEnum_Free(void)
441 while (MMDevice_count)
442 MMDevice_Destroy(MMDevice_head[0]);
443 RegCloseKey(key_render);
444 RegCloseKey(key_capture);
445 key_render = key_capture = NULL;
446 HeapFree(GetProcessHeap(), 0, MMDevEnumerator);
447 MMDevEnumerator = NULL;
450 static HRESULT WINAPI MMDevEnum_QueryInterface(IMMDeviceEnumerator *iface, REFIID riid, void **ppv)
452 MMDevEnumImpl *This = (MMDevEnumImpl*)iface;
456 if (IsEqualIID(riid, &IID_IUnknown)
457 || IsEqualIID(riid, &IID_IMMDeviceEnumerator))
462 return E_NOINTERFACE;
463 IUnknown_AddRef((IUnknown*)*ppv);
467 static ULONG WINAPI MMDevEnum_AddRef(IMMDeviceEnumerator *iface)
469 MMDevEnumImpl *This = (MMDevEnumImpl*)iface;
470 LONG ref = InterlockedIncrement(&This->ref);
471 TRACE("Refcount now %i\n", ref);
475 static ULONG WINAPI MMDevEnum_Release(IMMDeviceEnumerator *iface)
477 MMDevEnumImpl *This = (MMDevEnumImpl*)iface;
478 LONG ref = InterlockedDecrement(&This->ref);
481 TRACE("Refcount now %i\n", ref);
485 static HRESULT WINAPI MMDevEnum_EnumAudioEndpoints(IMMDeviceEnumerator *iface, EDataFlow flow, DWORD mask, IMMDeviceCollection **devices)
487 MMDevEnumImpl *This = (MMDevEnumImpl*)iface;
488 TRACE("(%p)->(%u,%u,%p)\n", This, flow, mask, devices);
492 if (flow >= EDataFlow_enum_count)
494 if (mask & ~DEVICE_STATEMASK_ALL)
496 return MMDevCol_Create(devices, flow, mask);
499 static HRESULT WINAPI MMDevEnum_GetDefaultAudioEndpoint(IMMDeviceEnumerator *iface, EDataFlow flow, ERole role, IMMDevice **device)
501 MMDevEnumImpl *This = (MMDevEnumImpl*)iface;
502 TRACE("(%p)->(%u,%u,%p)\n", This, flow, role, device);
509 *device = (IMMDevice*)MMDevice_def_play;
510 else if (flow == eCapture)
511 *device = (IMMDevice*)MMDevice_def_rec;
514 WARN("Unknown flow %u\n", flow);
520 IMMDevice_AddRef(*device);
524 static HRESULT WINAPI MMDevEnum_GetDevice(IMMDeviceEnumerator *iface, const WCHAR *name, IMMDevice **device)
526 MMDevEnumImpl *This = (MMDevEnumImpl*)iface;
527 TRACE("(%p)->(%s,%p)\n", This, debugstr_w(name), device);
532 static HRESULT WINAPI MMDevEnum_RegisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
534 MMDevEnumImpl *This = (MMDevEnumImpl*)iface;
535 TRACE("(%p)->(%p)\n", This, client);
540 static HRESULT WINAPI MMDevEnum_UnregisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
542 MMDevEnumImpl *This = (MMDevEnumImpl*)iface;
543 TRACE("(%p)->(%p)\n", This, client);
548 static const IMMDeviceEnumeratorVtbl MMDevEnumVtbl =
550 MMDevEnum_QueryInterface,
553 MMDevEnum_EnumAudioEndpoints,
554 MMDevEnum_GetDefaultAudioEndpoint,
556 MMDevEnum_RegisterEndpointNotificationCallback,
557 MMDevEnum_UnregisterEndpointNotificationCallback