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