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