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