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