xref: /reactos/dll/win32/mmdevapi/devenum.c (revision 84ccccab)
1 /*
2  * Copyright 2009 Maarten Lankhorst
3  *
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.
8  *
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.
13  *
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
17  */
18 
19 #include "mmdevapi.h"
20 
21 #include <wine/list.h>
22 
23 #include <oleauto.h>
24 #include <initguid.h>
25 #define _WINDOWS_H
26 #include <dshow.h>
27 #include <devpkey.h>
28 
29 static const WCHAR software_mmdevapi[] =
30     { 'S','o','f','t','w','a','r','e','\\',
31       'M','i','c','r','o','s','o','f','t','\\',
32       'W','i','n','d','o','w','s','\\',
33       'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
34       'M','M','D','e','v','i','c','e','s','\\',
35       'A','u','d','i','o',0};
36 static const WCHAR reg_render[] =
37     { 'R','e','n','d','e','r',0 };
38 static const WCHAR reg_capture[] =
39     { 'C','a','p','t','u','r','e',0 };
40 static const WCHAR reg_devicestate[] =
41     { 'D','e','v','i','c','e','S','t','a','t','e',0 };
42 static const WCHAR reg_properties[] =
43     { 'P','r','o','p','e','r','t','i','e','s',0 };
44 static const WCHAR slashW[] = {'\\',0};
45 static const WCHAR reg_out_nameW[] = {'D','e','f','a','u','l','t','O','u','t','p','u','t',0};
46 static const WCHAR reg_vout_nameW[] = {'D','e','f','a','u','l','t','V','o','i','c','e','O','u','t','p','u','t',0};
47 static const WCHAR reg_in_nameW[] = {'D','e','f','a','u','l','t','I','n','p','u','t',0};
48 static const WCHAR reg_vin_nameW[] = {'D','e','f','a','u','l','t','V','o','i','c','e','I','n','p','u','t',0};
49 
50 static HKEY key_render;
51 static HKEY key_capture;
52 
53 typedef struct MMDevPropStoreImpl
54 {
55     IPropertyStore IPropertyStore_iface;
56     LONG ref;
57     MMDevice *parent;
58     DWORD access;
59 } MMDevPropStore;
60 
61 typedef struct MMDevEnumImpl
62 {
63     IMMDeviceEnumerator IMMDeviceEnumerator_iface;
64     LONG ref;
65 } MMDevEnumImpl;
66 
67 static MMDevEnumImpl *MMDevEnumerator;
68 static MMDevice **MMDevice_head;
69 static MMDevice *MMDevice_def_rec, *MMDevice_def_play;
70 static DWORD MMDevice_count;
71 static const IMMDeviceEnumeratorVtbl MMDevEnumVtbl;
72 static const IMMDeviceCollectionVtbl MMDevColVtbl;
73 static const IMMDeviceVtbl MMDeviceVtbl;
74 static const IPropertyStoreVtbl MMDevPropVtbl;
75 static const IMMEndpointVtbl MMEndpointVtbl;
76 
77 static IMMDevice info_device;
78 
79 typedef struct MMDevColImpl
80 {
81     IMMDeviceCollection IMMDeviceCollection_iface;
82     LONG ref;
83     EDataFlow flow;
84     DWORD state;
85 } MMDevColImpl;
86 
87 typedef struct IPropertyBagImpl {
88     IPropertyBag IPropertyBag_iface;
89     GUID devguid;
90 } IPropertyBagImpl;
91 
92 static const IPropertyBagVtbl PB_Vtbl;
93 
94 static HRESULT MMDevPropStore_Create(MMDevice *This, DWORD access, IPropertyStore **ppv);
95 
96 static inline MMDevPropStore *impl_from_IPropertyStore(IPropertyStore *iface)
97 {
98     return CONTAINING_RECORD(iface, MMDevPropStore, IPropertyStore_iface);
99 }
100 
101 static inline MMDevEnumImpl *impl_from_IMMDeviceEnumerator(IMMDeviceEnumerator *iface)
102 {
103     return CONTAINING_RECORD(iface, MMDevEnumImpl, IMMDeviceEnumerator_iface);
104 }
105 
106 static inline MMDevColImpl *impl_from_IMMDeviceCollection(IMMDeviceCollection *iface)
107 {
108     return CONTAINING_RECORD(iface, MMDevColImpl, IMMDeviceCollection_iface);
109 }
110 
111 static inline IPropertyBagImpl *impl_from_IPropertyBag(IPropertyBag *iface)
112 {
113     return CONTAINING_RECORD(iface, IPropertyBagImpl, IPropertyBag_iface);
114 }
115 
116 static const WCHAR propkey_formatW[] = {
117     '{','%','0','8','X','-','%','0','4','X','-',
118     '%','0','4','X','-','%','0','2','X','%','0','2','X','-',
119     '%','0','2','X','%','0','2','X','%','0','2','X','%','0','2','X',
120     '%','0','2','X','%','0','2','X','}',',','%','d',0 };
121 
122 static HRESULT MMDevPropStore_OpenPropKey(const GUID *guid, DWORD flow, HKEY *propkey)
123 {
124     WCHAR buffer[39];
125     LONG ret;
126     HKEY key;
127     StringFromGUID2(guid, buffer, 39);
128     if ((ret = RegOpenKeyExW(flow == eRender ? key_render : key_capture, buffer, 0, KEY_READ|KEY_WRITE|KEY_WOW64_64KEY, &key)) != ERROR_SUCCESS)
129     {
130         WARN("Opening key %s failed with %u\n", debugstr_w(buffer), ret);
131         return E_FAIL;
132     }
133     ret = RegOpenKeyExW(key, reg_properties, 0, KEY_READ|KEY_WRITE|KEY_WOW64_64KEY, propkey);
134     RegCloseKey(key);
135     if (ret != ERROR_SUCCESS)
136     {
137         WARN("Opening key %s failed with %u\n", debugstr_w(reg_properties), ret);
138         return E_FAIL;
139     }
140     return S_OK;
141 }
142 
143 static HRESULT MMDevice_GetPropValue(const GUID *devguid, DWORD flow, REFPROPERTYKEY key, PROPVARIANT *pv)
144 {
145     WCHAR buffer[80];
146     const GUID *id = &key->fmtid;
147     DWORD type, size;
148     HRESULT hr = S_OK;
149     HKEY regkey;
150     LONG ret;
151 
152     hr = MMDevPropStore_OpenPropKey(devguid, flow, &regkey);
153     if (FAILED(hr))
154         return hr;
155     wsprintfW( buffer, propkey_formatW, id->Data1, id->Data2, id->Data3,
156                id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
157                id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7], key->pid );
158     ret = RegGetValueW(regkey, NULL, buffer, RRF_RT_ANY, &type, NULL, &size);
159     if (ret != ERROR_SUCCESS)
160     {
161         WARN("Reading %s returned %d\n", debugstr_w(buffer), ret);
162         RegCloseKey(regkey);
163         PropVariantClear(pv);
164         return S_OK;
165     }
166 
167     switch (type)
168     {
169         case REG_SZ:
170         {
171             pv->vt = VT_LPWSTR;
172             pv->u.pwszVal = CoTaskMemAlloc(size);
173             if (!pv->u.pwszVal)
174                 hr = E_OUTOFMEMORY;
175             else
176                 RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_SZ, NULL, (BYTE*)pv->u.pwszVal, &size);
177             break;
178         }
179         case REG_DWORD:
180         {
181             pv->vt = VT_UI4;
182             RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_DWORD, NULL, (BYTE*)&pv->u.ulVal, &size);
183             break;
184         }
185         case REG_BINARY:
186         {
187             pv->vt = VT_BLOB;
188             pv->u.blob.cbSize = size;
189             pv->u.blob.pBlobData = CoTaskMemAlloc(size);
190             if (!pv->u.blob.pBlobData)
191                 hr = E_OUTOFMEMORY;
192             else
193                 RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_BINARY, NULL, (BYTE*)pv->u.blob.pBlobData, &size);
194             break;
195         }
196         default:
197             ERR("Unknown/unhandled type: %u\n", type);
198             PropVariantClear(pv);
199             break;
200     }
201     RegCloseKey(regkey);
202     return hr;
203 }
204 
205 static HRESULT MMDevice_SetPropValue(const GUID *devguid, DWORD flow, REFPROPERTYKEY key, REFPROPVARIANT pv)
206 {
207     WCHAR buffer[80];
208     const GUID *id = &key->fmtid;
209     HRESULT hr;
210     HKEY regkey;
211     LONG ret;
212 
213     hr = MMDevPropStore_OpenPropKey(devguid, flow, &regkey);
214     if (FAILED(hr))
215         return hr;
216     wsprintfW( buffer, propkey_formatW, id->Data1, id->Data2, id->Data3,
217                id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
218                id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7], key->pid );
219     switch (pv->vt)
220     {
221         case VT_UI4:
222         {
223             ret = RegSetValueExW(regkey, buffer, 0, REG_DWORD, (const BYTE*)&pv->u.ulVal, sizeof(DWORD));
224             break;
225         }
226         case VT_BLOB:
227         {
228             ret = RegSetValueExW(regkey, buffer, 0, REG_BINARY, pv->u.blob.pBlobData, pv->u.blob.cbSize);
229             TRACE("Blob %p %u\n", pv->u.blob.pBlobData, pv->u.blob.cbSize);
230 
231             break;
232         }
233         case VT_LPWSTR:
234         {
235             ret = RegSetValueExW(regkey, buffer, 0, REG_SZ, (const BYTE*)pv->u.pwszVal, sizeof(WCHAR)*(1+lstrlenW(pv->u.pwszVal)));
236             break;
237         }
238         default:
239             ret = 0;
240             FIXME("Unhandled type %u\n", pv->vt);
241             hr = E_INVALIDARG;
242             break;
243     }
244     RegCloseKey(regkey);
245     TRACE("Writing %s returned %u\n", debugstr_w(buffer), ret);
246     return hr;
247 }
248 
249 static HRESULT set_driver_prop_value(GUID *id, const EDataFlow flow, const PROPERTYKEY *prop)
250 {
251     HRESULT hr;
252     PROPVARIANT pv;
253 
254     if (!drvs.pGetPropValue)
255         return E_NOTIMPL;
256 
257     hr = drvs.pGetPropValue(id, prop, &pv);
258 
259     if (SUCCEEDED(hr))
260     {
261         MMDevice_SetPropValue(id, flow, prop, &pv);
262         PropVariantClear(&pv);
263     }
264 
265     return hr;
266 }
267 
268 /* Creates or updates the state of a device
269  * If GUID is null, a random guid will be assigned
270  * and the device will be created
271  */
272 static MMDevice *MMDevice_Create(WCHAR *name, GUID *id, EDataFlow flow, DWORD state, BOOL setdefault)
273 {
274     HKEY key, root;
275     MMDevice *cur = NULL;
276     WCHAR guidstr[39];
277     DWORD i;
278 
279     static const PROPERTYKEY deviceinterface_key = {
280         {0x233164c8, 0x1b2c, 0x4c7d, {0xbc, 0x68, 0xb6, 0x71, 0x68, 0x7a, 0x25, 0x67}}, 1
281     };
282 
283     static const PROPERTYKEY devicepath_key = {
284         {0xb3f8fa53, 0x0004, 0x438e, {0x90, 0x03, 0x51, 0xa4, 0x6e, 0x13, 0x9b, 0xfc}}, 2
285     };
286 
287     for (i = 0; i < MMDevice_count; ++i)
288     {
289         MMDevice *device = MMDevice_head[i];
290         if (device->flow == flow && IsEqualGUID(&device->devguid, id)){
291             cur = device;
292             break;
293         }
294     }
295 
296     if(!cur){
297         /* No device found, allocate new one */
298         cur = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*cur));
299         if (!cur)
300             return NULL;
301 
302         cur->IMMDevice_iface.lpVtbl = &MMDeviceVtbl;
303         cur->IMMEndpoint_iface.lpVtbl = &MMEndpointVtbl;
304 
305         InitializeCriticalSection(&cur->crst);
306         cur->crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": MMDevice.crst");
307 
308         if (!MMDevice_head)
309             MMDevice_head = HeapAlloc(GetProcessHeap(), 0, sizeof(*MMDevice_head));
310         else
311             MMDevice_head = HeapReAlloc(GetProcessHeap(), 0, MMDevice_head, sizeof(*MMDevice_head)*(1+MMDevice_count));
312         MMDevice_head[MMDevice_count++] = cur;
313     }else if(cur->ref > 0)
314         WARN("Modifying an MMDevice with postitive reference count!\n");
315 
316     HeapFree(GetProcessHeap(), 0, cur->drv_id);
317     cur->drv_id = name;
318 
319     cur->flow = flow;
320     cur->state = state;
321     cur->devguid = *id;
322 
323     StringFromGUID2(&cur->devguid, guidstr, sizeof(guidstr)/sizeof(*guidstr));
324 
325     if (flow == eRender)
326         root = key_render;
327     else
328         root = key_capture;
329 
330     if (RegCreateKeyExW(root, guidstr, 0, NULL, 0, KEY_WRITE|KEY_READ|KEY_WOW64_64KEY, NULL, &key, NULL) == ERROR_SUCCESS)
331     {
332         HKEY keyprop;
333         RegSetValueExW(key, reg_devicestate, 0, REG_DWORD, (const BYTE*)&state, sizeof(DWORD));
334         if (!RegCreateKeyExW(key, reg_properties, 0, NULL, 0, KEY_WRITE|KEY_READ|KEY_WOW64_64KEY, NULL, &keyprop, NULL))
335         {
336             PROPVARIANT pv;
337 
338             pv.vt = VT_LPWSTR;
339             pv.u.pwszVal = name;
340             MMDevice_SetPropValue(id, flow, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pv);
341             MMDevice_SetPropValue(id, flow, (const PROPERTYKEY*)&DEVPKEY_DeviceInterface_FriendlyName, &pv);
342             MMDevice_SetPropValue(id, flow, (const PROPERTYKEY*)&DEVPKEY_Device_DeviceDesc, &pv);
343 
344             pv.u.pwszVal = guidstr;
345             MMDevice_SetPropValue(id, flow, &deviceinterface_key, &pv);
346 
347             set_driver_prop_value(id, flow, &devicepath_key);
348 
349             if (FAILED(set_driver_prop_value(id, flow, &PKEY_AudioEndpoint_FormFactor)))
350             {
351                 pv.vt = VT_UI4;
352                 pv.u.ulVal = (flow == eCapture) ? Microphone : Speakers;
353 
354                 MMDevice_SetPropValue(id, flow, &PKEY_AudioEndpoint_FormFactor, &pv);
355             }
356 
357             if (flow != eCapture)
358             {
359                 PROPVARIANT pv2;
360 
361                 PropVariantInit(&pv2);
362 
363                 /* make read-write by not overwriting if already set */
364                 if (FAILED(MMDevice_GetPropValue(id, flow, &PKEY_AudioEndpoint_PhysicalSpeakers, &pv2)) || pv2.vt != VT_UI4)
365                     set_driver_prop_value(id, flow, &PKEY_AudioEndpoint_PhysicalSpeakers);
366 
367                 PropVariantClear(&pv2);
368             }
369 
370             RegCloseKey(keyprop);
371         }
372         RegCloseKey(key);
373     }
374 
375     if (setdefault)
376     {
377         if (flow == eRender)
378             MMDevice_def_play = cur;
379         else
380             MMDevice_def_rec = cur;
381     }
382     return cur;
383 }
384 
385 static HRESULT load_devices_from_reg(void)
386 {
387     DWORD i = 0;
388     HKEY root, cur;
389     LONG ret;
390     DWORD curflow;
391 
392     ret = RegCreateKeyExW(HKEY_LOCAL_MACHINE, software_mmdevapi, 0, NULL, 0, KEY_WRITE|KEY_READ|KEY_WOW64_64KEY, NULL, &root, NULL);
393     if (ret == ERROR_SUCCESS)
394         ret = RegCreateKeyExW(root, reg_capture, 0, NULL, 0, KEY_READ|KEY_WRITE|KEY_WOW64_64KEY, NULL, &key_capture, NULL);
395     if (ret == ERROR_SUCCESS)
396         ret = RegCreateKeyExW(root, reg_render, 0, NULL, 0, KEY_READ|KEY_WRITE|KEY_WOW64_64KEY, NULL, &key_render, NULL);
397     RegCloseKey(root);
398     cur = key_capture;
399     curflow = eCapture;
400     if (ret != ERROR_SUCCESS)
401     {
402         RegCloseKey(key_capture);
403         key_render = key_capture = NULL;
404         WARN("Couldn't create key: %u\n", ret);
405         return E_FAIL;
406     }
407 
408     do {
409         WCHAR guidvalue[39];
410         GUID guid;
411         DWORD len;
412         PROPVARIANT pv = { VT_EMPTY };
413 
414         len = sizeof(guidvalue)/sizeof(guidvalue[0]);
415         ret = RegEnumKeyExW(cur, i++, guidvalue, &len, NULL, NULL, NULL, NULL);
416         if (ret == ERROR_NO_MORE_ITEMS)
417         {
418             if (cur == key_capture)
419             {
420                 cur = key_render;
421                 curflow = eRender;
422                 i = 0;
423                 continue;
424             }
425             break;
426         }
427         if (ret != ERROR_SUCCESS)
428             continue;
429         if (SUCCEEDED(CLSIDFromString(guidvalue, &guid))
430             && SUCCEEDED(MMDevice_GetPropValue(&guid, curflow, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pv))
431             && pv.vt == VT_LPWSTR)
432         {
433             DWORD size_bytes = (strlenW(pv.u.pwszVal) + 1) * sizeof(WCHAR);
434             WCHAR *name = HeapAlloc(GetProcessHeap(), 0, size_bytes);
435             memcpy(name, pv.u.pwszVal, size_bytes);
436             MMDevice_Create(name, &guid, curflow,
437                     DEVICE_STATE_NOTPRESENT, FALSE);
438             CoTaskMemFree(pv.u.pwszVal);
439         }
440     } while (1);
441 
442     return S_OK;
443 }
444 
445 static HRESULT set_format(MMDevice *dev)
446 {
447     HRESULT hr;
448     IAudioClient *client;
449     WAVEFORMATEX *fmt;
450     PROPVARIANT pv = { VT_EMPTY };
451 
452     hr = drvs.pGetAudioEndpoint(&dev->devguid, &dev->IMMDevice_iface, &client);
453     if(FAILED(hr))
454         return hr;
455 
456     hr = IAudioClient_GetMixFormat(client, &fmt);
457     if(FAILED(hr)){
458         IAudioClient_Release(client);
459         return hr;
460     }
461 
462     IAudioClient_Release(client);
463 
464     pv.vt = VT_BLOB;
465     pv.u.blob.cbSize = sizeof(WAVEFORMATEX) + fmt->cbSize;
466     pv.u.blob.pBlobData = (BYTE*)fmt;
467     MMDevice_SetPropValue(&dev->devguid, dev->flow,
468             &PKEY_AudioEngine_DeviceFormat, &pv);
469     MMDevice_SetPropValue(&dev->devguid, dev->flow,
470             &PKEY_AudioEngine_OEMFormat, &pv);
471     CoTaskMemFree(fmt);
472 
473     return S_OK;
474 }
475 
476 static HRESULT load_driver_devices(EDataFlow flow)
477 {
478     WCHAR **ids;
479     GUID *guids;
480     UINT num, def, i;
481     HRESULT hr;
482 
483     if(!drvs.pGetEndpointIDs)
484         return S_OK;
485 
486     hr = drvs.pGetEndpointIDs(flow, &ids, &guids, &num, &def);
487     if(FAILED(hr))
488         return hr;
489 
490     for(i = 0; i < num; ++i){
491         MMDevice *dev;
492         dev = MMDevice_Create(ids[i], &guids[i], flow, DEVICE_STATE_ACTIVE,
493                 def == i);
494         set_format(dev);
495     }
496 
497     HeapFree(GetProcessHeap(), 0, guids);
498     HeapFree(GetProcessHeap(), 0, ids);
499 
500     return S_OK;
501 }
502 
503 static void MMDevice_Destroy(MMDevice *This)
504 {
505     DWORD i;
506     TRACE("Freeing %s\n", debugstr_w(This->drv_id));
507     /* Since this function is called at destruction time, reordering of the list is unimportant */
508     for (i = 0; i < MMDevice_count; ++i)
509     {
510         if (MMDevice_head[i] == This)
511         {
512             MMDevice_head[i] = MMDevice_head[--MMDevice_count];
513             break;
514         }
515     }
516     This->crst.DebugInfo->Spare[0] = 0;
517     DeleteCriticalSection(&This->crst);
518     HeapFree(GetProcessHeap(), 0, This->drv_id);
519     HeapFree(GetProcessHeap(), 0, This);
520 }
521 
522 static inline MMDevice *impl_from_IMMDevice(IMMDevice *iface)
523 {
524     return CONTAINING_RECORD(iface, MMDevice, IMMDevice_iface);
525 }
526 
527 static HRESULT WINAPI MMDevice_QueryInterface(IMMDevice *iface, REFIID riid, void **ppv)
528 {
529     MMDevice *This = impl_from_IMMDevice(iface);
530     TRACE("(%p)->(%s,%p)\n", iface, debugstr_guid(riid), ppv);
531 
532     if (!ppv)
533         return E_POINTER;
534     *ppv = NULL;
535     if (IsEqualIID(riid, &IID_IUnknown)
536         || IsEqualIID(riid, &IID_IMMDevice))
537         *ppv = &This->IMMDevice_iface;
538     else if (IsEqualIID(riid, &IID_IMMEndpoint))
539         *ppv = &This->IMMEndpoint_iface;
540     if (*ppv)
541     {
542         IUnknown_AddRef((IUnknown*)*ppv);
543         return S_OK;
544     }
545     WARN("Unknown interface %s\n", debugstr_guid(riid));
546     return E_NOINTERFACE;
547 }
548 
549 static ULONG WINAPI MMDevice_AddRef(IMMDevice *iface)
550 {
551     MMDevice *This = impl_from_IMMDevice(iface);
552     LONG ref;
553 
554     ref = InterlockedIncrement(&This->ref);
555     TRACE("Refcount now %i\n", ref);
556     return ref;
557 }
558 
559 static ULONG WINAPI MMDevice_Release(IMMDevice *iface)
560 {
561     MMDevice *This = impl_from_IMMDevice(iface);
562     LONG ref;
563 
564     ref = InterlockedDecrement(&This->ref);
565     TRACE("Refcount now %i\n", ref);
566     return ref;
567 }
568 
569 static HRESULT WINAPI MMDevice_Activate(IMMDevice *iface, REFIID riid, DWORD clsctx, PROPVARIANT *params, void **ppv)
570 {
571     HRESULT hr = E_NOINTERFACE;
572     MMDevice *This = impl_from_IMMDevice(iface);
573 
574     TRACE("(%p)->(%s, %x, %p, %p)\n", iface, debugstr_guid(riid), clsctx, params, ppv);
575 
576     if (!ppv)
577         return E_POINTER;
578 
579     if (IsEqualIID(riid, &IID_IAudioClient)){
580         hr = drvs.pGetAudioEndpoint(&This->devguid, iface, (IAudioClient**)ppv);
581     }else if (IsEqualIID(riid, &IID_IAudioEndpointVolume) ||
582             IsEqualIID(riid, &IID_IAudioEndpointVolumeEx))
583         hr = AudioEndpointVolume_Create(This, (IAudioEndpointVolumeEx**)ppv);
584     else if (IsEqualIID(riid, &IID_IAudioSessionManager)
585              || IsEqualIID(riid, &IID_IAudioSessionManager2))
586     {
587         hr = drvs.pGetAudioSessionManager(iface, (IAudioSessionManager2**)ppv);
588     }
589     else if (IsEqualIID(riid, &IID_IBaseFilter))
590     {
591         if (This->flow == eRender)
592             hr = CoCreateInstance(&CLSID_DSoundRender, NULL, clsctx, riid, ppv);
593         else
594             ERR("Not supported for recording?\n");
595         if (SUCCEEDED(hr))
596         {
597             IPersistPropertyBag *ppb;
598             hr = IUnknown_QueryInterface((IUnknown*)*ppv, &IID_IPersistPropertyBag, (void*)&ppb);
599             if (SUCCEEDED(hr))
600             {
601                 /* ::Load cannot assume the interface stays alive after the function returns,
602                  * so just create the interface on the stack, saves a lot of complicated code */
603                 IPropertyBagImpl bag = { { &PB_Vtbl } };
604                 bag.devguid = This->devguid;
605                 hr = IPersistPropertyBag_Load(ppb, &bag.IPropertyBag_iface, NULL);
606                 IPersistPropertyBag_Release(ppb);
607                 if (FAILED(hr))
608                     IBaseFilter_Release((IBaseFilter*)*ppv);
609             }
610             else
611             {
612                 FIXME("Wine doesn't support IPersistPropertyBag on DSoundRender yet, ignoring..\n");
613                 hr = S_OK;
614             }
615         }
616     }
617     else if (IsEqualIID(riid, &IID_IDeviceTopology))
618     {
619         FIXME("IID_IDeviceTopology unsupported\n");
620     }
621     else if (IsEqualIID(riid, &IID_IDirectSound)
622              || IsEqualIID(riid, &IID_IDirectSound8))
623     {
624         if (This->flow == eRender)
625             hr = CoCreateInstance(&CLSID_DirectSound8, NULL, clsctx, riid, ppv);
626         if (SUCCEEDED(hr))
627         {
628             hr = IDirectSound_Initialize((IDirectSound*)*ppv, &This->devguid);
629             if (FAILED(hr))
630                 IDirectSound_Release((IDirectSound*)*ppv);
631         }
632     }
633     else if (IsEqualIID(riid, &IID_IDirectSoundCapture))
634     {
635         if (This->flow == eCapture)
636             hr = CoCreateInstance(&CLSID_DirectSoundCapture8, NULL, clsctx, riid, ppv);
637         if (SUCCEEDED(hr))
638         {
639             hr = IDirectSoundCapture_Initialize((IDirectSoundCapture*)*ppv, &This->devguid);
640             if (FAILED(hr))
641                 IDirectSoundCapture_Release((IDirectSoundCapture*)*ppv);
642         }
643     }
644     else
645         ERR("Invalid/unknown iid %s\n", debugstr_guid(riid));
646 
647     if (FAILED(hr))
648         *ppv = NULL;
649 
650     TRACE("Returning %08x\n", hr);
651     return hr;
652 }
653 
654 static HRESULT WINAPI MMDevice_OpenPropertyStore(IMMDevice *iface, DWORD access, IPropertyStore **ppv)
655 {
656     MMDevice *This = impl_from_IMMDevice(iface);
657     TRACE("(%p)->(%x,%p)\n", This, access, ppv);
658 
659     if (!ppv)
660         return E_POINTER;
661     return MMDevPropStore_Create(This, access, ppv);
662 }
663 
664 static HRESULT WINAPI MMDevice_GetId(IMMDevice *iface, WCHAR **itemid)
665 {
666     MMDevice *This = impl_from_IMMDevice(iface);
667     WCHAR *str;
668     GUID *id = &This->devguid;
669     static const WCHAR formatW[] = { '{','0','.','0','.','%','u','.','0','0','0','0','0','0','0','0','}','.',
670                                      '{','%','0','8','X','-','%','0','4','X','-',
671                                      '%','0','4','X','-','%','0','2','X','%','0','2','X','-',
672                                      '%','0','2','X','%','0','2','X','%','0','2','X','%','0','2','X',
673                                      '%','0','2','X','%','0','2','X','}',0 };
674 
675     TRACE("(%p)->(%p)\n", This, itemid);
676     if (!itemid)
677         return E_POINTER;
678     *itemid = str = CoTaskMemAlloc(56 * sizeof(WCHAR));
679     if (!str)
680         return E_OUTOFMEMORY;
681     wsprintfW( str, formatW, This->flow, id->Data1, id->Data2, id->Data3,
682                id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
683                id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7] );
684     TRACE("returning %s\n", wine_dbgstr_w(str));
685     return S_OK;
686 }
687 
688 static HRESULT WINAPI MMDevice_GetState(IMMDevice *iface, DWORD *state)
689 {
690     MMDevice *This = impl_from_IMMDevice(iface);
691     TRACE("(%p)->(%p)\n", iface, state);
692 
693     if (!state)
694         return E_POINTER;
695     *state = This->state;
696     return S_OK;
697 }
698 
699 static const IMMDeviceVtbl MMDeviceVtbl =
700 {
701     MMDevice_QueryInterface,
702     MMDevice_AddRef,
703     MMDevice_Release,
704     MMDevice_Activate,
705     MMDevice_OpenPropertyStore,
706     MMDevice_GetId,
707     MMDevice_GetState
708 };
709 
710 static inline MMDevice *impl_from_IMMEndpoint(IMMEndpoint *iface)
711 {
712     return CONTAINING_RECORD(iface, MMDevice, IMMEndpoint_iface);
713 }
714 
715 static HRESULT WINAPI MMEndpoint_QueryInterface(IMMEndpoint *iface, REFIID riid, void **ppv)
716 {
717     MMDevice *This = impl_from_IMMEndpoint(iface);
718     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
719     return IMMDevice_QueryInterface(&This->IMMDevice_iface, riid, ppv);
720 }
721 
722 static ULONG WINAPI MMEndpoint_AddRef(IMMEndpoint *iface)
723 {
724     MMDevice *This = impl_from_IMMEndpoint(iface);
725     TRACE("(%p)\n", This);
726     return IMMDevice_AddRef(&This->IMMDevice_iface);
727 }
728 
729 static ULONG WINAPI MMEndpoint_Release(IMMEndpoint *iface)
730 {
731     MMDevice *This = impl_from_IMMEndpoint(iface);
732     TRACE("(%p)\n", This);
733     return IMMDevice_Release(&This->IMMDevice_iface);
734 }
735 
736 static HRESULT WINAPI MMEndpoint_GetDataFlow(IMMEndpoint *iface, EDataFlow *flow)
737 {
738     MMDevice *This = impl_from_IMMEndpoint(iface);
739     TRACE("(%p)->(%p)\n", This, flow);
740     if (!flow)
741         return E_POINTER;
742     *flow = This->flow;
743     return S_OK;
744 }
745 
746 static const IMMEndpointVtbl MMEndpointVtbl =
747 {
748     MMEndpoint_QueryInterface,
749     MMEndpoint_AddRef,
750     MMEndpoint_Release,
751     MMEndpoint_GetDataFlow
752 };
753 
754 static HRESULT MMDevCol_Create(IMMDeviceCollection **ppv, EDataFlow flow, DWORD state)
755 {
756     MMDevColImpl *This;
757 
758     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
759     *ppv = NULL;
760     if (!This)
761         return E_OUTOFMEMORY;
762     This->IMMDeviceCollection_iface.lpVtbl = &MMDevColVtbl;
763     This->ref = 1;
764     This->flow = flow;
765     This->state = state;
766     *ppv = &This->IMMDeviceCollection_iface;
767     return S_OK;
768 }
769 
770 static void MMDevCol_Destroy(MMDevColImpl *This)
771 {
772     HeapFree(GetProcessHeap(), 0, This);
773 }
774 
775 static HRESULT WINAPI MMDevCol_QueryInterface(IMMDeviceCollection *iface, REFIID riid, void **ppv)
776 {
777     MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
778     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
779 
780     if (!ppv)
781         return E_POINTER;
782     if (IsEqualIID(riid, &IID_IUnknown)
783         || IsEqualIID(riid, &IID_IMMDeviceCollection))
784         *ppv = &This->IMMDeviceCollection_iface;
785     else
786         *ppv = NULL;
787     if (!*ppv)
788         return E_NOINTERFACE;
789     IUnknown_AddRef((IUnknown*)*ppv);
790     return S_OK;
791 }
792 
793 static ULONG WINAPI MMDevCol_AddRef(IMMDeviceCollection *iface)
794 {
795     MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
796     LONG ref = InterlockedIncrement(&This->ref);
797     TRACE("Refcount now %i\n", ref);
798     return ref;
799 }
800 
801 static ULONG WINAPI MMDevCol_Release(IMMDeviceCollection *iface)
802 {
803     MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
804     LONG ref = InterlockedDecrement(&This->ref);
805     TRACE("Refcount now %i\n", ref);
806     if (!ref)
807         MMDevCol_Destroy(This);
808     return ref;
809 }
810 
811 static HRESULT WINAPI MMDevCol_GetCount(IMMDeviceCollection *iface, UINT *numdevs)
812 {
813     MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
814     DWORD i;
815 
816     TRACE("(%p)->(%p)\n", This, numdevs);
817     if (!numdevs)
818         return E_POINTER;
819 
820     *numdevs = 0;
821     for (i = 0; i < MMDevice_count; ++i)
822     {
823         MMDevice *cur = MMDevice_head[i];
824         if ((cur->flow == This->flow || This->flow == eAll)
825             && (cur->state & This->state))
826             ++(*numdevs);
827     }
828     return S_OK;
829 }
830 
831 static HRESULT WINAPI MMDevCol_Item(IMMDeviceCollection *iface, UINT n, IMMDevice **dev)
832 {
833     MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
834     DWORD i = 0, j = 0;
835 
836     TRACE("(%p)->(%u, %p)\n", This, n, dev);
837     if (!dev)
838         return E_POINTER;
839 
840     for (j = 0; j < MMDevice_count; ++j)
841     {
842         MMDevice *cur = MMDevice_head[j];
843         if ((cur->flow == This->flow || This->flow == eAll)
844             && (cur->state & This->state)
845             && i++ == n)
846         {
847             *dev = &cur->IMMDevice_iface;
848             IMMDevice_AddRef(*dev);
849             return S_OK;
850         }
851     }
852     WARN("Could not obtain item %u\n", n);
853     *dev = NULL;
854     return E_INVALIDARG;
855 }
856 
857 static const IMMDeviceCollectionVtbl MMDevColVtbl =
858 {
859     MMDevCol_QueryInterface,
860     MMDevCol_AddRef,
861     MMDevCol_Release,
862     MMDevCol_GetCount,
863     MMDevCol_Item
864 };
865 
866 HRESULT MMDevEnum_Create(REFIID riid, void **ppv)
867 {
868     MMDevEnumImpl *This = MMDevEnumerator;
869 
870     if (!This)
871     {
872         This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
873         *ppv = NULL;
874         if (!This)
875             return E_OUTOFMEMORY;
876         This->ref = 1;
877         This->IMMDeviceEnumerator_iface.lpVtbl = &MMDevEnumVtbl;
878         MMDevEnumerator = This;
879 
880         load_devices_from_reg();
881         load_driver_devices(eRender);
882         load_driver_devices(eCapture);
883     }
884     return IMMDeviceEnumerator_QueryInterface(&This->IMMDeviceEnumerator_iface, riid, ppv);
885 }
886 
887 void MMDevEnum_Free(void)
888 {
889     while (MMDevice_count)
890         MMDevice_Destroy(MMDevice_head[0]);
891     RegCloseKey(key_render);
892     RegCloseKey(key_capture);
893     key_render = key_capture = NULL;
894     HeapFree(GetProcessHeap(), 0, MMDevEnumerator);
895     MMDevEnumerator = NULL;
896 }
897 
898 static HRESULT WINAPI MMDevEnum_QueryInterface(IMMDeviceEnumerator *iface, REFIID riid, void **ppv)
899 {
900     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
901     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
902 
903     if (!ppv)
904         return E_POINTER;
905     if (IsEqualIID(riid, &IID_IUnknown)
906         || IsEqualIID(riid, &IID_IMMDeviceEnumerator))
907         *ppv = &This->IMMDeviceEnumerator_iface;
908     else
909         *ppv = NULL;
910     if (!*ppv)
911         return E_NOINTERFACE;
912     IUnknown_AddRef((IUnknown*)*ppv);
913     return S_OK;
914 }
915 
916 static ULONG WINAPI MMDevEnum_AddRef(IMMDeviceEnumerator *iface)
917 {
918     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
919     LONG ref = InterlockedIncrement(&This->ref);
920     TRACE("Refcount now %i\n", ref);
921     return ref;
922 }
923 
924 static ULONG WINAPI MMDevEnum_Release(IMMDeviceEnumerator *iface)
925 {
926     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
927     LONG ref = InterlockedDecrement(&This->ref);
928     if (!ref)
929         MMDevEnum_Free();
930     TRACE("Refcount now %i\n", ref);
931     return ref;
932 }
933 
934 static HRESULT WINAPI MMDevEnum_EnumAudioEndpoints(IMMDeviceEnumerator *iface, EDataFlow flow, DWORD mask, IMMDeviceCollection **devices)
935 {
936     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
937     TRACE("(%p)->(%u,%u,%p)\n", This, flow, mask, devices);
938     if (!devices)
939         return E_POINTER;
940     *devices = NULL;
941     if (flow >= EDataFlow_enum_count)
942         return E_INVALIDARG;
943     if (mask & ~DEVICE_STATEMASK_ALL)
944         return E_INVALIDARG;
945     return MMDevCol_Create(devices, flow, mask);
946 }
947 
948 static HRESULT WINAPI MMDevEnum_GetDefaultAudioEndpoint(IMMDeviceEnumerator *iface, EDataFlow flow, ERole role, IMMDevice **device)
949 {
950     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
951     WCHAR reg_key[256];
952     HKEY key;
953     HRESULT hr;
954 
955     TRACE("(%p)->(%u,%u,%p)\n", This, flow, role, device);
956 
957     if (!device)
958         return E_POINTER;
959 
960     if((flow != eRender && flow != eCapture) ||
961             (role != eConsole && role != eMultimedia && role != eCommunications)){
962         WARN("Unknown flow (%u) or role (%u)\n", flow, role);
963         return E_INVALIDARG;
964     }
965 
966     *device = NULL;
967 
968     if(!drvs.module_name[0])
969         return E_NOTFOUND;
970 
971     lstrcpyW(reg_key, drv_keyW);
972     lstrcatW(reg_key, slashW);
973     lstrcatW(reg_key, drvs.module_name);
974 
975     if(RegOpenKeyW(HKEY_CURRENT_USER, reg_key, &key) == ERROR_SUCCESS){
976         const WCHAR *reg_x_name, *reg_vx_name;
977         WCHAR def_id[256];
978         DWORD size = sizeof(def_id), state;
979 
980         if(flow == eRender){
981             reg_x_name = reg_out_nameW;
982             reg_vx_name = reg_vout_nameW;
983         }else{
984             reg_x_name = reg_in_nameW;
985             reg_vx_name = reg_vin_nameW;
986         }
987 
988         if(role == eCommunications &&
989                 RegQueryValueExW(key, reg_vx_name, 0, NULL,
990                     (BYTE*)def_id, &size) == ERROR_SUCCESS){
991             hr = IMMDeviceEnumerator_GetDevice(iface, def_id, device);
992             if(SUCCEEDED(hr)){
993                 if(SUCCEEDED(IMMDevice_GetState(*device, &state)) &&
994                         state == DEVICE_STATE_ACTIVE){
995                     RegCloseKey(key);
996                     return S_OK;
997                 }
998             }
999 
1000             TRACE("Unable to find voice device %s\n", wine_dbgstr_w(def_id));
1001         }
1002 
1003         if(RegQueryValueExW(key, reg_x_name, 0, NULL,
1004                     (BYTE*)def_id, &size) == ERROR_SUCCESS){
1005             hr = IMMDeviceEnumerator_GetDevice(iface, def_id, device);
1006             if(SUCCEEDED(hr)){
1007                 if(SUCCEEDED(IMMDevice_GetState(*device, &state)) &&
1008                         state == DEVICE_STATE_ACTIVE){
1009                     RegCloseKey(key);
1010                     return S_OK;
1011                 }
1012             }
1013 
1014             TRACE("Unable to find device %s\n", wine_dbgstr_w(def_id));
1015         }
1016 
1017         RegCloseKey(key);
1018     }
1019 
1020     if (flow == eRender)
1021         *device = &MMDevice_def_play->IMMDevice_iface;
1022     else
1023         *device = &MMDevice_def_rec->IMMDevice_iface;
1024 
1025     if (!*device)
1026         return E_NOTFOUND;
1027     IMMDevice_AddRef(*device);
1028     return S_OK;
1029 }
1030 
1031 static HRESULT WINAPI MMDevEnum_GetDevice(IMMDeviceEnumerator *iface, const WCHAR *name, IMMDevice **device)
1032 {
1033     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1034     DWORD i=0;
1035     IMMDevice *dev = NULL;
1036 
1037     static const WCHAR wine_info_deviceW[] = {'W','i','n','e',' ',
1038         'i','n','f','o',' ','d','e','v','i','c','e',0};
1039 
1040     TRACE("(%p)->(%s,%p)\n", This, debugstr_w(name), device);
1041 
1042     if(!name || !device)
1043         return E_POINTER;
1044 
1045     if(!lstrcmpW(name, wine_info_deviceW)){
1046         *device = &info_device;
1047         return S_OK;
1048     }
1049 
1050     for (i = 0; i < MMDevice_count; ++i)
1051     {
1052         HRESULT hr;
1053         WCHAR *str;
1054         dev = &MMDevice_head[i]->IMMDevice_iface;
1055         hr = IMMDevice_GetId(dev, &str);
1056         if (FAILED(hr))
1057         {
1058             WARN("GetId failed: %08x\n", hr);
1059             continue;
1060         }
1061 
1062         if (str && !lstrcmpW(str, name))
1063         {
1064             CoTaskMemFree(str);
1065             IMMDevice_AddRef(dev);
1066             *device = dev;
1067             return S_OK;
1068         }
1069         CoTaskMemFree(str);
1070     }
1071     TRACE("Could not find device %s\n", debugstr_w(name));
1072     return E_INVALIDARG;
1073 }
1074 
1075 struct NotificationClientWrapper {
1076     IMMNotificationClient *client;
1077     struct list entry;
1078 };
1079 
1080 static struct list g_notif_clients = LIST_INIT(g_notif_clients);
1081 static HANDLE g_notif_thread;
1082 
1083 static CRITICAL_SECTION g_notif_lock;
1084 static CRITICAL_SECTION_DEBUG g_notif_lock_debug =
1085 {
1086     0, 0, &g_notif_lock,
1087     { &g_notif_lock_debug.ProcessLocksList, &g_notif_lock_debug.ProcessLocksList },
1088       0, 0, { (DWORD_PTR)(__FILE__ ": g_notif_lock") }
1089 };
1090 static CRITICAL_SECTION g_notif_lock = { &g_notif_lock_debug, -1, 0, 0, 0, 0 };
1091 
1092 static void notify_clients(EDataFlow flow, ERole role, const WCHAR *id)
1093 {
1094     struct NotificationClientWrapper *wrapper;
1095     LIST_FOR_EACH_ENTRY(wrapper, &g_notif_clients,
1096             struct NotificationClientWrapper, entry)
1097         IMMNotificationClient_OnDefaultDeviceChanged(wrapper->client, flow,
1098                 role, id);
1099 
1100     /* Windows 7 treats changes to eConsole as changes to eMultimedia */
1101     if(role == eConsole)
1102         notify_clients(flow, eMultimedia, id);
1103 }
1104 
1105 static BOOL notify_if_changed(EDataFlow flow, ERole role, HKEY key,
1106                               const WCHAR *val_name, WCHAR *old_val, IMMDevice *def_dev)
1107 {
1108     WCHAR new_val[64], *id;
1109     DWORD size;
1110     HRESULT hr;
1111 
1112     size = sizeof(new_val);
1113     if(RegQueryValueExW(key, val_name, 0, NULL,
1114                 (BYTE*)new_val, &size) != ERROR_SUCCESS){
1115         if(old_val[0] != 0){
1116             /* set by user -> system default */
1117             if(def_dev){
1118                 hr = IMMDevice_GetId(def_dev, &id);
1119                 if(FAILED(hr)){
1120                     ERR("GetId failed: %08x\n", hr);
1121                     return FALSE;
1122                 }
1123             }else
1124                 id = NULL;
1125 
1126             notify_clients(flow, role, id);
1127             old_val[0] = 0;
1128             CoTaskMemFree(id);
1129 
1130             return TRUE;
1131         }
1132 
1133         /* system default -> system default, noop */
1134         return FALSE;
1135     }
1136 
1137     if(!lstrcmpW(old_val, new_val)){
1138         /* set by user -> same value */
1139         return FALSE;
1140     }
1141 
1142     if(new_val[0] != 0){
1143         /* set by user -> different value */
1144         notify_clients(flow, role, new_val);
1145         memcpy(old_val, new_val, sizeof(new_val));
1146         return TRUE;
1147     }
1148 
1149     /* set by user -> system default */
1150     if(def_dev){
1151         hr = IMMDevice_GetId(def_dev, &id);
1152         if(FAILED(hr)){
1153             ERR("GetId failed: %08x\n", hr);
1154             return FALSE;
1155         }
1156     }else
1157         id = NULL;
1158 
1159     notify_clients(flow, role, id);
1160     old_val[0] = 0;
1161     CoTaskMemFree(id);
1162 
1163     return TRUE;
1164 }
1165 
1166 static DWORD WINAPI notif_thread_proc(void *user)
1167 {
1168     HKEY key;
1169     WCHAR reg_key[256];
1170     WCHAR out_name[64], vout_name[64], in_name[64], vin_name[64];
1171     DWORD size;
1172 
1173     lstrcpyW(reg_key, drv_keyW);
1174     lstrcatW(reg_key, slashW);
1175     lstrcatW(reg_key, drvs.module_name);
1176 
1177     if(RegCreateKeyExW(HKEY_CURRENT_USER, reg_key, 0, NULL, 0,
1178                 MAXIMUM_ALLOWED, NULL, &key, NULL) != ERROR_SUCCESS){
1179         ERR("RegCreateKeyEx failed: %u\n", GetLastError());
1180         return 1;
1181     }
1182 
1183     size = sizeof(out_name);
1184     if(RegQueryValueExW(key, reg_out_nameW, 0, NULL,
1185                 (BYTE*)out_name, &size) != ERROR_SUCCESS)
1186         out_name[0] = 0;
1187 
1188     size = sizeof(vout_name);
1189     if(RegQueryValueExW(key, reg_vout_nameW, 0, NULL,
1190                 (BYTE*)vout_name, &size) != ERROR_SUCCESS)
1191         vout_name[0] = 0;
1192 
1193     size = sizeof(in_name);
1194     if(RegQueryValueExW(key, reg_in_nameW, 0, NULL,
1195                 (BYTE*)in_name, &size) != ERROR_SUCCESS)
1196         in_name[0] = 0;
1197 
1198     size = sizeof(vin_name);
1199     if(RegQueryValueExW(key, reg_vin_nameW, 0, NULL,
1200                 (BYTE*)vin_name, &size) != ERROR_SUCCESS)
1201         vin_name[0] = 0;
1202 
1203     while(1){
1204         if(RegNotifyChangeKeyValue(key, FALSE, REG_NOTIFY_CHANGE_LAST_SET,
1205                     NULL, FALSE) != ERROR_SUCCESS){
1206             ERR("RegNotifyChangeKeyValue failed: %u\n", GetLastError());
1207             RegCloseKey(key);
1208             g_notif_thread = NULL;
1209             return 1;
1210         }
1211 
1212         EnterCriticalSection(&g_notif_lock);
1213 
1214         notify_if_changed(eRender, eConsole, key, reg_out_nameW,
1215                 out_name, &MMDevice_def_play->IMMDevice_iface);
1216         notify_if_changed(eRender, eCommunications, key, reg_vout_nameW,
1217                 vout_name, &MMDevice_def_play->IMMDevice_iface);
1218         notify_if_changed(eCapture, eConsole, key, reg_in_nameW,
1219                 in_name, &MMDevice_def_rec->IMMDevice_iface);
1220         notify_if_changed(eCapture, eCommunications, key, reg_vin_nameW,
1221                 vin_name, &MMDevice_def_rec->IMMDevice_iface);
1222 
1223         LeaveCriticalSection(&g_notif_lock);
1224     }
1225 
1226     RegCloseKey(key);
1227 
1228     g_notif_thread = NULL;
1229 
1230     return 0;
1231 }
1232 
1233 static HRESULT WINAPI MMDevEnum_RegisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
1234 {
1235     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1236     struct NotificationClientWrapper *wrapper;
1237 
1238     TRACE("(%p)->(%p)\n", This, client);
1239 
1240     if(!client)
1241         return E_POINTER;
1242 
1243     wrapper = HeapAlloc(GetProcessHeap(), 0, sizeof(*wrapper));
1244     if(!wrapper)
1245         return E_OUTOFMEMORY;
1246 
1247     wrapper->client = client;
1248 
1249     EnterCriticalSection(&g_notif_lock);
1250 
1251     list_add_tail(&g_notif_clients, &wrapper->entry);
1252 
1253     if(!g_notif_thread){
1254         g_notif_thread = CreateThread(NULL, 0, notif_thread_proc, NULL, 0, NULL);
1255         if(!g_notif_thread)
1256             ERR("CreateThread failed: %u\n", GetLastError());
1257     }
1258 
1259     LeaveCriticalSection(&g_notif_lock);
1260 
1261     return S_OK;
1262 }
1263 
1264 static HRESULT WINAPI MMDevEnum_UnregisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
1265 {
1266     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1267     struct NotificationClientWrapper *wrapper;
1268 
1269     TRACE("(%p)->(%p)\n", This, client);
1270 
1271     if(!client)
1272         return E_POINTER;
1273 
1274     EnterCriticalSection(&g_notif_lock);
1275 
1276     LIST_FOR_EACH_ENTRY(wrapper, &g_notif_clients, struct NotificationClientWrapper, entry){
1277         if(wrapper->client == client){
1278             list_remove(&wrapper->entry);
1279             HeapFree(GetProcessHeap(), 0, wrapper);
1280             LeaveCriticalSection(&g_notif_lock);
1281             return S_OK;
1282         }
1283     }
1284 
1285     LeaveCriticalSection(&g_notif_lock);
1286 
1287     return E_NOTFOUND;
1288 }
1289 
1290 static const IMMDeviceEnumeratorVtbl MMDevEnumVtbl =
1291 {
1292     MMDevEnum_QueryInterface,
1293     MMDevEnum_AddRef,
1294     MMDevEnum_Release,
1295     MMDevEnum_EnumAudioEndpoints,
1296     MMDevEnum_GetDefaultAudioEndpoint,
1297     MMDevEnum_GetDevice,
1298     MMDevEnum_RegisterEndpointNotificationCallback,
1299     MMDevEnum_UnregisterEndpointNotificationCallback
1300 };
1301 
1302 static HRESULT MMDevPropStore_Create(MMDevice *parent, DWORD access, IPropertyStore **ppv)
1303 {
1304     MMDevPropStore *This;
1305     if (access != STGM_READ
1306         && access != STGM_WRITE
1307         && access != STGM_READWRITE)
1308     {
1309         WARN("Invalid access %08x\n", access);
1310         return E_INVALIDARG;
1311     }
1312     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
1313     *ppv = &This->IPropertyStore_iface;
1314     if (!This)
1315         return E_OUTOFMEMORY;
1316     This->IPropertyStore_iface.lpVtbl = &MMDevPropVtbl;
1317     This->ref = 1;
1318     This->parent = parent;
1319     This->access = access;
1320     return S_OK;
1321 }
1322 
1323 static void MMDevPropStore_Destroy(MMDevPropStore *This)
1324 {
1325     HeapFree(GetProcessHeap(), 0, This);
1326 }
1327 
1328 static HRESULT WINAPI MMDevPropStore_QueryInterface(IPropertyStore *iface, REFIID riid, void **ppv)
1329 {
1330     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1331     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1332 
1333     if (!ppv)
1334         return E_POINTER;
1335     if (IsEqualIID(riid, &IID_IUnknown)
1336         || IsEqualIID(riid, &IID_IPropertyStore))
1337         *ppv = &This->IPropertyStore_iface;
1338     else
1339         *ppv = NULL;
1340     if (!*ppv)
1341         return E_NOINTERFACE;
1342     IUnknown_AddRef((IUnknown*)*ppv);
1343     return S_OK;
1344 }
1345 
1346 static ULONG WINAPI MMDevPropStore_AddRef(IPropertyStore *iface)
1347 {
1348     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1349     LONG ref = InterlockedIncrement(&This->ref);
1350     TRACE("Refcount now %i\n", ref);
1351     return ref;
1352 }
1353 
1354 static ULONG WINAPI MMDevPropStore_Release(IPropertyStore *iface)
1355 {
1356     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1357     LONG ref = InterlockedDecrement(&This->ref);
1358     TRACE("Refcount now %i\n", ref);
1359     if (!ref)
1360         MMDevPropStore_Destroy(This);
1361     return ref;
1362 }
1363 
1364 static HRESULT WINAPI MMDevPropStore_GetCount(IPropertyStore *iface, DWORD *nprops)
1365 {
1366     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1367     WCHAR buffer[50];
1368     DWORD i = 0;
1369     HKEY propkey;
1370     HRESULT hr;
1371 
1372     TRACE("(%p)->(%p)\n", iface, nprops);
1373     if (!nprops)
1374         return E_POINTER;
1375     hr = MMDevPropStore_OpenPropKey(&This->parent->devguid, This->parent->flow, &propkey);
1376     if (FAILED(hr))
1377         return hr;
1378     *nprops = 0;
1379     do {
1380         DWORD len = sizeof(buffer)/sizeof(*buffer);
1381         if (RegEnumValueW(propkey, i, buffer, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
1382             break;
1383         i++;
1384     } while (1);
1385     RegCloseKey(propkey);
1386     TRACE("Returning %i\n", i);
1387     *nprops = i;
1388     return S_OK;
1389 }
1390 
1391 static HRESULT WINAPI MMDevPropStore_GetAt(IPropertyStore *iface, DWORD prop, PROPERTYKEY *key)
1392 {
1393     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1394     WCHAR buffer[50];
1395     DWORD len = sizeof(buffer)/sizeof(*buffer);
1396     HRESULT hr;
1397     HKEY propkey;
1398 
1399     TRACE("(%p)->(%u,%p)\n", iface, prop, key);
1400     if (!key)
1401         return E_POINTER;
1402 
1403     hr = MMDevPropStore_OpenPropKey(&This->parent->devguid, This->parent->flow, &propkey);
1404     if (FAILED(hr))
1405         return hr;
1406 
1407     if (RegEnumValueW(propkey, prop, buffer, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS
1408         || len <= 39)
1409     {
1410         WARN("GetAt %u failed\n", prop);
1411         return E_INVALIDARG;
1412     }
1413     RegCloseKey(propkey);
1414     buffer[38] = 0;
1415     CLSIDFromString(buffer, &key->fmtid);
1416     key->pid = atoiW(&buffer[39]);
1417     return S_OK;
1418 }
1419 
1420 static HRESULT WINAPI MMDevPropStore_GetValue(IPropertyStore *iface, REFPROPERTYKEY key, PROPVARIANT *pv)
1421 {
1422     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1423     TRACE("(%p)->(\"%s,%u\", %p)\n", This, key ? debugstr_guid(&key->fmtid) : NULL, key ? key->pid : 0, pv);
1424 
1425     if (!key || !pv)
1426         return E_POINTER;
1427     if (This->access != STGM_READ
1428         && This->access != STGM_READWRITE)
1429         return STG_E_ACCESSDENIED;
1430 
1431     /* Special case */
1432     if (IsEqualPropertyKey(*key, PKEY_AudioEndpoint_GUID))
1433     {
1434         pv->vt = VT_LPWSTR;
1435         pv->u.pwszVal = CoTaskMemAlloc(39 * sizeof(WCHAR));
1436         if (!pv->u.pwszVal)
1437             return E_OUTOFMEMORY;
1438         StringFromGUID2(&This->parent->devguid, pv->u.pwszVal, 39);
1439         return S_OK;
1440     }
1441 
1442     return MMDevice_GetPropValue(&This->parent->devguid, This->parent->flow, key, pv);
1443 }
1444 
1445 static HRESULT WINAPI MMDevPropStore_SetValue(IPropertyStore *iface, REFPROPERTYKEY key, REFPROPVARIANT pv)
1446 {
1447     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1448     TRACE("(%p)->(\"%s,%u\", %p)\n", This, key ? debugstr_guid(&key->fmtid) : NULL, key ? key->pid : 0, pv);
1449 
1450     if (!key || !pv)
1451         return E_POINTER;
1452 
1453     if (This->access != STGM_WRITE
1454         && This->access != STGM_READWRITE)
1455         return STG_E_ACCESSDENIED;
1456     return MMDevice_SetPropValue(&This->parent->devguid, This->parent->flow, key, pv);
1457 }
1458 
1459 static HRESULT WINAPI MMDevPropStore_Commit(IPropertyStore *iface)
1460 {
1461     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1462     TRACE("(%p)\n", iface);
1463 
1464     if (This->access != STGM_WRITE
1465         && This->access != STGM_READWRITE)
1466         return STG_E_ACCESSDENIED;
1467 
1468     /* Does nothing - for mmdevapi, the propstore values are written on SetValue,
1469      * not on Commit. */
1470 
1471     return S_OK;
1472 }
1473 
1474 static const IPropertyStoreVtbl MMDevPropVtbl =
1475 {
1476     MMDevPropStore_QueryInterface,
1477     MMDevPropStore_AddRef,
1478     MMDevPropStore_Release,
1479     MMDevPropStore_GetCount,
1480     MMDevPropStore_GetAt,
1481     MMDevPropStore_GetValue,
1482     MMDevPropStore_SetValue,
1483     MMDevPropStore_Commit
1484 };
1485 
1486 
1487 /* Property bag for IBaseFilter activation */
1488 static HRESULT WINAPI PB_QueryInterface(IPropertyBag *iface, REFIID riid, void **ppv)
1489 {
1490     ERR("Should not be called\n");
1491     *ppv = NULL;
1492     return E_NOINTERFACE;
1493 }
1494 
1495 static ULONG WINAPI PB_AddRef(IPropertyBag *iface)
1496 {
1497     ERR("Should not be called\n");
1498     return 2;
1499 }
1500 
1501 static ULONG WINAPI PB_Release(IPropertyBag *iface)
1502 {
1503     ERR("Should not be called\n");
1504     return 1;
1505 }
1506 
1507 static HRESULT WINAPI PB_Read(IPropertyBag *iface, LPCOLESTR name, VARIANT *var, IErrorLog *log)
1508 {
1509     static const WCHAR dsguid[] = { 'D','S','G','u','i','d', 0 };
1510     IPropertyBagImpl *This = impl_from_IPropertyBag(iface);
1511     TRACE("Trying to read %s, type %u\n", debugstr_w(name), var->n1.n2.vt);
1512     if (!lstrcmpW(name, dsguid))
1513     {
1514         WCHAR guidstr[39];
1515         StringFromGUID2(&This->devguid, guidstr,sizeof(guidstr)/sizeof(*guidstr));
1516         var->n1.n2.vt = VT_BSTR;
1517         var->n1.n2.n3.bstrVal = SysAllocString(guidstr);
1518         return S_OK;
1519     }
1520     ERR("Unknown property '%s' queried\n", debugstr_w(name));
1521     return E_FAIL;
1522 }
1523 
1524 static HRESULT WINAPI PB_Write(IPropertyBag *iface, LPCOLESTR name, VARIANT *var)
1525 {
1526     ERR("Should not be called\n");
1527     return E_FAIL;
1528 }
1529 
1530 static const IPropertyBagVtbl PB_Vtbl =
1531 {
1532     PB_QueryInterface,
1533     PB_AddRef,
1534     PB_Release,
1535     PB_Read,
1536     PB_Write
1537 };
1538 
1539 static ULONG WINAPI info_device_ps_AddRef(IPropertyStore *iface)
1540 {
1541     return 2;
1542 }
1543 
1544 static ULONG WINAPI info_device_ps_Release(IPropertyStore *iface)
1545 {
1546     return 1;
1547 }
1548 
1549 static HRESULT WINAPI info_device_ps_GetValue(IPropertyStore *iface,
1550         REFPROPERTYKEY key, PROPVARIANT *pv)
1551 {
1552     TRACE("(static)->(\"%s,%u\", %p)\n", debugstr_guid(&key->fmtid), key ? key->pid : 0, pv);
1553 
1554     if (!key || !pv)
1555         return E_POINTER;
1556 
1557     if (IsEqualPropertyKey(*key, DEVPKEY_Device_Driver))
1558     {
1559         INT size = (lstrlenW(drvs.module_name) + 1) * sizeof(WCHAR);
1560         pv->vt = VT_LPWSTR;
1561         pv->u.pwszVal = CoTaskMemAlloc(size);
1562         if (!pv->u.pwszVal)
1563             return E_OUTOFMEMORY;
1564         memcpy(pv->u.pwszVal, drvs.module_name, size);
1565         return S_OK;
1566     }
1567 
1568     return E_INVALIDARG;
1569 }
1570 
1571 static const IPropertyStoreVtbl info_device_ps_Vtbl =
1572 {
1573     NULL,
1574     info_device_ps_AddRef,
1575     info_device_ps_Release,
1576     NULL,
1577     NULL,
1578     info_device_ps_GetValue,
1579     NULL,
1580     NULL
1581 };
1582 
1583 static IPropertyStore info_device_ps = {
1584     &info_device_ps_Vtbl
1585 };
1586 
1587 static ULONG WINAPI info_device_AddRef(IMMDevice *iface)
1588 {
1589     return 2;
1590 }
1591 
1592 static ULONG WINAPI info_device_Release(IMMDevice *iface)
1593 {
1594     return 1;
1595 }
1596 
1597 static HRESULT WINAPI info_device_OpenPropertyStore(IMMDevice *iface,
1598         DWORD access, IPropertyStore **ppv)
1599 {
1600     TRACE("(static)->(%x, %p)\n", access, ppv);
1601     *ppv = &info_device_ps;
1602     return S_OK;
1603 }
1604 
1605 static const IMMDeviceVtbl info_device_Vtbl =
1606 {
1607     NULL,
1608     info_device_AddRef,
1609     info_device_Release,
1610     NULL,
1611     info_device_OpenPropertyStore,
1612     NULL,
1613     NULL
1614 };
1615 
1616 static IMMDevice info_device = {
1617     &info_device_Vtbl
1618 };
1619