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