shell32/shellview: Implement IFolderView::GetFolder() for IID_IShellFolder.
[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 MMDevEnumImpl
66 {
67     const IMMDeviceEnumeratorVtbl *lpVtbl;
68     LONG ref;
69 } MMDevEnumImpl;
70
71 static MMDevEnumImpl *MMDevEnumerator;
72 static MMDevice **MMDevice_head;
73 static MMDevice *MMDevice_def_rec, *MMDevice_def_play;
74 static DWORD MMDevice_count;
75 static const IMMDeviceEnumeratorVtbl MMDevEnumVtbl;
76 static const IMMDeviceCollectionVtbl MMDevColVtbl;
77 static const IMMDeviceVtbl MMDeviceVtbl;
78
79 typedef struct MMDevColImpl
80 {
81     const IMMDeviceCollectionVtbl *lpVtbl;
82     LONG ref;
83     EDataFlow flow;
84     DWORD state;
85 } MMDevColImpl;
86
87 /* Creates or updates the state of a device
88  * If GUID is null, a random guid will be assigned
89  * and the device will be created
90  */
91 static void MMDevice_Create(WCHAR *name, GUID *id, EDataFlow flow, DWORD state, BOOL setdefault)
92 {
93     HKEY key, root;
94     MMDevice *cur;
95     WCHAR guidstr[39];
96     DWORD i;
97
98     for (i = 0; i < MMDevice_count; ++i)
99     {
100         cur = MMDevice_head[i];
101         if (cur->flow == flow && !lstrcmpW(cur->alname, name))
102         {
103             LONG ret;
104             /* Same device, update state */
105             cur->state = state;
106             StringFromGUID2(&cur->devguid, guidstr, sizeof(guidstr)/sizeof(*guidstr));
107             ret = RegOpenKeyExW(flow == eRender ? key_render : key_capture, guidstr, 0, KEY_WRITE, &key);
108             if (ret == ERROR_SUCCESS)
109             {
110                 RegSetValueExW(key, reg_devicestate, 0, REG_DWORD, (const BYTE*)&state, sizeof(DWORD));
111                 RegCloseKey(key);
112             }
113             goto done;
114         }
115     }
116
117     /* No device found, allocate new one */
118     cur = HeapAlloc(GetProcessHeap(), 0, sizeof(*cur));
119     if (!cur)
120         return;
121     cur->alname = HeapAlloc(GetProcessHeap(), 0, (lstrlenW(name)+1)*sizeof(WCHAR));
122     if (!cur->alname)
123     {
124         HeapFree(GetProcessHeap(), 0, cur);
125         return;
126     }
127     lstrcpyW(cur->alname, name);
128     cur->lpVtbl = &MMDeviceVtbl;
129     cur->ref = 0;
130     InitializeCriticalSection(&cur->crst);
131     cur->crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": MMDevice.crst");
132     cur->flow = flow;
133     cur->state = state;
134     if (!id)
135     {
136         id = &cur->devguid;
137         CoCreateGuid(id);
138     }
139     cur->devguid = *id;
140     StringFromGUID2(id, guidstr, sizeof(guidstr)/sizeof(*guidstr));
141     if (flow == eRender)
142         root = key_render;
143     else
144         root = key_capture;
145     if (!RegCreateKeyExW(root, guidstr, 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &key, NULL))
146     {
147         HKEY keyprop;
148         RegSetValueExW(key, reg_devicestate, 0, REG_DWORD, (const BYTE*)&state, sizeof(DWORD));
149         if (!RegCreateKeyExW(key, reg_properties, 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &keyprop, NULL))
150         {
151             RegCloseKey(keyprop);
152         }
153         RegCloseKey(key);
154     }
155     if (!MMDevice_head)
156         MMDevice_head = HeapAlloc(GetProcessHeap(), 0, sizeof(*MMDevice_head));
157     else
158         MMDevice_head = HeapReAlloc(GetProcessHeap(), 0, MMDevice_head, sizeof(*MMDevice_head)*(1+MMDevice_count));
159     MMDevice_head[MMDevice_count++] = cur;
160
161 done:
162     if (setdefault)
163     {
164         if (flow == eRender)
165             MMDevice_def_play = cur;
166         else
167             MMDevice_def_rec = cur;
168     }
169 }
170
171 static void MMDevice_Destroy(MMDevice *This)
172 {
173     DWORD i;
174     TRACE("Freeing %s\n", debugstr_w(This->alname));
175     /* Since this function is called at destruction time, reordering of the list is unimportant */
176     for (i = 0; i < MMDevice_count; ++i)
177     {
178         if (MMDevice_head[i] == This)
179         {
180             MMDevice_head[i] = MMDevice_head[--MMDevice_count];
181             break;
182         }
183     }
184     This->crst.DebugInfo->Spare[0] = 0;
185     DeleteCriticalSection(&This->crst);
186     HeapFree(GetProcessHeap(), 0, This->alname);
187     HeapFree(GetProcessHeap(), 0, This);
188 }
189
190 static HRESULT WINAPI MMDevice_QueryInterface(IMMDevice *iface, REFIID riid, void **ppv)
191 {
192     MMDevice *This = (MMDevice *)iface;
193     TRACE("(%p)->(%s,%p)\n", iface, debugstr_guid(riid), ppv);
194
195     if (!ppv)
196         return E_POINTER;
197     *ppv = NULL;
198     if (IsEqualIID(riid, &IID_IUnknown)
199         || IsEqualIID(riid, &IID_IMMDevice))
200         *ppv = This;
201     if (*ppv)
202     {
203         IUnknown_AddRef((IUnknown*)*ppv);
204         return S_OK;
205     }
206     WARN("Unknown interface %s\n", debugstr_guid(riid));
207     return E_NOINTERFACE;
208 }
209
210 static ULONG WINAPI MMDevice_AddRef(IMMDevice *iface)
211 {
212     MMDevice *This = (MMDevice *)iface;
213     LONG ref;
214
215     ref = InterlockedIncrement(&This->ref);
216     TRACE("Refcount now %i\n", ref);
217     return ref;
218 }
219
220 static ULONG WINAPI MMDevice_Release(IMMDevice *iface)
221 {
222     MMDevice *This = (MMDevice *)iface;
223     LONG ref;
224
225     ref = InterlockedDecrement(&This->ref);
226     TRACE("Refcount now %i\n", ref);
227     return ref;
228 }
229
230 static HRESULT WINAPI MMDevice_Activate(IMMDevice *iface, REFIID riid, DWORD clsctx, PROPVARIANT *params, void **ppv)
231 {
232     MMDevice *This = (MMDevice *)iface;
233     HRESULT hr = E_NOINTERFACE;
234     TRACE("(%p)->(%p,%x,%p,%p)\n", This, riid, clsctx, params, ppv);
235
236     if (!ppv)
237         return E_POINTER;
238
239     FIXME("stub\n");
240     hr = E_NOTIMPL;
241     TRACE("Returning %08x\n", hr);
242     return hr;
243 }
244
245 static HRESULT WINAPI MMDevice_OpenPropertyStore(IMMDevice *iface, DWORD access, IPropertyStore **ppv)
246 {
247     MMDevice *This = (MMDevice *)iface;
248     TRACE("(%p)->(%x,%p)\n", This, access, ppv);
249
250     if (!ppv)
251         return E_POINTER;
252     FIXME("stub\n");
253     return E_NOTIMPL;
254 }
255
256 static HRESULT WINAPI MMDevice_GetId(IMMDevice *iface, WCHAR **itemid)
257 {
258     MMDevice *This = (MMDevice *)iface;
259
260     TRACE("(%p)->(%p)\n", This, itemid);
261     if (!itemid)
262         return E_POINTER;
263     *itemid = NULL;
264     FIXME("stub\n");
265     return E_NOTIMPL;
266 }
267
268 static HRESULT WINAPI MMDevice_GetState(IMMDevice *iface, DWORD *state)
269 {
270     MMDevice *This = (MMDevice *)iface;
271     TRACE("(%p)->(%p)\n", iface, state);
272
273     if (!state)
274         return E_POINTER;
275     *state = This->state;
276     return S_OK;
277 }
278
279 static const IMMDeviceVtbl MMDeviceVtbl =
280 {
281     MMDevice_QueryInterface,
282     MMDevice_AddRef,
283     MMDevice_Release,
284     MMDevice_Activate,
285     MMDevice_OpenPropertyStore,
286     MMDevice_GetId,
287     MMDevice_GetState
288 };
289
290 static HRESULT MMDevCol_Create(IMMDeviceCollection **ppv, EDataFlow flow, DWORD state)
291 {
292     MMDevColImpl *This;
293
294     This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
295     *ppv = NULL;
296     if (!This)
297         return E_OUTOFMEMORY;
298     This->lpVtbl = &MMDevColVtbl;
299     This->ref = 1;
300     This->flow = flow;
301     This->state = state;
302     *ppv = (IMMDeviceCollection*)This;
303     return S_OK;
304 }
305
306 static void MMDevCol_Destroy(MMDevColImpl *This)
307 {
308     HeapFree(GetProcessHeap(), 0, This);
309 }
310
311 static HRESULT WINAPI MMDevCol_QueryInterface(IMMDeviceCollection *iface, REFIID riid, void **ppv)
312 {
313     MMDevColImpl *This = (MMDevColImpl*)iface;
314
315     if (!ppv)
316         return E_POINTER;
317     if (IsEqualIID(riid, &IID_IUnknown)
318         || IsEqualIID(riid, &IID_IMMDeviceCollection))
319         *ppv = This;
320     else
321         *ppv = NULL;
322     if (!*ppv)
323         return E_NOINTERFACE;
324     IUnknown_AddRef((IUnknown*)*ppv);
325     return S_OK;
326 }
327
328 static ULONG WINAPI MMDevCol_AddRef(IMMDeviceCollection *iface)
329 {
330     MMDevColImpl *This = (MMDevColImpl*)iface;
331     LONG ref = InterlockedIncrement(&This->ref);
332     TRACE("Refcount now %i\n", ref);
333     return ref;
334 }
335
336 static ULONG WINAPI MMDevCol_Release(IMMDeviceCollection *iface)
337 {
338     MMDevColImpl *This = (MMDevColImpl*)iface;
339     LONG ref = InterlockedDecrement(&This->ref);
340     TRACE("Refcount now %i\n", ref);
341     if (!ref)
342         MMDevCol_Destroy(This);
343     return ref;
344 }
345
346 static HRESULT WINAPI MMDevCol_GetCount(IMMDeviceCollection *iface, UINT *numdevs)
347 {
348     MMDevColImpl *This = (MMDevColImpl*)iface;
349
350     TRACE("(%p)->(%p)\n", This, numdevs);
351     if (!numdevs)
352         return E_POINTER;
353     *numdevs = 0;
354     return S_OK;
355 }
356
357 static HRESULT WINAPI MMDevCol_Item(IMMDeviceCollection *iface, UINT i, IMMDevice **dev)
358 {
359     MMDevColImpl *This = (MMDevColImpl*)iface;
360     TRACE("(%p)->(%u, %p)\n", This, i, dev);
361     if (!dev)
362         return E_POINTER;
363     *dev = NULL;
364     return E_INVALIDARG;
365 }
366
367 static const IMMDeviceCollectionVtbl MMDevColVtbl =
368 {
369     MMDevCol_QueryInterface,
370     MMDevCol_AddRef,
371     MMDevCol_Release,
372     MMDevCol_GetCount,
373     MMDevCol_Item
374 };
375
376 HRESULT MMDevEnum_Create(REFIID riid, void **ppv)
377 {
378     MMDevEnumImpl *This = MMDevEnumerator;
379
380     if (!This)
381     {
382         DWORD i = 0;
383         HKEY root, cur;
384         LONG ret;
385         DWORD curflow;
386
387         This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
388         *ppv = NULL;
389         if (!This)
390             return E_OUTOFMEMORY;
391         This->ref = 1;
392         This->lpVtbl = &MMDevEnumVtbl;
393         MMDevEnumerator = This;
394
395         ret = RegCreateKeyExW(HKEY_LOCAL_MACHINE, software_mmdevapi, 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &root, NULL);
396         if (ret == ERROR_SUCCESS)
397             ret = RegCreateKeyExW(root, reg_capture, 0, NULL, 0, KEY_READ|KEY_WRITE, NULL, &key_capture, NULL);
398         if (ret == ERROR_SUCCESS)
399             ret = RegCreateKeyExW(root, reg_render, 0, NULL, 0, KEY_READ|KEY_WRITE, NULL, &key_render, NULL);
400         RegCloseKey(root);
401         cur = key_capture;
402         curflow = eCapture;
403         if (ret != ERROR_SUCCESS)
404         {
405             RegCloseKey(key_capture);
406             key_render = key_capture = NULL;
407             WARN("Couldn't create key: %u\n", ret);
408             return E_FAIL;
409         }
410         else do {
411             WCHAR guidvalue[39];
412             GUID guid;
413             DWORD len;
414
415             len = sizeof(guidvalue);
416             ret = RegEnumKeyExW(cur, i++, guidvalue, &len, NULL, NULL, NULL, NULL);
417             if (ret == ERROR_NO_MORE_ITEMS)
418             {
419                 if (cur == key_capture)
420                 {
421                     cur = key_render;
422                     curflow = eRender;
423                     i = 0;
424                     continue;
425                 }
426                 break;
427             }
428             if (ret != ERROR_SUCCESS)
429                 continue;
430             if (SUCCEEDED(CLSIDFromString(guidvalue, &guid)))
431                 /* Using guid as friendly name till property store works */
432                 MMDevice_Create(guidvalue, &guid, curflow,
433                                 DEVICE_STATE_NOTPRESENT, FALSE);
434         } while (1);
435     }
436     return IUnknown_QueryInterface((IUnknown*)This, riid, ppv);
437 }
438
439 void MMDevEnum_Free(void)
440 {
441     while (MMDevice_count)
442         MMDevice_Destroy(MMDevice_head[0]);
443     RegCloseKey(key_render);
444     RegCloseKey(key_capture);
445     key_render = key_capture = NULL;
446     HeapFree(GetProcessHeap(), 0, MMDevEnumerator);
447     MMDevEnumerator = NULL;
448 }
449
450 static HRESULT WINAPI MMDevEnum_QueryInterface(IMMDeviceEnumerator *iface, REFIID riid, void **ppv)
451 {
452     MMDevEnumImpl *This = (MMDevEnumImpl*)iface;
453
454     if (!ppv)
455         return E_POINTER;
456     if (IsEqualIID(riid, &IID_IUnknown)
457         || IsEqualIID(riid, &IID_IMMDeviceEnumerator))
458         *ppv = This;
459     else
460         *ppv = NULL;
461     if (!*ppv)
462         return E_NOINTERFACE;
463     IUnknown_AddRef((IUnknown*)*ppv);
464     return S_OK;
465 }
466
467 static ULONG WINAPI MMDevEnum_AddRef(IMMDeviceEnumerator *iface)
468 {
469     MMDevEnumImpl *This = (MMDevEnumImpl*)iface;
470     LONG ref = InterlockedIncrement(&This->ref);
471     TRACE("Refcount now %i\n", ref);
472     return ref;
473 }
474
475 static ULONG WINAPI MMDevEnum_Release(IMMDeviceEnumerator *iface)
476 {
477     MMDevEnumImpl *This = (MMDevEnumImpl*)iface;
478     LONG ref = InterlockedDecrement(&This->ref);
479     if (!ref)
480         MMDevEnum_Free();
481     TRACE("Refcount now %i\n", ref);
482     return ref;
483 }
484
485 static HRESULT WINAPI MMDevEnum_EnumAudioEndpoints(IMMDeviceEnumerator *iface, EDataFlow flow, DWORD mask, IMMDeviceCollection **devices)
486 {
487     MMDevEnumImpl *This = (MMDevEnumImpl*)iface;
488     TRACE("(%p)->(%u,%u,%p)\n", This, flow, mask, devices);
489     if (!devices)
490         return E_POINTER;
491     *devices = NULL;
492     if (flow >= EDataFlow_enum_count)
493         return E_INVALIDARG;
494     if (mask & ~DEVICE_STATEMASK_ALL)
495         return E_INVALIDARG;
496     return MMDevCol_Create(devices, flow, mask);
497 }
498
499 static HRESULT WINAPI MMDevEnum_GetDefaultAudioEndpoint(IMMDeviceEnumerator *iface, EDataFlow flow, ERole role, IMMDevice **device)
500 {
501     MMDevEnumImpl *This = (MMDevEnumImpl*)iface;
502     TRACE("(%p)->(%u,%u,%p)\n", This, flow, role, device);
503
504     if (!device)
505         return E_POINTER;
506     *device = NULL;
507
508     if (flow == eRender)
509         *device = (IMMDevice*)MMDevice_def_play;
510     else if (flow == eCapture)
511         *device = (IMMDevice*)MMDevice_def_rec;
512     else
513     {
514         WARN("Unknown flow %u\n", flow);
515         return E_INVALIDARG;
516     }
517
518     if (!*device)
519         return E_NOTFOUND;
520     IMMDevice_AddRef(*device);
521     return S_OK;
522 }
523
524 static HRESULT WINAPI MMDevEnum_GetDevice(IMMDeviceEnumerator *iface, const WCHAR *name, IMMDevice **device)
525 {
526     MMDevEnumImpl *This = (MMDevEnumImpl*)iface;
527     TRACE("(%p)->(%s,%p)\n", This, debugstr_w(name), device);
528     FIXME("stub\n");
529     return E_NOTIMPL;
530 }
531
532 static HRESULT WINAPI MMDevEnum_RegisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
533 {
534     MMDevEnumImpl *This = (MMDevEnumImpl*)iface;
535     TRACE("(%p)->(%p)\n", This, client);
536     FIXME("stub\n");
537     return E_NOTIMPL;
538 }
539
540 static HRESULT WINAPI MMDevEnum_UnregisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
541 {
542     MMDevEnumImpl *This = (MMDevEnumImpl*)iface;
543     TRACE("(%p)->(%p)\n", This, client);
544     FIXME("stub\n");
545     return E_NOTIMPL;
546 }
547
548 static const IMMDeviceEnumeratorVtbl MMDevEnumVtbl =
549 {
550     MMDevEnum_QueryInterface,
551     MMDevEnum_AddRef,
552     MMDevEnum_Release,
553     MMDevEnum_EnumAudioEndpoints,
554     MMDevEnum_GetDefaultAudioEndpoint,
555     MMDevEnum_GetDevice,
556     MMDevEnum_RegisterEndpointNotificationCallback,
557     MMDevEnum_UnregisterEndpointNotificationCallback
558 };