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