joy.cpl: Remove uneeded FIXME message.
[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 static 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, GUID *id, EDataFlow flow, DWORD state, BOOL setdefault)
265 {
266     HKEY key, root;
267     MMDevice *cur = NULL;
268     WCHAR guidstr[39];
269     DWORD i;
270
271     static const PROPERTYKEY deviceinterface_key = {
272         {0x233164c8, 0x1b2c, 0x4c7d, {0xbc, 0x68, 0xb6, 0x71, 0x68, 0x7a, 0x25, 0x67}}, 1
273     };
274
275     for (i = 0; i < MMDevice_count; ++i)
276     {
277         MMDevice *device = MMDevice_head[i];
278         if (device->flow == flow && IsEqualGUID(&device->devguid, id)){
279             cur = device;
280             break;
281         }
282     }
283
284     if(!cur){
285         /* No device found, allocate new one */
286         cur = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*cur));
287         if (!cur)
288             return NULL;
289
290         cur->IMMDevice_iface.lpVtbl = &MMDeviceVtbl;
291         cur->IMMEndpoint_iface.lpVtbl = &MMEndpointVtbl;
292
293         InitializeCriticalSection(&cur->crst);
294         cur->crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": MMDevice.crst");
295
296         if (!MMDevice_head)
297             MMDevice_head = HeapAlloc(GetProcessHeap(), 0, sizeof(*MMDevice_head));
298         else
299             MMDevice_head = HeapReAlloc(GetProcessHeap(), 0, MMDevice_head, sizeof(*MMDevice_head)*(1+MMDevice_count));
300         MMDevice_head[MMDevice_count++] = cur;
301     }else if(cur->ref > 0)
302         WARN("Modifying an MMDevice with postitive reference count!\n");
303
304     HeapFree(GetProcessHeap(), 0, cur->drv_id);
305     cur->drv_id = name;
306
307     cur->flow = flow;
308     cur->state = state;
309     cur->devguid = *id;
310
311     StringFromGUID2(&cur->devguid, guidstr, sizeof(guidstr)/sizeof(*guidstr));
312
313     if (flow == eRender)
314         root = key_render;
315     else
316         root = key_capture;
317
318     if (RegCreateKeyExW(root, guidstr, 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &key, NULL) == ERROR_SUCCESS)
319     {
320         HKEY keyprop;
321         RegSetValueExW(key, reg_devicestate, 0, REG_DWORD, (const BYTE*)&state, sizeof(DWORD));
322         if (!RegCreateKeyExW(key, reg_properties, 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &keyprop, NULL))
323         {
324             PROPVARIANT pv;
325
326             pv.vt = VT_LPWSTR;
327             pv.u.pwszVal = name;
328             MMDevice_SetPropValue(id, flow, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pv);
329             MMDevice_SetPropValue(id, flow, (const PROPERTYKEY*)&DEVPKEY_Device_DeviceDesc, &pv);
330
331             pv.u.pwszVal = guidstr;
332             MMDevice_SetPropValue(id, flow, &deviceinterface_key, &pv);
333
334             RegCloseKey(keyprop);
335         }
336         RegCloseKey(key);
337     }
338
339     if (setdefault)
340     {
341         if (flow == eRender)
342             MMDevice_def_play = cur;
343         else
344             MMDevice_def_rec = cur;
345     }
346     return cur;
347 }
348
349 static HRESULT load_devices_from_reg(void)
350 {
351     DWORD i = 0;
352     HKEY root, cur;
353     LONG ret;
354     DWORD curflow;
355
356     ret = RegCreateKeyExW(HKEY_LOCAL_MACHINE, software_mmdevapi, 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &root, NULL);
357     if (ret == ERROR_SUCCESS)
358         ret = RegCreateKeyExW(root, reg_capture, 0, NULL, 0, KEY_READ|KEY_WRITE, NULL, &key_capture, NULL);
359     if (ret == ERROR_SUCCESS)
360         ret = RegCreateKeyExW(root, reg_render, 0, NULL, 0, KEY_READ|KEY_WRITE, NULL, &key_render, NULL);
361     RegCloseKey(root);
362     cur = key_capture;
363     curflow = eCapture;
364     if (ret != ERROR_SUCCESS)
365     {
366         RegCloseKey(key_capture);
367         key_render = key_capture = NULL;
368         WARN("Couldn't create key: %u\n", ret);
369         return E_FAIL;
370     }
371
372     do {
373         WCHAR guidvalue[39];
374         GUID guid;
375         DWORD len;
376         PROPVARIANT pv = { VT_EMPTY };
377
378         len = sizeof(guidvalue)/sizeof(guidvalue[0]);
379         ret = RegEnumKeyExW(cur, i++, guidvalue, &len, NULL, NULL, NULL, NULL);
380         if (ret == ERROR_NO_MORE_ITEMS)
381         {
382             if (cur == key_capture)
383             {
384                 cur = key_render;
385                 curflow = eRender;
386                 i = 0;
387                 continue;
388             }
389             break;
390         }
391         if (ret != ERROR_SUCCESS)
392             continue;
393         if (SUCCEEDED(CLSIDFromString(guidvalue, &guid))
394             && SUCCEEDED(MMDevice_GetPropValue(&guid, curflow, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pv))
395             && pv.vt == VT_LPWSTR)
396         {
397             DWORD size_bytes = (strlenW(pv.u.pwszVal) + 1) * sizeof(WCHAR);
398             WCHAR *name = HeapAlloc(GetProcessHeap(), 0, size_bytes);
399             memcpy(name, pv.u.pwszVal, size_bytes);
400             MMDevice_Create(name, &guid, curflow,
401                     DEVICE_STATE_NOTPRESENT, FALSE);
402             CoTaskMemFree(pv.u.pwszVal);
403         }
404     } while (1);
405
406     return S_OK;
407 }
408
409 static HRESULT set_format(MMDevice *dev)
410 {
411     HRESULT hr;
412     IAudioClient *client;
413     WAVEFORMATEX *fmt;
414     PROPVARIANT pv = { VT_EMPTY };
415
416     hr = drvs.pGetAudioEndpoint(&dev->devguid, &dev->IMMDevice_iface, &client);
417     if(FAILED(hr))
418         return hr;
419
420     hr = IAudioClient_GetMixFormat(client, &fmt);
421     if(FAILED(hr)){
422         IAudioClient_Release(client);
423         return hr;
424     }
425
426     IAudioClient_Release(client);
427
428     pv.vt = VT_BLOB;
429     pv.u.blob.cbSize = sizeof(WAVEFORMATEX) + fmt->cbSize;
430     pv.u.blob.pBlobData = (BYTE*)fmt;
431     MMDevice_SetPropValue(&dev->devguid, dev->flow,
432             &PKEY_AudioEngine_DeviceFormat, &pv);
433     MMDevice_SetPropValue(&dev->devguid, dev->flow,
434             &PKEY_AudioEngine_OEMFormat, &pv);
435
436     return S_OK;
437 }
438
439 static HRESULT load_driver_devices(EDataFlow flow)
440 {
441     WCHAR **ids;
442     GUID *guids;
443     UINT num, def, i;
444     HRESULT hr;
445
446     if(!drvs.pGetEndpointIDs)
447         return S_OK;
448
449     hr = drvs.pGetEndpointIDs(flow, &ids, &guids, &num, &def);
450     if(FAILED(hr))
451         return hr;
452
453     for(i = 0; i < num; ++i){
454         MMDevice *dev;
455         dev = MMDevice_Create(ids[i], &guids[i], flow, DEVICE_STATE_ACTIVE,
456                 def == i);
457         set_format(dev);
458     }
459
460     HeapFree(GetProcessHeap(), 0, guids);
461     HeapFree(GetProcessHeap(), 0, ids);
462
463     return S_OK;
464 }
465
466 static void MMDevice_Destroy(MMDevice *This)
467 {
468     DWORD i;
469     TRACE("Freeing %s\n", debugstr_w(This->drv_id));
470     /* Since this function is called at destruction time, reordering of the list is unimportant */
471     for (i = 0; i < MMDevice_count; ++i)
472     {
473         if (MMDevice_head[i] == This)
474         {
475             MMDevice_head[i] = MMDevice_head[--MMDevice_count];
476             break;
477         }
478     }
479     This->crst.DebugInfo->Spare[0] = 0;
480     DeleteCriticalSection(&This->crst);
481     HeapFree(GetProcessHeap(), 0, This->drv_id);
482     HeapFree(GetProcessHeap(), 0, This);
483 }
484
485 static inline MMDevice *impl_from_IMMDevice(IMMDevice *iface)
486 {
487     return CONTAINING_RECORD(iface, MMDevice, IMMDevice_iface);
488 }
489
490 static HRESULT WINAPI MMDevice_QueryInterface(IMMDevice *iface, REFIID riid, void **ppv)
491 {
492     MMDevice *This = impl_from_IMMDevice(iface);
493     TRACE("(%p)->(%s,%p)\n", iface, debugstr_guid(riid), ppv);
494
495     if (!ppv)
496         return E_POINTER;
497     *ppv = NULL;
498     if (IsEqualIID(riid, &IID_IUnknown)
499         || IsEqualIID(riid, &IID_IMMDevice))
500         *ppv = This;
501     else if (IsEqualIID(riid, &IID_IMMEndpoint))
502         *ppv = &This->IMMEndpoint_iface;
503     if (*ppv)
504     {
505         IUnknown_AddRef((IUnknown*)*ppv);
506         return S_OK;
507     }
508     WARN("Unknown interface %s\n", debugstr_guid(riid));
509     return E_NOINTERFACE;
510 }
511
512 static ULONG WINAPI MMDevice_AddRef(IMMDevice *iface)
513 {
514     MMDevice *This = impl_from_IMMDevice(iface);
515     LONG ref;
516
517     ref = InterlockedIncrement(&This->ref);
518     TRACE("Refcount now %i\n", ref);
519     return ref;
520 }
521
522 static ULONG WINAPI MMDevice_Release(IMMDevice *iface)
523 {
524     MMDevice *This = impl_from_IMMDevice(iface);
525     LONG ref;
526
527     ref = InterlockedDecrement(&This->ref);
528     TRACE("Refcount now %i\n", ref);
529     return ref;
530 }
531
532 static HRESULT WINAPI MMDevice_Activate(IMMDevice *iface, REFIID riid, DWORD clsctx, PROPVARIANT *params, void **ppv)
533 {
534     HRESULT hr = E_NOINTERFACE;
535     MMDevice *This = impl_from_IMMDevice(iface);
536
537     TRACE("(%p)->(%p,%x,%p,%p)\n", iface, riid, clsctx, params, ppv);
538
539     if (!ppv)
540         return E_POINTER;
541
542     if (IsEqualIID(riid, &IID_IAudioClient)){
543         hr = drvs.pGetAudioEndpoint(&This->devguid, iface, (IAudioClient**)ppv);
544     }else if (IsEqualIID(riid, &IID_IAudioEndpointVolume))
545         hr = AudioEndpointVolume_Create(This, (IAudioEndpointVolume**)ppv);
546     else if (IsEqualIID(riid, &IID_IAudioSessionManager)
547              || IsEqualIID(riid, &IID_IAudioSessionManager2))
548     {
549         hr = drvs.pGetAudioSessionManager(iface, (IAudioSessionManager2**)ppv);
550     }
551     else if (IsEqualIID(riid, &IID_IBaseFilter))
552     {
553         if (This->flow == eRender)
554             hr = CoCreateInstance(&CLSID_DSoundRender, NULL, clsctx, riid, ppv);
555         else
556             ERR("Not supported for recording?\n");
557         if (SUCCEEDED(hr))
558         {
559             IPersistPropertyBag *ppb;
560             hr = IUnknown_QueryInterface((IUnknown*)*ppv, &IID_IPersistPropertyBag, (void*)&ppb);
561             if (SUCCEEDED(hr))
562             {
563                 /* ::Load cannot assume the interface stays alive after the function returns,
564                  * so just create the interface on the stack, saves a lot of complicated code */
565                 IPropertyBagImpl bag = { { &PB_Vtbl }, This->devguid };
566                 hr = IPersistPropertyBag_Load(ppb, &bag.IPropertyBag_iface, NULL);
567                 IPersistPropertyBag_Release(ppb);
568                 if (FAILED(hr))
569                     IBaseFilter_Release((IBaseFilter*)*ppv);
570             }
571             else
572             {
573                 FIXME("Wine doesn't support IPersistPropertyBag on DSoundRender yet, ignoring..\n");
574                 hr = S_OK;
575             }
576         }
577     }
578     else if (IsEqualIID(riid, &IID_IDeviceTopology))
579     {
580         FIXME("IID_IDeviceTopology unsupported\n");
581     }
582     else if (IsEqualIID(riid, &IID_IDirectSound)
583              || IsEqualIID(riid, &IID_IDirectSound8))
584     {
585         if (This->flow == eRender)
586             hr = CoCreateInstance(&CLSID_DirectSound8, NULL, clsctx, riid, ppv);
587         if (SUCCEEDED(hr))
588         {
589             hr = IDirectSound_Initialize((IDirectSound*)*ppv, &This->devguid);
590             if (FAILED(hr))
591                 IDirectSound_Release((IDirectSound*)*ppv);
592         }
593     }
594     else if (IsEqualIID(riid, &IID_IDirectSoundCapture)
595              || IsEqualIID(riid, &IID_IDirectSoundCapture8))
596     {
597         if (This->flow == eCapture)
598             hr = CoCreateInstance(&CLSID_DirectSoundCapture8, NULL, clsctx, riid, ppv);
599         if (SUCCEEDED(hr))
600         {
601             hr = IDirectSoundCapture_Initialize((IDirectSoundCapture*)*ppv, &This->devguid);
602             if (FAILED(hr))
603                 IDirectSoundCapture_Release((IDirectSoundCapture*)*ppv);
604         }
605     }
606     else
607         ERR("Invalid/unknown iid %s\n", debugstr_guid(riid));
608
609     if (FAILED(hr))
610         *ppv = NULL;
611
612     TRACE("Returning %08x\n", hr);
613     return hr;
614 }
615
616 static HRESULT WINAPI MMDevice_OpenPropertyStore(IMMDevice *iface, DWORD access, IPropertyStore **ppv)
617 {
618     MMDevice *This = impl_from_IMMDevice(iface);
619     TRACE("(%p)->(%x,%p)\n", This, access, ppv);
620
621     if (!ppv)
622         return E_POINTER;
623     return MMDevPropStore_Create(This, access, ppv);
624 }
625
626 static HRESULT WINAPI MMDevice_GetId(IMMDevice *iface, WCHAR **itemid)
627 {
628     MMDevice *This = impl_from_IMMDevice(iface);
629     WCHAR *str;
630     GUID *id = &This->devguid;
631     static const WCHAR formatW[] = { '{','0','.','0','.','%','u','.','0','0','0','0','0','0','0','0','}','.',
632                                      '{','%','0','8','X','-','%','0','4','X','-',
633                                      '%','0','4','X','-','%','0','2','X','%','0','2','X','-',
634                                      '%','0','2','X','%','0','2','X','%','0','2','X','%','0','2','X',
635                                      '%','0','2','X','%','0','2','X','}',0 };
636
637     TRACE("(%p)->(%p)\n", This, itemid);
638     if (!itemid)
639         return E_POINTER;
640     *itemid = str = CoTaskMemAlloc(56 * sizeof(WCHAR));
641     if (!str)
642         return E_OUTOFMEMORY;
643     wsprintfW( str, formatW, This->flow, id->Data1, id->Data2, id->Data3,
644                id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
645                id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7] );
646     TRACE("returning %s\n", wine_dbgstr_w(str));
647     return S_OK;
648 }
649
650 static HRESULT WINAPI MMDevice_GetState(IMMDevice *iface, DWORD *state)
651 {
652     MMDevice *This = impl_from_IMMDevice(iface);
653     TRACE("(%p)->(%p)\n", iface, state);
654
655     if (!state)
656         return E_POINTER;
657     *state = This->state;
658     return S_OK;
659 }
660
661 static const IMMDeviceVtbl MMDeviceVtbl =
662 {
663     MMDevice_QueryInterface,
664     MMDevice_AddRef,
665     MMDevice_Release,
666     MMDevice_Activate,
667     MMDevice_OpenPropertyStore,
668     MMDevice_GetId,
669     MMDevice_GetState
670 };
671
672 static inline MMDevice *impl_from_IMMEndpoint(IMMEndpoint *iface)
673 {
674     return CONTAINING_RECORD(iface, MMDevice, IMMEndpoint_iface);
675 }
676
677 static HRESULT WINAPI MMEndpoint_QueryInterface(IMMEndpoint *iface, REFIID riid, void **ppv)
678 {
679     MMDevice *This = impl_from_IMMEndpoint(iface);
680     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
681     return IMMDevice_QueryInterface(&This->IMMDevice_iface, riid, ppv);
682 }
683
684 static ULONG WINAPI MMEndpoint_AddRef(IMMEndpoint *iface)
685 {
686     MMDevice *This = impl_from_IMMEndpoint(iface);
687     TRACE("(%p)\n", This);
688     return IMMDevice_AddRef(&This->IMMDevice_iface);
689 }
690
691 static ULONG WINAPI MMEndpoint_Release(IMMEndpoint *iface)
692 {
693     MMDevice *This = impl_from_IMMEndpoint(iface);
694     TRACE("(%p)\n", This);
695     return IMMDevice_Release(&This->IMMDevice_iface);
696 }
697
698 static HRESULT WINAPI MMEndpoint_GetDataFlow(IMMEndpoint *iface, EDataFlow *flow)
699 {
700     MMDevice *This = impl_from_IMMEndpoint(iface);
701     TRACE("(%p)->(%p)\n", This, flow);
702     if (!flow)
703         return E_POINTER;
704     *flow = This->flow;
705     return S_OK;
706 }
707
708 static const IMMEndpointVtbl MMEndpointVtbl =
709 {
710     MMEndpoint_QueryInterface,
711     MMEndpoint_AddRef,
712     MMEndpoint_Release,
713     MMEndpoint_GetDataFlow
714 };
715
716 static HRESULT MMDevCol_Create(IMMDeviceCollection **ppv, EDataFlow flow, DWORD state)
717 {
718     MMDevColImpl *This;
719
720     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
721     *ppv = NULL;
722     if (!This)
723         return E_OUTOFMEMORY;
724     This->IMMDeviceCollection_iface.lpVtbl = &MMDevColVtbl;
725     This->ref = 1;
726     This->flow = flow;
727     This->state = state;
728     *ppv = &This->IMMDeviceCollection_iface;
729     return S_OK;
730 }
731
732 static void MMDevCol_Destroy(MMDevColImpl *This)
733 {
734     HeapFree(GetProcessHeap(), 0, This);
735 }
736
737 static HRESULT WINAPI MMDevCol_QueryInterface(IMMDeviceCollection *iface, REFIID riid, void **ppv)
738 {
739     MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
740     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
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     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
864
865     if (!ppv)
866         return E_POINTER;
867     if (IsEqualIID(riid, &IID_IUnknown)
868         || IsEqualIID(riid, &IID_IMMDeviceEnumerator))
869         *ppv = This;
870     else
871         *ppv = NULL;
872     if (!*ppv)
873         return E_NOINTERFACE;
874     IUnknown_AddRef((IUnknown*)*ppv);
875     return S_OK;
876 }
877
878 static ULONG WINAPI MMDevEnum_AddRef(IMMDeviceEnumerator *iface)
879 {
880     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
881     LONG ref = InterlockedIncrement(&This->ref);
882     TRACE("Refcount now %i\n", ref);
883     return ref;
884 }
885
886 static ULONG WINAPI MMDevEnum_Release(IMMDeviceEnumerator *iface)
887 {
888     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
889     LONG ref = InterlockedDecrement(&This->ref);
890     if (!ref)
891         MMDevEnum_Free();
892     TRACE("Refcount now %i\n", ref);
893     return ref;
894 }
895
896 static HRESULT WINAPI MMDevEnum_EnumAudioEndpoints(IMMDeviceEnumerator *iface, EDataFlow flow, DWORD mask, IMMDeviceCollection **devices)
897 {
898     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
899     TRACE("(%p)->(%u,%u,%p)\n", This, flow, mask, devices);
900     if (!devices)
901         return E_POINTER;
902     *devices = NULL;
903     if (flow >= EDataFlow_enum_count)
904         return E_INVALIDARG;
905     if (mask & ~DEVICE_STATEMASK_ALL)
906         return E_INVALIDARG;
907     return MMDevCol_Create(devices, flow, mask);
908 }
909
910 static HRESULT WINAPI MMDevEnum_GetDefaultAudioEndpoint(IMMDeviceEnumerator *iface, EDataFlow flow, ERole role, IMMDevice **device)
911 {
912     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
913     WCHAR reg_key[256];
914     HKEY key;
915     HRESULT hr;
916
917     static const WCHAR slashW[] = {'\\',0};
918     static const WCHAR reg_out_nameW[] = {'D','e','f','a','u','l','t','O','u','t','p','u','t',0};
919     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};
920     static const WCHAR reg_in_nameW[] = {'D','e','f','a','u','l','t','I','n','p','u','t',0};
921     static const WCHAR reg_vin_nameW[] = {'D','e','f','a','u','l','t','V','o','i','c','e','I','n','p','u','t',0};
922
923     TRACE("(%p)->(%u,%u,%p)\n", This, flow, role, device);
924
925     if (!device)
926         return E_POINTER;
927
928     if((flow != eRender && flow != eCapture) ||
929             (role != eConsole && role != eMultimedia && role != eCommunications)){
930         WARN("Unknown flow (%u) or role (%u)\n", flow, role);
931         return E_INVALIDARG;
932     }
933
934     *device = NULL;
935
936     if(!drvs.module_name[0])
937         return E_NOTFOUND;
938
939     lstrcpyW(reg_key, drv_keyW);
940     lstrcatW(reg_key, slashW);
941     lstrcatW(reg_key, drvs.module_name);
942
943     if(RegOpenKeyW(HKEY_CURRENT_USER, reg_key, &key) == ERROR_SUCCESS){
944         const WCHAR *reg_x_name, *reg_vx_name;
945         WCHAR def_id[256];
946         DWORD size = sizeof(def_id), state;
947
948         if(flow == eRender){
949             reg_x_name = reg_out_nameW;
950             reg_vx_name = reg_vout_nameW;
951         }else{
952             reg_x_name = reg_in_nameW;
953             reg_vx_name = reg_vin_nameW;
954         }
955
956         if(role == eCommunications &&
957                 RegQueryValueExW(key, reg_vx_name, 0, NULL,
958                     (BYTE*)def_id, &size) == ERROR_SUCCESS){
959             hr = IMMDeviceEnumerator_GetDevice(iface, def_id, device);
960             if(SUCCEEDED(hr)){
961                 if(SUCCEEDED(IMMDevice_GetState(*device, &state)) &&
962                         state == DEVICE_STATE_ACTIVE){
963                     RegCloseKey(key);
964                     return S_OK;
965                 }
966             }
967
968             TRACE("Unable to find voice device %s\n", wine_dbgstr_w(def_id));
969         }
970
971         if(RegQueryValueExW(key, reg_x_name, 0, NULL,
972                     (BYTE*)def_id, &size) == ERROR_SUCCESS){
973             hr = IMMDeviceEnumerator_GetDevice(iface, def_id, device);
974             if(SUCCEEDED(hr)){
975                 if(SUCCEEDED(IMMDevice_GetState(*device, &state)) &&
976                         state == DEVICE_STATE_ACTIVE){
977                     RegCloseKey(key);
978                     return S_OK;
979                 }
980             }
981
982             TRACE("Unable to find device %s\n", wine_dbgstr_w(def_id));
983         }
984
985         RegCloseKey(key);
986     }
987
988     if (flow == eRender)
989         *device = &MMDevice_def_play->IMMDevice_iface;
990     else
991         *device = &MMDevice_def_rec->IMMDevice_iface;
992
993     if (!*device)
994         return E_NOTFOUND;
995     IMMDevice_AddRef(*device);
996     return S_OK;
997 }
998
999 static HRESULT WINAPI MMDevEnum_GetDevice(IMMDeviceEnumerator *iface, const WCHAR *name, IMMDevice **device)
1000 {
1001     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1002     DWORD i=0;
1003     IMMDevice *dev = NULL;
1004
1005     static const WCHAR wine_info_deviceW[] = {'W','i','n','e',' ',
1006         'i','n','f','o',' ','d','e','v','i','c','e',0};
1007
1008     TRACE("(%p)->(%s,%p)\n", This, debugstr_w(name), device);
1009
1010     if(!name || !device)
1011         return E_POINTER;
1012
1013     if(!lstrcmpW(name, wine_info_deviceW)){
1014         *device = &info_device;
1015         return S_OK;
1016     }
1017
1018     for (i = 0; i < MMDevice_count; ++i)
1019     {
1020         WCHAR *str;
1021         dev = &MMDevice_head[i]->IMMDevice_iface;
1022         IMMDevice_GetId(dev, &str);
1023
1024         if (str && !lstrcmpW(str, name))
1025         {
1026             CoTaskMemFree(str);
1027             IUnknown_AddRef(dev);
1028             *device = dev;
1029             return S_OK;
1030         }
1031         CoTaskMemFree(str);
1032     }
1033     TRACE("Could not find device %s\n", debugstr_w(name));
1034     return E_INVALIDARG;
1035 }
1036
1037 static HRESULT WINAPI MMDevEnum_RegisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
1038 {
1039     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1040     TRACE("(%p)->(%p)\n", This, client);
1041     FIXME("stub\n");
1042     return S_OK;
1043 }
1044
1045 static HRESULT WINAPI MMDevEnum_UnregisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
1046 {
1047     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1048     TRACE("(%p)->(%p)\n", This, client);
1049     FIXME("stub\n");
1050     return S_OK;
1051 }
1052
1053 static const IMMDeviceEnumeratorVtbl MMDevEnumVtbl =
1054 {
1055     MMDevEnum_QueryInterface,
1056     MMDevEnum_AddRef,
1057     MMDevEnum_Release,
1058     MMDevEnum_EnumAudioEndpoints,
1059     MMDevEnum_GetDefaultAudioEndpoint,
1060     MMDevEnum_GetDevice,
1061     MMDevEnum_RegisterEndpointNotificationCallback,
1062     MMDevEnum_UnregisterEndpointNotificationCallback
1063 };
1064
1065 static HRESULT MMDevPropStore_Create(MMDevice *parent, DWORD access, IPropertyStore **ppv)
1066 {
1067     MMDevPropStore *This;
1068     if (access != STGM_READ
1069         && access != STGM_WRITE
1070         && access != STGM_READWRITE)
1071     {
1072         WARN("Invalid access %08x\n", access);
1073         return E_INVALIDARG;
1074     }
1075     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
1076     *ppv = &This->IPropertyStore_iface;
1077     if (!This)
1078         return E_OUTOFMEMORY;
1079     This->IPropertyStore_iface.lpVtbl = &MMDevPropVtbl;
1080     This->ref = 1;
1081     This->parent = parent;
1082     This->access = access;
1083     return S_OK;
1084 }
1085
1086 static void MMDevPropStore_Destroy(MMDevPropStore *This)
1087 {
1088     HeapFree(GetProcessHeap(), 0, This);
1089 }
1090
1091 static HRESULT WINAPI MMDevPropStore_QueryInterface(IPropertyStore *iface, REFIID riid, void **ppv)
1092 {
1093     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1094     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1095
1096     if (!ppv)
1097         return E_POINTER;
1098     if (IsEqualIID(riid, &IID_IUnknown)
1099         || IsEqualIID(riid, &IID_IPropertyStore))
1100         *ppv = This;
1101     else
1102         *ppv = NULL;
1103     if (!*ppv)
1104         return E_NOINTERFACE;
1105     IUnknown_AddRef((IUnknown*)*ppv);
1106     return S_OK;
1107 }
1108
1109 static ULONG WINAPI MMDevPropStore_AddRef(IPropertyStore *iface)
1110 {
1111     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1112     LONG ref = InterlockedIncrement(&This->ref);
1113     TRACE("Refcount now %i\n", ref);
1114     return ref;
1115 }
1116
1117 static ULONG WINAPI MMDevPropStore_Release(IPropertyStore *iface)
1118 {
1119     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1120     LONG ref = InterlockedDecrement(&This->ref);
1121     TRACE("Refcount now %i\n", ref);
1122     if (!ref)
1123         MMDevPropStore_Destroy(This);
1124     return ref;
1125 }
1126
1127 static HRESULT WINAPI MMDevPropStore_GetCount(IPropertyStore *iface, DWORD *nprops)
1128 {
1129     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1130     WCHAR buffer[50];
1131     DWORD i = 0;
1132     HKEY propkey;
1133     HRESULT hr;
1134
1135     TRACE("(%p)->(%p)\n", iface, nprops);
1136     if (!nprops)
1137         return E_POINTER;
1138     hr = MMDevPropStore_OpenPropKey(&This->parent->devguid, This->parent->flow, &propkey);
1139     if (FAILED(hr))
1140         return hr;
1141     *nprops = 0;
1142     do {
1143         DWORD len = sizeof(buffer)/sizeof(*buffer);
1144         if (RegEnumKeyExW(propkey, i, buffer, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
1145             break;
1146         i++;
1147     } while (0);
1148     RegCloseKey(propkey);
1149     TRACE("Returning %i\n", i);
1150     *nprops = i;
1151     return S_OK;
1152 }
1153
1154 static HRESULT WINAPI MMDevPropStore_GetAt(IPropertyStore *iface, DWORD prop, PROPERTYKEY *key)
1155 {
1156     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1157     WCHAR buffer[50];
1158     DWORD len = sizeof(buffer)/sizeof(*buffer);
1159     HRESULT hr;
1160     HKEY propkey;
1161
1162     TRACE("(%p)->(%u,%p)\n", iface, prop, key);
1163     if (!key)
1164         return E_POINTER;
1165
1166     hr = MMDevPropStore_OpenPropKey(&This->parent->devguid, This->parent->flow, &propkey);
1167     if (FAILED(hr))
1168         return hr;
1169
1170     if (RegEnumKeyExW(propkey, prop, buffer, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS
1171         || len <= 40)
1172     {
1173         WARN("GetAt %u failed\n", prop);
1174         return E_INVALIDARG;
1175     }
1176     RegCloseKey(propkey);
1177     buffer[39] = 0;
1178     CLSIDFromString(buffer, &key->fmtid);
1179     key->pid = atoiW(&buffer[40]);
1180     return S_OK;
1181 }
1182
1183 static HRESULT WINAPI MMDevPropStore_GetValue(IPropertyStore *iface, REFPROPERTYKEY key, PROPVARIANT *pv)
1184 {
1185     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1186     TRACE("(%p)->(\"%s,%u\", %p)\n", This, key ? debugstr_guid(&key->fmtid) : NULL, key ? key->pid : 0, pv);
1187
1188     if (!key || !pv)
1189         return E_POINTER;
1190     if (This->access != STGM_READ
1191         && This->access != STGM_READWRITE)
1192         return STG_E_ACCESSDENIED;
1193
1194     /* Special case */
1195     if (IsEqualPropertyKey(*key, PKEY_AudioEndpoint_GUID))
1196     {
1197         pv->vt = VT_LPWSTR;
1198         pv->u.pwszVal = CoTaskMemAlloc(39 * sizeof(WCHAR));
1199         if (!pv->u.pwszVal)
1200             return E_OUTOFMEMORY;
1201         StringFromGUID2(&This->parent->devguid, pv->u.pwszVal, 39);
1202         return S_OK;
1203     }
1204
1205     return MMDevice_GetPropValue(&This->parent->devguid, This->parent->flow, key, pv);
1206 }
1207
1208 static HRESULT WINAPI MMDevPropStore_SetValue(IPropertyStore *iface, REFPROPERTYKEY key, REFPROPVARIANT pv)
1209 {
1210     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1211     TRACE("(%p)->(\"%s,%u\", %p)\n", This, key ? debugstr_guid(&key->fmtid) : NULL, key ? key->pid : 0, pv);
1212
1213     if (!key || !pv)
1214         return E_POINTER;
1215
1216     if (This->access != STGM_WRITE
1217         && This->access != STGM_READWRITE)
1218         return STG_E_ACCESSDENIED;
1219     return MMDevice_SetPropValue(&This->parent->devguid, This->parent->flow, key, pv);
1220 }
1221
1222 static HRESULT WINAPI MMDevPropStore_Commit(IPropertyStore *iface)
1223 {
1224     FIXME("stub\n");
1225     return E_NOTIMPL;
1226 }
1227
1228 static const IPropertyStoreVtbl MMDevPropVtbl =
1229 {
1230     MMDevPropStore_QueryInterface,
1231     MMDevPropStore_AddRef,
1232     MMDevPropStore_Release,
1233     MMDevPropStore_GetCount,
1234     MMDevPropStore_GetAt,
1235     MMDevPropStore_GetValue,
1236     MMDevPropStore_SetValue,
1237     MMDevPropStore_Commit
1238 };
1239
1240
1241 /* Property bag for IBaseFilter activation */
1242 static HRESULT WINAPI PB_QueryInterface(IPropertyBag *iface, REFIID riid, void **ppv)
1243 {
1244     ERR("Should not be called\n");
1245     *ppv = NULL;
1246     return E_NOINTERFACE;
1247 }
1248
1249 static ULONG WINAPI PB_AddRef(IPropertyBag *iface)
1250 {
1251     ERR("Should not be called\n");
1252     return 2;
1253 }
1254
1255 static ULONG WINAPI PB_Release(IPropertyBag *iface)
1256 {
1257     ERR("Should not be called\n");
1258     return 1;
1259 }
1260
1261 static HRESULT WINAPI PB_Read(IPropertyBag *iface, LPCOLESTR name, VARIANT *var, IErrorLog *log)
1262 {
1263     static const WCHAR dsguid[] = { 'D','S','G','u','i','d', 0 };
1264     IPropertyBagImpl *This = impl_from_IPropertyBag(iface);
1265     TRACE("Trying to read %s, type %u\n", debugstr_w(name), var->n1.n2.vt);
1266     if (!lstrcmpW(name, dsguid))
1267     {
1268         WCHAR guidstr[39];
1269         StringFromGUID2(&This->devguid, guidstr,sizeof(guidstr)/sizeof(*guidstr));
1270         var->n1.n2.vt = VT_BSTR;
1271         var->n1.n2.n3.bstrVal = SysAllocString(guidstr);
1272         return S_OK;
1273     }
1274     ERR("Unknown property '%s' queried\n", debugstr_w(name));
1275     return E_FAIL;
1276 }
1277
1278 static HRESULT WINAPI PB_Write(IPropertyBag *iface, LPCOLESTR name, VARIANT *var)
1279 {
1280     ERR("Should not be called\n");
1281     return E_FAIL;
1282 }
1283
1284 static const IPropertyBagVtbl PB_Vtbl =
1285 {
1286     PB_QueryInterface,
1287     PB_AddRef,
1288     PB_Release,
1289     PB_Read,
1290     PB_Write
1291 };
1292
1293 static ULONG WINAPI info_device_ps_AddRef(IPropertyStore *iface)
1294 {
1295     return 2;
1296 }
1297
1298 static ULONG WINAPI info_device_ps_Release(IPropertyStore *iface)
1299 {
1300     return 1;
1301 }
1302
1303 static HRESULT WINAPI info_device_ps_GetValue(IPropertyStore *iface,
1304         REFPROPERTYKEY key, PROPVARIANT *pv)
1305 {
1306     TRACE("(static)->(\"%s,%u\", %p)\n", debugstr_guid(&key->fmtid), key ? key->pid : 0, pv);
1307
1308     if (!key || !pv)
1309         return E_POINTER;
1310
1311     if (IsEqualPropertyKey(*key, DEVPKEY_Device_Driver))
1312     {
1313         INT size = (lstrlenW(drvs.module_name) + 1) * sizeof(WCHAR);
1314         pv->vt = VT_LPWSTR;
1315         pv->u.pwszVal = CoTaskMemAlloc(size);
1316         if (!pv->u.pwszVal)
1317             return E_OUTOFMEMORY;
1318         memcpy(pv->u.pwszVal, drvs.module_name, size);
1319         return S_OK;
1320     }
1321
1322     return E_INVALIDARG;
1323 }
1324
1325 static const IPropertyStoreVtbl info_device_ps_Vtbl =
1326 {
1327     NULL,
1328     info_device_ps_AddRef,
1329     info_device_ps_Release,
1330     NULL,
1331     NULL,
1332     info_device_ps_GetValue,
1333     NULL,
1334     NULL
1335 };
1336
1337 static IPropertyStore info_device_ps = {
1338     &info_device_ps_Vtbl
1339 };
1340
1341 static ULONG WINAPI info_device_AddRef(IMMDevice *iface)
1342 {
1343     return 2;
1344 }
1345
1346 static ULONG WINAPI info_device_Release(IMMDevice *iface)
1347 {
1348     return 1;
1349 }
1350
1351 static HRESULT WINAPI info_device_OpenPropertyStore(IMMDevice *iface,
1352         DWORD access, IPropertyStore **ppv)
1353 {
1354     TRACE("(static)->(%x, %p)\n", access, ppv);
1355     *ppv = &info_device_ps;
1356     return S_OK;
1357 }
1358
1359 static const IMMDeviceVtbl info_device_Vtbl =
1360 {
1361     NULL,
1362     info_device_AddRef,
1363     info_device_Release,
1364     NULL,
1365     info_device_OpenPropertyStore,
1366     NULL,
1367     NULL
1368 };
1369
1370 static IMMDevice info_device = {
1371     &info_device_Vtbl
1372 };