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