ntdll: Also call the unwind handler for the target frame.
[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     TRACE("returning %s\n", wine_dbgstr_w(str));
653     return S_OK;
654 }
655
656 static HRESULT WINAPI MMDevice_GetState(IMMDevice *iface, DWORD *state)
657 {
658     MMDevice *This = impl_from_IMMDevice(iface);
659     TRACE("(%p)->(%p)\n", iface, state);
660
661     if (!state)
662         return E_POINTER;
663     *state = This->state;
664     return S_OK;
665 }
666
667 static const IMMDeviceVtbl MMDeviceVtbl =
668 {
669     MMDevice_QueryInterface,
670     MMDevice_AddRef,
671     MMDevice_Release,
672     MMDevice_Activate,
673     MMDevice_OpenPropertyStore,
674     MMDevice_GetId,
675     MMDevice_GetState
676 };
677
678 static inline MMDevice *impl_from_IMMEndpoint(IMMEndpoint *iface)
679 {
680     return CONTAINING_RECORD(iface, MMDevice, IMMEndpoint_iface);
681 }
682
683 static HRESULT WINAPI MMEndpoint_QueryInterface(IMMEndpoint *iface, REFIID riid, void **ppv)
684 {
685     MMDevice *This = impl_from_IMMEndpoint(iface);
686     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
687     return IMMDevice_QueryInterface(&This->IMMDevice_iface, riid, ppv);
688 }
689
690 static ULONG WINAPI MMEndpoint_AddRef(IMMEndpoint *iface)
691 {
692     MMDevice *This = impl_from_IMMEndpoint(iface);
693     TRACE("(%p)\n", This);
694     return IMMDevice_AddRef(&This->IMMDevice_iface);
695 }
696
697 static ULONG WINAPI MMEndpoint_Release(IMMEndpoint *iface)
698 {
699     MMDevice *This = impl_from_IMMEndpoint(iface);
700     TRACE("(%p)\n", This);
701     return IMMDevice_Release(&This->IMMDevice_iface);
702 }
703
704 static HRESULT WINAPI MMEndpoint_GetDataFlow(IMMEndpoint *iface, EDataFlow *flow)
705 {
706     MMDevice *This = impl_from_IMMEndpoint(iface);
707     TRACE("(%p)->(%p)\n", This, flow);
708     if (!flow)
709         return E_POINTER;
710     *flow = This->flow;
711     return S_OK;
712 }
713
714 static const IMMEndpointVtbl MMEndpointVtbl =
715 {
716     MMEndpoint_QueryInterface,
717     MMEndpoint_AddRef,
718     MMEndpoint_Release,
719     MMEndpoint_GetDataFlow
720 };
721
722 static HRESULT MMDevCol_Create(IMMDeviceCollection **ppv, EDataFlow flow, DWORD state)
723 {
724     MMDevColImpl *This;
725
726     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
727     *ppv = NULL;
728     if (!This)
729         return E_OUTOFMEMORY;
730     This->IMMDeviceCollection_iface.lpVtbl = &MMDevColVtbl;
731     This->ref = 1;
732     This->flow = flow;
733     This->state = state;
734     *ppv = &This->IMMDeviceCollection_iface;
735     return S_OK;
736 }
737
738 static void MMDevCol_Destroy(MMDevColImpl *This)
739 {
740     HeapFree(GetProcessHeap(), 0, This);
741 }
742
743 static HRESULT WINAPI MMDevCol_QueryInterface(IMMDeviceCollection *iface, REFIID riid, void **ppv)
744 {
745     MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
746     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
747
748     if (!ppv)
749         return E_POINTER;
750     if (IsEqualIID(riid, &IID_IUnknown)
751         || IsEqualIID(riid, &IID_IMMDeviceCollection))
752         *ppv = This;
753     else
754         *ppv = NULL;
755     if (!*ppv)
756         return E_NOINTERFACE;
757     IUnknown_AddRef((IUnknown*)*ppv);
758     return S_OK;
759 }
760
761 static ULONG WINAPI MMDevCol_AddRef(IMMDeviceCollection *iface)
762 {
763     MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
764     LONG ref = InterlockedIncrement(&This->ref);
765     TRACE("Refcount now %i\n", ref);
766     return ref;
767 }
768
769 static ULONG WINAPI MMDevCol_Release(IMMDeviceCollection *iface)
770 {
771     MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
772     LONG ref = InterlockedDecrement(&This->ref);
773     TRACE("Refcount now %i\n", ref);
774     if (!ref)
775         MMDevCol_Destroy(This);
776     return ref;
777 }
778
779 static HRESULT WINAPI MMDevCol_GetCount(IMMDeviceCollection *iface, UINT *numdevs)
780 {
781     MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
782     DWORD i;
783
784     TRACE("(%p)->(%p)\n", This, numdevs);
785     if (!numdevs)
786         return E_POINTER;
787
788     *numdevs = 0;
789     for (i = 0; i < MMDevice_count; ++i)
790     {
791         MMDevice *cur = MMDevice_head[i];
792         if ((cur->flow == This->flow || This->flow == eAll)
793             && (cur->state & This->state))
794             ++(*numdevs);
795     }
796     return S_OK;
797 }
798
799 static HRESULT WINAPI MMDevCol_Item(IMMDeviceCollection *iface, UINT n, IMMDevice **dev)
800 {
801     MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
802     DWORD i = 0, j = 0;
803
804     TRACE("(%p)->(%u, %p)\n", This, n, dev);
805     if (!dev)
806         return E_POINTER;
807
808     for (j = 0; j < MMDevice_count; ++j)
809     {
810         MMDevice *cur = MMDevice_head[j];
811         if ((cur->flow == This->flow || This->flow == eAll)
812             && (cur->state & This->state)
813             && i++ == n)
814         {
815             *dev = &cur->IMMDevice_iface;
816             IMMDevice_AddRef(*dev);
817             return S_OK;
818         }
819     }
820     WARN("Could not obtain item %u\n", n);
821     *dev = NULL;
822     return E_INVALIDARG;
823 }
824
825 static const IMMDeviceCollectionVtbl MMDevColVtbl =
826 {
827     MMDevCol_QueryInterface,
828     MMDevCol_AddRef,
829     MMDevCol_Release,
830     MMDevCol_GetCount,
831     MMDevCol_Item
832 };
833
834 HRESULT MMDevEnum_Create(REFIID riid, void **ppv)
835 {
836     MMDevEnumImpl *This = MMDevEnumerator;
837
838     if (!This)
839     {
840         This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
841         *ppv = NULL;
842         if (!This)
843             return E_OUTOFMEMORY;
844         This->ref = 1;
845         This->IMMDeviceEnumerator_iface.lpVtbl = &MMDevEnumVtbl;
846         MMDevEnumerator = This;
847
848         load_devices_from_reg();
849         load_driver_devices(eRender);
850         load_driver_devices(eCapture);
851     }
852     return IUnknown_QueryInterface((IUnknown*)This, riid, ppv);
853 }
854
855 void MMDevEnum_Free(void)
856 {
857     while (MMDevice_count)
858         MMDevice_Destroy(MMDevice_head[0]);
859     RegCloseKey(key_render);
860     RegCloseKey(key_capture);
861     key_render = key_capture = NULL;
862     HeapFree(GetProcessHeap(), 0, MMDevEnumerator);
863     MMDevEnumerator = NULL;
864 }
865
866 static HRESULT WINAPI MMDevEnum_QueryInterface(IMMDeviceEnumerator *iface, REFIID riid, void **ppv)
867 {
868     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
869     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
870
871     if (!ppv)
872         return E_POINTER;
873     if (IsEqualIID(riid, &IID_IUnknown)
874         || IsEqualIID(riid, &IID_IMMDeviceEnumerator))
875         *ppv = This;
876     else
877         *ppv = NULL;
878     if (!*ppv)
879         return E_NOINTERFACE;
880     IUnknown_AddRef((IUnknown*)*ppv);
881     return S_OK;
882 }
883
884 static ULONG WINAPI MMDevEnum_AddRef(IMMDeviceEnumerator *iface)
885 {
886     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
887     LONG ref = InterlockedIncrement(&This->ref);
888     TRACE("Refcount now %i\n", ref);
889     return ref;
890 }
891
892 static ULONG WINAPI MMDevEnum_Release(IMMDeviceEnumerator *iface)
893 {
894     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
895     LONG ref = InterlockedDecrement(&This->ref);
896     if (!ref)
897         MMDevEnum_Free();
898     TRACE("Refcount now %i\n", ref);
899     return ref;
900 }
901
902 static HRESULT WINAPI MMDevEnum_EnumAudioEndpoints(IMMDeviceEnumerator *iface, EDataFlow flow, DWORD mask, IMMDeviceCollection **devices)
903 {
904     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
905     TRACE("(%p)->(%u,%u,%p)\n", This, flow, mask, devices);
906     if (!devices)
907         return E_POINTER;
908     *devices = NULL;
909     if (flow >= EDataFlow_enum_count)
910         return E_INVALIDARG;
911     if (mask & ~DEVICE_STATEMASK_ALL)
912         return E_INVALIDARG;
913     return MMDevCol_Create(devices, flow, mask);
914 }
915
916 static HRESULT WINAPI MMDevEnum_GetDefaultAudioEndpoint(IMMDeviceEnumerator *iface, EDataFlow flow, ERole role, IMMDevice **device)
917 {
918     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
919     WCHAR reg_key[256];
920     HKEY key;
921     HRESULT hr;
922
923     static const WCHAR slashW[] = {'\\',0};
924     static const WCHAR reg_out_nameW[] = {'D','e','f','a','u','l','t','O','u','t','p','u','t',0};
925     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};
926     static const WCHAR reg_in_nameW[] = {'D','e','f','a','u','l','t','I','n','p','u','t',0};
927     static const WCHAR reg_vin_nameW[] = {'D','e','f','a','u','l','t','V','o','i','c','e','I','n','p','u','t',0};
928
929     TRACE("(%p)->(%u,%u,%p)\n", This, flow, role, device);
930
931     if (!device)
932         return E_POINTER;
933
934     if((flow != eRender && flow != eCapture) ||
935             (role != eConsole && role != eMultimedia && role != eCommunications)){
936         WARN("Unknown flow (%u) or role (%u)\n", flow, role);
937         return E_INVALIDARG;
938     }
939
940     *device = NULL;
941
942     if(!drvs.module_name[0])
943         return E_NOTFOUND;
944
945     lstrcpyW(reg_key, drv_keyW);
946     lstrcatW(reg_key, slashW);
947     lstrcatW(reg_key, drvs.module_name);
948
949     if(RegOpenKeyW(HKEY_CURRENT_USER, reg_key, &key) == ERROR_SUCCESS){
950         const WCHAR *reg_x_name, *reg_vx_name;
951         WCHAR def_id[256];
952         DWORD size = sizeof(def_id), state;
953
954         if(flow == eRender){
955             reg_x_name = reg_out_nameW;
956             reg_vx_name = reg_vout_nameW;
957         }else{
958             reg_x_name = reg_in_nameW;
959             reg_vx_name = reg_vin_nameW;
960         }
961
962         if(role == eCommunications &&
963                 RegQueryValueExW(key, reg_vx_name, 0, NULL,
964                     (BYTE*)def_id, &size) == ERROR_SUCCESS){
965             hr = IMMDeviceEnumerator_GetDevice(iface, def_id, device);
966             if(SUCCEEDED(hr)){
967                 if(SUCCEEDED(IMMDevice_GetState(*device, &state)) &&
968                         state == DEVICE_STATE_ACTIVE){
969                     RegCloseKey(key);
970                     return S_OK;
971                 }
972             }
973
974             TRACE("Unable to find voice device %s\n", wine_dbgstr_w(def_id));
975         }
976
977         if(RegQueryValueExW(key, reg_x_name, 0, NULL,
978                     (BYTE*)def_id, &size) == ERROR_SUCCESS){
979             hr = IMMDeviceEnumerator_GetDevice(iface, def_id, device);
980             if(SUCCEEDED(hr)){
981                 if(SUCCEEDED(IMMDevice_GetState(*device, &state)) &&
982                         state == DEVICE_STATE_ACTIVE){
983                     RegCloseKey(key);
984                     return S_OK;
985                 }
986             }
987
988             TRACE("Unable to find device %s\n", wine_dbgstr_w(def_id));
989         }
990
991         RegCloseKey(key);
992     }
993
994     if (flow == eRender)
995         *device = &MMDevice_def_play->IMMDevice_iface;
996     else
997         *device = &MMDevice_def_rec->IMMDevice_iface;
998
999     if (!*device)
1000         return E_NOTFOUND;
1001     IMMDevice_AddRef(*device);
1002     return S_OK;
1003 }
1004
1005 static HRESULT WINAPI MMDevEnum_GetDevice(IMMDeviceEnumerator *iface, const WCHAR *name, IMMDevice **device)
1006 {
1007     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1008     DWORD i=0;
1009     IMMDevice *dev = NULL;
1010
1011     static const WCHAR wine_info_deviceW[] = {'W','i','n','e',' ',
1012         'i','n','f','o',' ','d','e','v','i','c','e',0};
1013
1014     TRACE("(%p)->(%s,%p)\n", This, debugstr_w(name), device);
1015
1016     if(!name || !device)
1017         return E_POINTER;
1018
1019     if(!lstrcmpW(name, wine_info_deviceW)){
1020         *device = &info_device;
1021         return S_OK;
1022     }
1023
1024     for (i = 0; i < MMDevice_count; ++i)
1025     {
1026         WCHAR *str;
1027         dev = &MMDevice_head[i]->IMMDevice_iface;
1028         IMMDevice_GetId(dev, &str);
1029
1030         if (str && !lstrcmpW(str, name))
1031         {
1032             CoTaskMemFree(str);
1033             IUnknown_AddRef(dev);
1034             *device = dev;
1035             return S_OK;
1036         }
1037         CoTaskMemFree(str);
1038     }
1039     TRACE("Could not find device %s\n", debugstr_w(name));
1040     return E_INVALIDARG;
1041 }
1042
1043 static HRESULT WINAPI MMDevEnum_RegisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
1044 {
1045     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1046     TRACE("(%p)->(%p)\n", This, client);
1047     FIXME("stub\n");
1048     return S_OK;
1049 }
1050
1051 static HRESULT WINAPI MMDevEnum_UnregisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
1052 {
1053     MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1054     TRACE("(%p)->(%p)\n", This, client);
1055     FIXME("stub\n");
1056     return S_OK;
1057 }
1058
1059 static const IMMDeviceEnumeratorVtbl MMDevEnumVtbl =
1060 {
1061     MMDevEnum_QueryInterface,
1062     MMDevEnum_AddRef,
1063     MMDevEnum_Release,
1064     MMDevEnum_EnumAudioEndpoints,
1065     MMDevEnum_GetDefaultAudioEndpoint,
1066     MMDevEnum_GetDevice,
1067     MMDevEnum_RegisterEndpointNotificationCallback,
1068     MMDevEnum_UnregisterEndpointNotificationCallback
1069 };
1070
1071 static HRESULT MMDevPropStore_Create(MMDevice *parent, DWORD access, IPropertyStore **ppv)
1072 {
1073     MMDevPropStore *This;
1074     if (access != STGM_READ
1075         && access != STGM_WRITE
1076         && access != STGM_READWRITE)
1077     {
1078         WARN("Invalid access %08x\n", access);
1079         return E_INVALIDARG;
1080     }
1081     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
1082     *ppv = &This->IPropertyStore_iface;
1083     if (!This)
1084         return E_OUTOFMEMORY;
1085     This->IPropertyStore_iface.lpVtbl = &MMDevPropVtbl;
1086     This->ref = 1;
1087     This->parent = parent;
1088     This->access = access;
1089     return S_OK;
1090 }
1091
1092 static void MMDevPropStore_Destroy(MMDevPropStore *This)
1093 {
1094     HeapFree(GetProcessHeap(), 0, This);
1095 }
1096
1097 static HRESULT WINAPI MMDevPropStore_QueryInterface(IPropertyStore *iface, REFIID riid, void **ppv)
1098 {
1099     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1100     TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1101
1102     if (!ppv)
1103         return E_POINTER;
1104     if (IsEqualIID(riid, &IID_IUnknown)
1105         || IsEqualIID(riid, &IID_IPropertyStore))
1106         *ppv = This;
1107     else
1108         *ppv = NULL;
1109     if (!*ppv)
1110         return E_NOINTERFACE;
1111     IUnknown_AddRef((IUnknown*)*ppv);
1112     return S_OK;
1113 }
1114
1115 static ULONG WINAPI MMDevPropStore_AddRef(IPropertyStore *iface)
1116 {
1117     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1118     LONG ref = InterlockedIncrement(&This->ref);
1119     TRACE("Refcount now %i\n", ref);
1120     return ref;
1121 }
1122
1123 static ULONG WINAPI MMDevPropStore_Release(IPropertyStore *iface)
1124 {
1125     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1126     LONG ref = InterlockedDecrement(&This->ref);
1127     TRACE("Refcount now %i\n", ref);
1128     if (!ref)
1129         MMDevPropStore_Destroy(This);
1130     return ref;
1131 }
1132
1133 static HRESULT WINAPI MMDevPropStore_GetCount(IPropertyStore *iface, DWORD *nprops)
1134 {
1135     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1136     WCHAR buffer[50];
1137     DWORD i = 0;
1138     HKEY propkey;
1139     HRESULT hr;
1140
1141     TRACE("(%p)->(%p)\n", iface, nprops);
1142     if (!nprops)
1143         return E_POINTER;
1144     hr = MMDevPropStore_OpenPropKey(&This->parent->devguid, This->parent->flow, &propkey);
1145     if (FAILED(hr))
1146         return hr;
1147     *nprops = 0;
1148     do {
1149         DWORD len = sizeof(buffer)/sizeof(*buffer);
1150         if (RegEnumKeyExW(propkey, i, buffer, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
1151             break;
1152         i++;
1153     } while (0);
1154     RegCloseKey(propkey);
1155     TRACE("Returning %i\n", i);
1156     *nprops = i;
1157     return S_OK;
1158 }
1159
1160 static HRESULT WINAPI MMDevPropStore_GetAt(IPropertyStore *iface, DWORD prop, PROPERTYKEY *key)
1161 {
1162     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1163     WCHAR buffer[50];
1164     DWORD len = sizeof(buffer)/sizeof(*buffer);
1165     HRESULT hr;
1166     HKEY propkey;
1167
1168     TRACE("(%p)->(%u,%p)\n", iface, prop, key);
1169     if (!key)
1170         return E_POINTER;
1171
1172     hr = MMDevPropStore_OpenPropKey(&This->parent->devguid, This->parent->flow, &propkey);
1173     if (FAILED(hr))
1174         return hr;
1175
1176     if (RegEnumKeyExW(propkey, prop, buffer, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS
1177         || len <= 40)
1178     {
1179         WARN("GetAt %u failed\n", prop);
1180         return E_INVALIDARG;
1181     }
1182     RegCloseKey(propkey);
1183     buffer[39] = 0;
1184     CLSIDFromString(buffer, &key->fmtid);
1185     key->pid = atoiW(&buffer[40]);
1186     return S_OK;
1187 }
1188
1189 static HRESULT WINAPI MMDevPropStore_GetValue(IPropertyStore *iface, REFPROPERTYKEY key, PROPVARIANT *pv)
1190 {
1191     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1192     TRACE("(%p)->(\"%s,%u\", %p)\n", This, key ? debugstr_guid(&key->fmtid) : NULL, key ? key->pid : 0, pv);
1193
1194     if (!key || !pv)
1195         return E_POINTER;
1196     if (This->access != STGM_READ
1197         && This->access != STGM_READWRITE)
1198         return STG_E_ACCESSDENIED;
1199
1200     /* Special case */
1201     if (IsEqualPropertyKey(*key, PKEY_AudioEndpoint_GUID))
1202     {
1203         pv->vt = VT_LPWSTR;
1204         pv->u.pwszVal = CoTaskMemAlloc(39 * sizeof(WCHAR));
1205         if (!pv->u.pwszVal)
1206             return E_OUTOFMEMORY;
1207         StringFromGUID2(&This->parent->devguid, pv->u.pwszVal, 39);
1208         return S_OK;
1209     }
1210
1211     return MMDevice_GetPropValue(&This->parent->devguid, This->parent->flow, key, pv);
1212 }
1213
1214 static HRESULT WINAPI MMDevPropStore_SetValue(IPropertyStore *iface, REFPROPERTYKEY key, REFPROPVARIANT pv)
1215 {
1216     MMDevPropStore *This = impl_from_IPropertyStore(iface);
1217     TRACE("(%p)->(\"%s,%u\", %p)\n", This, key ? debugstr_guid(&key->fmtid) : NULL, key ? key->pid : 0, pv);
1218
1219     if (!key || !pv)
1220         return E_POINTER;
1221
1222     if (This->access != STGM_WRITE
1223         && This->access != STGM_READWRITE)
1224         return STG_E_ACCESSDENIED;
1225     return MMDevice_SetPropValue(&This->parent->devguid, This->parent->flow, key, pv);
1226 }
1227
1228 static HRESULT WINAPI MMDevPropStore_Commit(IPropertyStore *iface)
1229 {
1230     FIXME("stub\n");
1231     return E_NOTIMPL;
1232 }
1233
1234 static const IPropertyStoreVtbl MMDevPropVtbl =
1235 {
1236     MMDevPropStore_QueryInterface,
1237     MMDevPropStore_AddRef,
1238     MMDevPropStore_Release,
1239     MMDevPropStore_GetCount,
1240     MMDevPropStore_GetAt,
1241     MMDevPropStore_GetValue,
1242     MMDevPropStore_SetValue,
1243     MMDevPropStore_Commit
1244 };
1245
1246
1247 /* Property bag for IBaseFilter activation */
1248 static HRESULT WINAPI PB_QueryInterface(IPropertyBag *iface, REFIID riid, void **ppv)
1249 {
1250     ERR("Should not be called\n");
1251     *ppv = NULL;
1252     return E_NOINTERFACE;
1253 }
1254
1255 static ULONG WINAPI PB_AddRef(IPropertyBag *iface)
1256 {
1257     ERR("Should not be called\n");
1258     return 2;
1259 }
1260
1261 static ULONG WINAPI PB_Release(IPropertyBag *iface)
1262 {
1263     ERR("Should not be called\n");
1264     return 1;
1265 }
1266
1267 static HRESULT WINAPI PB_Read(IPropertyBag *iface, LPCOLESTR name, VARIANT *var, IErrorLog *log)
1268 {
1269     static const WCHAR dsguid[] = { 'D','S','G','u','i','d', 0 };
1270     IPropertyBagImpl *This = impl_from_IPropertyBag(iface);
1271     TRACE("Trying to read %s, type %u\n", debugstr_w(name), var->n1.n2.vt);
1272     if (!lstrcmpW(name, dsguid))
1273     {
1274         WCHAR guidstr[39];
1275         StringFromGUID2(&This->devguid, guidstr,sizeof(guidstr)/sizeof(*guidstr));
1276         var->n1.n2.vt = VT_BSTR;
1277         var->n1.n2.n3.bstrVal = SysAllocString(guidstr);
1278         return S_OK;
1279     }
1280     ERR("Unknown property '%s' queried\n", debugstr_w(name));
1281     return E_FAIL;
1282 }
1283
1284 static HRESULT WINAPI PB_Write(IPropertyBag *iface, LPCOLESTR name, VARIANT *var)
1285 {
1286     ERR("Should not be called\n");
1287     return E_FAIL;
1288 }
1289
1290 static const IPropertyBagVtbl PB_Vtbl =
1291 {
1292     PB_QueryInterface,
1293     PB_AddRef,
1294     PB_Release,
1295     PB_Read,
1296     PB_Write
1297 };
1298
1299 static ULONG WINAPI info_device_ps_AddRef(IPropertyStore *iface)
1300 {
1301     return 2;
1302 }
1303
1304 static ULONG WINAPI info_device_ps_Release(IPropertyStore *iface)
1305 {
1306     return 1;
1307 }
1308
1309 static HRESULT WINAPI info_device_ps_GetValue(IPropertyStore *iface,
1310         REFPROPERTYKEY key, PROPVARIANT *pv)
1311 {
1312     TRACE("(static)->(\"%s,%u\", %p)\n", debugstr_guid(&key->fmtid), key ? key->pid : 0, pv);
1313
1314     if (!key || !pv)
1315         return E_POINTER;
1316
1317     if (IsEqualPropertyKey(*key, DEVPKEY_Device_Driver))
1318     {
1319         INT size = (lstrlenW(drvs.module_name) + 1) * sizeof(WCHAR);
1320         pv->vt = VT_LPWSTR;
1321         pv->u.pwszVal = CoTaskMemAlloc(size);
1322         if (!pv->u.pwszVal)
1323             return E_OUTOFMEMORY;
1324         memcpy(pv->u.pwszVal, drvs.module_name, size);
1325         return S_OK;
1326     }
1327
1328     return E_INVALIDARG;
1329 }
1330
1331 static const IPropertyStoreVtbl info_device_ps_Vtbl =
1332 {
1333     NULL,
1334     info_device_ps_AddRef,
1335     info_device_ps_Release,
1336     NULL,
1337     NULL,
1338     info_device_ps_GetValue,
1339     NULL,
1340     NULL
1341 };
1342
1343 static IPropertyStore info_device_ps = {
1344     &info_device_ps_Vtbl
1345 };
1346
1347 static ULONG WINAPI info_device_AddRef(IMMDevice *iface)
1348 {
1349     return 2;
1350 }
1351
1352 static ULONG WINAPI info_device_Release(IMMDevice *iface)
1353 {
1354     return 1;
1355 }
1356
1357 static HRESULT WINAPI info_device_OpenPropertyStore(IMMDevice *iface,
1358         DWORD access, IPropertyStore **ppv)
1359 {
1360     TRACE("(static)->(%x, %p)\n", access, ppv);
1361     *ppv = &info_device_ps;
1362     return S_OK;
1363 }
1364
1365 static const IMMDeviceVtbl info_device_Vtbl =
1366 {
1367     NULL,
1368     info_device_AddRef,
1369     info_device_Release,
1370     NULL,
1371     info_device_OpenPropertyStore,
1372     NULL,
1373     NULL
1374 };
1375
1376 static IMMDevice info_device = {
1377     &info_device_Vtbl
1378 };