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