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