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