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