oledb32: Add tests for DBTYPE_CY to DBTYPE_WSTR/DBTYPE_STR conversion.
[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 void openal_scanrender(void)
736 {
737     WCHAR name[MAX_PATH];
738     ALCdevice *dev;
739     const ALCchar *devstr, *defaultstr;
740     EnterCriticalSection(&openal_crst);
741     if (palcIsExtensionPresent(NULL, "ALC_ENUMERATE_ALL_EXT")) {
742         defaultstr = palcGetString(NULL, ALC_DEFAULT_ALL_DEVICES_SPECIFIER);
743         devstr = palcGetString(NULL, ALC_ALL_DEVICES_SPECIFIER);
744     } else {
745         defaultstr = palcGetString(NULL, ALC_DEFAULT_DEVICE_SPECIFIER);
746         devstr = palcGetString(NULL, ALC_DEVICE_SPECIFIER);
747     }
748     if (devstr && *devstr)
749         do {
750             MMDevice *mmdev;
751             MultiByteToWideChar( CP_UNIXCP, 0, devstr, -1,
752                                  name, sizeof(name)/sizeof(*name)-1 );
753             name[sizeof(name)/sizeof(*name)-1] = 0;
754             TRACE("Adding %s\n", devstr);
755             dev = palcOpenDevice(devstr);
756             MMDevice_Create(&mmdev, name, NULL, eRender, dev ? DEVICE_STATE_ACTIVE : DEVICE_STATE_NOTPRESENT, !strcmp(devstr, defaultstr));
757             if (dev)
758             {
759                 ALint freq = 44100;
760                 palcGetIntegerv(dev, ALC_FREQUENCY, 1, &freq);
761                 openal_setformat(mmdev, freq);
762                 palcCloseDevice(dev);
763             }
764             else
765                 WARN("Could not open device: %04x\n", palcGetError(NULL));
766             devstr += strlen(devstr)+1;
767         } while (*devstr);
768     LeaveCriticalSection(&openal_crst);
769 }
770
771 static void openal_scancapture(void)
772 {
773     WCHAR name[MAX_PATH];
774     ALCdevice *dev;
775     const ALCchar *devstr, *defaultstr;
776
777     EnterCriticalSection(&openal_crst);
778     devstr = palcGetString(NULL, ALC_CAPTURE_DEVICE_SPECIFIER);
779     defaultstr = palcGetString(NULL, ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER);
780     if (devstr && *devstr)
781         do {
782             ALint freq = 44100;
783             MultiByteToWideChar( CP_UNIXCP, 0, devstr, -1,
784                                  name, sizeof(name)/sizeof(*name)-1 );
785             name[sizeof(name)/sizeof(*name)-1] = 0;
786             TRACE("Adding %s\n", devstr);
787             dev = palcCaptureOpenDevice(devstr, freq, AL_FORMAT_MONO16, 65536);
788             MMDevice_Create(NULL, name, NULL, eCapture, dev ? DEVICE_STATE_ACTIVE : DEVICE_STATE_NOTPRESENT, !strcmp(devstr, defaultstr));
789             if (dev)
790                 palcCaptureCloseDevice(dev);
791             else
792                 WARN("Could not open device: %04x\n", palcGetError(NULL));
793             devstr += strlen(devstr)+1;
794         } while (*devstr);
795     LeaveCriticalSection(&openal_crst);
796 }
797 #endif /*HAVE_OPENAL*/
798
799 HRESULT MMDevEnum_Create(REFIID riid, void **ppv)
800 {
801     MMDevEnumImpl *This = MMDevEnumerator;
802
803     if (!This)
804     {
805         DWORD i = 0;
806         HKEY root, cur;
807         LONG ret;
808         DWORD curflow;
809
810         This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
811         *ppv = NULL;
812         if (!This)
813             return E_OUTOFMEMORY;
814         This->ref = 1;
815         This->lpVtbl = &MMDevEnumVtbl;
816         MMDevEnumerator = This;
817
818         ret = RegCreateKeyExW(HKEY_LOCAL_MACHINE, software_mmdevapi, 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &root, NULL);
819         if (ret == ERROR_SUCCESS)
820             ret = RegCreateKeyExW(root, reg_capture, 0, NULL, 0, KEY_READ|KEY_WRITE, NULL, &key_capture, NULL);
821         if (ret == ERROR_SUCCESS)
822             ret = RegCreateKeyExW(root, reg_render, 0, NULL, 0, KEY_READ|KEY_WRITE, NULL, &key_render, NULL);
823         RegCloseKey(root);
824         cur = key_capture;
825         curflow = eCapture;
826         if (ret != ERROR_SUCCESS)
827         {
828             RegCloseKey(key_capture);
829             key_render = key_capture = NULL;
830             WARN("Couldn't create key: %u\n", ret);
831             return E_FAIL;
832         }
833         else do {
834             WCHAR guidvalue[39];
835             GUID guid;
836             DWORD len;
837             PROPVARIANT pv = { VT_EMPTY };
838
839             len = sizeof(guidvalue);
840             ret = RegEnumKeyExW(cur, i++, guidvalue, &len, NULL, NULL, NULL, NULL);
841             if (ret == ERROR_NO_MORE_ITEMS)
842             {
843                 if (cur == key_capture)
844                 {
845                     cur = key_render;
846                     curflow = eRender;
847                     i = 0;
848                     continue;
849                 }
850                 break;
851             }
852             if (ret != ERROR_SUCCESS)
853                 continue;
854             if (SUCCEEDED(CLSIDFromString(guidvalue, &guid))
855                 && SUCCEEDED(MMDevice_GetPropValue(&guid, curflow, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pv))
856                 && pv.vt == VT_LPWSTR)
857             {
858                 MMDevice_Create(NULL, pv.u.pwszVal, &guid, curflow,
859                                 DEVICE_STATE_NOTPRESENT, FALSE);
860                 CoTaskMemFree(pv.u.pwszVal);
861             }
862         } while (1);
863 #ifdef HAVE_OPENAL
864         if (openal_loaded)
865         {
866             openal_scanrender();
867             openal_scancapture();
868         }
869 #endif /*HAVE_OPENAL*/
870     }
871     return IUnknown_QueryInterface((IUnknown*)This, riid, ppv);
872 }
873
874 void MMDevEnum_Free(void)
875 {
876     while (MMDevice_count)
877         MMDevice_Destroy(MMDevice_head[0]);
878     RegCloseKey(key_render);
879     RegCloseKey(key_capture);
880     key_render = key_capture = NULL;
881     HeapFree(GetProcessHeap(), 0, MMDevEnumerator);
882     MMDevEnumerator = NULL;
883 }
884
885 static HRESULT WINAPI MMDevEnum_QueryInterface(IMMDeviceEnumerator *iface, REFIID riid, void **ppv)
886 {
887     MMDevEnumImpl *This = (MMDevEnumImpl*)iface;
888
889     if (!ppv)
890         return E_POINTER;
891     if (IsEqualIID(riid, &IID_IUnknown)
892         || IsEqualIID(riid, &IID_IMMDeviceEnumerator))
893         *ppv = This;
894     else
895         *ppv = NULL;
896     if (!*ppv)
897         return E_NOINTERFACE;
898     IUnknown_AddRef((IUnknown*)*ppv);
899     return S_OK;
900 }
901
902 static ULONG WINAPI MMDevEnum_AddRef(IMMDeviceEnumerator *iface)
903 {
904     MMDevEnumImpl *This = (MMDevEnumImpl*)iface;
905     LONG ref = InterlockedIncrement(&This->ref);
906     TRACE("Refcount now %i\n", ref);
907     return ref;
908 }
909
910 static ULONG WINAPI MMDevEnum_Release(IMMDeviceEnumerator *iface)
911 {
912     MMDevEnumImpl *This = (MMDevEnumImpl*)iface;
913     LONG ref = InterlockedDecrement(&This->ref);
914     if (!ref)
915         MMDevEnum_Free();
916     TRACE("Refcount now %i\n", ref);
917     return ref;
918 }
919
920 static HRESULT WINAPI MMDevEnum_EnumAudioEndpoints(IMMDeviceEnumerator *iface, EDataFlow flow, DWORD mask, IMMDeviceCollection **devices)
921 {
922     MMDevEnumImpl *This = (MMDevEnumImpl*)iface;
923     TRACE("(%p)->(%u,%u,%p)\n", This, flow, mask, devices);
924     if (!devices)
925         return E_POINTER;
926     *devices = NULL;
927     if (flow >= EDataFlow_enum_count)
928         return E_INVALIDARG;
929     if (mask & ~DEVICE_STATEMASK_ALL)
930         return E_INVALIDARG;
931     return MMDevCol_Create(devices, flow, mask);
932 }
933
934 static HRESULT WINAPI MMDevEnum_GetDefaultAudioEndpoint(IMMDeviceEnumerator *iface, EDataFlow flow, ERole role, IMMDevice **device)
935 {
936     MMDevEnumImpl *This = (MMDevEnumImpl*)iface;
937     TRACE("(%p)->(%u,%u,%p)\n", This, flow, role, device);
938
939     if (!device)
940         return E_POINTER;
941     *device = NULL;
942
943     if (flow == eRender)
944         *device = (IMMDevice*)MMDevice_def_play;
945     else if (flow == eCapture)
946         *device = (IMMDevice*)MMDevice_def_rec;
947     else
948     {
949         WARN("Unknown flow %u\n", flow);
950         return E_INVALIDARG;
951     }
952
953     if (!*device)
954         return E_NOTFOUND;
955     IMMDevice_AddRef(*device);
956     return S_OK;
957 }
958
959 static HRESULT WINAPI MMDevEnum_GetDevice(IMMDeviceEnumerator *iface, const WCHAR *name, IMMDevice **device)
960 {
961     MMDevEnumImpl *This = (MMDevEnumImpl*)iface;
962     DWORD i=0;
963     IMMDevice *dev = NULL;
964
965     TRACE("(%p)->(%s,%p)\n", This, debugstr_w(name), device);
966     for (i = 0; i < MMDevice_count; ++i)
967     {
968         WCHAR *str;
969         dev = (IMMDevice*)MMDevice_head[i];
970         IMMDevice_GetId(dev, &str);
971
972         if (str && !lstrcmpW(str, name))
973         {
974             CoTaskMemFree(str);
975             break;
976         }
977         CoTaskMemFree(str);
978     }
979     if (dev)
980     {
981         IUnknown_AddRef(dev);
982         *device = dev;
983         return S_OK;
984     }
985     WARN("Could not find device %s\n", debugstr_w(name));
986     return E_NOTFOUND;
987 }
988
989 static HRESULT WINAPI MMDevEnum_RegisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
990 {
991     MMDevEnumImpl *This = (MMDevEnumImpl*)iface;
992     TRACE("(%p)->(%p)\n", This, client);
993     FIXME("stub\n");
994     return E_NOTIMPL;
995 }
996
997 static HRESULT WINAPI MMDevEnum_UnregisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
998 {
999     MMDevEnumImpl *This = (MMDevEnumImpl*)iface;
1000     TRACE("(%p)->(%p)\n", This, client);
1001     FIXME("stub\n");
1002     return E_NOTIMPL;
1003 }
1004
1005 static const IMMDeviceEnumeratorVtbl MMDevEnumVtbl =
1006 {
1007     MMDevEnum_QueryInterface,
1008     MMDevEnum_AddRef,
1009     MMDevEnum_Release,
1010     MMDevEnum_EnumAudioEndpoints,
1011     MMDevEnum_GetDefaultAudioEndpoint,
1012     MMDevEnum_GetDevice,
1013     MMDevEnum_RegisterEndpointNotificationCallback,
1014     MMDevEnum_UnregisterEndpointNotificationCallback
1015 };
1016
1017 static HRESULT MMDevPropStore_Create(MMDevice *parent, DWORD access, IPropertyStore **ppv)
1018 {
1019     MMDevPropStore *This;
1020     if (access != STGM_READ
1021         && access != STGM_WRITE
1022         && access != STGM_READWRITE)
1023     {
1024         WARN("Invalid access %08x\n", access);
1025         return E_INVALIDARG;
1026     }
1027     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
1028     *ppv = (IPropertyStore*)This;
1029     if (!This)
1030         return E_OUTOFMEMORY;
1031     This->lpVtbl = &MMDevPropVtbl;
1032     This->ref = 1;
1033     This->parent = parent;
1034     This->access = access;
1035     return S_OK;
1036 }
1037
1038 static void MMDevPropStore_Destroy(MMDevPropStore *This)
1039 {
1040     HeapFree(GetProcessHeap(), 0, This);
1041 }
1042
1043 static HRESULT WINAPI MMDevPropStore_QueryInterface(IPropertyStore *iface, REFIID riid, void **ppv)
1044 {
1045     MMDevPropStore *This = (MMDevPropStore*)iface;
1046
1047     if (!ppv)
1048         return E_POINTER;
1049     if (IsEqualIID(riid, &IID_IUnknown)
1050         || IsEqualIID(riid, &IID_IPropertyStore))
1051         *ppv = This;
1052     else
1053         *ppv = NULL;
1054     if (!*ppv)
1055         return E_NOINTERFACE;
1056     IUnknown_AddRef((IUnknown*)*ppv);
1057     return S_OK;
1058 }
1059
1060 static ULONG WINAPI MMDevPropStore_AddRef(IPropertyStore *iface)
1061 {
1062     MMDevPropStore *This = (MMDevPropStore*)iface;
1063     LONG ref = InterlockedIncrement(&This->ref);
1064     TRACE("Refcount now %i\n", ref);
1065     return ref;
1066 }
1067
1068 static ULONG WINAPI MMDevPropStore_Release(IPropertyStore *iface)
1069 {
1070     MMDevPropStore *This = (MMDevPropStore*)iface;
1071     LONG ref = InterlockedDecrement(&This->ref);
1072     TRACE("Refcount now %i\n", ref);
1073     if (!ref)
1074         MMDevPropStore_Destroy(This);
1075     return ref;
1076 }
1077
1078 static HRESULT WINAPI MMDevPropStore_GetCount(IPropertyStore *iface, DWORD *nprops)
1079 {
1080     MMDevPropStore *This = (MMDevPropStore*)iface;
1081     WCHAR buffer[50];
1082     DWORD i = 0;
1083     HKEY propkey;
1084     HRESULT hr;
1085
1086     TRACE("(%p)->(%p)\n", iface, nprops);
1087     if (!nprops)
1088         return E_POINTER;
1089     hr = MMDevPropStore_OpenPropKey(&This->parent->devguid, This->parent->flow, &propkey);
1090     if (FAILED(hr))
1091         return hr;
1092     *nprops = 0;
1093     do {
1094         DWORD len = sizeof(buffer)/sizeof(*buffer);
1095         if (RegEnumKeyExW(propkey, i, buffer, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
1096             break;
1097         i++;
1098     } while (0);
1099     RegCloseKey(propkey);
1100     TRACE("Returning %i\n", i);
1101     *nprops = i;
1102     return S_OK;
1103 }
1104
1105 static HRESULT WINAPI MMDevPropStore_GetAt(IPropertyStore *iface, DWORD prop, PROPERTYKEY *key)
1106 {
1107     MMDevPropStore *This = (MMDevPropStore*)iface;
1108     WCHAR buffer[50];
1109     DWORD len = sizeof(buffer)/sizeof(*buffer);
1110     HRESULT hr;
1111     HKEY propkey;
1112
1113     TRACE("(%p)->(%u,%p)\n", iface, prop, key);
1114     if (!key)
1115         return E_POINTER;
1116
1117     hr = MMDevPropStore_OpenPropKey(&This->parent->devguid, This->parent->flow, &propkey);
1118     if (FAILED(hr))
1119         return hr;
1120
1121     if (RegEnumKeyExW(propkey, prop, buffer, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS
1122         || len <= 40)
1123     {
1124         WARN("GetAt %u failed\n", prop);
1125         return E_INVALIDARG;
1126     }
1127     RegCloseKey(propkey);
1128     buffer[39] = 0;
1129     CLSIDFromString(buffer, &key->fmtid);
1130     key->pid = atoiW(&buffer[40]);
1131     return S_OK;
1132 }
1133
1134 static HRESULT WINAPI MMDevPropStore_GetValue(IPropertyStore *iface, REFPROPERTYKEY key, PROPVARIANT *pv)
1135 {
1136     MMDevPropStore *This = (MMDevPropStore*)iface;
1137     TRACE("(%p)->(\"%s,%u\", %p\n", This, debugstr_guid(&key->fmtid), key ? key->pid : 0, pv);
1138
1139     if (!key || !pv)
1140         return E_POINTER;
1141     if (This->access != STGM_READ
1142         && This->access != STGM_READWRITE)
1143         return STG_E_ACCESSDENIED;
1144
1145     /* Special case */
1146     if (IsEqualPropertyKey(*key, PKEY_AudioEndpoint_GUID))
1147     {
1148         pv->u.pwszVal = CoTaskMemAlloc(39 * sizeof(WCHAR));
1149         if (!pv->u.pwszVal)
1150             return E_OUTOFMEMORY;
1151         StringFromGUID2(&This->parent->devguid, pv->u.pwszVal, 39);
1152         return S_OK;
1153     }
1154
1155     return MMDevice_GetPropValue(&This->parent->devguid, This->parent->flow, key, pv);
1156 }
1157
1158 static HRESULT WINAPI MMDevPropStore_SetValue(IPropertyStore *iface, REFPROPERTYKEY key, REFPROPVARIANT pv)
1159 {
1160     MMDevPropStore *This = (MMDevPropStore*)iface;
1161
1162     if (!key || !pv)
1163         return E_POINTER;
1164
1165     if (This->access != STGM_WRITE
1166         && This->access != STGM_READWRITE)
1167         return STG_E_ACCESSDENIED;
1168     return MMDevice_SetPropValue(&This->parent->devguid, This->parent->flow, key, pv);
1169 }
1170
1171 static HRESULT WINAPI MMDevPropStore_Commit(IPropertyStore *iface)
1172 {
1173     FIXME("stub\n");
1174     return E_NOTIMPL;
1175 }
1176
1177 static const IPropertyStoreVtbl MMDevPropVtbl =
1178 {
1179     MMDevPropStore_QueryInterface,
1180     MMDevPropStore_AddRef,
1181     MMDevPropStore_Release,
1182     MMDevPropStore_GetCount,
1183     MMDevPropStore_GetAt,
1184     MMDevPropStore_GetValue,
1185     MMDevPropStore_SetValue,
1186     MMDevPropStore_Commit
1187 };
1188
1189
1190 /* Property bag for IBaseFilter activation */
1191 static HRESULT WINAPI PB_QueryInterface(IPropertyBag *iface, REFIID riid, void **ppv)
1192 {
1193     ERR("Should not be called\n");
1194     *ppv = NULL;
1195     return E_NOINTERFACE;
1196 }
1197
1198 static ULONG WINAPI PB_AddRef(IPropertyBag *iface)
1199 {
1200     ERR("Should not be called\n");
1201     return 2;
1202 }
1203
1204 static ULONG WINAPI PB_Release(IPropertyBag *iface)
1205 {
1206     ERR("Should not be called\n");
1207     return 1;
1208 }
1209
1210 static HRESULT WINAPI PB_Read(IPropertyBag *iface, LPCOLESTR name, VARIANT *var, IErrorLog *log)
1211 {
1212     static const WCHAR dsguid[] = { 'D','S','G','u','i','d', 0 };
1213     IPropertyBagImpl *This = (IPropertyBagImpl*)iface;
1214     TRACE("Trying to read %s, type %u\n", debugstr_w(name), var->n1.n2.vt);
1215     if (!lstrcmpW(name, dsguid))
1216     {
1217         WCHAR guidstr[39];
1218         StringFromGUID2(&This->devguid, guidstr,sizeof(guidstr)/sizeof(*guidstr));
1219         var->n1.n2.vt = VT_BSTR;
1220         var->n1.n2.n3.bstrVal = SysAllocString(guidstr);
1221         return S_OK;
1222     }
1223     ERR("Unknown property '%s' queried\n", debugstr_w(name));
1224     return E_FAIL;
1225 }
1226
1227 static HRESULT WINAPI PB_Write(IPropertyBag *iface, LPCOLESTR name, VARIANT *var)
1228 {
1229     ERR("Should not be called\n");
1230     return E_FAIL;
1231 }
1232
1233 static const IPropertyBagVtbl PB_Vtbl =
1234 {
1235     PB_QueryInterface,
1236     PB_AddRef,
1237     PB_Release,
1238     PB_Read,
1239     PB_Write
1240 };