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