user32/tests: Flush events before the key flags test.
[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/unicode.h"
31
32 #include "ole2.h"
33 #include "mmdeviceapi.h"
34 #include "dshow.h"
35 #include "dsound.h"
36 #include "audioclient.h"
37 #include "endpointvolume.h"
38 #include "audiopolicy.h"
39
40 #include "mmdevapi.h"
41 #include "devpkey.h"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(mmdevapi);
44
45 static const WCHAR software_mmdevapi[] =
46     { 'S','o','f','t','w','a','r','e','\\',
47       'M','i','c','r','o','s','o','f','t','\\',
48       'W','i','n','d','o','w','s','\\',
49       'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
50       'M','M','D','e','v','i','c','e','s','\\',
51       'A','u','d','i','o',0};
52 static const WCHAR reg_render[] =
53     { 'R','e','n','d','e','r',0 };
54 static const WCHAR reg_capture[] =
55     { 'C','a','p','t','u','r','e',0 };
56 static const WCHAR reg_devicestate[] =
57     { 'D','e','v','i','c','e','S','t','a','t','e',0 };
58 static const WCHAR reg_properties[] =
59     { 'P','r','o','p','e','r','t','i','e','s',0 };
60
61 static HKEY key_render;
62 static HKEY key_capture;
63
64 typedef struct MMDevPropStoreImpl
65 {
66     IPropertyStore IPropertyStore_iface;
67     LONG ref;
68     MMDevice *parent;
69     DWORD access;
70 } MMDevPropStore;
71
72 typedef struct MMDevEnumImpl
73 {
74     IMMDeviceEnumerator IMMDeviceEnumerator_iface;
75     LONG ref;
76 } MMDevEnumImpl;
77
78 static MMDevEnumImpl *MMDevEnumerator;
79 static MMDevice **MMDevice_head;
80 static MMDevice *MMDevice_def_rec, *MMDevice_def_play;
81 static DWORD MMDevice_count;
82 static const IMMDeviceEnumeratorVtbl MMDevEnumVtbl;
83 static const IMMDeviceCollectionVtbl MMDevColVtbl;
84 static const IMMDeviceVtbl MMDeviceVtbl;
85 static const IPropertyStoreVtbl MMDevPropVtbl;
86 static const IMMEndpointVtbl MMEndpointVtbl;
87
88 static IMMDevice info_device;
89
90 typedef struct MMDevColImpl
91 {
92     IMMDeviceCollection IMMDeviceCollection_iface;
93     LONG ref;
94     EDataFlow flow;
95     DWORD state;
96 } MMDevColImpl;
97
98 typedef struct IPropertyBagImpl {
99     IPropertyBag IPropertyBag_iface;
100     GUID devguid;
101 } IPropertyBagImpl;
102
103 static const IPropertyBagVtbl PB_Vtbl;
104
105 static HRESULT MMDevPropStore_Create(MMDevice *This, DWORD access, IPropertyStore **ppv);
106
107 static inline MMDevPropStore *impl_from_IPropertyStore(IPropertyStore *iface)
108 {
109     return CONTAINING_RECORD(iface, MMDevPropStore, IPropertyStore_iface);
110 }
111
112 static inline MMDevEnumImpl *impl_from_IMMDeviceEnumerator(IMMDeviceEnumerator *iface)
113 {
114     return CONTAINING_RECORD(iface, MMDevEnumImpl, IMMDeviceEnumerator_iface);
115 }
116
117 static inline MMDevColImpl *impl_from_IMMDeviceCollection(IMMDeviceCollection *iface)
118 {
119     return CONTAINING_RECORD(iface, MMDevColImpl, IMMDeviceCollection_iface);
120 }
121
122 static inline IPropertyBagImpl *impl_from_IPropertyBag(IPropertyBag *iface)
123 {
124     return CONTAINING_RECORD(iface, IPropertyBagImpl, IPropertyBag_iface);
125 }
126
127 static const WCHAR propkey_formatW[] = {
128     '{','%','0','8','X','-','%','0','4','X','-',
129     '%','0','4','X','-','%','0','2','X','%','0','2','X','-',
130     '%','0','2','X','%','0','2','X','%','0','2','X','%','0','2','X',
131     '%','0','2','X','%','0','2','X','}',',','%','d',0 };
132
133 static HRESULT MMDevPropStore_OpenPropKey(const GUID *guid, DWORD flow, HKEY *propkey)
134 {
135     WCHAR buffer[39];
136     LONG ret;
137     HKEY key;
138     StringFromGUID2(guid, buffer, 39);
139     if ((ret = RegOpenKeyExW(flow == eRender ? key_render : key_capture, buffer, 0, KEY_READ|KEY_WRITE, &key)) != ERROR_SUCCESS)
140     {
141         WARN("Opening key %s failed with %u\n", debugstr_w(buffer), ret);
142         return E_FAIL;
143     }
144     ret = RegOpenKeyExW(key, reg_properties, 0, KEY_READ|KEY_WRITE, propkey);
145     RegCloseKey(key);
146     if (ret != ERROR_SUCCESS)
147     {
148         WARN("Opening key %s failed with %u\n", debugstr_w(reg_properties), ret);
149         return E_FAIL;
150     }
151     return S_OK;
152 }
153
154 HRESULT MMDevice_GetPropValue(const GUID *devguid, DWORD flow, REFPROPERTYKEY key, PROPVARIANT *pv)
155 {
156     WCHAR buffer[80];
157     const GUID *id = &key->fmtid;
158     DWORD type, size;
159     HRESULT hr = S_OK;
160     HKEY regkey;
161     LONG ret;
162
163     hr = MMDevPropStore_OpenPropKey(devguid, flow, &regkey);
164     if (FAILED(hr))
165         return hr;
166     wsprintfW( buffer, propkey_formatW, id->Data1, id->Data2, id->Data3,
167                id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
168                id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7], key->pid );
169     ret = RegGetValueW(regkey, NULL, buffer, RRF_RT_ANY, &type, NULL, &size);
170     if (ret != ERROR_SUCCESS)
171     {
172         WARN("Reading %s returned %d\n", debugstr_w(buffer), ret);
173         RegCloseKey(regkey);
174         PropVariantClear(pv);
175         return S_OK;
176     }
177
178     switch (type)
179     {
180         case REG_SZ:
181         {
182             pv->vt = VT_LPWSTR;
183             pv->u.pwszVal = CoTaskMemAlloc(size);
184             if (!pv->u.pwszVal)
185                 hr = E_OUTOFMEMORY;
186             else
187                 RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_SZ, NULL, (BYTE*)pv->u.pwszVal, &size);
188             break;
189         }
190         case REG_DWORD:
191         {
192             pv->vt = VT_UI4;
193             RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_DWORD, NULL, (BYTE*)&pv->u.ulVal, &size);
194             break;
195         }
196         case REG_BINARY:
197         {
198             pv->vt = VT_BLOB;
199             pv->u.blob.cbSize = size;
200             pv->u.blob.pBlobData = CoTaskMemAlloc(size);
201             if (!pv->u.blob.pBlobData)
202                 hr = E_OUTOFMEMORY;
203             else
204                 RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_BINARY, NULL, (BYTE*)pv->u.blob.pBlobData, &size);
205             break;
206         }
207         default:
208             ERR("Unknown/unhandled type: %u\n", type);
209             PropVariantClear(pv);
210             break;
211     }
212     RegCloseKey(regkey);
213     return hr;
214 }
215
216 static HRESULT MMDevice_SetPropValue(const GUID *devguid, DWORD flow, REFPROPERTYKEY key, REFPROPVARIANT pv)
217 {
218     WCHAR buffer[80];
219     const GUID *id = &key->fmtid;
220     HRESULT hr;
221     HKEY regkey;
222     LONG ret;
223
224     hr = MMDevPropStore_OpenPropKey(devguid, flow, &regkey);
225     if (FAILED(hr))
226         return hr;
227     wsprintfW( buffer, propkey_formatW, id->Data1, id->Data2, id->Data3,
228                id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
229                id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7], key->pid );
230     switch (pv->vt)
231     {
232         case VT_UI4:
233         {
234             ret = RegSetValueExW(regkey, buffer, 0, REG_DWORD, (const BYTE*)&pv->u.ulVal, sizeof(DWORD));
235             break;
236         }
237         case VT_BLOB:
238         {
239             ret = RegSetValueExW(regkey, buffer, 0, REG_BINARY, pv->u.blob.pBlobData, pv->u.blob.cbSize);
240             TRACE("Blob %p %u\n", pv->u.blob.pBlobData, pv->u.blob.cbSize);
241
242             break;
243         }
244         case VT_LPWSTR:
245         {
246             ret = RegSetValueExW(regkey, buffer, 0, REG_SZ, (const BYTE*)pv->u.pwszVal, sizeof(WCHAR)*(1+lstrlenW(pv->u.pwszVal)));
247             break;
248         }
249         default:
250             ret = 0;
251             FIXME("Unhandled type %u\n", pv->vt);
252             hr = E_INVALIDARG;
253             break;
254     }
255     RegCloseKey(regkey);
256     TRACE("Writing %s returned %u\n", debugstr_w(buffer), ret);
257     return hr;
258 }
259
260 /* Creates or updates the state of a device
261  * If GUID is null, a random guid will be assigned
262  * and the device will be created
263  */
264 static MMDevice *MMDevice_Create(WCHAR *name, void *devkey, GUID *id, EDataFlow flow, DWORD state, BOOL setdefault)
265 {
266     HKEY key, root;
267     MMDevice *cur;
268     WCHAR guidstr[39];
269     DWORD i;
270
271     for (i = 0; i < MMDevice_count; ++i)
272     {
273         cur = MMDevice_head[i];
274         if (cur->flow == flow && !lstrcmpW(cur->drv_id, name))
275         {
276             LONG ret;
277             /* Same device, update state */
278             cur->state = state;
279             cur->key = devkey;
280             StringFromGUID2(&cur->devguid, guidstr, sizeof(guidstr)/sizeof(*guidstr));
281             ret = RegOpenKeyExW(flow == eRender ? key_render : key_capture, guidstr, 0, KEY_WRITE, &key);
282             if (ret == ERROR_SUCCESS)
283             {
284                 RegSetValueExW(key, reg_devicestate, 0, REG_DWORD, (const BYTE*)&state, sizeof(DWORD));
285                 RegCloseKey(key);
286             }
287             goto done;
288         }
289     }
290
291     /* No device found, allocate new one */
292     cur = HeapAlloc(GetProcessHeap(), 0, sizeof(*cur));
293     if (!cur){
294         HeapFree(GetProcessHeap(), 0, devkey);
295         return NULL;
296     }
297     cur->drv_id = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(name)+1)*sizeof(WCHAR));
298     if (!cur->drv_id)
299     {
300         HeapFree(GetProcessHeap(), 0, cur);
301         HeapFree(GetProcessHeap(), 0, devkey);
302         return NULL;
303     }
304     lstrcpyW(cur->drv_id, name);
305     cur->key = devkey;
306     cur->IMMDevice_iface.lpVtbl = &MMDeviceVtbl;
307     cur->IMMEndpoint_iface.lpVtbl = &MMEndpointVtbl;
308     cur->ref = 0;
309     InitializeCriticalSection(&cur->crst);
310     cur->crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": MMDevice.crst");
311     cur->flow = flow;
312     cur->state = state;
313     if (!id)
314     {
315         id = &cur->devguid;
316         CoCreateGuid(id);
317     }
318     cur->devguid = *id;
319     StringFromGUID2(id, guidstr, sizeof(guidstr)/sizeof(*guidstr));
320     if (flow == eRender)
321         root = key_render;
322     else
323         root = key_capture;
324     if (!RegCreateKeyExW(root, guidstr, 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &key, NULL))
325     {
326         HKEY keyprop;
327         RegSetValueExW(key, reg_devicestate, 0, REG_DWORD, (const BYTE*)&state, sizeof(DWORD));
328         if (!RegCreateKeyExW(key, reg_properties, 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &keyprop, NULL))
329         {
330             PROPVARIANT pv;
331             pv.vt = VT_LPWSTR;
332             pv.u.pwszVal = name;
333             MMDevice_SetPropValue(id, flow, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pv);
334             MMDevice_SetPropValue(id, flow, (const PROPERTYKEY*)&DEVPKEY_Device_DeviceDesc, &pv);
335             RegCloseKey(keyprop);
336         }
337         RegCloseKey(key);
338     }
339     if (!MMDevice_head)
340         MMDevice_head = HeapAlloc(GetProcessHeap(), 0, sizeof(*MMDevice_head));
341     else
342         MMDevice_head = HeapReAlloc(GetProcessHeap(), 0, MMDevice_head, sizeof(*MMDevice_head)*(1+MMDevice_count));
343     MMDevice_head[MMDevice_count++] = cur;
344
345 done:
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             MMDevice_Create(pv.u.pwszVal, NULL, &guid, curflow,
405                     DEVICE_STATE_NOTPRESENT, FALSE);
406             CoTaskMemFree(pv.u.pwszVal);
407         }
408     } while (1);
409
410     return S_OK;
411 }
412
413 static HRESULT set_format(MMDevice *dev)
414 {
415     HRESULT hr;
416     IAudioClient *client;
417     WAVEFORMATEX *fmt;
418     PROPVARIANT pv = { VT_EMPTY };
419
420     hr = drvs.pGetAudioEndpoint(dev->key, &dev->IMMDevice_iface, dev->flow, &client);
421     if(FAILED(hr))
422         return hr;
423
424     hr = IAudioClient_GetMixFormat(client, &fmt);
425     if(FAILED(hr)){
426         IAudioClient_Release(client);
427         return hr;
428     }
429
430     IAudioClient_Release(client);
431
432     pv.vt = VT_BLOB;
433     pv.u.blob.cbSize = sizeof(WAVEFORMATEX) + fmt->cbSize;
434     pv.u.blob.pBlobData = (BYTE*)fmt;
435     MMDevice_SetPropValue(&dev->devguid, dev->flow,
436             &PKEY_AudioEngine_DeviceFormat, &pv);
437     MMDevice_SetPropValue(&dev->devguid, dev->flow,
438             &PKEY_AudioEngine_OEMFormat, &pv);
439
440     return S_OK;
441 }
442
443 static HRESULT load_driver_devices(EDataFlow flow)
444 {
445     WCHAR **ids;
446     void **keys;
447     UINT num, def, i;
448     HRESULT hr;
449
450     if(!drvs.pGetEndpointIDs)
451         return S_OK;
452
453     hr = drvs.pGetEndpointIDs(flow, &ids, &keys, &num, &def);
454     if(FAILED(hr))
455         return hr;
456
457     for(i = 0; i < num; ++i){
458         MMDevice *dev;
459         dev = MMDevice_Create(ids[i], keys[i], NULL, flow, DEVICE_STATE_ACTIVE,
460                 def == i);
461         set_format(dev);
462         HeapFree(GetProcessHeap(), 0, ids[i]);
463     }
464
465     HeapFree(GetProcessHeap(), 0, keys);
466     HeapFree(GetProcessHeap(), 0, ids);
467
468     return S_OK;
469 }
470
471 static void MMDevice_Destroy(MMDevice *This)
472 {
473     DWORD i;
474     TRACE("Freeing %s\n", debugstr_w(This->drv_id));
475     /* Since this function is called at destruction time, reordering of the list is unimportant */
476     for (i = 0; i < MMDevice_count; ++i)
477     {
478         if (MMDevice_head[i] == This)
479         {
480             MMDevice_head[i] = MMDevice_head[--MMDevice_count];
481             break;
482         }
483     }
484     This->crst.DebugInfo->Spare[0] = 0;
485     DeleteCriticalSection(&This->crst);
486     HeapFree(GetProcessHeap(), 0, This->drv_id);
487     HeapFree(GetProcessHeap(), 0, This->key);
488     HeapFree(GetProcessHeap(), 0, This);
489 }
490
491 static inline MMDevice *impl_from_IMMDevice(IMMDevice *iface)
492 {
493     return CONTAINING_RECORD(iface, MMDevice, IMMDevice_iface);
494 }
495
496 static HRESULT WINAPI MMDevice_QueryInterface(IMMDevice *iface, REFIID riid, void **ppv)
497 {
498     MMDevice *This = impl_from_IMMDevice(iface);
499     TRACE("(%p)->(%s,%p)\n", iface, debugstr_guid(riid), ppv);
500
501     if (!ppv)
502         return E_POINTER;
503     *ppv = NULL;
504     if (IsEqualIID(riid, &IID_IUnknown)
505         || IsEqualIID(riid, &IID_IMMDevice))
506         *ppv = This;
507     else if (IsEqualIID(riid, &IID_IMMEndpoint))
508         *ppv = &This->IMMEndpoint_iface;
509     if (*ppv)
510     {
511         IUnknown_AddRef((IUnknown*)*ppv);
512         return S_OK;
513     }
514     WARN("Unknown interface %s\n", debugstr_guid(riid));
515     return E_NOINTERFACE;
516 }
517
518 static ULONG WINAPI MMDevice_AddRef(IMMDevice *iface)
519 {
520     MMDevice *This = impl_from_IMMDevice(iface);
521     LONG ref;
522
523     ref = InterlockedIncrement(&This->ref);
524     TRACE("Refcount now %i\n", ref);
525     return ref;
526 }
527
528 static ULONG WINAPI MMDevice_Release(IMMDevice *iface)
529 {
530     MMDevice *This = impl_from_IMMDevice(iface);
531     LONG ref;
532
533     ref = InterlockedDecrement(&This->ref);
534     TRACE("Refcount now %i\n", ref);
535     return ref;
536 }
537
538 static HRESULT WINAPI MMDevice_Activate(IMMDevice *iface, REFIID riid, DWORD clsctx, PROPVARIANT *params, void **ppv)
539 {
540     HRESULT hr = E_NOINTERFACE;
541     MMDevice *This = impl_from_IMMDevice(iface);
542
543     TRACE("(%p)->(%p,%x,%p,%p)\n", iface, riid, clsctx, params, ppv);
544
545     if (!ppv)
546         return E_POINTER;
547
548     if (IsEqualIID(riid, &IID_IAudioClient)){
549         hr = drvs.pGetAudioEndpoint(This->key, iface, This->flow, (IAudioClient**)ppv);
550     }else if (IsEqualIID(riid, &IID_IAudioEndpointVolume))
551         hr = AudioEndpointVolume_Create(This, (IAudioEndpointVolume**)ppv);
552     else if (IsEqualIID(riid, &IID_IAudioSessionManager)
553              || IsEqualIID(riid, &IID_IAudioSessionManager2))
554     {
555         hr = drvs.pGetAudioSessionManager(iface, (IAudioSessionManager2**)ppv);
556     }
557     else if (IsEqualIID(riid, &IID_IBaseFilter))
558     {
559         if (This->flow == eRender)
560             hr = CoCreateInstance(&CLSID_DSoundRender, NULL, clsctx, riid, ppv);
561         else
562             ERR("Not supported for recording?\n");
563         if (SUCCEEDED(hr))
564         {
565             IPersistPropertyBag *ppb;
566             hr = IUnknown_QueryInterface((IUnknown*)*ppv, &IID_IPersistPropertyBag, (void*)&ppb);
567             if (SUCCEEDED(hr))
568             {
569                 /* ::Load cannot assume the interface stays alive after the function returns,
570                  * so just create the interface on the stack, saves a lot of complicated code */
571                 IPropertyBagImpl bag = { { &PB_Vtbl }, This->devguid };
572                 hr = IPersistPropertyBag_Load(ppb, &bag.IPropertyBag_iface, NULL);
573                 IPersistPropertyBag_Release(ppb);
574                 if (FAILED(hr))
575                     IBaseFilter_Release((IBaseFilter*)*ppv);
576             }
577             else
578             {
579                 FIXME("Wine doesn't support IPersistPropertyBag on DSoundRender yet, ignoring..\n");
580                 hr = S_OK;
581             }
582         }
583     }
584     else if (IsEqualIID(riid, &IID_IDeviceTopology))
585     {
586         FIXME("IID_IDeviceTopology unsupported\n");
587     }
588     else if (IsEqualIID(riid, &IID_IDirectSound)
589              || IsEqualIID(riid, &IID_IDirectSound8))
590     {
591         if (This->flow == eRender)
592             hr = CoCreateInstance(&CLSID_DirectSound8, NULL, clsctx, riid, ppv);
593         if (SUCCEEDED(hr))
594         {
595             hr = IDirectSound_Initialize((IDirectSound*)*ppv, &This->devguid);
596             if (FAILED(hr))
597                 IDirectSound_Release((IDirectSound*)*ppv);
598         }
599     }
600     else if (IsEqualIID(riid, &IID_IDirectSoundCapture)
601              || IsEqualIID(riid, &IID_IDirectSoundCapture8))
602     {
603         if (This->flow == eCapture)
604             hr = CoCreateInstance(&CLSID_DirectSoundCapture8, NULL, clsctx, riid, ppv);
605         if (SUCCEEDED(hr))
606         {
607             hr = IDirectSoundCapture_Initialize((IDirectSoundCapture*)*ppv, &This->devguid);
608             if (FAILED(hr))
609                 IDirectSoundCapture_Release((IDirectSoundCapture*)*ppv);
610         }
611     }
612     else
613         ERR("Invalid/unknown iid %s\n", debugstr_guid(riid));
614
615     if (FAILED(hr))
616         *ppv = NULL;
617
618     TRACE("Returning %08x\n", hr);
619     return hr;
620 }
621
622 static HRESULT WINAPI MMDevice_OpenPropertyStore(IMMDevice *iface, DWORD access, IPropertyStore **ppv)
623 {
624     MMDevice *This = impl_from_IMMDevice(iface);
625     TRACE("(%p)->(%x,%p)\n", This, access, ppv);
626
627     if (!ppv)
628         return E_POINTER;
629     return MMDevPropStore_Create(This, access, ppv);
630 }
631
632 static HRESULT WINAPI MMDevice_GetId(IMMDevice *iface, WCHAR **itemid)
633 {
634     MMDevice *This = impl_from_IMMDevice(iface);
635     WCHAR *str;
636     GUID *id = &This->devguid;
637     static const WCHAR formatW[] = { '{','0','.','0','.','0','.','0','0','0','0','0','0','0','0','}','.',
638                                      '{','%','0','8','X','-','%','0','4','X','-',
639                                      '%','0','4','X','-','%','0','2','X','%','0','2','X','-',
640                                      '%','0','2','X','%','0','2','X','%','0','2','X','%','0','2','X',
641                                      '%','0','2','X','%','0','2','X','}',0 };
642
643     TRACE("(%p)->(%p)\n", This, itemid);
644     if (!itemid)
645         return E_POINTER;
646     *itemid = str = CoTaskMemAlloc(56 * sizeof(WCHAR));
647     if (!str)
648         return E_OUTOFMEMORY;
649     wsprintfW( str, formatW, id->Data1, id->Data2, id->Data3,
650                id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
651                id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7] );
652     return S_OK;
653 }
654
655 static HRESULT WINAPI MMDevice_GetState(IMMDevice *iface, DWORD *state)
656 {
657     MMDevice *This = impl_from_IMMDevice(iface);
658     TRACE("(%p)->(%p)\n", iface, state);
659
660     if (!state)
661         return E_POINTER;
662     *state = This->state;
663     return S_OK;
664 }
665
666 static const IMMDeviceVtbl MMDeviceVtbl =
667 {
668     MMDevice_QueryInterface,
669     MMDevice_AddRef,
670     MMDevice_Release,
671     MMDevice_Activate,
672     MMDevice_OpenPropertyStore,
673     MMDevice_GetId,
674     MMDevice_GetState
675 };
676
677 static inline MMDevice *impl_from_IMMEndpoint(IMMEndpoint *iface)
678 {
679     return CONTAINING_RECORD(iface, MMDevice, IMMEndpoint_iface);
680 }
681
682 static HRESULT WINAPI MMEndpoint_QueryInterface(IMMEndpoint *iface, REFIID riid, void **ppv)
683 {
684     MMDevice *This = impl_from_IMMEndpoint(iface);
685     return IMMDevice_QueryInterface(&This->IMMDevice_iface, riid, ppv);
686 }
687
688 static ULONG WINAPI MMEndpoint_AddRef(IMMEndpoint *iface)
689 {
690     MMDevice *This = impl_from_IMMEndpoint(iface);
691     return IMMDevice_AddRef(&This->IMMDevice_iface);
692 }
693
694 static ULONG WINAPI MMEndpoint_Release(IMMEndpoint *iface)
695 {
696     MMDevice *This = impl_from_IMMEndpoint(iface);
697     return IMMDevice_Release(&This->IMMDevice_iface);
698 }
699
700 static HRESULT WINAPI MMEndpoint_GetDataFlow(IMMEndpoint *iface, EDataFlow *flow)
701 {
702     MMDevice *This = impl_from_IMMEndpoint(iface);
703     if (!flow)
704         return E_POINTER;
705     *flow = This->flow;
706     return S_OK;
707 }
708
709 static const IMMEndpointVtbl MMEndpointVtbl =
710 {
711     MMEndpoint_QueryInterface,
712     MMEndpoint_AddRef,
713     MMEndpoint_Release,
714     MMEndpoint_GetDataFlow
715 };
716
717 static HRESULT MMDevCol_Create(IMMDeviceCollection **ppv, EDataFlow flow, DWORD state)
718 {
719     MMDevColImpl *This;
720
721     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
722     *ppv = NULL;
723     if (!This)
724         return E_OUTOFMEMORY;
725     This->IMMDeviceCollection_iface.lpVtbl = &MMDevColVtbl;
726     This->ref = 1;
727     This->flow = flow;
728     This->state = state;
729     *ppv = &This->IMMDeviceCollection_iface;
730     return S_OK;
731 }
732
733 static void MMDevCol_Destroy(MMDevColImpl *This)
734 {
735     HeapFree(GetProcessHeap(), 0, This);
736 }
737
738 static HRESULT WINAPI MMDevCol_QueryInterface(IMMDeviceCollection *iface, REFIID riid, void **ppv)
739 {
740     MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
741
742     if (!ppv)
743         return E_POINTER;
744     if (IsEqualIID(riid, &IID_IUnknown)
745         || IsEqualIID(riid, &IID_IMMDeviceCollection))
746         *ppv = This;
747     else
748         *ppv = NULL;
749     if (!*ppv)
750         return E_NOINTERFACE;
751     IUnknown_AddRef((IUnknown*)*ppv);
752     return S_OK;
753 }
754
755 static ULONG WINAPI MMDevCol_AddRef(IMMDeviceCollection *iface)
756 {
757     MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
758     LONG ref = InterlockedIncrement(&This->ref);
759     TRACE("Refcount now %i\n", ref);
760     return ref;
761 }
762
763 static ULONG WINAPI MMDevCol_Release(IMMDeviceCollection *iface)
764 {
765     MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
766     LONG ref = InterlockedDecrement(&This->ref);
767     TRACE("Refcount now %i\n", ref);
768     if (!ref)
769         MMDevCol_Destroy(This);
770     return ref;
771 }
772
773 static HRESULT WINAPI MMDevCol_GetCount(IMMDeviceCollection *iface, UINT *numdevs)
774 {
775     MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
776     DWORD i;
777
778     TRACE("(%p)->(%p)\n", This, numdevs);
779     if (!numdevs)
780         return E_POINTER;
781
782     *numdevs = 0;
783     for (i = 0; i < MMDevice_count; ++i)
784     {
785         MMDevice *cur = MMDevice_head[i];
786         if ((cur->flow == This->flow || This->flow == eAll)
787             && (cur->state & This->state))
788             ++(*numdevs);
789     }
790     return S_OK;
791 }
792
793 static HRESULT WINAPI MMDevCol_Item(IMMDeviceCollection *iface, UINT n, IMMDevice **dev)
794 {
795     MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
796     DWORD i = 0, j = 0;
797
798     TRACE("(%p)->(%u, %p)\n", This, n, dev);
799     if (!dev)
800         return E_POINTER;
801
802     for (j = 0; j < MMDevice_count; ++j)
803     {
804         MMDevice *cur = MMDevice_head[j];
805         if ((cur->flow == This->flow || This->flow == eAll)
806             && (cur->state & This->state)
807             && i++ == n)
808         {
809             *dev = &cur->IMMDevice_iface;
810             IMMDevice_AddRef(*dev);
811             return S_OK;
812         }
813     }
814     WARN("Could not obtain item %u\n", n);
815     *dev = NULL;
816     return E_INVALIDARG;
817 }
818
819 static const IMMDeviceCollectionVtbl MMDevColVtbl =
820 {
821     MMDevCol_QueryInterface,
822     MMDevCol_AddRef,
823     MMDevCol_Release,
824     MMDevCol_GetCount,
825     MMDevCol_Item
826 };
827
828 HRESULT MMDevEnum_Create(REFIID riid, void **ppv)
829 {
830     MMDevEnumImpl *This = MMDevEnumerator;
831
832     if (!This)
833     {
834         This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
835         *ppv = NULL;
836         if (!This)
837             return E_OUTOFMEMORY;
838         This->ref = 1;
839         This->IMMDeviceEnumerator_iface.lpVtbl = &MMDevEnumVtbl;
840         MMDevEnumerator = This;
841
842         load_devices_from_reg();
843         load_driver_devices(eRender);
844         load_driver_devices(eCapture);
845     }
846     return IUnknown_QueryInterface((IUnknown*)This, riid, ppv);
847 }
848
849 void MMDevEnum_Free(void)
850 {
851     while (MMDevice_count)
852         MMDevice_Destroy(MMDevice_head[0]);
853     RegCloseKey(key_render);
854     RegCloseKey(key_capture);
855     key_render = key_capture = NULL;
856     HeapFree(GetProcessHeap(), 0, MMDevEnumerator);
857     MMDevEnumerator = NULL;
858 }
859
860 static HRESULT WINAPI MMDevEnum_QueryInterface(IMMDeviceEnumerator *iface, REFIID riid, void **ppv)
861 {
862     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
863
864     if (!ppv)
865         return E_POINTER;
866     if (IsEqualIID(riid, &IID_IUnknown)
867         || IsEqualIID(riid, &IID_IMMDeviceEnumerator))
868         *ppv = This;
869     else
870         *ppv = NULL;
871     if (!*ppv)
872         return E_NOINTERFACE;
873     IUnknown_AddRef((IUnknown*)*ppv);
874     return S_OK;
875 }
876
877 static ULONG WINAPI MMDevEnum_AddRef(IMMDeviceEnumerator *iface)
878 {
879     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
880     LONG ref = InterlockedIncrement(&This->ref);
881     TRACE("Refcount now %i\n", ref);
882     return ref;
883 }
884
885 static ULONG WINAPI MMDevEnum_Release(IMMDeviceEnumerator *iface)
886 {
887     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
888     LONG ref = InterlockedDecrement(&This->ref);
889     if (!ref)
890         MMDevEnum_Free();
891     TRACE("Refcount now %i\n", ref);
892     return ref;
893 }
894
895 static HRESULT WINAPI MMDevEnum_EnumAudioEndpoints(IMMDeviceEnumerator *iface, EDataFlow flow, DWORD mask, IMMDeviceCollection **devices)
896 {
897     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
898     TRACE("(%p)->(%u,%u,%p)\n", This, flow, mask, devices);
899     if (!devices)
900         return E_POINTER;
901     *devices = NULL;
902     if (flow >= EDataFlow_enum_count)
903         return E_INVALIDARG;
904     if (mask & ~DEVICE_STATEMASK_ALL)
905         return E_INVALIDARG;
906     return MMDevCol_Create(devices, flow, mask);
907 }
908
909 static HRESULT WINAPI MMDevEnum_GetDefaultAudioEndpoint(IMMDeviceEnumerator *iface, EDataFlow flow, ERole role, IMMDevice **device)
910 {
911     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
912     WCHAR reg_key[256];
913     HKEY key;
914     HRESULT hr;
915
916     static const WCHAR slashW[] = {'\\',0};
917     static const WCHAR reg_out_nameW[] = {'D','e','f','a','u','l','t','O','u','t','p','u','t',0};
918     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};
919     static const WCHAR reg_in_nameW[] = {'D','e','f','a','u','l','t','I','n','p','u','t',0};
920     static const WCHAR reg_vin_nameW[] = {'D','e','f','a','u','l','t','V','o','i','c','e','I','n','p','u','t',0};
921
922     TRACE("(%p)->(%u,%u,%p)\n", This, flow, role, device);
923
924     if (!device)
925         return E_POINTER;
926
927     if((flow != eRender && flow != eCapture) ||
928             (role != eConsole && role != eMultimedia && role != eCommunications)){
929         WARN("Unknown flow (%u) or role (%u)\n", flow, role);
930         return E_INVALIDARG;
931     }
932
933     *device = NULL;
934
935     if(!drvs.module_name[0])
936         return E_NOTFOUND;
937
938     lstrcpyW(reg_key, drv_keyW);
939     lstrcatW(reg_key, slashW);
940     lstrcatW(reg_key, drvs.module_name);
941
942     if(RegOpenKeyW(HKEY_CURRENT_USER, reg_key, &key) == ERROR_SUCCESS){
943         const WCHAR *reg_x_name, *reg_vx_name;
944         WCHAR def_id[256];
945         DWORD size = sizeof(def_id);
946
947         if(flow == eRender){
948             reg_x_name = reg_out_nameW;
949             reg_vx_name = reg_vout_nameW;
950         }else{
951             reg_x_name = reg_in_nameW;
952             reg_vx_name = reg_vin_nameW;
953         }
954
955         if(role == eCommunications &&
956                 RegQueryValueExW(key, reg_vx_name, 0, NULL,
957                     (BYTE*)def_id, &size) == ERROR_SUCCESS){
958             hr = IMMDeviceEnumerator_GetDevice(iface, def_id, device);
959             if(SUCCEEDED(hr)){
960                 RegCloseKey(key);
961                 return S_OK;
962             }
963
964             TRACE("Unable to find voice device %s\n", wine_dbgstr_w(def_id));
965         }
966
967         if(RegQueryValueExW(key, reg_x_name, 0, NULL,
968                     (BYTE*)def_id, &size) == ERROR_SUCCESS){
969             hr = IMMDeviceEnumerator_GetDevice(iface, def_id, device);
970             if(SUCCEEDED(hr)){
971                 RegCloseKey(key);
972                 return S_OK;
973             }
974
975             TRACE("Unable to find device %s\n", wine_dbgstr_w(def_id));
976         }
977
978         RegCloseKey(key);
979     }
980
981     if (flow == eRender)
982         *device = &MMDevice_def_play->IMMDevice_iface;
983     else
984         *device = &MMDevice_def_rec->IMMDevice_iface;
985
986     if (!*device)
987         return E_NOTFOUND;
988     IMMDevice_AddRef(*device);
989     return S_OK;
990 }
991
992 static HRESULT WINAPI MMDevEnum_GetDevice(IMMDeviceEnumerator *iface, const WCHAR *name, IMMDevice **device)
993 {
994     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
995     DWORD i=0;
996     IMMDevice *dev = NULL;
997
998     static const WCHAR wine_info_deviceW[] = {'W','i','n','e',' ',
999         'i','n','f','o',' ','d','e','v','i','c','e',0};
1000
1001     TRACE("(%p)->(%s,%p)\n", This, debugstr_w(name), device);
1002
1003     if(!name || !device)
1004         return E_POINTER;
1005
1006     if(!lstrcmpW(name, wine_info_deviceW)){
1007         *device = &info_device;
1008         return S_OK;
1009     }
1010
1011     for (i = 0; i < MMDevice_count; ++i)
1012     {
1013         WCHAR *str;
1014         dev = &MMDevice_head[i]->IMMDevice_iface;
1015         IMMDevice_GetId(dev, &str);
1016
1017         if (str && !lstrcmpW(str, name))
1018         {
1019             CoTaskMemFree(str);
1020             IUnknown_AddRef(dev);
1021             *device = dev;
1022             return S_OK;
1023         }
1024         CoTaskMemFree(str);
1025     }
1026     TRACE("Could not find device %s\n", debugstr_w(name));
1027     return E_INVALIDARG;
1028 }
1029
1030 static HRESULT WINAPI MMDevEnum_RegisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
1031 {
1032     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1033     TRACE("(%p)->(%p)\n", This, client);
1034     FIXME("stub\n");
1035     return S_OK;
1036 }
1037
1038 static HRESULT WINAPI MMDevEnum_UnregisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
1039 {
1040     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1041     TRACE("(%p)->(%p)\n", This, client);
1042     FIXME("stub\n");
1043     return S_OK;
1044 }
1045
1046 static const IMMDeviceEnumeratorVtbl MMDevEnumVtbl =
1047 {
1048     MMDevEnum_QueryInterface,
1049     MMDevEnum_AddRef,
1050     MMDevEnum_Release,
1051     MMDevEnum_EnumAudioEndpoints,
1052     MMDevEnum_GetDefaultAudioEndpoint,
1053     MMDevEnum_GetDevice,
1054     MMDevEnum_RegisterEndpointNotificationCallback,
1055     MMDevEnum_UnregisterEndpointNotificationCallback
1056 };
1057
1058 static HRESULT MMDevPropStore_Create(MMDevice *parent, DWORD access, IPropertyStore **ppv)
1059 {
1060     MMDevPropStore *This;
1061     if (access != STGM_READ
1062         && access != STGM_WRITE
1063         && access != STGM_READWRITE)
1064     {
1065         WARN("Invalid access %08x\n", access);
1066         return E_INVALIDARG;
1067     }
1068     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
1069     *ppv = &This->IPropertyStore_iface;
1070     if (!This)
1071         return E_OUTOFMEMORY;
1072     This->IPropertyStore_iface.lpVtbl = &MMDevPropVtbl;
1073     This->ref = 1;
1074     This->parent = parent;
1075     This->access = access;
1076     return S_OK;
1077 }
1078
1079 static void MMDevPropStore_Destroy(MMDevPropStore *This)
1080 {
1081     HeapFree(GetProcessHeap(), 0, This);
1082 }
1083
1084 static HRESULT WINAPI MMDevPropStore_QueryInterface(IPropertyStore *iface, REFIID riid, void **ppv)
1085 {
1086     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1087
1088     if (!ppv)
1089         return E_POINTER;
1090     if (IsEqualIID(riid, &IID_IUnknown)
1091         || IsEqualIID(riid, &IID_IPropertyStore))
1092         *ppv = This;
1093     else
1094         *ppv = NULL;
1095     if (!*ppv)
1096         return E_NOINTERFACE;
1097     IUnknown_AddRef((IUnknown*)*ppv);
1098     return S_OK;
1099 }
1100
1101 static ULONG WINAPI MMDevPropStore_AddRef(IPropertyStore *iface)
1102 {
1103     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1104     LONG ref = InterlockedIncrement(&This->ref);
1105     TRACE("Refcount now %i\n", ref);
1106     return ref;
1107 }
1108
1109 static ULONG WINAPI MMDevPropStore_Release(IPropertyStore *iface)
1110 {
1111     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1112     LONG ref = InterlockedDecrement(&This->ref);
1113     TRACE("Refcount now %i\n", ref);
1114     if (!ref)
1115         MMDevPropStore_Destroy(This);
1116     return ref;
1117 }
1118
1119 static HRESULT WINAPI MMDevPropStore_GetCount(IPropertyStore *iface, DWORD *nprops)
1120 {
1121     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1122     WCHAR buffer[50];
1123     DWORD i = 0;
1124     HKEY propkey;
1125     HRESULT hr;
1126
1127     TRACE("(%p)->(%p)\n", iface, nprops);
1128     if (!nprops)
1129         return E_POINTER;
1130     hr = MMDevPropStore_OpenPropKey(&This->parent->devguid, This->parent->flow, &propkey);
1131     if (FAILED(hr))
1132         return hr;
1133     *nprops = 0;
1134     do {
1135         DWORD len = sizeof(buffer)/sizeof(*buffer);
1136         if (RegEnumKeyExW(propkey, i, buffer, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
1137             break;
1138         i++;
1139     } while (0);
1140     RegCloseKey(propkey);
1141     TRACE("Returning %i\n", i);
1142     *nprops = i;
1143     return S_OK;
1144 }
1145
1146 static HRESULT WINAPI MMDevPropStore_GetAt(IPropertyStore *iface, DWORD prop, PROPERTYKEY *key)
1147 {
1148     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1149     WCHAR buffer[50];
1150     DWORD len = sizeof(buffer)/sizeof(*buffer);
1151     HRESULT hr;
1152     HKEY propkey;
1153
1154     TRACE("(%p)->(%u,%p)\n", iface, prop, key);
1155     if (!key)
1156         return E_POINTER;
1157
1158     hr = MMDevPropStore_OpenPropKey(&This->parent->devguid, This->parent->flow, &propkey);
1159     if (FAILED(hr))
1160         return hr;
1161
1162     if (RegEnumKeyExW(propkey, prop, buffer, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS
1163         || len <= 40)
1164     {
1165         WARN("GetAt %u failed\n", prop);
1166         return E_INVALIDARG;
1167     }
1168     RegCloseKey(propkey);
1169     buffer[39] = 0;
1170     CLSIDFromString(buffer, &key->fmtid);
1171     key->pid = atoiW(&buffer[40]);
1172     return S_OK;
1173 }
1174
1175 static HRESULT WINAPI MMDevPropStore_GetValue(IPropertyStore *iface, REFPROPERTYKEY key, PROPVARIANT *pv)
1176 {
1177     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1178     TRACE("(%p)->(\"%s,%u\", %p\n", This, debugstr_guid(&key->fmtid), key ? key->pid : 0, pv);
1179
1180     if (!key || !pv)
1181         return E_POINTER;
1182     if (This->access != STGM_READ
1183         && This->access != STGM_READWRITE)
1184         return STG_E_ACCESSDENIED;
1185
1186     /* Special case */
1187     if (IsEqualPropertyKey(*key, PKEY_AudioEndpoint_GUID))
1188     {
1189         pv->vt = VT_LPWSTR;
1190         pv->u.pwszVal = CoTaskMemAlloc(39 * sizeof(WCHAR));
1191         if (!pv->u.pwszVal)
1192             return E_OUTOFMEMORY;
1193         StringFromGUID2(&This->parent->devguid, pv->u.pwszVal, 39);
1194         return S_OK;
1195     }
1196
1197     return MMDevice_GetPropValue(&This->parent->devguid, This->parent->flow, key, pv);
1198 }
1199
1200 static HRESULT WINAPI MMDevPropStore_SetValue(IPropertyStore *iface, REFPROPERTYKEY key, REFPROPVARIANT pv)
1201 {
1202     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1203
1204     if (!key || !pv)
1205         return E_POINTER;
1206
1207     if (This->access != STGM_WRITE
1208         && This->access != STGM_READWRITE)
1209         return STG_E_ACCESSDENIED;
1210     return MMDevice_SetPropValue(&This->parent->devguid, This->parent->flow, key, pv);
1211 }
1212
1213 static HRESULT WINAPI MMDevPropStore_Commit(IPropertyStore *iface)
1214 {
1215     FIXME("stub\n");
1216     return E_NOTIMPL;
1217 }
1218
1219 static const IPropertyStoreVtbl MMDevPropVtbl =
1220 {
1221     MMDevPropStore_QueryInterface,
1222     MMDevPropStore_AddRef,
1223     MMDevPropStore_Release,
1224     MMDevPropStore_GetCount,
1225     MMDevPropStore_GetAt,
1226     MMDevPropStore_GetValue,
1227     MMDevPropStore_SetValue,
1228     MMDevPropStore_Commit
1229 };
1230
1231
1232 /* Property bag for IBaseFilter activation */
1233 static HRESULT WINAPI PB_QueryInterface(IPropertyBag *iface, REFIID riid, void **ppv)
1234 {
1235     ERR("Should not be called\n");
1236     *ppv = NULL;
1237     return E_NOINTERFACE;
1238 }
1239
1240 static ULONG WINAPI PB_AddRef(IPropertyBag *iface)
1241 {
1242     ERR("Should not be called\n");
1243     return 2;
1244 }
1245
1246 static ULONG WINAPI PB_Release(IPropertyBag *iface)
1247 {
1248     ERR("Should not be called\n");
1249     return 1;
1250 }
1251
1252 static HRESULT WINAPI PB_Read(IPropertyBag *iface, LPCOLESTR name, VARIANT *var, IErrorLog *log)
1253 {
1254     static const WCHAR dsguid[] = { 'D','S','G','u','i','d', 0 };
1255     IPropertyBagImpl *This = impl_from_IPropertyBag(iface);
1256     TRACE("Trying to read %s, type %u\n", debugstr_w(name), var->n1.n2.vt);
1257     if (!lstrcmpW(name, dsguid))
1258     {
1259         WCHAR guidstr[39];
1260         StringFromGUID2(&This->devguid, guidstr,sizeof(guidstr)/sizeof(*guidstr));
1261         var->n1.n2.vt = VT_BSTR;
1262         var->n1.n2.n3.bstrVal = SysAllocString(guidstr);
1263         return S_OK;
1264     }
1265     ERR("Unknown property '%s' queried\n", debugstr_w(name));
1266     return E_FAIL;
1267 }
1268
1269 static HRESULT WINAPI PB_Write(IPropertyBag *iface, LPCOLESTR name, VARIANT *var)
1270 {
1271     ERR("Should not be called\n");
1272     return E_FAIL;
1273 }
1274
1275 static const IPropertyBagVtbl PB_Vtbl =
1276 {
1277     PB_QueryInterface,
1278     PB_AddRef,
1279     PB_Release,
1280     PB_Read,
1281     PB_Write
1282 };
1283
1284 static ULONG WINAPI info_device_ps_AddRef(IPropertyStore *iface)
1285 {
1286     return 2;
1287 }
1288
1289 static ULONG WINAPI info_device_ps_Release(IPropertyStore *iface)
1290 {
1291     return 1;
1292 }
1293
1294 static HRESULT WINAPI info_device_ps_GetValue(IPropertyStore *iface,
1295         REFPROPERTYKEY key, PROPVARIANT *pv)
1296 {
1297     TRACE("(static)->(\"%s,%u\", %p)\n", debugstr_guid(&key->fmtid), key ? key->pid : 0, pv);
1298
1299     if (!key || !pv)
1300         return E_POINTER;
1301
1302     if (IsEqualPropertyKey(*key, DEVPKEY_Device_Driver))
1303     {
1304         INT size = (lstrlenW(drvs.module_name) + 1) * sizeof(WCHAR);
1305         pv->vt = VT_LPWSTR;
1306         pv->u.pwszVal = CoTaskMemAlloc(size);
1307         if (!pv->u.pwszVal)
1308             return E_OUTOFMEMORY;
1309         memcpy(pv->u.pwszVal, drvs.module_name, size);
1310         return S_OK;
1311     }
1312
1313     return E_INVALIDARG;
1314 }
1315
1316 static const IPropertyStoreVtbl info_device_ps_Vtbl =
1317 {
1318     NULL,
1319     info_device_ps_AddRef,
1320     info_device_ps_Release,
1321     NULL,
1322     NULL,
1323     info_device_ps_GetValue,
1324     NULL,
1325     NULL
1326 };
1327
1328 static IPropertyStore info_device_ps = {
1329     &info_device_ps_Vtbl
1330 };
1331
1332 static ULONG WINAPI info_device_AddRef(IMMDevice *iface)
1333 {
1334     return 2;
1335 }
1336
1337 static ULONG WINAPI info_device_Release(IMMDevice *iface)
1338 {
1339     return 1;
1340 }
1341
1342 static HRESULT WINAPI info_device_OpenPropertyStore(IMMDevice *iface,
1343         DWORD access, IPropertyStore **ppv)
1344 {
1345     *ppv = &info_device_ps;
1346     return S_OK;
1347 }
1348
1349 static const IMMDeviceVtbl info_device_Vtbl =
1350 {
1351     NULL,
1352     info_device_AddRef,
1353     info_device_Release,
1354     NULL,
1355     info_device_OpenPropertyStore,
1356     NULL,
1357     NULL
1358 };
1359
1360 static IMMDevice info_device = {
1361     &info_device_Vtbl
1362 };