[MMDEVAPI]
[reactos.git] / reactos / dll / win32 / 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 "mmdevapi.h"
20
21 #include <wine/list.h>
22
23 #include <oleauto.h>
24 #include <initguid.h>
25 #define _WINDOWS_H
26 #include <dshow.h>
27 #include <devpkey.h>
28
29 static const WCHAR software_mmdevapi[] =
30 { 'S','o','f','t','w','a','r','e','\\',
31 'M','i','c','r','o','s','o','f','t','\\',
32 'W','i','n','d','o','w','s','\\',
33 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
34 'M','M','D','e','v','i','c','e','s','\\',
35 'A','u','d','i','o',0};
36 static const WCHAR reg_render[] =
37 { 'R','e','n','d','e','r',0 };
38 static const WCHAR reg_capture[] =
39 { 'C','a','p','t','u','r','e',0 };
40 static const WCHAR reg_devicestate[] =
41 { 'D','e','v','i','c','e','S','t','a','t','e',0 };
42 static const WCHAR reg_properties[] =
43 { 'P','r','o','p','e','r','t','i','e','s',0 };
44 static const WCHAR slashW[] = {'\\',0};
45 static const WCHAR reg_out_nameW[] = {'D','e','f','a','u','l','t','O','u','t','p','u','t',0};
46 static const WCHAR reg_vout_nameW[] = {'D','e','f','a','u','l','t','V','o','i','c','e','O','u','t','p','u','t',0};
47 static const WCHAR reg_in_nameW[] = {'D','e','f','a','u','l','t','I','n','p','u','t',0};
48 static const WCHAR reg_vin_nameW[] = {'D','e','f','a','u','l','t','V','o','i','c','e','I','n','p','u','t',0};
49
50 static HKEY key_render;
51 static HKEY key_capture;
52
53 typedef struct MMDevPropStoreImpl
54 {
55 IPropertyStore IPropertyStore_iface;
56 LONG ref;
57 MMDevice *parent;
58 DWORD access;
59 } MMDevPropStore;
60
61 typedef struct MMDevEnumImpl
62 {
63 IMMDeviceEnumerator IMMDeviceEnumerator_iface;
64 LONG ref;
65 } MMDevEnumImpl;
66
67 static MMDevEnumImpl *MMDevEnumerator;
68 static MMDevice **MMDevice_head;
69 static MMDevice *MMDevice_def_rec, *MMDevice_def_play;
70 static DWORD MMDevice_count;
71 static const IMMDeviceEnumeratorVtbl MMDevEnumVtbl;
72 static const IMMDeviceCollectionVtbl MMDevColVtbl;
73 static const IMMDeviceVtbl MMDeviceVtbl;
74 static const IPropertyStoreVtbl MMDevPropVtbl;
75 static const IMMEndpointVtbl MMEndpointVtbl;
76
77 static IMMDevice info_device;
78
79 typedef struct MMDevColImpl
80 {
81 IMMDeviceCollection IMMDeviceCollection_iface;
82 LONG ref;
83 EDataFlow flow;
84 DWORD state;
85 } MMDevColImpl;
86
87 typedef struct IPropertyBagImpl {
88 IPropertyBag IPropertyBag_iface;
89 GUID devguid;
90 } IPropertyBagImpl;
91
92 static const IPropertyBagVtbl PB_Vtbl;
93
94 static HRESULT MMDevPropStore_Create(MMDevice *This, DWORD access, IPropertyStore **ppv);
95
96 static inline MMDevPropStore *impl_from_IPropertyStore(IPropertyStore *iface)
97 {
98 return CONTAINING_RECORD(iface, MMDevPropStore, IPropertyStore_iface);
99 }
100
101 static inline MMDevEnumImpl *impl_from_IMMDeviceEnumerator(IMMDeviceEnumerator *iface)
102 {
103 return CONTAINING_RECORD(iface, MMDevEnumImpl, IMMDeviceEnumerator_iface);
104 }
105
106 static inline MMDevColImpl *impl_from_IMMDeviceCollection(IMMDeviceCollection *iface)
107 {
108 return CONTAINING_RECORD(iface, MMDevColImpl, IMMDeviceCollection_iface);
109 }
110
111 static inline IPropertyBagImpl *impl_from_IPropertyBag(IPropertyBag *iface)
112 {
113 return CONTAINING_RECORD(iface, IPropertyBagImpl, IPropertyBag_iface);
114 }
115
116 static const WCHAR propkey_formatW[] = {
117 '{','%','0','8','X','-','%','0','4','X','-',
118 '%','0','4','X','-','%','0','2','X','%','0','2','X','-',
119 '%','0','2','X','%','0','2','X','%','0','2','X','%','0','2','X',
120 '%','0','2','X','%','0','2','X','}',',','%','d',0 };
121
122 static HRESULT MMDevPropStore_OpenPropKey(const GUID *guid, DWORD flow, HKEY *propkey)
123 {
124 WCHAR buffer[39];
125 LONG ret;
126 HKEY key;
127 StringFromGUID2(guid, buffer, 39);
128 if ((ret = RegOpenKeyExW(flow == eRender ? key_render : key_capture, buffer, 0, KEY_READ|KEY_WRITE, &key)) != ERROR_SUCCESS)
129 {
130 WARN("Opening key %s failed with %u\n", debugstr_w(buffer), ret);
131 return E_FAIL;
132 }
133 ret = RegOpenKeyExW(key, reg_properties, 0, KEY_READ|KEY_WRITE, propkey);
134 RegCloseKey(key);
135 if (ret != ERROR_SUCCESS)
136 {
137 WARN("Opening key %s failed with %u\n", debugstr_w(reg_properties), ret);
138 return E_FAIL;
139 }
140 return S_OK;
141 }
142
143 static HRESULT MMDevice_GetPropValue(const GUID *devguid, DWORD flow, REFPROPERTYKEY key, PROPVARIANT *pv)
144 {
145 WCHAR buffer[80];
146 const GUID *id = &key->fmtid;
147 DWORD type, size;
148 HRESULT hr = S_OK;
149 HKEY regkey;
150 LONG ret;
151
152 hr = MMDevPropStore_OpenPropKey(devguid, flow, &regkey);
153 if (FAILED(hr))
154 return hr;
155 wsprintfW( buffer, propkey_formatW, id->Data1, id->Data2, id->Data3,
156 id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
157 id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7], key->pid );
158 ret = RegGetValueW(regkey, NULL, buffer, RRF_RT_ANY, &type, NULL, &size);
159 if (ret != ERROR_SUCCESS)
160 {
161 WARN("Reading %s returned %d\n", debugstr_w(buffer), ret);
162 RegCloseKey(regkey);
163 PropVariantClear(pv);
164 return S_OK;
165 }
166
167 switch (type)
168 {
169 case REG_SZ:
170 {
171 pv->vt = VT_LPWSTR;
172 pv->u.pwszVal = CoTaskMemAlloc(size);
173 if (!pv->u.pwszVal)
174 hr = E_OUTOFMEMORY;
175 else
176 RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_SZ, NULL, (BYTE*)pv->u.pwszVal, &size);
177 break;
178 }
179 case REG_DWORD:
180 {
181 pv->vt = VT_UI4;
182 RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_DWORD, NULL, (BYTE*)&pv->u.ulVal, &size);
183 break;
184 }
185 case REG_BINARY:
186 {
187 pv->vt = VT_BLOB;
188 pv->u.blob.cbSize = size;
189 pv->u.blob.pBlobData = CoTaskMemAlloc(size);
190 if (!pv->u.blob.pBlobData)
191 hr = E_OUTOFMEMORY;
192 else
193 RegGetValueW(regkey, NULL, buffer, RRF_RT_REG_BINARY, NULL, (BYTE*)pv->u.blob.pBlobData, &size);
194 break;
195 }
196 default:
197 ERR("Unknown/unhandled type: %u\n", type);
198 PropVariantClear(pv);
199 break;
200 }
201 RegCloseKey(regkey);
202 return hr;
203 }
204
205 static HRESULT MMDevice_SetPropValue(const GUID *devguid, DWORD flow, REFPROPERTYKEY key, REFPROPVARIANT pv)
206 {
207 WCHAR buffer[80];
208 const GUID *id = &key->fmtid;
209 HRESULT hr;
210 HKEY regkey;
211 LONG ret;
212
213 hr = MMDevPropStore_OpenPropKey(devguid, flow, &regkey);
214 if (FAILED(hr))
215 return hr;
216 wsprintfW( buffer, propkey_formatW, id->Data1, id->Data2, id->Data3,
217 id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
218 id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7], key->pid );
219 switch (pv->vt)
220 {
221 case VT_UI4:
222 {
223 ret = RegSetValueExW(regkey, buffer, 0, REG_DWORD, (const BYTE*)&pv->u.ulVal, sizeof(DWORD));
224 break;
225 }
226 case VT_BLOB:
227 {
228 ret = RegSetValueExW(regkey, buffer, 0, REG_BINARY, pv->u.blob.pBlobData, pv->u.blob.cbSize);
229 TRACE("Blob %p %u\n", pv->u.blob.pBlobData, pv->u.blob.cbSize);
230
231 break;
232 }
233 case VT_LPWSTR:
234 {
235 ret = RegSetValueExW(regkey, buffer, 0, REG_SZ, (const BYTE*)pv->u.pwszVal, sizeof(WCHAR)*(1+lstrlenW(pv->u.pwszVal)));
236 break;
237 }
238 default:
239 ret = 0;
240 FIXME("Unhandled type %u\n", pv->vt);
241 hr = E_INVALIDARG;
242 break;
243 }
244 RegCloseKey(regkey);
245 TRACE("Writing %s returned %u\n", debugstr_w(buffer), ret);
246 return hr;
247 }
248
249 /* Creates or updates the state of a device
250 * If GUID is null, a random guid will be assigned
251 * and the device will be created
252 */
253 static MMDevice *MMDevice_Create(WCHAR *name, GUID *id, EDataFlow flow, DWORD state, BOOL setdefault)
254 {
255 HKEY key, root;
256 MMDevice *cur = NULL;
257 WCHAR guidstr[39];
258 DWORD i;
259
260 static const PROPERTYKEY deviceinterface_key = {
261 {0x233164c8, 0x1b2c, 0x4c7d, {0xbc, 0x68, 0xb6, 0x71, 0x68, 0x7a, 0x25, 0x67}}, 1
262 };
263
264 for (i = 0; i < MMDevice_count; ++i)
265 {
266 MMDevice *device = MMDevice_head[i];
267 if (device->flow == flow && IsEqualGUID(&device->devguid, id)){
268 cur = device;
269 break;
270 }
271 }
272
273 if(!cur){
274 /* No device found, allocate new one */
275 cur = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*cur));
276 if (!cur)
277 return NULL;
278
279 cur->IMMDevice_iface.lpVtbl = &MMDeviceVtbl;
280 cur->IMMEndpoint_iface.lpVtbl = &MMEndpointVtbl;
281
282 InitializeCriticalSection(&cur->crst);
283 cur->crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": MMDevice.crst");
284
285 if (!MMDevice_head)
286 MMDevice_head = HeapAlloc(GetProcessHeap(), 0, sizeof(*MMDevice_head));
287 else
288 MMDevice_head = HeapReAlloc(GetProcessHeap(), 0, MMDevice_head, sizeof(*MMDevice_head)*(1+MMDevice_count));
289 MMDevice_head[MMDevice_count++] = cur;
290 }else if(cur->ref > 0)
291 WARN("Modifying an MMDevice with postitive reference count!\n");
292
293 HeapFree(GetProcessHeap(), 0, cur->drv_id);
294 cur->drv_id = name;
295
296 cur->flow = flow;
297 cur->state = state;
298 cur->devguid = *id;
299
300 StringFromGUID2(&cur->devguid, guidstr, sizeof(guidstr)/sizeof(*guidstr));
301
302 if (flow == eRender)
303 root = key_render;
304 else
305 root = key_capture;
306
307 if (RegCreateKeyExW(root, guidstr, 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &key, NULL) == ERROR_SUCCESS)
308 {
309 HKEY keyprop;
310 RegSetValueExW(key, reg_devicestate, 0, REG_DWORD, (const BYTE*)&state, sizeof(DWORD));
311 if (!RegCreateKeyExW(key, reg_properties, 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &keyprop, NULL))
312 {
313 PROPVARIANT pv;
314
315 pv.vt = VT_LPWSTR;
316 pv.u.pwszVal = name;
317 MMDevice_SetPropValue(id, flow, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pv);
318 MMDevice_SetPropValue(id, flow, (const PROPERTYKEY*)&DEVPKEY_Device_DeviceDesc, &pv);
319
320 pv.u.pwszVal = guidstr;
321 MMDevice_SetPropValue(id, flow, &deviceinterface_key, &pv);
322
323 RegCloseKey(keyprop);
324 }
325 RegCloseKey(key);
326 }
327
328 if (setdefault)
329 {
330 if (flow == eRender)
331 MMDevice_def_play = cur;
332 else
333 MMDevice_def_rec = cur;
334 }
335 return cur;
336 }
337
338 static HRESULT load_devices_from_reg(void)
339 {
340 DWORD i = 0;
341 HKEY root, cur;
342 LONG ret;
343 DWORD curflow;
344
345 ret = RegCreateKeyExW(HKEY_LOCAL_MACHINE, software_mmdevapi, 0, NULL, 0, KEY_WRITE|KEY_READ, NULL, &root, NULL);
346 if (ret == ERROR_SUCCESS)
347 ret = RegCreateKeyExW(root, reg_capture, 0, NULL, 0, KEY_READ|KEY_WRITE, NULL, &key_capture, NULL);
348 if (ret == ERROR_SUCCESS)
349 ret = RegCreateKeyExW(root, reg_render, 0, NULL, 0, KEY_READ|KEY_WRITE, NULL, &key_render, NULL);
350 RegCloseKey(root);
351 cur = key_capture;
352 curflow = eCapture;
353 if (ret != ERROR_SUCCESS)
354 {
355 RegCloseKey(key_capture);
356 key_render = key_capture = NULL;
357 WARN("Couldn't create key: %u\n", ret);
358 return E_FAIL;
359 }
360
361 do {
362 WCHAR guidvalue[39];
363 GUID guid;
364 DWORD len;
365 PROPVARIANT pv = { VT_EMPTY };
366
367 len = sizeof(guidvalue)/sizeof(guidvalue[0]);
368 ret = RegEnumKeyExW(cur, i++, guidvalue, &len, NULL, NULL, NULL, NULL);
369 if (ret == ERROR_NO_MORE_ITEMS)
370 {
371 if (cur == key_capture)
372 {
373 cur = key_render;
374 curflow = eRender;
375 i = 0;
376 continue;
377 }
378 break;
379 }
380 if (ret != ERROR_SUCCESS)
381 continue;
382 if (SUCCEEDED(CLSIDFromString(guidvalue, &guid))
383 && SUCCEEDED(MMDevice_GetPropValue(&guid, curflow, (const PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pv))
384 && pv.vt == VT_LPWSTR)
385 {
386 DWORD size_bytes = (strlenW(pv.u.pwszVal) + 1) * sizeof(WCHAR);
387 WCHAR *name = HeapAlloc(GetProcessHeap(), 0, size_bytes);
388 memcpy(name, pv.u.pwszVal, size_bytes);
389 MMDevice_Create(name, &guid, curflow,
390 DEVICE_STATE_NOTPRESENT, FALSE);
391 CoTaskMemFree(pv.u.pwszVal);
392 }
393 } while (1);
394
395 return S_OK;
396 }
397
398 static HRESULT set_format(MMDevice *dev)
399 {
400 HRESULT hr;
401 IAudioClient *client;
402 WAVEFORMATEX *fmt;
403 PROPVARIANT pv = { VT_EMPTY };
404
405 hr = drvs.pGetAudioEndpoint(&dev->devguid, &dev->IMMDevice_iface, &client);
406 if(FAILED(hr))
407 return hr;
408
409 hr = IAudioClient_GetMixFormat(client, &fmt);
410 if(FAILED(hr)){
411 IAudioClient_Release(client);
412 return hr;
413 }
414
415 IAudioClient_Release(client);
416
417 pv.vt = VT_BLOB;
418 pv.u.blob.cbSize = sizeof(WAVEFORMATEX) + fmt->cbSize;
419 pv.u.blob.pBlobData = (BYTE*)fmt;
420 MMDevice_SetPropValue(&dev->devguid, dev->flow,
421 &PKEY_AudioEngine_DeviceFormat, &pv);
422 MMDevice_SetPropValue(&dev->devguid, dev->flow,
423 &PKEY_AudioEngine_OEMFormat, &pv);
424
425 return S_OK;
426 }
427
428 static HRESULT load_driver_devices(EDataFlow flow)
429 {
430 WCHAR **ids;
431 GUID *guids;
432 UINT num, def, i;
433 HRESULT hr;
434
435 if(!drvs.pGetEndpointIDs)
436 return S_OK;
437
438 hr = drvs.pGetEndpointIDs(flow, &ids, &guids, &num, &def);
439 if(FAILED(hr))
440 return hr;
441
442 for(i = 0; i < num; ++i){
443 MMDevice *dev;
444 dev = MMDevice_Create(ids[i], &guids[i], flow, DEVICE_STATE_ACTIVE,
445 def == i);
446 set_format(dev);
447 }
448
449 HeapFree(GetProcessHeap(), 0, guids);
450 HeapFree(GetProcessHeap(), 0, ids);
451
452 return S_OK;
453 }
454
455 static void MMDevice_Destroy(MMDevice *This)
456 {
457 DWORD i;
458 TRACE("Freeing %s\n", debugstr_w(This->drv_id));
459 /* Since this function is called at destruction time, reordering of the list is unimportant */
460 for (i = 0; i < MMDevice_count; ++i)
461 {
462 if (MMDevice_head[i] == This)
463 {
464 MMDevice_head[i] = MMDevice_head[--MMDevice_count];
465 break;
466 }
467 }
468 This->crst.DebugInfo->Spare[0] = 0;
469 DeleteCriticalSection(&This->crst);
470 HeapFree(GetProcessHeap(), 0, This->drv_id);
471 HeapFree(GetProcessHeap(), 0, This);
472 }
473
474 static inline MMDevice *impl_from_IMMDevice(IMMDevice *iface)
475 {
476 return CONTAINING_RECORD(iface, MMDevice, IMMDevice_iface);
477 }
478
479 static HRESULT WINAPI MMDevice_QueryInterface(IMMDevice *iface, REFIID riid, void **ppv)
480 {
481 MMDevice *This = impl_from_IMMDevice(iface);
482 TRACE("(%p)->(%s,%p)\n", iface, debugstr_guid(riid), ppv);
483
484 if (!ppv)
485 return E_POINTER;
486 *ppv = NULL;
487 if (IsEqualIID(riid, &IID_IUnknown)
488 || IsEqualIID(riid, &IID_IMMDevice))
489 *ppv = This;
490 else if (IsEqualIID(riid, &IID_IMMEndpoint))
491 *ppv = &This->IMMEndpoint_iface;
492 if (*ppv)
493 {
494 IUnknown_AddRef((IUnknown*)*ppv);
495 return S_OK;
496 }
497 WARN("Unknown interface %s\n", debugstr_guid(riid));
498 return E_NOINTERFACE;
499 }
500
501 static ULONG WINAPI MMDevice_AddRef(IMMDevice *iface)
502 {
503 MMDevice *This = impl_from_IMMDevice(iface);
504 LONG ref;
505
506 ref = InterlockedIncrement(&This->ref);
507 TRACE("Refcount now %i\n", ref);
508 return ref;
509 }
510
511 static ULONG WINAPI MMDevice_Release(IMMDevice *iface)
512 {
513 MMDevice *This = impl_from_IMMDevice(iface);
514 LONG ref;
515
516 ref = InterlockedDecrement(&This->ref);
517 TRACE("Refcount now %i\n", ref);
518 return ref;
519 }
520
521 static HRESULT WINAPI MMDevice_Activate(IMMDevice *iface, REFIID riid, DWORD clsctx, PROPVARIANT *params, void **ppv)
522 {
523 HRESULT hr = E_NOINTERFACE;
524 MMDevice *This = impl_from_IMMDevice(iface);
525
526 TRACE("(%p)->(%p,%x,%p,%p)\n", iface, riid, clsctx, params, ppv);
527
528 if (!ppv)
529 return E_POINTER;
530
531 if (IsEqualIID(riid, &IID_IAudioClient)){
532 hr = drvs.pGetAudioEndpoint(&This->devguid, iface, (IAudioClient**)ppv);
533 }else if (IsEqualIID(riid, &IID_IAudioEndpointVolume))
534 hr = AudioEndpointVolume_Create(This, (IAudioEndpointVolume**)ppv);
535 else if (IsEqualIID(riid, &IID_IAudioSessionManager)
536 || IsEqualIID(riid, &IID_IAudioSessionManager2))
537 {
538 hr = drvs.pGetAudioSessionManager(iface, (IAudioSessionManager2**)ppv);
539 }
540 else if (IsEqualIID(riid, &IID_IBaseFilter))
541 {
542 if (This->flow == eRender)
543 hr = CoCreateInstance(&CLSID_DSoundRender, NULL, clsctx, riid, ppv);
544 else
545 ERR("Not supported for recording?\n");
546 if (SUCCEEDED(hr))
547 {
548 IPersistPropertyBag *ppb;
549 hr = IUnknown_QueryInterface((IUnknown*)*ppv, &IID_IPersistPropertyBag, (void*)&ppb);
550 if (SUCCEEDED(hr))
551 {
552 /* ::Load cannot assume the interface stays alive after the function returns,
553 * so just create the interface on the stack, saves a lot of complicated code */
554 IPropertyBagImpl bag = { { &PB_Vtbl } };
555 bag.devguid = This->devguid;
556 hr = IPersistPropertyBag_Load(ppb, &bag.IPropertyBag_iface, NULL);
557 IPersistPropertyBag_Release(ppb);
558 if (FAILED(hr))
559 IBaseFilter_Release((IBaseFilter*)*ppv);
560 }
561 else
562 {
563 FIXME("Wine doesn't support IPersistPropertyBag on DSoundRender yet, ignoring..\n");
564 hr = S_OK;
565 }
566 }
567 }
568 else if (IsEqualIID(riid, &IID_IDeviceTopology))
569 {
570 FIXME("IID_IDeviceTopology unsupported\n");
571 }
572 else if (IsEqualIID(riid, &IID_IDirectSound)
573 || IsEqualIID(riid, &IID_IDirectSound8))
574 {
575 if (This->flow == eRender)
576 hr = CoCreateInstance(&CLSID_DirectSound8, NULL, clsctx, riid, ppv);
577 if (SUCCEEDED(hr))
578 {
579 hr = IDirectSound_Initialize((IDirectSound*)*ppv, &This->devguid);
580 if (FAILED(hr))
581 IDirectSound_Release((IDirectSound*)*ppv);
582 }
583 }
584 else if (IsEqualIID(riid, &IID_IDirectSoundCapture)
585 || IsEqualIID(riid, &IID_IDirectSoundCapture8))
586 {
587 if (This->flow == eCapture)
588 hr = CoCreateInstance(&CLSID_DirectSoundCapture8, NULL, clsctx, riid, ppv);
589 if (SUCCEEDED(hr))
590 {
591 hr = IDirectSoundCapture_Initialize((IDirectSoundCapture*)*ppv, &This->devguid);
592 if (FAILED(hr))
593 IDirectSoundCapture_Release((IDirectSoundCapture*)*ppv);
594 }
595 }
596 else
597 ERR("Invalid/unknown iid %s\n", debugstr_guid(riid));
598
599 if (FAILED(hr))
600 *ppv = NULL;
601
602 TRACE("Returning %08x\n", hr);
603 return hr;
604 }
605
606 static HRESULT WINAPI MMDevice_OpenPropertyStore(IMMDevice *iface, DWORD access, IPropertyStore **ppv)
607 {
608 MMDevice *This = impl_from_IMMDevice(iface);
609 TRACE("(%p)->(%x,%p)\n", This, access, ppv);
610
611 if (!ppv)
612 return E_POINTER;
613 return MMDevPropStore_Create(This, access, ppv);
614 }
615
616 static HRESULT WINAPI MMDevice_GetId(IMMDevice *iface, WCHAR **itemid)
617 {
618 MMDevice *This = impl_from_IMMDevice(iface);
619 WCHAR *str;
620 GUID *id = &This->devguid;
621 static const WCHAR formatW[] = { '{','0','.','0','.','%','u','.','0','0','0','0','0','0','0','0','}','.',
622 '{','%','0','8','X','-','%','0','4','X','-',
623 '%','0','4','X','-','%','0','2','X','%','0','2','X','-',
624 '%','0','2','X','%','0','2','X','%','0','2','X','%','0','2','X',
625 '%','0','2','X','%','0','2','X','}',0 };
626
627 TRACE("(%p)->(%p)\n", This, itemid);
628 if (!itemid)
629 return E_POINTER;
630 *itemid = str = CoTaskMemAlloc(56 * sizeof(WCHAR));
631 if (!str)
632 return E_OUTOFMEMORY;
633 wsprintfW( str, formatW, This->flow, id->Data1, id->Data2, id->Data3,
634 id->Data4[0], id->Data4[1], id->Data4[2], id->Data4[3],
635 id->Data4[4], id->Data4[5], id->Data4[6], id->Data4[7] );
636 TRACE("returning %s\n", wine_dbgstr_w(str));
637 return S_OK;
638 }
639
640 static HRESULT WINAPI MMDevice_GetState(IMMDevice *iface, DWORD *state)
641 {
642 MMDevice *This = impl_from_IMMDevice(iface);
643 TRACE("(%p)->(%p)\n", iface, state);
644
645 if (!state)
646 return E_POINTER;
647 *state = This->state;
648 return S_OK;
649 }
650
651 static const IMMDeviceVtbl MMDeviceVtbl =
652 {
653 MMDevice_QueryInterface,
654 MMDevice_AddRef,
655 MMDevice_Release,
656 MMDevice_Activate,
657 MMDevice_OpenPropertyStore,
658 MMDevice_GetId,
659 MMDevice_GetState
660 };
661
662 static inline MMDevice *impl_from_IMMEndpoint(IMMEndpoint *iface)
663 {
664 return CONTAINING_RECORD(iface, MMDevice, IMMEndpoint_iface);
665 }
666
667 static HRESULT WINAPI MMEndpoint_QueryInterface(IMMEndpoint *iface, REFIID riid, void **ppv)
668 {
669 MMDevice *This = impl_from_IMMEndpoint(iface);
670 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
671 return IMMDevice_QueryInterface(&This->IMMDevice_iface, riid, ppv);
672 }
673
674 static ULONG WINAPI MMEndpoint_AddRef(IMMEndpoint *iface)
675 {
676 MMDevice *This = impl_from_IMMEndpoint(iface);
677 TRACE("(%p)\n", This);
678 return IMMDevice_AddRef(&This->IMMDevice_iface);
679 }
680
681 static ULONG WINAPI MMEndpoint_Release(IMMEndpoint *iface)
682 {
683 MMDevice *This = impl_from_IMMEndpoint(iface);
684 TRACE("(%p)\n", This);
685 return IMMDevice_Release(&This->IMMDevice_iface);
686 }
687
688 static HRESULT WINAPI MMEndpoint_GetDataFlow(IMMEndpoint *iface, EDataFlow *flow)
689 {
690 MMDevice *This = impl_from_IMMEndpoint(iface);
691 TRACE("(%p)->(%p)\n", This, flow);
692 if (!flow)
693 return E_POINTER;
694 *flow = This->flow;
695 return S_OK;
696 }
697
698 static const IMMEndpointVtbl MMEndpointVtbl =
699 {
700 MMEndpoint_QueryInterface,
701 MMEndpoint_AddRef,
702 MMEndpoint_Release,
703 MMEndpoint_GetDataFlow
704 };
705
706 static HRESULT MMDevCol_Create(IMMDeviceCollection **ppv, EDataFlow flow, DWORD state)
707 {
708 MMDevColImpl *This;
709
710 This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
711 *ppv = NULL;
712 if (!This)
713 return E_OUTOFMEMORY;
714 This->IMMDeviceCollection_iface.lpVtbl = &MMDevColVtbl;
715 This->ref = 1;
716 This->flow = flow;
717 This->state = state;
718 *ppv = &This->IMMDeviceCollection_iface;
719 return S_OK;
720 }
721
722 static void MMDevCol_Destroy(MMDevColImpl *This)
723 {
724 HeapFree(GetProcessHeap(), 0, This);
725 }
726
727 static HRESULT WINAPI MMDevCol_QueryInterface(IMMDeviceCollection *iface, REFIID riid, void **ppv)
728 {
729 MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
730 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
731
732 if (!ppv)
733 return E_POINTER;
734 if (IsEqualIID(riid, &IID_IUnknown)
735 || IsEqualIID(riid, &IID_IMMDeviceCollection))
736 *ppv = This;
737 else
738 *ppv = NULL;
739 if (!*ppv)
740 return E_NOINTERFACE;
741 IUnknown_AddRef((IUnknown*)*ppv);
742 return S_OK;
743 }
744
745 static ULONG WINAPI MMDevCol_AddRef(IMMDeviceCollection *iface)
746 {
747 MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
748 LONG ref = InterlockedIncrement(&This->ref);
749 TRACE("Refcount now %i\n", ref);
750 return ref;
751 }
752
753 static ULONG WINAPI MMDevCol_Release(IMMDeviceCollection *iface)
754 {
755 MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
756 LONG ref = InterlockedDecrement(&This->ref);
757 TRACE("Refcount now %i\n", ref);
758 if (!ref)
759 MMDevCol_Destroy(This);
760 return ref;
761 }
762
763 static HRESULT WINAPI MMDevCol_GetCount(IMMDeviceCollection *iface, UINT *numdevs)
764 {
765 MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
766 DWORD i;
767
768 TRACE("(%p)->(%p)\n", This, numdevs);
769 if (!numdevs)
770 return E_POINTER;
771
772 *numdevs = 0;
773 for (i = 0; i < MMDevice_count; ++i)
774 {
775 MMDevice *cur = MMDevice_head[i];
776 if ((cur->flow == This->flow || This->flow == eAll)
777 && (cur->state & This->state))
778 ++(*numdevs);
779 }
780 return S_OK;
781 }
782
783 static HRESULT WINAPI MMDevCol_Item(IMMDeviceCollection *iface, UINT n, IMMDevice **dev)
784 {
785 MMDevColImpl *This = impl_from_IMMDeviceCollection(iface);
786 DWORD i = 0, j = 0;
787
788 TRACE("(%p)->(%u, %p)\n", This, n, dev);
789 if (!dev)
790 return E_POINTER;
791
792 for (j = 0; j < MMDevice_count; ++j)
793 {
794 MMDevice *cur = MMDevice_head[j];
795 if ((cur->flow == This->flow || This->flow == eAll)
796 && (cur->state & This->state)
797 && i++ == n)
798 {
799 *dev = &cur->IMMDevice_iface;
800 IMMDevice_AddRef(*dev);
801 return S_OK;
802 }
803 }
804 WARN("Could not obtain item %u\n", n);
805 *dev = NULL;
806 return E_INVALIDARG;
807 }
808
809 static const IMMDeviceCollectionVtbl MMDevColVtbl =
810 {
811 MMDevCol_QueryInterface,
812 MMDevCol_AddRef,
813 MMDevCol_Release,
814 MMDevCol_GetCount,
815 MMDevCol_Item
816 };
817
818 HRESULT MMDevEnum_Create(REFIID riid, void **ppv)
819 {
820 MMDevEnumImpl *This = MMDevEnumerator;
821
822 if (!This)
823 {
824 This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
825 *ppv = NULL;
826 if (!This)
827 return E_OUTOFMEMORY;
828 This->ref = 1;
829 This->IMMDeviceEnumerator_iface.lpVtbl = &MMDevEnumVtbl;
830 MMDevEnumerator = This;
831
832 load_devices_from_reg();
833 load_driver_devices(eRender);
834 load_driver_devices(eCapture);
835 }
836 return IUnknown_QueryInterface((IUnknown*)This, riid, ppv);
837 }
838
839 void MMDevEnum_Free(void)
840 {
841 while (MMDevice_count)
842 MMDevice_Destroy(MMDevice_head[0]);
843 RegCloseKey(key_render);
844 RegCloseKey(key_capture);
845 key_render = key_capture = NULL;
846 HeapFree(GetProcessHeap(), 0, MMDevEnumerator);
847 MMDevEnumerator = NULL;
848 }
849
850 static HRESULT WINAPI MMDevEnum_QueryInterface(IMMDeviceEnumerator *iface, REFIID riid, void **ppv)
851 {
852 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
853 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
854
855 if (!ppv)
856 return E_POINTER;
857 if (IsEqualIID(riid, &IID_IUnknown)
858 || IsEqualIID(riid, &IID_IMMDeviceEnumerator))
859 *ppv = This;
860 else
861 *ppv = NULL;
862 if (!*ppv)
863 return E_NOINTERFACE;
864 IUnknown_AddRef((IUnknown*)*ppv);
865 return S_OK;
866 }
867
868 static ULONG WINAPI MMDevEnum_AddRef(IMMDeviceEnumerator *iface)
869 {
870 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
871 LONG ref = InterlockedIncrement(&This->ref);
872 TRACE("Refcount now %i\n", ref);
873 return ref;
874 }
875
876 static ULONG WINAPI MMDevEnum_Release(IMMDeviceEnumerator *iface)
877 {
878 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
879 LONG ref = InterlockedDecrement(&This->ref);
880 if (!ref)
881 MMDevEnum_Free();
882 TRACE("Refcount now %i\n", ref);
883 return ref;
884 }
885
886 static HRESULT WINAPI MMDevEnum_EnumAudioEndpoints(IMMDeviceEnumerator *iface, EDataFlow flow, DWORD mask, IMMDeviceCollection **devices)
887 {
888 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
889 TRACE("(%p)->(%u,%u,%p)\n", This, flow, mask, devices);
890 if (!devices)
891 return E_POINTER;
892 *devices = NULL;
893 if (flow >= EDataFlow_enum_count)
894 return E_INVALIDARG;
895 if (mask & ~DEVICE_STATEMASK_ALL)
896 return E_INVALIDARG;
897 return MMDevCol_Create(devices, flow, mask);
898 }
899
900 static HRESULT WINAPI MMDevEnum_GetDefaultAudioEndpoint(IMMDeviceEnumerator *iface, EDataFlow flow, ERole role, IMMDevice **device)
901 {
902 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
903 WCHAR reg_key[256];
904 HKEY key;
905 HRESULT hr;
906
907 TRACE("(%p)->(%u,%u,%p)\n", This, flow, role, device);
908
909 if (!device)
910 return E_POINTER;
911
912 if((flow != eRender && flow != eCapture) ||
913 (role != eConsole && role != eMultimedia && role != eCommunications)){
914 WARN("Unknown flow (%u) or role (%u)\n", flow, role);
915 return E_INVALIDARG;
916 }
917
918 *device = NULL;
919
920 if(!drvs.module_name[0])
921 return E_NOTFOUND;
922
923 lstrcpyW(reg_key, drv_keyW);
924 lstrcatW(reg_key, slashW);
925 lstrcatW(reg_key, drvs.module_name);
926
927 if(RegOpenKeyW(HKEY_CURRENT_USER, reg_key, &key) == ERROR_SUCCESS){
928 const WCHAR *reg_x_name, *reg_vx_name;
929 WCHAR def_id[256];
930 DWORD size = sizeof(def_id), state;
931
932 if(flow == eRender){
933 reg_x_name = reg_out_nameW;
934 reg_vx_name = reg_vout_nameW;
935 }else{
936 reg_x_name = reg_in_nameW;
937 reg_vx_name = reg_vin_nameW;
938 }
939
940 if(role == eCommunications &&
941 RegQueryValueExW(key, reg_vx_name, 0, NULL,
942 (BYTE*)def_id, &size) == ERROR_SUCCESS){
943 hr = IMMDeviceEnumerator_GetDevice(iface, def_id, device);
944 if(SUCCEEDED(hr)){
945 if(SUCCEEDED(IMMDevice_GetState(*device, &state)) &&
946 state == DEVICE_STATE_ACTIVE){
947 RegCloseKey(key);
948 return S_OK;
949 }
950 }
951
952 TRACE("Unable to find voice device %s\n", wine_dbgstr_w(def_id));
953 }
954
955 if(RegQueryValueExW(key, reg_x_name, 0, NULL,
956 (BYTE*)def_id, &size) == ERROR_SUCCESS){
957 hr = IMMDeviceEnumerator_GetDevice(iface, def_id, device);
958 if(SUCCEEDED(hr)){
959 if(SUCCEEDED(IMMDevice_GetState(*device, &state)) &&
960 state == DEVICE_STATE_ACTIVE){
961 RegCloseKey(key);
962 return S_OK;
963 }
964 }
965
966 TRACE("Unable to find device %s\n", wine_dbgstr_w(def_id));
967 }
968
969 RegCloseKey(key);
970 }
971
972 if (flow == eRender)
973 *device = &MMDevice_def_play->IMMDevice_iface;
974 else
975 *device = &MMDevice_def_rec->IMMDevice_iface;
976
977 if (!*device)
978 return E_NOTFOUND;
979 IMMDevice_AddRef(*device);
980 return S_OK;
981 }
982
983 static HRESULT WINAPI MMDevEnum_GetDevice(IMMDeviceEnumerator *iface, const WCHAR *name, IMMDevice **device)
984 {
985 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
986 DWORD i=0;
987 IMMDevice *dev = NULL;
988
989 static const WCHAR wine_info_deviceW[] = {'W','i','n','e',' ',
990 'i','n','f','o',' ','d','e','v','i','c','e',0};
991
992 TRACE("(%p)->(%s,%p)\n", This, debugstr_w(name), device);
993
994 if(!name || !device)
995 return E_POINTER;
996
997 if(!lstrcmpW(name, wine_info_deviceW)){
998 *device = &info_device;
999 return S_OK;
1000 }
1001
1002 for (i = 0; i < MMDevice_count; ++i)
1003 {
1004 WCHAR *str;
1005 dev = &MMDevice_head[i]->IMMDevice_iface;
1006 IMMDevice_GetId(dev, &str);
1007
1008 if (str && !lstrcmpW(str, name))
1009 {
1010 CoTaskMemFree(str);
1011 IMMDevice_AddRef(dev);
1012 *device = dev;
1013 return S_OK;
1014 }
1015 CoTaskMemFree(str);
1016 }
1017 TRACE("Could not find device %s\n", debugstr_w(name));
1018 return E_INVALIDARG;
1019 }
1020
1021 struct NotificationClientWrapper {
1022 IMMNotificationClient *client;
1023 struct list entry;
1024 };
1025
1026 static struct list g_notif_clients = LIST_INIT(g_notif_clients);
1027 static HANDLE g_notif_thread;
1028
1029 static CRITICAL_SECTION g_notif_lock;
1030 static CRITICAL_SECTION_DEBUG g_notif_lock_debug =
1031 {
1032 0, 0, &g_notif_lock,
1033 { &g_notif_lock_debug.ProcessLocksList, &g_notif_lock_debug.ProcessLocksList },
1034 0, 0, { (DWORD_PTR)(__FILE__ ": g_notif_lock") }
1035 };
1036 static CRITICAL_SECTION g_notif_lock = { &g_notif_lock_debug, -1, 0, 0, 0, 0 };
1037
1038 static void notify_clients(EDataFlow flow, ERole role, const WCHAR *id)
1039 {
1040 struct NotificationClientWrapper *wrapper;
1041 LIST_FOR_EACH_ENTRY(wrapper, &g_notif_clients,
1042 struct NotificationClientWrapper, entry)
1043 IMMNotificationClient_OnDefaultDeviceChanged(wrapper->client, flow,
1044 role, id);
1045
1046 /* Windows 7 treats changes to eConsole as changes to eMultimedia */
1047 if(role == eConsole)
1048 notify_clients(flow, eMultimedia, id);
1049 }
1050
1051 static BOOL notify_if_changed(EDataFlow flow, ERole role, HKEY key,
1052 const WCHAR *val_name, WCHAR *old_val, IMMDevice *def_dev)
1053 {
1054 WCHAR new_val[64], *id;
1055 DWORD size;
1056 HRESULT hr;
1057
1058 size = sizeof(new_val);
1059 if(RegQueryValueExW(key, val_name, 0, NULL,
1060 (BYTE*)new_val, &size) != ERROR_SUCCESS){
1061 if(old_val[0] != 0){
1062 /* set by user -> system default */
1063 if(def_dev){
1064 hr = IMMDevice_GetId(def_dev, &id);
1065 if(FAILED(hr)){
1066 ERR("GetId failed: %08x\n", hr);
1067 return FALSE;
1068 }
1069 }else
1070 id = NULL;
1071
1072 notify_clients(flow, role, id);
1073 old_val[0] = 0;
1074 CoTaskMemFree(id);
1075
1076 return TRUE;
1077 }
1078
1079 /* system default -> system default, noop */
1080 return FALSE;
1081 }
1082
1083 if(!lstrcmpW(old_val, new_val)){
1084 /* set by user -> same value */
1085 return FALSE;
1086 }
1087
1088 if(new_val[0] != 0){
1089 /* set by user -> different value */
1090 notify_clients(flow, role, new_val);
1091 memcpy(old_val, new_val, sizeof(new_val));
1092 return TRUE;
1093 }
1094
1095 /* set by user -> system default */
1096 if(def_dev){
1097 hr = IMMDevice_GetId(def_dev, &id);
1098 if(FAILED(hr)){
1099 ERR("GetId failed: %08x\n", hr);
1100 return FALSE;
1101 }
1102 }else
1103 id = NULL;
1104
1105 notify_clients(flow, role, id);
1106 old_val[0] = 0;
1107 CoTaskMemFree(id);
1108
1109 return TRUE;
1110 }
1111
1112 static DWORD WINAPI notif_thread_proc(void *user)
1113 {
1114 HKEY key;
1115 WCHAR reg_key[256];
1116 WCHAR out_name[64], vout_name[64], in_name[64], vin_name[64];
1117 DWORD size;
1118
1119 lstrcpyW(reg_key, drv_keyW);
1120 lstrcatW(reg_key, slashW);
1121 lstrcatW(reg_key, drvs.module_name);
1122
1123 if(RegCreateKeyExW(HKEY_CURRENT_USER, reg_key, 0, NULL, 0,
1124 MAXIMUM_ALLOWED, NULL, &key, NULL) != ERROR_SUCCESS){
1125 ERR("RegCreateKeyEx failed: %u\n", GetLastError());
1126 return 1;
1127 }
1128
1129 size = sizeof(out_name);
1130 if(RegQueryValueExW(key, reg_out_nameW, 0, NULL,
1131 (BYTE*)out_name, &size) != ERROR_SUCCESS)
1132 out_name[0] = 0;
1133
1134 size = sizeof(vout_name);
1135 if(RegQueryValueExW(key, reg_vout_nameW, 0, NULL,
1136 (BYTE*)vout_name, &size) != ERROR_SUCCESS)
1137 vout_name[0] = 0;
1138
1139 size = sizeof(in_name);
1140 if(RegQueryValueExW(key, reg_in_nameW, 0, NULL,
1141 (BYTE*)in_name, &size) != ERROR_SUCCESS)
1142 in_name[0] = 0;
1143
1144 size = sizeof(vin_name);
1145 if(RegQueryValueExW(key, reg_vin_nameW, 0, NULL,
1146 (BYTE*)vin_name, &size) != ERROR_SUCCESS)
1147 vin_name[0] = 0;
1148
1149 while(1){
1150 if(RegNotifyChangeKeyValue(key, FALSE, REG_NOTIFY_CHANGE_LAST_SET,
1151 NULL, FALSE) != ERROR_SUCCESS){
1152 ERR("RegNotifyChangeKeyValue failed: %u\n", GetLastError());
1153 RegCloseKey(key);
1154 g_notif_thread = NULL;
1155 return 1;
1156 }
1157
1158 EnterCriticalSection(&g_notif_lock);
1159
1160 notify_if_changed(eRender, eConsole, key, reg_out_nameW,
1161 out_name, &MMDevice_def_play->IMMDevice_iface);
1162 notify_if_changed(eRender, eCommunications, key, reg_vout_nameW,
1163 vout_name, &MMDevice_def_play->IMMDevice_iface);
1164 notify_if_changed(eCapture, eConsole, key, reg_in_nameW,
1165 in_name, &MMDevice_def_rec->IMMDevice_iface);
1166 notify_if_changed(eCapture, eCommunications, key, reg_vin_nameW,
1167 vin_name, &MMDevice_def_rec->IMMDevice_iface);
1168
1169 LeaveCriticalSection(&g_notif_lock);
1170 }
1171
1172 RegCloseKey(key);
1173
1174 g_notif_thread = NULL;
1175
1176 return 0;
1177 }
1178
1179 static HRESULT WINAPI MMDevEnum_RegisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
1180 {
1181 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1182 struct NotificationClientWrapper *wrapper;
1183
1184 TRACE("(%p)->(%p)\n", This, client);
1185
1186 if(!client)
1187 return E_POINTER;
1188
1189 wrapper = HeapAlloc(GetProcessHeap(), 0, sizeof(*wrapper));
1190 if(!wrapper)
1191 return E_OUTOFMEMORY;
1192
1193 wrapper->client = client;
1194
1195 EnterCriticalSection(&g_notif_lock);
1196
1197 list_add_tail(&g_notif_clients, &wrapper->entry);
1198
1199 if(!g_notif_thread){
1200 g_notif_thread = CreateThread(NULL, 0, notif_thread_proc, NULL, 0, NULL);
1201 if(!g_notif_thread)
1202 ERR("CreateThread failed: %u\n", GetLastError());
1203 }
1204
1205 LeaveCriticalSection(&g_notif_lock);
1206
1207 return S_OK;
1208 }
1209
1210 static HRESULT WINAPI MMDevEnum_UnregisterEndpointNotificationCallback(IMMDeviceEnumerator *iface, IMMNotificationClient *client)
1211 {
1212 MMDevEnumImpl *This = impl_from_IMMDeviceEnumerator(iface);
1213 struct NotificationClientWrapper *wrapper, *wrapper2;
1214
1215 TRACE("(%p)->(%p)\n", This, client);
1216
1217 if(!client)
1218 return E_POINTER;
1219
1220 EnterCriticalSection(&g_notif_lock);
1221
1222 LIST_FOR_EACH_ENTRY_SAFE(wrapper, wrapper2, &g_notif_clients,
1223 struct NotificationClientWrapper, entry){
1224 if(wrapper->client == client){
1225 list_remove(&wrapper->entry);
1226 HeapFree(GetProcessHeap(), 0, wrapper);
1227 LeaveCriticalSection(&g_notif_lock);
1228 return S_OK;
1229 }
1230 }
1231
1232 LeaveCriticalSection(&g_notif_lock);
1233
1234 return E_NOTFOUND;
1235 }
1236
1237 static const IMMDeviceEnumeratorVtbl MMDevEnumVtbl =
1238 {
1239 MMDevEnum_QueryInterface,
1240 MMDevEnum_AddRef,
1241 MMDevEnum_Release,
1242 MMDevEnum_EnumAudioEndpoints,
1243 MMDevEnum_GetDefaultAudioEndpoint,
1244 MMDevEnum_GetDevice,
1245 MMDevEnum_RegisterEndpointNotificationCallback,
1246 MMDevEnum_UnregisterEndpointNotificationCallback
1247 };
1248
1249 static HRESULT MMDevPropStore_Create(MMDevice *parent, DWORD access, IPropertyStore **ppv)
1250 {
1251 MMDevPropStore *This;
1252 if (access != STGM_READ
1253 && access != STGM_WRITE
1254 && access != STGM_READWRITE)
1255 {
1256 WARN("Invalid access %08x\n", access);
1257 return E_INVALIDARG;
1258 }
1259 This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
1260 *ppv = &This->IPropertyStore_iface;
1261 if (!This)
1262 return E_OUTOFMEMORY;
1263 This->IPropertyStore_iface.lpVtbl = &MMDevPropVtbl;
1264 This->ref = 1;
1265 This->parent = parent;
1266 This->access = access;
1267 return S_OK;
1268 }
1269
1270 static void MMDevPropStore_Destroy(MMDevPropStore *This)
1271 {
1272 HeapFree(GetProcessHeap(), 0, This);
1273 }
1274
1275 static HRESULT WINAPI MMDevPropStore_QueryInterface(IPropertyStore *iface, REFIID riid, void **ppv)
1276 {
1277 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1278 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), ppv);
1279
1280 if (!ppv)
1281 return E_POINTER;
1282 if (IsEqualIID(riid, &IID_IUnknown)
1283 || IsEqualIID(riid, &IID_IPropertyStore))
1284 *ppv = This;
1285 else
1286 *ppv = NULL;
1287 if (!*ppv)
1288 return E_NOINTERFACE;
1289 IUnknown_AddRef((IUnknown*)*ppv);
1290 return S_OK;
1291 }
1292
1293 static ULONG WINAPI MMDevPropStore_AddRef(IPropertyStore *iface)
1294 {
1295 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1296 LONG ref = InterlockedIncrement(&This->ref);
1297 TRACE("Refcount now %i\n", ref);
1298 return ref;
1299 }
1300
1301 static ULONG WINAPI MMDevPropStore_Release(IPropertyStore *iface)
1302 {
1303 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1304 LONG ref = InterlockedDecrement(&This->ref);
1305 TRACE("Refcount now %i\n", ref);
1306 if (!ref)
1307 MMDevPropStore_Destroy(This);
1308 return ref;
1309 }
1310
1311 static HRESULT WINAPI MMDevPropStore_GetCount(IPropertyStore *iface, DWORD *nprops)
1312 {
1313 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1314 WCHAR buffer[50];
1315 DWORD i = 0;
1316 HKEY propkey;
1317 HRESULT hr;
1318
1319 TRACE("(%p)->(%p)\n", iface, nprops);
1320 if (!nprops)
1321 return E_POINTER;
1322 hr = MMDevPropStore_OpenPropKey(&This->parent->devguid, This->parent->flow, &propkey);
1323 if (FAILED(hr))
1324 return hr;
1325 *nprops = 0;
1326 do {
1327 DWORD len = sizeof(buffer)/sizeof(*buffer);
1328 if (RegEnumValueW(propkey, i, buffer, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
1329 break;
1330 i++;
1331 } while (1);
1332 RegCloseKey(propkey);
1333 TRACE("Returning %i\n", i);
1334 *nprops = i;
1335 return S_OK;
1336 }
1337
1338 static HRESULT WINAPI MMDevPropStore_GetAt(IPropertyStore *iface, DWORD prop, PROPERTYKEY *key)
1339 {
1340 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1341 WCHAR buffer[50];
1342 DWORD len = sizeof(buffer)/sizeof(*buffer);
1343 HRESULT hr;
1344 HKEY propkey;
1345
1346 TRACE("(%p)->(%u,%p)\n", iface, prop, key);
1347 if (!key)
1348 return E_POINTER;
1349
1350 hr = MMDevPropStore_OpenPropKey(&This->parent->devguid, This->parent->flow, &propkey);
1351 if (FAILED(hr))
1352 return hr;
1353
1354 if (RegEnumValueW(propkey, prop, buffer, &len, NULL, NULL, NULL, NULL) != ERROR_SUCCESS
1355 || len <= 39)
1356 {
1357 WARN("GetAt %u failed\n", prop);
1358 return E_INVALIDARG;
1359 }
1360 RegCloseKey(propkey);
1361 buffer[38] = 0;
1362 CLSIDFromString(buffer, &key->fmtid);
1363 key->pid = atoiW(&buffer[39]);
1364 return S_OK;
1365 }
1366
1367 static HRESULT WINAPI MMDevPropStore_GetValue(IPropertyStore *iface, REFPROPERTYKEY key, PROPVARIANT *pv)
1368 {
1369 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1370 TRACE("(%p)->(\"%s,%u\", %p)\n", This, key ? debugstr_guid(&key->fmtid) : NULL, key ? key->pid : 0, pv);
1371
1372 if (!key || !pv)
1373 return E_POINTER;
1374 if (This->access != STGM_READ
1375 && This->access != STGM_READWRITE)
1376 return STG_E_ACCESSDENIED;
1377
1378 /* Special case */
1379 if (IsEqualPropertyKey(*key, PKEY_AudioEndpoint_GUID))
1380 {
1381 pv->vt = VT_LPWSTR;
1382 pv->u.pwszVal = CoTaskMemAlloc(39 * sizeof(WCHAR));
1383 if (!pv->u.pwszVal)
1384 return E_OUTOFMEMORY;
1385 StringFromGUID2(&This->parent->devguid, pv->u.pwszVal, 39);
1386 return S_OK;
1387 }
1388
1389 return MMDevice_GetPropValue(&This->parent->devguid, This->parent->flow, key, pv);
1390 }
1391
1392 static HRESULT WINAPI MMDevPropStore_SetValue(IPropertyStore *iface, REFPROPERTYKEY key, REFPROPVARIANT pv)
1393 {
1394 MMDevPropStore *This = impl_from_IPropertyStore(iface);
1395 TRACE("(%p)->(\"%s,%u\", %p)\n", This, key ? debugstr_guid(&key->fmtid) : NULL, key ? key->pid : 0, pv);
1396
1397 if (!key || !pv)
1398 return E_POINTER;
1399
1400 if (This->access != STGM_WRITE
1401 && This->access != STGM_READWRITE)
1402 return STG_E_ACCESSDENIED;
1403 return MMDevice_SetPropValue(&This->parent->devguid, This->parent->flow, key, pv);
1404 }
1405
1406 static HRESULT WINAPI MMDevPropStore_Commit(IPropertyStore *iface)
1407 {
1408 FIXME("stub\n");
1409 return E_NOTIMPL;
1410 }
1411
1412 static const IPropertyStoreVtbl MMDevPropVtbl =
1413 {
1414 MMDevPropStore_QueryInterface,
1415 MMDevPropStore_AddRef,
1416 MMDevPropStore_Release,
1417 MMDevPropStore_GetCount,
1418 MMDevPropStore_GetAt,
1419 MMDevPropStore_GetValue,
1420 MMDevPropStore_SetValue,
1421 MMDevPropStore_Commit
1422 };
1423
1424
1425 /* Property bag for IBaseFilter activation */
1426 static HRESULT WINAPI PB_QueryInterface(IPropertyBag *iface, REFIID riid, void **ppv)
1427 {
1428 ERR("Should not be called\n");
1429 *ppv = NULL;
1430 return E_NOINTERFACE;
1431 }
1432
1433 static ULONG WINAPI PB_AddRef(IPropertyBag *iface)
1434 {
1435 ERR("Should not be called\n");
1436 return 2;
1437 }
1438
1439 static ULONG WINAPI PB_Release(IPropertyBag *iface)
1440 {
1441 ERR("Should not be called\n");
1442 return 1;
1443 }
1444
1445 static HRESULT WINAPI PB_Read(IPropertyBag *iface, LPCOLESTR name, VARIANT *var, IErrorLog *log)
1446 {
1447 static const WCHAR dsguid[] = { 'D','S','G','u','i','d', 0 };
1448 IPropertyBagImpl *This = impl_from_IPropertyBag(iface);
1449 TRACE("Trying to read %s, type %u\n", debugstr_w(name), var->n1.n2.vt);
1450 if (!lstrcmpW(name, dsguid))
1451 {
1452 WCHAR guidstr[39];
1453 StringFromGUID2(&This->devguid, guidstr,sizeof(guidstr)/sizeof(*guidstr));
1454 var->n1.n2.vt = VT_BSTR;
1455 var->n1.n2.n3.bstrVal = SysAllocString(guidstr);
1456 return S_OK;
1457 }
1458 ERR("Unknown property '%s' queried\n", debugstr_w(name));
1459 return E_FAIL;
1460 }
1461
1462 static HRESULT WINAPI PB_Write(IPropertyBag *iface, LPCOLESTR name, VARIANT *var)
1463 {
1464 ERR("Should not be called\n");
1465 return E_FAIL;
1466 }
1467
1468 static const IPropertyBagVtbl PB_Vtbl =
1469 {
1470 PB_QueryInterface,
1471 PB_AddRef,
1472 PB_Release,
1473 PB_Read,
1474 PB_Write
1475 };
1476
1477 static ULONG WINAPI info_device_ps_AddRef(IPropertyStore *iface)
1478 {
1479 return 2;
1480 }
1481
1482 static ULONG WINAPI info_device_ps_Release(IPropertyStore *iface)
1483 {
1484 return 1;
1485 }
1486
1487 static HRESULT WINAPI info_device_ps_GetValue(IPropertyStore *iface,
1488 REFPROPERTYKEY key, PROPVARIANT *pv)
1489 {
1490 TRACE("(static)->(\"%s,%u\", %p)\n", debugstr_guid(&key->fmtid), key ? key->pid : 0, pv);
1491
1492 if (!key || !pv)
1493 return E_POINTER;
1494
1495 if (IsEqualPropertyKey(*key, DEVPKEY_Device_Driver))
1496 {
1497 INT size = (lstrlenW(drvs.module_name) + 1) * sizeof(WCHAR);
1498 pv->vt = VT_LPWSTR;
1499 pv->u.pwszVal = CoTaskMemAlloc(size);
1500 if (!pv->u.pwszVal)
1501 return E_OUTOFMEMORY;
1502 memcpy(pv->u.pwszVal, drvs.module_name, size);
1503 return S_OK;
1504 }
1505
1506 return E_INVALIDARG;
1507 }
1508
1509 static const IPropertyStoreVtbl info_device_ps_Vtbl =
1510 {
1511 NULL,
1512 info_device_ps_AddRef,
1513 info_device_ps_Release,
1514 NULL,
1515 NULL,
1516 info_device_ps_GetValue,
1517 NULL,
1518 NULL
1519 };
1520
1521 static IPropertyStore info_device_ps = {
1522 &info_device_ps_Vtbl
1523 };
1524
1525 static ULONG WINAPI info_device_AddRef(IMMDevice *iface)
1526 {
1527 return 2;
1528 }
1529
1530 static ULONG WINAPI info_device_Release(IMMDevice *iface)
1531 {
1532 return 1;
1533 }
1534
1535 static HRESULT WINAPI info_device_OpenPropertyStore(IMMDevice *iface,
1536 DWORD access, IPropertyStore **ppv)
1537 {
1538 TRACE("(static)->(%x, %p)\n", access, ppv);
1539 *ppv = &info_device_ps;
1540 return S_OK;
1541 }
1542
1543 static const IMMDeviceVtbl info_device_Vtbl =
1544 {
1545 NULL,
1546 info_device_AddRef,
1547 info_device_Release,
1548 NULL,
1549 info_device_OpenPropertyStore,
1550 NULL,
1551 NULL
1552 };
1553
1554 static IMMDevice info_device = {
1555 &info_device_Vtbl
1556 };