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