1 /* 2 * ICreateDevEnum implementation for DEVENUM.dll 3 * 4 * Copyright (C) 2002 Robert Shearman 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 * 20 * NOTES ON THIS FILE: 21 * - Implements ICreateDevEnum interface which creates an IEnumMoniker 22 * implementation 23 * - Also creates the special registry keys created at run-time 24 */ 25 26 #define NONAMELESSSTRUCT 27 #define NONAMELESSUNION 28 29 #include "devenum_private.h" 30 #include "vfw.h" 31 #include "aviriff.h" 32 #include "dsound.h" 33 34 #include "wine/debug.h" 35 #include "wine/unicode.h" 36 #include "wine/heap.h" 37 #include "mmddk.h" 38 39 #include "initguid.h" 40 #include "fil_data.h" 41 42 WINE_DEFAULT_DEBUG_CHANNEL(devenum); 43 44 extern HINSTANCE DEVENUM_hInstance; 45 46 static const WCHAR wszFilterKeyName[] = {'F','i','l','t','e','r',0}; 47 static const WCHAR wszMeritName[] = {'M','e','r','i','t',0}; 48 static const WCHAR wszPins[] = {'P','i','n','s',0}; 49 static const WCHAR wszAllowedMany[] = {'A','l','l','o','w','e','d','M','a','n','y',0}; 50 static const WCHAR wszAllowedZero[] = {'A','l','l','o','w','e','d','Z','e','r','o',0}; 51 static const WCHAR wszDirection[] = {'D','i','r','e','c','t','i','o','n',0}; 52 static const WCHAR wszIsRendered[] = {'I','s','R','e','n','d','e','r','e','d',0}; 53 static const WCHAR wszTypes[] = {'T','y','p','e','s',0}; 54 static const WCHAR wszFriendlyName[] = {'F','r','i','e','n','d','l','y','N','a','m','e',0}; 55 static const WCHAR wszFilterData[] = {'F','i','l','t','e','r','D','a','t','a',0}; 56 57 static ULONG WINAPI DEVENUM_ICreateDevEnum_AddRef(ICreateDevEnum * iface); 58 static HRESULT register_codecs(void); 59 static HRESULT DEVENUM_CreateAMCategoryKey(const CLSID * clsidCategory); 60 61 /********************************************************************** 62 * DEVENUM_ICreateDevEnum_QueryInterface (also IUnknown) 63 */ 64 static HRESULT WINAPI DEVENUM_ICreateDevEnum_QueryInterface(ICreateDevEnum *iface, REFIID riid, 65 void **ppv) 66 { 67 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv); 68 69 if (!ppv) 70 return E_POINTER; 71 72 if (IsEqualGUID(riid, &IID_IUnknown) || 73 IsEqualGUID(riid, &IID_ICreateDevEnum)) 74 { 75 *ppv = iface; 76 DEVENUM_ICreateDevEnum_AddRef(iface); 77 return S_OK; 78 } 79 80 FIXME("- no interface IID: %s\n", debugstr_guid(riid)); 81 *ppv = NULL; 82 return E_NOINTERFACE; 83 } 84 85 /********************************************************************** 86 * DEVENUM_ICreateDevEnum_AddRef (also IUnknown) 87 */ 88 static ULONG WINAPI DEVENUM_ICreateDevEnum_AddRef(ICreateDevEnum * iface) 89 { 90 TRACE("\n"); 91 92 DEVENUM_LockModule(); 93 94 return 2; /* non-heap based object */ 95 } 96 97 /********************************************************************** 98 * DEVENUM_ICreateDevEnum_Release (also IUnknown) 99 */ 100 static ULONG WINAPI DEVENUM_ICreateDevEnum_Release(ICreateDevEnum * iface) 101 { 102 TRACE("\n"); 103 104 DEVENUM_UnlockModule(); 105 106 return 1; /* non-heap based object */ 107 } 108 109 static HRESULT register_codec(const CLSID *class, const WCHAR *name, IMoniker **ret) 110 { 111 static const WCHAR deviceW[] = {'@','d','e','v','i','c','e',':','c','m',':',0}; 112 IParseDisplayName *parser; 113 WCHAR *buffer; 114 ULONG eaten; 115 HRESULT hr; 116 117 hr = CoCreateInstance(&CLSID_CDeviceMoniker, NULL, CLSCTX_INPROC, &IID_IParseDisplayName, (void **)&parser); 118 if (FAILED(hr)) 119 return hr; 120 121 buffer = heap_alloc((strlenW(deviceW) + CHARS_IN_GUID + strlenW(name) + 1) * sizeof(WCHAR)); 122 if (!buffer) 123 { 124 IParseDisplayName_Release(parser); 125 return E_OUTOFMEMORY; 126 } 127 128 strcpyW(buffer, deviceW); 129 StringFromGUID2(class, buffer + strlenW(buffer), CHARS_IN_GUID); 130 strcatW(buffer, backslashW); 131 strcatW(buffer, name); 132 133 hr = IParseDisplayName_ParseDisplayName(parser, NULL, buffer, &eaten, ret); 134 IParseDisplayName_Release(parser); 135 heap_free(buffer); 136 return hr; 137 } 138 139 static void DEVENUM_ReadPinTypes(HKEY hkeyPinKey, REGFILTERPINS2 *rgPin) 140 { 141 HKEY hkeyTypes = NULL; 142 DWORD dwMajorTypes, i; 143 REGPINTYPES *lpMediaType = NULL; 144 DWORD dwMediaTypeSize = 0; 145 146 if (RegOpenKeyExW(hkeyPinKey, wszTypes, 0, KEY_READ, &hkeyTypes) != ERROR_SUCCESS) 147 return ; 148 149 if (RegQueryInfoKeyW(hkeyTypes, NULL, NULL, NULL, &dwMajorTypes, NULL, NULL, NULL, NULL, NULL, NULL, NULL) 150 != ERROR_SUCCESS) 151 { 152 RegCloseKey(hkeyTypes); 153 return ; 154 } 155 156 for (i = 0; i < dwMajorTypes; i++) 157 { 158 HKEY hkeyMajorType = NULL; 159 WCHAR wszMajorTypeName[64]; 160 DWORD cName = sizeof(wszMajorTypeName) / sizeof(WCHAR); 161 DWORD dwMinorTypes, i1; 162 163 if (RegEnumKeyExW(hkeyTypes, i, wszMajorTypeName, &cName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) continue; 164 165 if (RegOpenKeyExW(hkeyTypes, wszMajorTypeName, 0, KEY_READ, &hkeyMajorType) != ERROR_SUCCESS) continue; 166 167 if (RegQueryInfoKeyW(hkeyMajorType, NULL, NULL, NULL, &dwMinorTypes, NULL, NULL, NULL, NULL, NULL, NULL, NULL) 168 != ERROR_SUCCESS) 169 { 170 RegCloseKey(hkeyMajorType); 171 continue; 172 } 173 174 for (i1 = 0; i1 < dwMinorTypes; i1++) 175 { 176 WCHAR wszMinorTypeName[64]; 177 CLSID *clsMajorType = NULL, *clsMinorType = NULL; 178 HRESULT hr; 179 180 cName = sizeof(wszMinorTypeName) / sizeof(WCHAR); 181 if (RegEnumKeyExW(hkeyMajorType, i1, wszMinorTypeName, &cName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) continue; 182 183 clsMinorType = CoTaskMemAlloc(sizeof(CLSID)); 184 if (!clsMinorType) continue; 185 186 clsMajorType = CoTaskMemAlloc(sizeof(CLSID)); 187 if (!clsMajorType) goto error_cleanup_types; 188 189 hr = CLSIDFromString(wszMinorTypeName, clsMinorType); 190 if (FAILED(hr)) goto error_cleanup_types; 191 192 hr = CLSIDFromString(wszMajorTypeName, clsMajorType); 193 if (FAILED(hr)) goto error_cleanup_types; 194 195 if (rgPin->nMediaTypes == dwMediaTypeSize) 196 { 197 DWORD dwNewSize = dwMediaTypeSize + (dwMediaTypeSize < 2 ? 1 : dwMediaTypeSize / 2); 198 REGPINTYPES *lpNewMediaType; 199 200 lpNewMediaType = CoTaskMemRealloc(lpMediaType, sizeof(REGPINTYPES) * dwNewSize); 201 if (!lpNewMediaType) goto error_cleanup_types; 202 203 lpMediaType = lpNewMediaType; 204 dwMediaTypeSize = dwNewSize; 205 } 206 207 lpMediaType[rgPin->nMediaTypes].clsMajorType = clsMajorType; 208 lpMediaType[rgPin->nMediaTypes].clsMinorType = clsMinorType; 209 rgPin->nMediaTypes++; 210 continue; 211 212 error_cleanup_types: 213 214 CoTaskMemFree(clsMajorType); 215 CoTaskMemFree(clsMinorType); 216 } 217 218 RegCloseKey(hkeyMajorType); 219 } 220 221 RegCloseKey(hkeyTypes); 222 223 if (lpMediaType && !rgPin->nMediaTypes) 224 { 225 CoTaskMemFree(lpMediaType); 226 lpMediaType = NULL; 227 } 228 229 rgPin->lpMediaType = lpMediaType; 230 } 231 232 static void DEVENUM_ReadPins(HKEY hkeyFilterClass, REGFILTER2 *rgf2) 233 { 234 HKEY hkeyPins = NULL; 235 DWORD dwPinsSubkeys, i; 236 REGFILTERPINS2 *rgPins = NULL; 237 238 rgf2->dwVersion = 2; 239 rgf2->u.s2.cPins2 = 0; 240 rgf2->u.s2.rgPins2 = NULL; 241 242 if (RegOpenKeyExW(hkeyFilterClass, wszPins, 0, KEY_READ, &hkeyPins) != ERROR_SUCCESS) 243 return ; 244 245 if (RegQueryInfoKeyW(hkeyPins, NULL, NULL, NULL, &dwPinsSubkeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL) 246 != ERROR_SUCCESS) 247 { 248 RegCloseKey(hkeyPins); 249 return ; 250 } 251 252 if (dwPinsSubkeys) 253 { 254 rgPins = CoTaskMemAlloc(sizeof(REGFILTERPINS2) * dwPinsSubkeys); 255 if (!rgPins) 256 { 257 RegCloseKey(hkeyPins); 258 return ; 259 } 260 } 261 262 for (i = 0; i < dwPinsSubkeys; i++) 263 { 264 HKEY hkeyPinKey = NULL; 265 WCHAR wszPinName[MAX_PATH]; 266 DWORD cName = sizeof(wszPinName) / sizeof(WCHAR); 267 REGFILTERPINS2 *rgPin = &rgPins[rgf2->u.s2.cPins2]; 268 DWORD value, size, Type; 269 LONG lRet; 270 271 memset(rgPin, 0, sizeof(*rgPin)); 272 273 if (RegEnumKeyExW(hkeyPins, i, wszPinName, &cName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) continue; 274 275 if (RegOpenKeyExW(hkeyPins, wszPinName, 0, KEY_READ, &hkeyPinKey) != ERROR_SUCCESS) continue; 276 277 size = sizeof(DWORD); 278 lRet = RegQueryValueExW(hkeyPinKey, wszAllowedMany, NULL, &Type, (BYTE *)&value, &size); 279 if (lRet != ERROR_SUCCESS || Type != REG_DWORD) 280 goto error_cleanup; 281 if (value) 282 rgPin->dwFlags |= REG_PINFLAG_B_MANY; 283 284 size = sizeof(DWORD); 285 lRet = RegQueryValueExW(hkeyPinKey, wszAllowedZero, NULL, &Type, (BYTE *)&value, &size); 286 if (lRet != ERROR_SUCCESS || Type != REG_DWORD) 287 goto error_cleanup; 288 if (value) 289 rgPin->dwFlags |= REG_PINFLAG_B_ZERO; 290 291 size = sizeof(DWORD); 292 lRet = RegQueryValueExW(hkeyPinKey, wszDirection, NULL, &Type, (BYTE *)&value, &size); 293 if (lRet != ERROR_SUCCESS || Type != REG_DWORD) 294 goto error_cleanup; 295 if (value) 296 rgPin->dwFlags |= REG_PINFLAG_B_OUTPUT; 297 298 299 size = sizeof(DWORD); 300 lRet = RegQueryValueExW(hkeyPinKey, wszIsRendered, NULL, &Type, (BYTE *)&value, &size); 301 if (lRet != ERROR_SUCCESS || Type != REG_DWORD) 302 goto error_cleanup; 303 if (value) 304 rgPin->dwFlags |= REG_PINFLAG_B_RENDERER; 305 306 DEVENUM_ReadPinTypes(hkeyPinKey, rgPin); 307 308 ++rgf2->u.s2.cPins2; 309 continue; 310 311 error_cleanup: 312 313 RegCloseKey(hkeyPinKey); 314 } 315 316 RegCloseKey(hkeyPins); 317 318 if (rgPins && !rgf2->u.s2.cPins2) 319 { 320 CoTaskMemFree(rgPins); 321 rgPins = NULL; 322 } 323 324 rgf2->u.s2.rgPins2 = rgPins; 325 } 326 327 static void free_regfilter2(REGFILTER2 *rgf) 328 { 329 if (rgf->u.s2.rgPins2) 330 { 331 UINT iPin; 332 333 for (iPin = 0; iPin < rgf->u.s2.cPins2; iPin++) 334 { 335 if (rgf->u.s2.rgPins2[iPin].lpMediaType) 336 { 337 UINT iType; 338 339 for (iType = 0; iType < rgf->u.s2.rgPins2[iPin].nMediaTypes; iType++) 340 { 341 CoTaskMemFree((void *)rgf->u.s2.rgPins2[iPin].lpMediaType[iType].clsMajorType); 342 CoTaskMemFree((void *)rgf->u.s2.rgPins2[iPin].lpMediaType[iType].clsMinorType); 343 } 344 345 CoTaskMemFree((void *)rgf->u.s2.rgPins2[iPin].lpMediaType); 346 } 347 } 348 349 CoTaskMemFree((void *)rgf->u.s2.rgPins2); 350 } 351 } 352 353 static void write_filter_data(IPropertyBag *prop_bag, REGFILTER2 *rgf) 354 { 355 IAMFilterData *fildata; 356 SAFEARRAYBOUND sabound; 357 BYTE *data, *array; 358 VARIANT var; 359 ULONG size; 360 HRESULT hr; 361 362 hr = CoCreateInstance(&CLSID_FilterMapper2, NULL, CLSCTX_INPROC, &IID_IAMFilterData, (void **)&fildata); 363 if (FAILED(hr)) goto cleanup; 364 365 hr = IAMFilterData_CreateFilterData(fildata, rgf, &data, &size); 366 if (FAILED(hr)) goto cleanup; 367 368 V_VT(&var) = VT_ARRAY | VT_UI1; 369 sabound.lLbound = 0; 370 sabound.cElements = size; 371 if (!(V_ARRAY(&var) = SafeArrayCreate(VT_UI1, 1, &sabound))) 372 goto cleanup; 373 hr = SafeArrayAccessData(V_ARRAY(&var), (void *)&array); 374 if (FAILED(hr)) goto cleanup; 375 376 memcpy(array, data, size); 377 hr = SafeArrayUnaccessData(V_ARRAY(&var)); 378 if (FAILED(hr)) goto cleanup; 379 380 hr = IPropertyBag_Write(prop_bag, wszFilterData, &var); 381 if (FAILED(hr)) goto cleanup; 382 383 cleanup: 384 VariantClear(&var); 385 CoTaskMemFree(data); 386 IAMFilterData_Release(fildata); 387 } 388 389 static void register_legacy_filters(void) 390 { 391 HKEY hkeyFilter = NULL; 392 DWORD dwFilterSubkeys, i; 393 LONG lRet; 394 HRESULT hr; 395 396 lRet = RegOpenKeyExW(HKEY_CLASSES_ROOT, wszFilterKeyName, 0, KEY_READ, &hkeyFilter); 397 hr = HRESULT_FROM_WIN32(lRet); 398 399 if (SUCCEEDED(hr)) 400 { 401 lRet = RegQueryInfoKeyW(hkeyFilter, NULL, NULL, NULL, &dwFilterSubkeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL); 402 hr = HRESULT_FROM_WIN32(lRet); 403 } 404 405 if (SUCCEEDED(hr)) 406 hr = DEVENUM_CreateAMCategoryKey(&CLSID_LegacyAmFilterCategory); 407 408 if (SUCCEEDED(hr)) 409 { 410 for (i = 0; i < dwFilterSubkeys; i++) 411 { 412 WCHAR wszFilterSubkeyName[64]; 413 DWORD cName = sizeof(wszFilterSubkeyName) / sizeof(WCHAR); 414 IPropertyBag *prop_bag = NULL; 415 WCHAR wszRegKey[MAX_PATH]; 416 HKEY classkey = NULL; 417 IMoniker *mon = NULL; 418 VARIANT var; 419 REGFILTER2 rgf2; 420 DWORD Type, len; 421 422 if (RegEnumKeyExW(hkeyFilter, i, wszFilterSubkeyName, &cName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) continue; 423 424 TRACE("Registering %s\n", debugstr_w(wszFilterSubkeyName)); 425 426 strcpyW(wszRegKey, clsidW); 427 strcatW(wszRegKey, wszFilterSubkeyName); 428 429 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, wszRegKey, 0, KEY_READ, &classkey) != ERROR_SUCCESS) 430 continue; 431 432 hr = register_codec(&CLSID_LegacyAmFilterCategory, wszFilterSubkeyName, &mon); 433 if (FAILED(hr)) goto cleanup; 434 435 hr = IMoniker_BindToStorage(mon, NULL, NULL, &IID_IPropertyBag, (void **)&prop_bag); 436 if (FAILED(hr)) goto cleanup; 437 438 /* write friendly name */ 439 len = 0; 440 V_VT(&var) = VT_BSTR; 441 if (!RegQueryValueExW(classkey, NULL, NULL, &Type, NULL, &len)) 442 { 443 WCHAR *friendlyname = heap_alloc(len); 444 if (!friendlyname) 445 goto cleanup; 446 RegQueryValueExW(classkey, NULL, NULL, &Type, (BYTE *)friendlyname, &len); 447 V_BSTR(&var) = SysAllocStringLen(friendlyname, len/sizeof(WCHAR)); 448 heap_free(friendlyname); 449 } 450 else 451 V_BSTR(&var) = SysAllocString(wszFilterSubkeyName); 452 453 if (!V_BSTR(&var)) 454 goto cleanup; 455 hr = IPropertyBag_Write(prop_bag, wszFriendlyName, &var); 456 if (FAILED(hr)) goto cleanup; 457 VariantClear(&var); 458 459 /* write clsid */ 460 V_VT(&var) = VT_BSTR; 461 if (!(V_BSTR(&var) = SysAllocString(wszFilterSubkeyName))) 462 goto cleanup; 463 hr = IPropertyBag_Write(prop_bag, clsid_keyname, &var); 464 if (FAILED(hr)) goto cleanup; 465 VariantClear(&var); 466 467 /* write filter data */ 468 rgf2.dwMerit = MERIT_NORMAL; 469 470 len = sizeof(rgf2.dwMerit); 471 RegQueryValueExW(classkey, wszMeritName, NULL, &Type, (BYTE *)&rgf2.dwMerit, &len); 472 473 DEVENUM_ReadPins(classkey, &rgf2); 474 475 write_filter_data(prop_bag, &rgf2); 476 477 cleanup: 478 if (prop_bag) IPropertyBag_Release(prop_bag); 479 if (mon) IMoniker_Release(mon); 480 RegCloseKey(classkey); 481 VariantClear(&var); 482 free_regfilter2(&rgf2); 483 } 484 } 485 486 if (hkeyFilter) RegCloseKey(hkeyFilter); 487 } 488 489 static BOOL CALLBACK register_dsound_devices(GUID *guid, const WCHAR *desc, const WCHAR *module, void *context) 490 { 491 static const WCHAR defaultW[] = {'D','e','f','a','u','l','t',' ','D','i','r','e','c','t','S','o','u','n','d',' ','D','e','v','i','c','e',0}; 492 static const WCHAR directsoundW[] = {'D','i','r','e','c','t','S','o','u','n','d',':',' ',0}; 493 static const WCHAR dsguidW[] = {'D','S','G','u','i','d',0}; 494 IPropertyBag *prop_bag = NULL; 495 REGFILTERPINS2 rgpins = {0}; 496 REGPINTYPES rgtypes = {0}; 497 REGFILTER2 rgf = {0}; 498 WCHAR clsid[CHARS_IN_GUID]; 499 IMoniker *mon = NULL; 500 VARIANT var; 501 HRESULT hr; 502 503 hr = DEVENUM_CreateAMCategoryKey(&CLSID_AudioRendererCategory); 504 if (FAILED(hr)) goto cleanup; 505 506 V_VT(&var) = VT_BSTR; 507 if (guid) 508 { 509 WCHAR *name = heap_alloc(sizeof(defaultW) + strlenW(desc) * sizeof(WCHAR)); 510 if (!name) 511 goto cleanup; 512 strcpyW(name, directsoundW); 513 strcatW(name, desc); 514 515 V_BSTR(&var) = SysAllocString(name); 516 heap_free(name); 517 } 518 else 519 V_BSTR(&var) = SysAllocString(defaultW); 520 521 if (!V_BSTR(&var)) 522 goto cleanup; 523 524 hr = register_codec(&CLSID_AudioRendererCategory, V_BSTR(&var), &mon); 525 if (FAILED(hr)) goto cleanup; 526 527 hr = IMoniker_BindToStorage(mon, NULL, NULL, &IID_IPropertyBag, (void **)&prop_bag); 528 if (FAILED(hr)) goto cleanup; 529 530 /* write friendly name */ 531 hr = IPropertyBag_Write(prop_bag, wszFriendlyName, &var); 532 if (FAILED(hr)) goto cleanup; 533 VariantClear(&var); 534 535 /* write clsid */ 536 V_VT(&var) = VT_BSTR; 537 StringFromGUID2(&CLSID_DSoundRender, clsid, CHARS_IN_GUID); 538 if (!(V_BSTR(&var) = SysAllocString(clsid))) 539 goto cleanup; 540 hr = IPropertyBag_Write(prop_bag, clsid_keyname, &var); 541 if (FAILED(hr)) goto cleanup; 542 VariantClear(&var); 543 544 /* write filter data */ 545 rgf.dwVersion = 2; 546 rgf.dwMerit = guid ? MERIT_DO_NOT_USE : MERIT_PREFERRED; 547 rgf.u.s2.cPins2 = 1; 548 rgf.u.s2.rgPins2 = &rgpins; 549 rgpins.dwFlags = REG_PINFLAG_B_RENDERER; 550 /* FIXME: native registers many more formats */ 551 rgpins.nMediaTypes = 1; 552 rgpins.lpMediaType = &rgtypes; 553 rgtypes.clsMajorType = &MEDIATYPE_Audio; 554 rgtypes.clsMinorType = &MEDIASUBTYPE_PCM; 555 556 write_filter_data(prop_bag, &rgf); 557 558 /* write DSound guid */ 559 V_VT(&var) = VT_BSTR; 560 StringFromGUID2(guid ? guid : &GUID_NULL, clsid, CHARS_IN_GUID); 561 if (!(V_BSTR(&var) = SysAllocString(clsid))) 562 goto cleanup; 563 hr = IPropertyBag_Write(prop_bag, dsguidW, &var); 564 if (FAILED(hr)) goto cleanup; 565 566 cleanup: 567 VariantClear(&var); 568 if (prop_bag) IPropertyBag_Release(prop_bag); 569 if (mon) IMoniker_Release(mon); 570 571 return TRUE; 572 } 573 574 static void register_waveout_devices(void) 575 { 576 static const WCHAR defaultW[] = {'D','e','f','a','u','l','t',' ','W','a','v','e','O','u','t',' ','D','e','v','i','c','e',0}; 577 static const WCHAR waveoutidW[] = {'W','a','v','e','O','u','t','I','d',0}; 578 IPropertyBag *prop_bag = NULL; 579 REGFILTERPINS2 rgpins = {0}; 580 REGPINTYPES rgtypes = {0}; 581 REGFILTER2 rgf = {0}; 582 WCHAR clsid[CHARS_IN_GUID]; 583 IMoniker *mon = NULL; 584 WAVEOUTCAPSW caps; 585 int i, count; 586 VARIANT var; 587 HRESULT hr; 588 589 hr = DEVENUM_CreateAMCategoryKey(&CLSID_AudioRendererCategory); 590 if (FAILED(hr)) return; 591 592 count = waveOutGetNumDevs(); 593 594 for (i = -1; i < count; i++) 595 { 596 waveOutGetDevCapsW(i, &caps, sizeof(caps)); 597 598 V_VT(&var) = VT_BSTR; 599 600 if (i == -1) /* WAVE_MAPPER */ 601 V_BSTR(&var) = SysAllocString(defaultW); 602 else 603 V_BSTR(&var) = SysAllocString(caps.szPname); 604 if (!(V_BSTR(&var))) 605 goto cleanup; 606 607 hr = register_codec(&CLSID_AudioRendererCategory, V_BSTR(&var), &mon); 608 if (FAILED(hr)) goto cleanup; 609 610 hr = IMoniker_BindToStorage(mon, NULL, NULL, &IID_IPropertyBag, (void **)&prop_bag); 611 if (FAILED(hr)) goto cleanup; 612 613 /* write friendly name */ 614 hr = IPropertyBag_Write(prop_bag, wszFriendlyName, &var); 615 if (FAILED(hr)) goto cleanup; 616 VariantClear(&var); 617 618 /* write clsid */ 619 V_VT(&var) = VT_BSTR; 620 StringFromGUID2(&CLSID_AudioRender, clsid, CHARS_IN_GUID); 621 if (!(V_BSTR(&var) = SysAllocString(clsid))) 622 goto cleanup; 623 hr = IPropertyBag_Write(prop_bag, clsid_keyname, &var); 624 if (FAILED(hr)) goto cleanup; 625 VariantClear(&var); 626 627 /* write filter data */ 628 rgf.dwVersion = 2; 629 rgf.dwMerit = MERIT_DO_NOT_USE; 630 rgf.u.s2.cPins2 = 1; 631 rgf.u.s2.rgPins2 = &rgpins; 632 rgpins.dwFlags = REG_PINFLAG_B_RENDERER; 633 rgpins.nMediaTypes = 1; 634 rgpins.lpMediaType = &rgtypes; 635 rgtypes.clsMajorType = &MEDIATYPE_Audio; 636 rgtypes.clsMinorType = &MEDIASUBTYPE_NULL; 637 638 write_filter_data(prop_bag, &rgf); 639 640 /* write WaveOutId */ 641 V_VT(&var) = VT_I4; 642 V_I4(&var) = i; 643 hr = IPropertyBag_Write(prop_bag, waveoutidW, &var); 644 if (FAILED(hr)) goto cleanup; 645 646 cleanup: 647 VariantClear(&var); 648 if (prop_bag) IPropertyBag_Release(prop_bag); 649 if (mon) IMoniker_Release(mon); 650 } 651 } 652 653 static void register_wavein_devices(void) 654 { 655 static const WCHAR waveinidW[] = {'W','a','v','e','I','n','I','d',0}; 656 IPropertyBag *prop_bag = NULL; 657 REGFILTER2 rgf = {0}; 658 WCHAR clsid[CHARS_IN_GUID]; 659 IMoniker *mon = NULL; 660 WAVEINCAPSW caps; 661 int i, count; 662 VARIANT var; 663 HRESULT hr; 664 665 hr = DEVENUM_CreateAMCategoryKey(&CLSID_AudioRendererCategory); 666 if (FAILED(hr)) return; 667 668 count = waveInGetNumDevs(); 669 670 for (i = 0; i < count; i++) 671 { 672 waveInGetDevCapsW(i, &caps, sizeof(caps)); 673 674 V_VT(&var) = VT_BSTR; 675 676 V_BSTR(&var) = SysAllocString(caps.szPname); 677 if (!(V_BSTR(&var))) 678 goto cleanup; 679 680 hr = register_codec(&CLSID_AudioInputDeviceCategory, V_BSTR(&var), &mon); 681 if (FAILED(hr)) goto cleanup; 682 683 hr = IMoniker_BindToStorage(mon, NULL, NULL, &IID_IPropertyBag, (void **)&prop_bag); 684 if (FAILED(hr)) goto cleanup; 685 686 /* write friendly name */ 687 hr = IPropertyBag_Write(prop_bag, wszFriendlyName, &var); 688 if (FAILED(hr)) goto cleanup; 689 VariantClear(&var); 690 691 /* write clsid */ 692 V_VT(&var) = VT_BSTR; 693 StringFromGUID2(&CLSID_AudioRecord, clsid, CHARS_IN_GUID); 694 if (!(V_BSTR(&var) = SysAllocString(clsid))) 695 goto cleanup; 696 hr = IPropertyBag_Write(prop_bag, clsid_keyname, &var); 697 if (FAILED(hr)) goto cleanup; 698 VariantClear(&var); 699 700 /* write filter data */ 701 rgf.dwVersion = 2; 702 rgf.dwMerit = MERIT_DO_NOT_USE; 703 704 write_filter_data(prop_bag, &rgf); 705 706 /* write WaveInId */ 707 V_VT(&var) = VT_I4; 708 V_I4(&var) = i; 709 hr = IPropertyBag_Write(prop_bag, waveinidW, &var); 710 if (FAILED(hr)) goto cleanup; 711 712 cleanup: 713 VariantClear(&var); 714 if (prop_bag) IPropertyBag_Release(prop_bag); 715 if (mon) IMoniker_Release(mon); 716 } 717 } 718 719 static void register_midiout_devices(void) 720 { 721 static const WCHAR defaultW[] = {'D','e','f','a','u','l','t',' ','M','i','d','i','O','u','t',' ','D','e','v','i','c','e',0}; 722 static const WCHAR midioutidW[] = {'M','i','d','i','O','u','t','I','d',0}; 723 IPropertyBag *prop_bag = NULL; 724 REGFILTERPINS2 rgpins = {0}; 725 REGPINTYPES rgtypes = {0}; 726 REGFILTER2 rgf = {0}; 727 WCHAR clsid[CHARS_IN_GUID]; 728 IMoniker *mon = NULL; 729 MIDIOUTCAPSW caps; 730 int i, count; 731 VARIANT var; 732 HRESULT hr; 733 734 hr = DEVENUM_CreateAMCategoryKey(&CLSID_AudioRendererCategory); 735 if (FAILED(hr)) return; 736 737 count = midiOutGetNumDevs(); 738 739 for (i = -1; i < count; i++) 740 { 741 midiOutGetDevCapsW(i, &caps, sizeof(caps)); 742 743 V_VT(&var) = VT_BSTR; 744 745 if (i == -1) /* MIDI_MAPPER */ 746 V_BSTR(&var) = SysAllocString(defaultW); 747 else 748 V_BSTR(&var) = SysAllocString(caps.szPname); 749 if (!(V_BSTR(&var))) 750 goto cleanup; 751 752 hr = register_codec(&CLSID_MidiRendererCategory, V_BSTR(&var), &mon); 753 if (FAILED(hr)) goto cleanup; 754 755 hr = IMoniker_BindToStorage(mon, NULL, NULL, &IID_IPropertyBag, (void **)&prop_bag); 756 if (FAILED(hr)) goto cleanup; 757 758 /* write friendly name */ 759 hr = IPropertyBag_Write(prop_bag, wszFriendlyName, &var); 760 if (FAILED(hr)) goto cleanup; 761 VariantClear(&var); 762 763 /* write clsid */ 764 V_VT(&var) = VT_BSTR; 765 StringFromGUID2(&CLSID_AVIMIDIRender, clsid, CHARS_IN_GUID); 766 if (!(V_BSTR(&var) = SysAllocString(clsid))) 767 goto cleanup; 768 hr = IPropertyBag_Write(prop_bag, clsid_keyname, &var); 769 if (FAILED(hr)) goto cleanup; 770 VariantClear(&var); 771 772 /* write filter data */ 773 rgf.dwVersion = 2; 774 rgf.dwMerit = (i == -1) ? MERIT_PREFERRED : MERIT_DO_NOT_USE; 775 rgf.u.s2.cPins2 = 1; 776 rgf.u.s2.rgPins2 = &rgpins; 777 rgpins.dwFlags = REG_PINFLAG_B_RENDERER; 778 rgpins.nMediaTypes = 1; 779 rgpins.lpMediaType = &rgtypes; 780 rgtypes.clsMajorType = &MEDIATYPE_Midi; 781 rgtypes.clsMinorType = &MEDIASUBTYPE_NULL; 782 783 write_filter_data(prop_bag, &rgf); 784 785 /* write MidiOutId */ 786 V_VT(&var) = VT_I4; 787 V_I4(&var) = i; 788 hr = IPropertyBag_Write(prop_bag, midioutidW, &var); 789 if (FAILED(hr)) goto cleanup; 790 791 cleanup: 792 VariantClear(&var); 793 if (prop_bag) IPropertyBag_Release(prop_bag); 794 if (mon) IMoniker_Release(mon); 795 } 796 } 797 798 static void register_vfw_codecs(void) 799 { 800 static const WCHAR fcchandlerW[] = {'F','c','c','H','a','n','d','l','e','r',0}; 801 REGFILTERPINS2 rgpins[2] = {{0}}; 802 IPropertyBag *prop_bag = NULL; 803 REGPINTYPES rgtypes[2]; 804 REGFILTER2 rgf; 805 WCHAR clsid[CHARS_IN_GUID]; 806 IMoniker *mon = NULL; 807 GUID typeguid; 808 ICINFO info; 809 VARIANT var; 810 HRESULT hr; 811 int i = 0; 812 HIC hic; 813 814 hr = DEVENUM_CreateAMCategoryKey(&CLSID_AudioRendererCategory); 815 if (FAILED(hr)) return; 816 817 while (ICInfo(ICTYPE_VIDEO, i++, &info)) 818 { 819 WCHAR name[5] = {LOBYTE(LOWORD(info.fccHandler)), HIBYTE(LOWORD(info.fccHandler)), 820 LOBYTE(HIWORD(info.fccHandler)), HIBYTE(HIWORD(info.fccHandler))}; 821 822 hic = ICOpen(ICTYPE_VIDEO, info.fccHandler, ICMODE_QUERY); 823 ICGetInfo(hic, &info, sizeof(info)); 824 ICClose(hic); 825 826 V_VT(&var) = VT_BSTR; 827 828 V_BSTR(&var) = SysAllocString(name); 829 if (!(V_BSTR(&var))) 830 goto cleanup; 831 832 hr = register_codec(&CLSID_VideoCompressorCategory, V_BSTR(&var), &mon); 833 if (FAILED(hr)) goto cleanup; 834 835 hr = IMoniker_BindToStorage(mon, NULL, NULL, &IID_IPropertyBag, (void **)&prop_bag); 836 if (FAILED(hr)) goto cleanup; 837 838 /* write WaveInId */ 839 hr = IPropertyBag_Write(prop_bag, fcchandlerW, &var); 840 if (FAILED(hr)) goto cleanup; 841 VariantClear(&var); 842 843 /* write friendly name */ 844 V_VT(&var) = VT_BSTR; 845 if (!(V_BSTR(&var) = SysAllocString(info.szDescription))) 846 goto cleanup; 847 848 hr = IPropertyBag_Write(prop_bag, wszFriendlyName, &var); 849 if (FAILED(hr)) goto cleanup; 850 VariantClear(&var); 851 852 /* write clsid */ 853 V_VT(&var) = VT_BSTR; 854 StringFromGUID2(&CLSID_AVICo, clsid, CHARS_IN_GUID); 855 if (!(V_BSTR(&var) = SysAllocString(clsid))) 856 goto cleanup; 857 hr = IPropertyBag_Write(prop_bag, clsid_keyname, &var); 858 if (FAILED(hr)) goto cleanup; 859 VariantClear(&var); 860 861 /* write filter data */ 862 rgf.dwVersion = 2; 863 rgf.dwMerit = MERIT_DO_NOT_USE; 864 rgf.u.s2.cPins2 = 2; 865 rgf.u.s2.rgPins2 = rgpins; 866 rgpins[0].dwFlags = 0; 867 rgpins[0].nMediaTypes = 1; 868 rgpins[0].lpMediaType = &rgtypes[0]; 869 rgtypes[0].clsMajorType = &MEDIATYPE_Video; 870 typeguid = MEDIASUBTYPE_PCM; 871 typeguid.Data1 = info.fccHandler; 872 rgtypes[0].clsMinorType = &typeguid; 873 rgpins[1].dwFlags = REG_PINFLAG_B_OUTPUT; 874 rgpins[1].nMediaTypes = 1; 875 rgpins[1].lpMediaType = &rgtypes[1]; 876 rgtypes[1].clsMajorType = &MEDIATYPE_Video; 877 rgtypes[1].clsMinorType = &GUID_NULL; 878 879 write_filter_data(prop_bag, &rgf); 880 881 cleanup: 882 VariantClear(&var); 883 if (prop_bag) IPropertyBag_Release(prop_bag); 884 if (mon) IMoniker_Release(mon); 885 } 886 } 887 888 /********************************************************************** 889 * DEVENUM_ICreateDevEnum_CreateClassEnumerator 890 */ 891 static HRESULT WINAPI DEVENUM_ICreateDevEnum_CreateClassEnumerator( 892 ICreateDevEnum * iface, 893 REFCLSID clsidDeviceClass, 894 IEnumMoniker **ppEnumMoniker, 895 DWORD dwFlags) 896 { 897 HRESULT hr; 898 899 TRACE("(%p)->(%s, %p, %x)\n", iface, debugstr_guid(clsidDeviceClass), ppEnumMoniker, dwFlags); 900 901 if (!ppEnumMoniker) 902 return E_POINTER; 903 904 *ppEnumMoniker = NULL; 905 906 register_codecs(); 907 register_legacy_filters(); 908 hr = DirectSoundEnumerateW(®ister_dsound_devices, NULL); 909 if (FAILED(hr)) return hr; 910 register_waveout_devices(); 911 register_wavein_devices(); 912 register_midiout_devices(); 913 register_vfw_codecs(); 914 915 return create_EnumMoniker(clsidDeviceClass, ppEnumMoniker); 916 } 917 918 /********************************************************************** 919 * ICreateDevEnum_Vtbl 920 */ 921 static const ICreateDevEnumVtbl ICreateDevEnum_Vtbl = 922 { 923 DEVENUM_ICreateDevEnum_QueryInterface, 924 DEVENUM_ICreateDevEnum_AddRef, 925 DEVENUM_ICreateDevEnum_Release, 926 DEVENUM_ICreateDevEnum_CreateClassEnumerator, 927 }; 928 929 /********************************************************************** 930 * static CreateDevEnum instance 931 */ 932 ICreateDevEnum DEVENUM_CreateDevEnum = { &ICreateDevEnum_Vtbl }; 933 934 /********************************************************************** 935 * DEVENUM_CreateAMCategoryKey (INTERNAL) 936 * 937 * Creates a registry key for a category at HKEY_CURRENT_USER\Software\ 938 * Microsoft\ActiveMovie\devenum\{clsid} 939 */ 940 static HRESULT DEVENUM_CreateAMCategoryKey(const CLSID * clsidCategory) 941 { 942 WCHAR wszRegKey[MAX_PATH]; 943 HRESULT res = S_OK; 944 HKEY hkeyDummy = NULL; 945 946 strcpyW(wszRegKey, wszActiveMovieKey); 947 948 if (!StringFromGUID2(clsidCategory, wszRegKey + strlenW(wszRegKey), sizeof(wszRegKey)/sizeof(wszRegKey[0]) - strlenW(wszRegKey))) 949 res = E_INVALIDARG; 950 951 if (SUCCEEDED(res)) 952 { 953 LONG lRes = RegCreateKeyW(HKEY_CURRENT_USER, wszRegKey, &hkeyDummy); 954 res = HRESULT_FROM_WIN32(lRes); 955 } 956 957 if (hkeyDummy) 958 RegCloseKey(hkeyDummy); 959 960 if (FAILED(res)) 961 ERR("Failed to create key HKEY_CURRENT_USER\\%s\n", debugstr_w(wszRegKey)); 962 963 return res; 964 } 965 966 static HRESULT register_codecs(void) 967 { 968 HRESULT res; 969 WCHAR class[CHARS_IN_GUID]; 970 DWORD iDefaultDevice = -1; 971 IFilterMapper2 * pMapper = NULL; 972 REGFILTER2 rf2; 973 REGFILTERPINS2 rfp2; 974 HKEY basekey; 975 976 /* Since devices can change between session, for example because you just plugged in a webcam 977 * or switched from pulseaudio to alsa, delete all old devices first 978 */ 979 RegOpenKeyW(HKEY_CURRENT_USER, wszActiveMovieKey, &basekey); 980 StringFromGUID2(&CLSID_LegacyAmFilterCategory, class, CHARS_IN_GUID); 981 RegDeleteTreeW(basekey, class); 982 StringFromGUID2(&CLSID_AudioRendererCategory, class, CHARS_IN_GUID); 983 RegDeleteTreeW(basekey, class); 984 StringFromGUID2(&CLSID_AudioInputDeviceCategory, class, CHARS_IN_GUID); 985 RegDeleteTreeW(basekey, class); 986 StringFromGUID2(&CLSID_VideoInputDeviceCategory, class, CHARS_IN_GUID); 987 RegDeleteTreeW(basekey, class); 988 StringFromGUID2(&CLSID_MidiRendererCategory, class, CHARS_IN_GUID); 989 RegDeleteTreeW(basekey, class); 990 StringFromGUID2(&CLSID_VideoCompressorCategory, class, CHARS_IN_GUID); 991 RegDeleteTreeW(basekey, class); 992 RegCloseKey(basekey); 993 994 rf2.dwVersion = 2; 995 rf2.dwMerit = MERIT_PREFERRED; 996 rf2.u.s2.cPins2 = 1; 997 rf2.u.s2.rgPins2 = &rfp2; 998 rfp2.cInstances = 1; 999 rfp2.nMediums = 0; 1000 rfp2.lpMedium = NULL; 1001 rfp2.clsPinCategory = &IID_NULL; 1002 1003 res = CoCreateInstance(&CLSID_FilterMapper2, NULL, CLSCTX_INPROC, 1004 &IID_IFilterMapper2, (void **) &pMapper); 1005 /* 1006 * Fill in info for devices 1007 */ 1008 if (SUCCEEDED(res)) 1009 { 1010 UINT i; 1011 REGPINTYPES * pTypes; 1012 IPropertyBag * pPropBag = NULL; 1013 1014 res = DEVENUM_CreateAMCategoryKey(&CLSID_VideoInputDeviceCategory); 1015 if (SUCCEEDED(res)) 1016 for (i = 0; i < 10; i++) 1017 { 1018 WCHAR szDeviceName[32], szDeviceVersion[32], szDevicePath[10]; 1019 1020 if (capGetDriverDescriptionW ((WORD) i, 1021 szDeviceName, sizeof(szDeviceName)/sizeof(WCHAR), 1022 szDeviceVersion, sizeof(szDeviceVersion)/sizeof(WCHAR))) 1023 { 1024 IMoniker * pMoniker = NULL; 1025 WCHAR dprintf[] = { 'v','i','d','e','o','%','d',0 }; 1026 snprintfW(szDevicePath, sizeof(szDevicePath)/sizeof(WCHAR), dprintf, i); 1027 /* The above code prevents 1 device with a different ID overwriting another */ 1028 1029 rfp2.nMediaTypes = 1; 1030 pTypes = CoTaskMemAlloc(rfp2.nMediaTypes * sizeof(REGPINTYPES)); 1031 if (!pTypes) { 1032 IFilterMapper2_Release(pMapper); 1033 return E_OUTOFMEMORY; 1034 } 1035 1036 pTypes[0].clsMajorType = &MEDIATYPE_Video; 1037 pTypes[0].clsMinorType = &MEDIASUBTYPE_None; 1038 1039 rfp2.lpMediaType = pTypes; 1040 1041 res = IFilterMapper2_RegisterFilter(pMapper, 1042 &CLSID_VfwCapture, 1043 szDeviceName, 1044 &pMoniker, 1045 &CLSID_VideoInputDeviceCategory, 1046 szDevicePath, 1047 &rf2); 1048 1049 if (pMoniker) { 1050 OLECHAR wszVfwIndex[] = { 'V','F','W','I','n','d','e','x',0 }; 1051 VARIANT var; 1052 V_VT(&var) = VT_I4; 1053 V_I4(&var) = i; 1054 res = IMoniker_BindToStorage(pMoniker, NULL, NULL, &IID_IPropertyBag, (LPVOID)&pPropBag); 1055 if (SUCCEEDED(res)) { 1056 res = IPropertyBag_Write(pPropBag, wszVfwIndex, &var); 1057 IPropertyBag_Release(pPropBag); 1058 } 1059 IMoniker_Release(pMoniker); 1060 } 1061 1062 if (i == iDefaultDevice) FIXME("Default device\n"); 1063 CoTaskMemFree(pTypes); 1064 } 1065 } 1066 } 1067 1068 if (pMapper) 1069 IFilterMapper2_Release(pMapper); 1070 1071 return res; 1072 } 1073