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