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/heap.h" 36 #include "mmddk.h" 37 38 #include "initguid.h" 39 #include "wine/fil_data.h" 40 41 WINE_DEFAULT_DEBUG_CHANNEL(devenum); 42 43 static const WCHAR wszFilterKeyName[] = {'F','i','l','t','e','r',0}; 44 static const WCHAR wszMeritName[] = {'M','e','r','i','t',0}; 45 static const WCHAR wszPins[] = {'P','i','n','s',0}; 46 static const WCHAR wszAllowedMany[] = {'A','l','l','o','w','e','d','M','a','n','y',0}; 47 static const WCHAR wszAllowedZero[] = {'A','l','l','o','w','e','d','Z','e','r','o',0}; 48 static const WCHAR wszDirection[] = {'D','i','r','e','c','t','i','o','n',0}; 49 static const WCHAR wszIsRendered[] = {'I','s','R','e','n','d','e','r','e','d',0}; 50 static const WCHAR wszTypes[] = {'T','y','p','e','s',0}; 51 static const WCHAR wszFriendlyName[] = {'F','r','i','e','n','d','l','y','N','a','m','e',0}; 52 static const WCHAR wszFilterData[] = {'F','i','l','t','e','r','D','a','t','a',0}; 53 54 static ULONG WINAPI DEVENUM_ICreateDevEnum_AddRef(ICreateDevEnum * iface); 55 static HRESULT DEVENUM_CreateAMCategoryKey(const CLSID * clsidCategory); 56 57 /********************************************************************** 58 * DEVENUM_ICreateDevEnum_QueryInterface (also IUnknown) 59 */ 60 static HRESULT WINAPI DEVENUM_ICreateDevEnum_QueryInterface(ICreateDevEnum *iface, REFIID riid, 61 void **ppv) 62 { 63 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv); 64 65 if (!ppv) 66 return E_POINTER; 67 68 if (IsEqualGUID(riid, &IID_IUnknown) || 69 IsEqualGUID(riid, &IID_ICreateDevEnum)) 70 { 71 *ppv = iface; 72 DEVENUM_ICreateDevEnum_AddRef(iface); 73 return S_OK; 74 } 75 76 FIXME("- no interface IID: %s\n", debugstr_guid(riid)); 77 *ppv = NULL; 78 return E_NOINTERFACE; 79 } 80 81 /********************************************************************** 82 * DEVENUM_ICreateDevEnum_AddRef (also IUnknown) 83 */ 84 static ULONG WINAPI DEVENUM_ICreateDevEnum_AddRef(ICreateDevEnum * iface) 85 { 86 TRACE("\n"); 87 88 DEVENUM_LockModule(); 89 90 return 2; /* non-heap based object */ 91 } 92 93 /********************************************************************** 94 * DEVENUM_ICreateDevEnum_Release (also IUnknown) 95 */ 96 static ULONG WINAPI DEVENUM_ICreateDevEnum_Release(ICreateDevEnum * iface) 97 { 98 TRACE("\n"); 99 100 DEVENUM_UnlockModule(); 101 102 return 1; /* non-heap based object */ 103 } 104 105 static HRESULT register_codec(const GUID *class, const WCHAR *name, 106 const GUID *clsid, const WCHAR *friendly_name, IPropertyBag **ret) 107 { 108 static const WCHAR deviceW[] = {'@','d','e','v','i','c','e',':','c','m',':',0}; 109 WCHAR guidstr[CHARS_IN_GUID]; 110 IParseDisplayName *parser; 111 IPropertyBag *propbag; 112 IMoniker *mon; 113 WCHAR *buffer; 114 VARIANT var; 115 ULONG eaten; 116 HRESULT hr; 117 118 hr = CoCreateInstance(&CLSID_CDeviceMoniker, NULL, CLSCTX_INPROC, &IID_IParseDisplayName, (void **)&parser); 119 if (FAILED(hr)) 120 return hr; 121 122 buffer = heap_alloc((lstrlenW(deviceW) + CHARS_IN_GUID + lstrlenW(name) + 1) * sizeof(WCHAR)); 123 if (!buffer) 124 { 125 IParseDisplayName_Release(parser); 126 return E_OUTOFMEMORY; 127 } 128 129 lstrcpyW(buffer, deviceW); 130 StringFromGUID2(class, buffer + lstrlenW(buffer), CHARS_IN_GUID); 131 lstrcatW(buffer, backslashW); 132 lstrcatW(buffer, name); 133 134 IParseDisplayName_ParseDisplayName(parser, NULL, buffer, &eaten, &mon); 135 IParseDisplayName_Release(parser); 136 heap_free(buffer); 137 138 IMoniker_BindToStorage(mon, NULL, NULL, &IID_IPropertyBag, (void **)&propbag); 139 IMoniker_Release(mon); 140 141 V_VT(&var) = VT_BSTR; 142 V_BSTR(&var) = SysAllocString(friendly_name); 143 hr = IPropertyBag_Write(propbag, wszFriendlyName, &var); 144 VariantClear(&var); 145 if (FAILED(hr)) 146 { 147 IPropertyBag_Release(propbag); 148 return hr; 149 } 150 151 V_VT(&var) = VT_BSTR; 152 StringFromGUID2(clsid, guidstr, ARRAY_SIZE(guidstr)); 153 V_BSTR(&var) = SysAllocString(guidstr); 154 hr = IPropertyBag_Write(propbag, clsidW, &var); 155 VariantClear(&var); 156 if (FAILED(hr)) 157 { 158 IPropertyBag_Release(propbag); 159 return hr; 160 } 161 162 *ret = propbag; 163 return S_OK; 164 } 165 166 static void DEVENUM_ReadPinTypes(HKEY hkeyPinKey, REGFILTERPINS2 *rgPin) 167 { 168 HKEY hkeyTypes = NULL; 169 DWORD dwMajorTypes, i; 170 REGPINTYPES *lpMediaType = NULL; 171 DWORD dwMediaTypeSize = 0; 172 173 if (RegOpenKeyExW(hkeyPinKey, wszTypes, 0, KEY_READ, &hkeyTypes) != ERROR_SUCCESS) 174 return ; 175 176 if (RegQueryInfoKeyW(hkeyTypes, NULL, NULL, NULL, &dwMajorTypes, NULL, NULL, NULL, NULL, NULL, NULL, NULL) 177 != ERROR_SUCCESS) 178 { 179 RegCloseKey(hkeyTypes); 180 return ; 181 } 182 183 for (i = 0; i < dwMajorTypes; i++) 184 { 185 HKEY hkeyMajorType = NULL; 186 WCHAR wszMajorTypeName[64]; 187 DWORD cName = ARRAY_SIZE(wszMajorTypeName); 188 DWORD dwMinorTypes, i1; 189 190 if (RegEnumKeyExW(hkeyTypes, i, wszMajorTypeName, &cName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) continue; 191 192 if (RegOpenKeyExW(hkeyTypes, wszMajorTypeName, 0, KEY_READ, &hkeyMajorType) != ERROR_SUCCESS) continue; 193 194 if (RegQueryInfoKeyW(hkeyMajorType, NULL, NULL, NULL, &dwMinorTypes, NULL, NULL, NULL, NULL, NULL, NULL, NULL) 195 != ERROR_SUCCESS) 196 { 197 RegCloseKey(hkeyMajorType); 198 continue; 199 } 200 201 for (i1 = 0; i1 < dwMinorTypes; i1++) 202 { 203 WCHAR wszMinorTypeName[64]; 204 CLSID *clsMajorType = NULL, *clsMinorType = NULL; 205 HRESULT hr; 206 207 cName = ARRAY_SIZE(wszMinorTypeName); 208 if (RegEnumKeyExW(hkeyMajorType, i1, wszMinorTypeName, &cName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) continue; 209 210 clsMinorType = CoTaskMemAlloc(sizeof(CLSID)); 211 if (!clsMinorType) continue; 212 213 clsMajorType = CoTaskMemAlloc(sizeof(CLSID)); 214 if (!clsMajorType) goto error_cleanup_types; 215 216 hr = CLSIDFromString(wszMinorTypeName, clsMinorType); 217 if (FAILED(hr)) goto error_cleanup_types; 218 219 hr = CLSIDFromString(wszMajorTypeName, clsMajorType); 220 if (FAILED(hr)) goto error_cleanup_types; 221 222 if (rgPin->nMediaTypes == dwMediaTypeSize) 223 { 224 DWORD dwNewSize = dwMediaTypeSize + (dwMediaTypeSize < 2 ? 1 : dwMediaTypeSize / 2); 225 REGPINTYPES *lpNewMediaType; 226 227 lpNewMediaType = CoTaskMemRealloc(lpMediaType, sizeof(REGPINTYPES) * dwNewSize); 228 if (!lpNewMediaType) goto error_cleanup_types; 229 230 lpMediaType = lpNewMediaType; 231 dwMediaTypeSize = dwNewSize; 232 } 233 234 lpMediaType[rgPin->nMediaTypes].clsMajorType = clsMajorType; 235 lpMediaType[rgPin->nMediaTypes].clsMinorType = clsMinorType; 236 rgPin->nMediaTypes++; 237 continue; 238 239 error_cleanup_types: 240 241 CoTaskMemFree(clsMajorType); 242 CoTaskMemFree(clsMinorType); 243 } 244 245 RegCloseKey(hkeyMajorType); 246 } 247 248 RegCloseKey(hkeyTypes); 249 250 if (lpMediaType && !rgPin->nMediaTypes) 251 { 252 CoTaskMemFree(lpMediaType); 253 lpMediaType = NULL; 254 } 255 256 rgPin->lpMediaType = lpMediaType; 257 } 258 259 static void DEVENUM_ReadPins(HKEY hkeyFilterClass, REGFILTER2 *rgf2) 260 { 261 HKEY hkeyPins = NULL; 262 DWORD dwPinsSubkeys, i; 263 REGFILTERPINS2 *rgPins = NULL; 264 265 rgf2->dwVersion = 2; 266 rgf2->u.s2.cPins2 = 0; 267 rgf2->u.s2.rgPins2 = NULL; 268 269 if (RegOpenKeyExW(hkeyFilterClass, wszPins, 0, KEY_READ, &hkeyPins) != ERROR_SUCCESS) 270 return ; 271 272 if (RegQueryInfoKeyW(hkeyPins, NULL, NULL, NULL, &dwPinsSubkeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL) 273 != ERROR_SUCCESS) 274 { 275 RegCloseKey(hkeyPins); 276 return ; 277 } 278 279 if (dwPinsSubkeys) 280 { 281 rgPins = CoTaskMemAlloc(sizeof(REGFILTERPINS2) * dwPinsSubkeys); 282 if (!rgPins) 283 { 284 RegCloseKey(hkeyPins); 285 return ; 286 } 287 } 288 289 for (i = 0; i < dwPinsSubkeys; i++) 290 { 291 HKEY hkeyPinKey = NULL; 292 WCHAR wszPinName[MAX_PATH]; 293 DWORD cName = ARRAY_SIZE(wszPinName); 294 REGFILTERPINS2 *rgPin = &rgPins[rgf2->u.s2.cPins2]; 295 DWORD value, size, Type; 296 LONG lRet; 297 298 memset(rgPin, 0, sizeof(*rgPin)); 299 300 if (RegEnumKeyExW(hkeyPins, i, wszPinName, &cName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) continue; 301 302 if (RegOpenKeyExW(hkeyPins, wszPinName, 0, KEY_READ, &hkeyPinKey) != ERROR_SUCCESS) continue; 303 304 size = sizeof(DWORD); 305 lRet = RegQueryValueExW(hkeyPinKey, wszAllowedMany, NULL, &Type, (BYTE *)&value, &size); 306 if (lRet != ERROR_SUCCESS || Type != REG_DWORD) 307 goto error_cleanup; 308 if (value) 309 rgPin->dwFlags |= REG_PINFLAG_B_MANY; 310 311 size = sizeof(DWORD); 312 lRet = RegQueryValueExW(hkeyPinKey, wszAllowedZero, NULL, &Type, (BYTE *)&value, &size); 313 if (lRet != ERROR_SUCCESS || Type != REG_DWORD) 314 goto error_cleanup; 315 if (value) 316 rgPin->dwFlags |= REG_PINFLAG_B_ZERO; 317 318 size = sizeof(DWORD); 319 lRet = RegQueryValueExW(hkeyPinKey, wszDirection, NULL, &Type, (BYTE *)&value, &size); 320 if (lRet != ERROR_SUCCESS || Type != REG_DWORD) 321 goto error_cleanup; 322 if (value) 323 rgPin->dwFlags |= REG_PINFLAG_B_OUTPUT; 324 325 326 size = sizeof(DWORD); 327 lRet = RegQueryValueExW(hkeyPinKey, wszIsRendered, NULL, &Type, (BYTE *)&value, &size); 328 if (lRet != ERROR_SUCCESS || Type != REG_DWORD) 329 goto error_cleanup; 330 if (value) 331 rgPin->dwFlags |= REG_PINFLAG_B_RENDERER; 332 333 DEVENUM_ReadPinTypes(hkeyPinKey, rgPin); 334 335 ++rgf2->u.s2.cPins2; 336 continue; 337 338 error_cleanup: 339 340 RegCloseKey(hkeyPinKey); 341 } 342 343 RegCloseKey(hkeyPins); 344 345 if (rgPins && !rgf2->u.s2.cPins2) 346 { 347 CoTaskMemFree(rgPins); 348 rgPins = NULL; 349 } 350 351 rgf2->u.s2.rgPins2 = rgPins; 352 } 353 354 static void free_regfilter2(REGFILTER2 *rgf) 355 { 356 if (rgf->u.s2.rgPins2) 357 { 358 UINT iPin; 359 360 for (iPin = 0; iPin < rgf->u.s2.cPins2; iPin++) 361 { 362 if (rgf->u.s2.rgPins2[iPin].lpMediaType) 363 { 364 UINT iType; 365 366 for (iType = 0; iType < rgf->u.s2.rgPins2[iPin].nMediaTypes; iType++) 367 { 368 CoTaskMemFree((void *)rgf->u.s2.rgPins2[iPin].lpMediaType[iType].clsMajorType); 369 CoTaskMemFree((void *)rgf->u.s2.rgPins2[iPin].lpMediaType[iType].clsMinorType); 370 } 371 372 CoTaskMemFree((void *)rgf->u.s2.rgPins2[iPin].lpMediaType); 373 } 374 } 375 376 CoTaskMemFree((void *)rgf->u.s2.rgPins2); 377 } 378 } 379 380 static void write_filter_data(IPropertyBag *prop_bag, REGFILTER2 *rgf) 381 { 382 BYTE *data = NULL, *array; 383 IAMFilterData *fildata; 384 SAFEARRAYBOUND sabound; 385 VARIANT var; 386 ULONG size; 387 HRESULT hr; 388 389 hr = CoCreateInstance(&CLSID_FilterMapper2, NULL, CLSCTX_INPROC, &IID_IAMFilterData, (void **)&fildata); 390 if (FAILED(hr)) goto cleanup; 391 392 hr = IAMFilterData_CreateFilterData(fildata, rgf, &data, &size); 393 if (FAILED(hr)) goto cleanup; 394 395 V_VT(&var) = VT_ARRAY | VT_UI1; 396 sabound.lLbound = 0; 397 sabound.cElements = size; 398 if (!(V_ARRAY(&var) = SafeArrayCreate(VT_UI1, 1, &sabound))) 399 goto cleanup; 400 hr = SafeArrayAccessData(V_ARRAY(&var), (void *)&array); 401 if (FAILED(hr)) goto cleanup; 402 403 memcpy(array, data, size); 404 hr = SafeArrayUnaccessData(V_ARRAY(&var)); 405 if (FAILED(hr)) goto cleanup; 406 407 hr = IPropertyBag_Write(prop_bag, wszFilterData, &var); 408 if (FAILED(hr)) goto cleanup; 409 410 cleanup: 411 VariantClear(&var); 412 CoTaskMemFree(data); 413 IAMFilterData_Release(fildata); 414 } 415 416 static void register_legacy_filters(void) 417 { 418 HKEY hkeyFilter = NULL; 419 DWORD dwFilterSubkeys, i; 420 LONG lRet; 421 HRESULT hr; 422 423 lRet = RegOpenKeyExW(HKEY_CLASSES_ROOT, wszFilterKeyName, 0, KEY_READ, &hkeyFilter); 424 hr = HRESULT_FROM_WIN32(lRet); 425 426 if (SUCCEEDED(hr)) 427 { 428 lRet = RegQueryInfoKeyW(hkeyFilter, NULL, NULL, NULL, &dwFilterSubkeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL); 429 hr = HRESULT_FROM_WIN32(lRet); 430 } 431 432 if (SUCCEEDED(hr)) 433 hr = DEVENUM_CreateAMCategoryKey(&CLSID_LegacyAmFilterCategory); 434 435 if (SUCCEEDED(hr)) 436 { 437 for (i = 0; i < dwFilterSubkeys; i++) 438 { 439 WCHAR wszFilterSubkeyName[64]; 440 DWORD cName = ARRAY_SIZE(wszFilterSubkeyName); 441 IPropertyBag *prop_bag = NULL; 442 WCHAR wszRegKey[MAX_PATH]; 443 HKEY classkey = NULL; 444 REGFILTER2 rgf2; 445 DWORD Type, len; 446 GUID clsid; 447 448 if (RegEnumKeyExW(hkeyFilter, i, wszFilterSubkeyName, &cName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) continue; 449 450 TRACE("Registering %s\n", debugstr_w(wszFilterSubkeyName)); 451 452 hr = CLSIDFromString(wszFilterSubkeyName, &clsid); 453 if (FAILED(hr)) 454 continue; 455 456 lstrcpyW(wszRegKey, clsidW); 457 lstrcatW(wszRegKey, backslashW); 458 lstrcatW(wszRegKey, wszFilterSubkeyName); 459 460 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, wszRegKey, 0, KEY_READ, &classkey) != ERROR_SUCCESS) 461 continue; 462 463 len = 0; 464 if (!RegQueryValueExW(classkey, NULL, NULL, &Type, NULL, &len)) 465 { 466 WCHAR *friendlyname = heap_alloc(len); 467 if (!friendlyname) 468 { 469 RegCloseKey(classkey); 470 continue; 471 } 472 RegQueryValueExW(classkey, NULL, NULL, &Type, (BYTE *)friendlyname, &len); 473 474 hr = register_codec(&CLSID_LegacyAmFilterCategory, wszFilterSubkeyName, 475 &clsid, friendlyname, &prop_bag); 476 477 heap_free(friendlyname); 478 } 479 else 480 hr = register_codec(&CLSID_LegacyAmFilterCategory, wszFilterSubkeyName, 481 &clsid, wszFilterSubkeyName, &prop_bag); 482 if (FAILED(hr)) 483 { 484 RegCloseKey(classkey); 485 continue; 486 } 487 488 /* write filter data */ 489 rgf2.dwMerit = MERIT_NORMAL; 490 491 len = sizeof(rgf2.dwMerit); 492 RegQueryValueExW(classkey, wszMeritName, NULL, &Type, (BYTE *)&rgf2.dwMerit, &len); 493 494 DEVENUM_ReadPins(classkey, &rgf2); 495 496 write_filter_data(prop_bag, &rgf2); 497 498 IPropertyBag_Release(prop_bag); 499 RegCloseKey(classkey); 500 free_regfilter2(&rgf2); 501 } 502 } 503 504 if (hkeyFilter) RegCloseKey(hkeyFilter); 505 } 506 507 static BOOL CALLBACK register_dsound_devices(GUID *guid, const WCHAR *desc, const WCHAR *module, void *context) 508 { 509 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}; 510 static const WCHAR directsoundW[] = {'D','i','r','e','c','t','S','o','u','n','d',':',' ',0}; 511 static const WCHAR dsguidW[] = {'D','S','G','u','i','d',0}; 512 IPropertyBag *prop_bag = NULL; 513 REGFILTERPINS2 rgpins = {0}; 514 REGPINTYPES rgtypes = {0}; 515 REGFILTER2 rgf = {0}; 516 WCHAR clsid[CHARS_IN_GUID]; 517 VARIANT var; 518 HRESULT hr; 519 520 hr = DEVENUM_CreateAMCategoryKey(&CLSID_AudioRendererCategory); 521 if (FAILED(hr)) 522 return FALSE; 523 524 if (guid) 525 { 526 WCHAR *name = heap_alloc(sizeof(defaultW) + lstrlenW(desc) * sizeof(WCHAR)); 527 if (!name) 528 return FALSE; 529 lstrcpyW(name, directsoundW); 530 lstrcatW(name, desc); 531 532 hr = register_codec(&CLSID_AudioRendererCategory, name, 533 &CLSID_DSoundRender, name, &prop_bag); 534 heap_free(name); 535 } 536 else 537 hr = register_codec(&CLSID_AudioRendererCategory, defaultW, 538 &CLSID_DSoundRender, defaultW, &prop_bag); 539 if (FAILED(hr)) 540 return FALSE; 541 542 /* write filter data */ 543 rgf.dwVersion = 2; 544 rgf.dwMerit = guid ? MERIT_DO_NOT_USE : MERIT_PREFERRED; 545 rgf.u.s2.cPins2 = 1; 546 rgf.u.s2.rgPins2 = &rgpins; 547 rgpins.dwFlags = REG_PINFLAG_B_RENDERER; 548 /* FIXME: native registers many more formats */ 549 rgpins.nMediaTypes = 1; 550 rgpins.lpMediaType = &rgtypes; 551 rgtypes.clsMajorType = &MEDIATYPE_Audio; 552 rgtypes.clsMinorType = &MEDIASUBTYPE_PCM; 553 554 write_filter_data(prop_bag, &rgf); 555 556 /* write DSound guid */ 557 V_VT(&var) = VT_BSTR; 558 StringFromGUID2(guid ? guid : &GUID_NULL, clsid, CHARS_IN_GUID); 559 if ((V_BSTR(&var) = SysAllocString(clsid))) 560 hr = IPropertyBag_Write(prop_bag, dsguidW, &var); 561 562 VariantClear(&var); 563 IPropertyBag_Release(prop_bag); 564 return TRUE; 565 } 566 567 static void register_waveout_devices(void) 568 { 569 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}; 570 static const WCHAR waveoutidW[] = {'W','a','v','e','O','u','t','I','d',0}; 571 IPropertyBag *prop_bag = NULL; 572 REGFILTERPINS2 rgpins = {0}; 573 REGPINTYPES rgtypes = {0}; 574 REGFILTER2 rgf = {0}; 575 WAVEOUTCAPSW caps; 576 const WCHAR *name; 577 int i, count; 578 VARIANT var; 579 HRESULT hr; 580 581 hr = DEVENUM_CreateAMCategoryKey(&CLSID_AudioRendererCategory); 582 if (FAILED(hr)) return; 583 584 count = waveOutGetNumDevs(); 585 586 for (i = -1; i < count; i++) 587 { 588 waveOutGetDevCapsW(i, &caps, sizeof(caps)); 589 590 name = (i == -1) ? defaultW : caps.szPname; 591 592 hr = register_codec(&CLSID_AudioRendererCategory, name, 593 &CLSID_AudioRender, name, &prop_bag); 594 if (FAILED(hr)) 595 continue; 596 597 /* write filter data */ 598 rgf.dwVersion = 2; 599 rgf.dwMerit = MERIT_DO_NOT_USE; 600 rgf.u.s2.cPins2 = 1; 601 rgf.u.s2.rgPins2 = &rgpins; 602 rgpins.dwFlags = REG_PINFLAG_B_RENDERER; 603 rgpins.nMediaTypes = 1; 604 rgpins.lpMediaType = &rgtypes; 605 rgtypes.clsMajorType = &MEDIATYPE_Audio; 606 rgtypes.clsMinorType = &MEDIASUBTYPE_NULL; 607 608 write_filter_data(prop_bag, &rgf); 609 610 /* write WaveOutId */ 611 V_VT(&var) = VT_I4; 612 V_I4(&var) = i; 613 IPropertyBag_Write(prop_bag, waveoutidW, &var); 614 615 VariantClear(&var); 616 if (prop_bag) IPropertyBag_Release(prop_bag); 617 } 618 } 619 620 static void register_wavein_devices(void) 621 { 622 static const WCHAR waveinidW[] = {'W','a','v','e','I','n','I','d',0}; 623 IPropertyBag *prop_bag = NULL; 624 REGFILTER2 rgf = {0}; 625 WAVEINCAPSW caps; 626 int i, count; 627 VARIANT var; 628 HRESULT hr; 629 630 hr = DEVENUM_CreateAMCategoryKey(&CLSID_AudioRendererCategory); 631 if (FAILED(hr)) return; 632 633 count = waveInGetNumDevs(); 634 635 for (i = 0; i < count; i++) 636 { 637 waveInGetDevCapsW(i, &caps, sizeof(caps)); 638 639 hr = register_codec(&CLSID_AudioInputDeviceCategory, caps.szPname, 640 &CLSID_AudioRecord, caps.szPname, &prop_bag); 641 if (FAILED(hr)) 642 continue; 643 644 /* write filter data */ 645 rgf.dwVersion = 2; 646 rgf.dwMerit = MERIT_DO_NOT_USE; 647 648 write_filter_data(prop_bag, &rgf); 649 650 /* write WaveInId */ 651 V_VT(&var) = VT_I4; 652 V_I4(&var) = i; 653 IPropertyBag_Write(prop_bag, waveinidW, &var); 654 655 VariantClear(&var); 656 IPropertyBag_Release(prop_bag); 657 } 658 } 659 660 static void register_midiout_devices(void) 661 { 662 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}; 663 static const WCHAR midioutidW[] = {'M','i','d','i','O','u','t','I','d',0}; 664 IPropertyBag *prop_bag = NULL; 665 REGFILTERPINS2 rgpins = {0}; 666 REGPINTYPES rgtypes = {0}; 667 REGFILTER2 rgf = {0}; 668 MIDIOUTCAPSW caps; 669 const WCHAR *name; 670 int i, count; 671 VARIANT var; 672 HRESULT hr; 673 674 hr = DEVENUM_CreateAMCategoryKey(&CLSID_AudioRendererCategory); 675 if (FAILED(hr)) return; 676 677 count = midiOutGetNumDevs(); 678 679 for (i = -1; i < count; i++) 680 { 681 midiOutGetDevCapsW(i, &caps, sizeof(caps)); 682 683 name = (i == -1) ? defaultW : caps.szPname; 684 685 hr = register_codec(&CLSID_MidiRendererCategory, name, 686 &CLSID_AVIMIDIRender, name, &prop_bag); 687 if (FAILED(hr)) 688 continue; 689 690 /* write filter data */ 691 rgf.dwVersion = 2; 692 rgf.dwMerit = (i == -1) ? MERIT_PREFERRED : MERIT_DO_NOT_USE; 693 rgf.u.s2.cPins2 = 1; 694 rgf.u.s2.rgPins2 = &rgpins; 695 rgpins.dwFlags = REG_PINFLAG_B_RENDERER; 696 rgpins.nMediaTypes = 1; 697 rgpins.lpMediaType = &rgtypes; 698 rgtypes.clsMajorType = &MEDIATYPE_Midi; 699 rgtypes.clsMinorType = &MEDIASUBTYPE_NULL; 700 701 write_filter_data(prop_bag, &rgf); 702 703 /* write MidiOutId */ 704 V_VT(&var) = VT_I4; 705 V_I4(&var) = i; 706 IPropertyBag_Write(prop_bag, midioutidW, &var); 707 708 VariantClear(&var); 709 IPropertyBag_Release(prop_bag); 710 } 711 } 712 713 static void register_vfw_codecs(void) 714 { 715 static const WCHAR fcchandlerW[] = {'F','c','c','H','a','n','d','l','e','r',0}; 716 REGFILTERPINS2 rgpins[2] = {{0}}; 717 IPropertyBag *prop_bag = NULL; 718 REGPINTYPES rgtypes[2]; 719 REGFILTER2 rgf; 720 GUID typeguid; 721 ICINFO info; 722 VARIANT var; 723 HRESULT hr; 724 int i = 0; 725 HIC hic; 726 727 hr = DEVENUM_CreateAMCategoryKey(&CLSID_AudioRendererCategory); 728 if (FAILED(hr)) return; 729 730 while (ICInfo(ICTYPE_VIDEO, i++, &info)) 731 { 732 WCHAR name[5] = {LOBYTE(LOWORD(info.fccHandler)), HIBYTE(LOWORD(info.fccHandler)), 733 LOBYTE(HIWORD(info.fccHandler)), HIBYTE(HIWORD(info.fccHandler))}; 734 735 hic = ICOpen(ICTYPE_VIDEO, info.fccHandler, ICMODE_QUERY); 736 ICGetInfo(hic, &info, sizeof(info)); 737 ICClose(hic); 738 739 hr = register_codec(&CLSID_VideoCompressorCategory, name, 740 &CLSID_AVICo, info.szDescription, &prop_bag); 741 if (FAILED(hr)) 742 continue; 743 744 /* write filter data */ 745 rgf.dwVersion = 2; 746 rgf.dwMerit = MERIT_DO_NOT_USE; 747 rgf.u.s2.cPins2 = 2; 748 rgf.u.s2.rgPins2 = rgpins; 749 rgpins[0].dwFlags = 0; 750 rgpins[0].nMediaTypes = 1; 751 rgpins[0].lpMediaType = &rgtypes[0]; 752 rgtypes[0].clsMajorType = &MEDIATYPE_Video; 753 typeguid = MEDIASUBTYPE_PCM; 754 typeguid.Data1 = info.fccHandler; 755 rgtypes[0].clsMinorType = &typeguid; 756 rgpins[1].dwFlags = REG_PINFLAG_B_OUTPUT; 757 rgpins[1].nMediaTypes = 1; 758 rgpins[1].lpMediaType = &rgtypes[1]; 759 rgtypes[1].clsMajorType = &MEDIATYPE_Video; 760 rgtypes[1].clsMinorType = &GUID_NULL; 761 762 write_filter_data(prop_bag, &rgf); 763 764 /* write WaveInId */ 765 V_VT(&var) = VT_BSTR; 766 V_BSTR(&var) = SysAllocString(name); 767 IPropertyBag_Write(prop_bag, fcchandlerW, &var); 768 769 VariantClear(&var); 770 IPropertyBag_Release(prop_bag); 771 } 772 } 773 774 static void register_avicap_devices(void) 775 { 776 static const WCHAR vfwindexW[] = {'V','F','W','I','n','d','e','x',0}; 777 WCHAR name[] = {'v','i','d','e','o','0',0}; 778 WCHAR friendlyname[32], version[32]; 779 IPropertyBag *prop_bag = NULL; 780 REGFILTERPINS2 rgpins = {0}; 781 REGPINTYPES rgtypes; 782 REGFILTER2 rgf; 783 VARIANT var; 784 HRESULT hr; 785 int i = 0; 786 787 hr = DEVENUM_CreateAMCategoryKey(&CLSID_VideoInputDeviceCategory); 788 if (FAILED(hr)) 789 return; 790 791 for (i = 0; i < 10; ++i) 792 { 793 if (!capGetDriverDescriptionW(i, friendlyname, ARRAY_SIZE(friendlyname), 794 version, ARRAY_SIZE(version))) 795 continue; 796 797 name[5] = '0' + i; 798 799 hr = register_codec(&CLSID_VideoInputDeviceCategory, name, 800 &CLSID_VfwCapture, friendlyname, &prop_bag); 801 if (FAILED(hr)) 802 continue; 803 804 rgf.dwVersion = 2; 805 rgf.dwMerit = MERIT_DO_NOT_USE; 806 rgf.u.s2.cPins2 = 1; 807 rgf.u.s2.rgPins2 = &rgpins; 808 rgpins.dwFlags = 0; 809 rgpins.nMediaTypes = 1; 810 rgpins.lpMediaType = &rgtypes; 811 rgtypes.clsMajorType = &MEDIATYPE_Video; 812 rgtypes.clsMinorType = &MEDIASUBTYPE_None; 813 814 write_filter_data(prop_bag, &rgf); 815 816 /* write VFWIndex */ 817 V_VT(&var) = VT_I4; 818 V_I4(&var) = i; 819 IPropertyBag_Write(prop_bag, vfwindexW, &var); 820 821 VariantClear(&var); 822 IPropertyBag_Release(prop_bag); 823 } 824 } 825 826 /********************************************************************** 827 * DEVENUM_ICreateDevEnum_CreateClassEnumerator 828 */ 829 static HRESULT WINAPI DEVENUM_ICreateDevEnum_CreateClassEnumerator( 830 ICreateDevEnum *iface, REFCLSID class, IEnumMoniker **out, DWORD flags) 831 { 832 WCHAR guidstr[CHARS_IN_GUID]; 833 HRESULT hr; 834 HKEY key; 835 836 TRACE("iface %p, class %s, out %p, flags %#x.\n", iface, debugstr_guid(class), out, flags); 837 838 if (!out) 839 return E_POINTER; 840 841 *out = NULL; 842 843 if (!RegOpenKeyW(HKEY_CURRENT_USER, wszActiveMovieKey, &key)) 844 { 845 StringFromGUID2(class, guidstr, ARRAY_SIZE(guidstr)); 846 RegDeleteTreeW(key, guidstr); 847 } 848 849 if (IsEqualGUID(class, &CLSID_LegacyAmFilterCategory)) 850 register_legacy_filters(); 851 else if (IsEqualGUID(class, &CLSID_AudioRendererCategory)) 852 { 853 hr = DirectSoundEnumerateW(®ister_dsound_devices, NULL); 854 if (FAILED(hr)) return hr; 855 register_waveout_devices(); 856 register_midiout_devices(); 857 } 858 else if (IsEqualGUID(class, &CLSID_AudioInputDeviceCategory)) 859 register_wavein_devices(); 860 else if (IsEqualGUID(class, &CLSID_VideoCompressorCategory)) 861 register_vfw_codecs(); 862 else if (IsEqualGUID(class, &CLSID_VideoInputDeviceCategory)) 863 register_avicap_devices(); 864 865 if (SUCCEEDED(hr = create_EnumMoniker(class, out))) 866 { 867 IMoniker *mon; 868 hr = IEnumMoniker_Next(*out, 1, &mon, NULL); 869 if (hr == S_OK) 870 { 871 IMoniker_Release(mon); 872 IEnumMoniker_Reset(*out); 873 } 874 else 875 { 876 IEnumMoniker_Release(*out); 877 *out = NULL; 878 } 879 } 880 881 return hr; 882 } 883 884 /********************************************************************** 885 * ICreateDevEnum_Vtbl 886 */ 887 static const ICreateDevEnumVtbl ICreateDevEnum_Vtbl = 888 { 889 DEVENUM_ICreateDevEnum_QueryInterface, 890 DEVENUM_ICreateDevEnum_AddRef, 891 DEVENUM_ICreateDevEnum_Release, 892 DEVENUM_ICreateDevEnum_CreateClassEnumerator, 893 }; 894 895 /********************************************************************** 896 * static CreateDevEnum instance 897 */ 898 ICreateDevEnum DEVENUM_CreateDevEnum = { &ICreateDevEnum_Vtbl }; 899 900 /********************************************************************** 901 * DEVENUM_CreateAMCategoryKey (INTERNAL) 902 * 903 * Creates a registry key for a category at HKEY_CURRENT_USER\Software\ 904 * Microsoft\ActiveMovie\devenum\{clsid} 905 */ 906 static HRESULT DEVENUM_CreateAMCategoryKey(const CLSID * clsidCategory) 907 { 908 WCHAR wszRegKey[MAX_PATH]; 909 HRESULT res = S_OK; 910 HKEY hkeyDummy = NULL; 911 912 lstrcpyW(wszRegKey, wszActiveMovieKey); 913 914 if (!StringFromGUID2(clsidCategory, wszRegKey + lstrlenW(wszRegKey), ARRAY_SIZE(wszRegKey) - lstrlenW(wszRegKey))) 915 res = E_INVALIDARG; 916 917 if (SUCCEEDED(res)) 918 { 919 LONG lRes = RegCreateKeyW(HKEY_CURRENT_USER, wszRegKey, &hkeyDummy); 920 res = HRESULT_FROM_WIN32(lRes); 921 } 922 923 if (hkeyDummy) 924 RegCloseKey(hkeyDummy); 925 926 if (FAILED(res)) 927 ERR("Failed to create key HKEY_CURRENT_USER\\%s\n", debugstr_w(wszRegKey)); 928 929 return res; 930 } 931