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