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 33 #include "wine/debug.h" 34 #include "wine/unicode.h" 35 #include "mmddk.h" 36 37 WINE_DEFAULT_DEBUG_CHANNEL(devenum); 38 39 extern HINSTANCE DEVENUM_hInstance; 40 41 const WCHAR wszInstanceKeyName[] ={'\\','I','n','s','t','a','n','c','e',0}; 42 43 static const WCHAR wszRegSeparator[] = {'\\', 0 }; 44 static const WCHAR wszActiveMovieKey[] = {'S','o','f','t','w','a','r','e','\\', 45 'M','i','c','r','o','s','o','f','t','\\', 46 'A','c','t','i','v','e','M','o','v','i','e','\\', 47 'd','e','v','e','n','u','m','\\',0}; 48 static const WCHAR wszFilterKeyName[] = {'F','i','l','t','e','r',0}; 49 static const WCHAR wszMeritName[] = {'M','e','r','i','t',0}; 50 static const WCHAR wszPins[] = {'P','i','n','s',0}; 51 static const WCHAR wszAllowedMany[] = {'A','l','l','o','w','e','d','M','a','n','y',0}; 52 static const WCHAR wszAllowedZero[] = {'A','l','l','o','w','e','d','Z','e','r','o',0}; 53 static const WCHAR wszDirection[] = {'D','i','r','e','c','t','i','o','n',0}; 54 static const WCHAR wszIsRendered[] = {'I','s','R','e','n','d','e','r','e','d',0}; 55 static const WCHAR wszTypes[] = {'T','y','p','e','s',0}; 56 static const WCHAR wszFriendlyName[] = {'F','r','i','e','n','d','l','y','N','a','m','e',0}; 57 static const WCHAR wszWaveInID[] = {'W','a','v','e','I','n','I','D',0}; 58 static const WCHAR wszWaveOutID[] = {'W','a','v','e','O','u','t','I','D',0}; 59 60 static ULONG WINAPI DEVENUM_ICreateDevEnum_AddRef(ICreateDevEnum * iface); 61 static HRESULT DEVENUM_CreateSpecialCategories(void); 62 63 /********************************************************************** 64 * DEVENUM_ICreateDevEnum_QueryInterface (also IUnknown) 65 */ 66 static HRESULT WINAPI DEVENUM_ICreateDevEnum_QueryInterface(ICreateDevEnum *iface, REFIID riid, 67 void **ppv) 68 { 69 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv); 70 71 if (!ppv) 72 return E_POINTER; 73 74 if (IsEqualGUID(riid, &IID_IUnknown) || 75 IsEqualGUID(riid, &IID_ICreateDevEnum)) 76 { 77 *ppv = iface; 78 DEVENUM_ICreateDevEnum_AddRef(iface); 79 return S_OK; 80 } 81 82 FIXME("- no interface IID: %s\n", debugstr_guid(riid)); 83 *ppv = NULL; 84 return E_NOINTERFACE; 85 } 86 87 /********************************************************************** 88 * DEVENUM_ICreateDevEnum_AddRef (also IUnknown) 89 */ 90 static ULONG WINAPI DEVENUM_ICreateDevEnum_AddRef(ICreateDevEnum * iface) 91 { 92 TRACE("\n"); 93 94 DEVENUM_LockModule(); 95 96 return 2; /* non-heap based object */ 97 } 98 99 /********************************************************************** 100 * DEVENUM_ICreateDevEnum_Release (also IUnknown) 101 */ 102 static ULONG WINAPI DEVENUM_ICreateDevEnum_Release(ICreateDevEnum * iface) 103 { 104 TRACE("\n"); 105 106 DEVENUM_UnlockModule(); 107 108 return 1; /* non-heap based object */ 109 } 110 111 static BOOL IsSpecialCategory(const CLSID *clsid) 112 { 113 return IsEqualGUID(clsid, &CLSID_AudioRendererCategory) || 114 IsEqualGUID(clsid, &CLSID_AudioInputDeviceCategory) || 115 IsEqualGUID(clsid, &CLSID_VideoInputDeviceCategory) || 116 IsEqualGUID(clsid, &CLSID_VideoCompressorCategory) || 117 IsEqualGUID(clsid, &CLSID_AudioCompressorCategory) || 118 IsEqualGUID(clsid, &CLSID_MidiRendererCategory); 119 } 120 121 HRESULT DEVENUM_GetCategoryKey(REFCLSID clsidDeviceClass, HKEY *pBaseKey, WCHAR *wszRegKeyName, UINT maxLen) 122 { 123 if (IsSpecialCategory(clsidDeviceClass)) 124 { 125 *pBaseKey = HKEY_CURRENT_USER; 126 strcpyW(wszRegKeyName, wszActiveMovieKey); 127 128 if (!StringFromGUID2(clsidDeviceClass, wszRegKeyName + strlenW(wszRegKeyName), maxLen - strlenW(wszRegKeyName))) 129 return E_OUTOFMEMORY; 130 } 131 else 132 { 133 *pBaseKey = HKEY_CLASSES_ROOT; 134 strcpyW(wszRegKeyName, clsid_keyname); 135 strcatW(wszRegKeyName, wszRegSeparator); 136 137 if (!StringFromGUID2(clsidDeviceClass, wszRegKeyName + CLSID_STR_LEN, maxLen - CLSID_STR_LEN)) 138 return E_OUTOFMEMORY; 139 140 strcatW(wszRegKeyName, wszInstanceKeyName); 141 } 142 143 return S_OK; 144 } 145 146 static HKEY open_category_key(const CLSID *clsid) 147 { 148 WCHAR key_name[sizeof(wszInstanceKeyName)/sizeof(WCHAR) + CHARS_IN_GUID-1 + 6 /* strlen("CLSID\") */], *ptr; 149 HKEY ret; 150 151 strcpyW(key_name, clsid_keyname); 152 ptr = key_name + strlenW(key_name); 153 *ptr++ = '\\'; 154 155 if (!StringFromGUID2(clsid, ptr, CHARS_IN_GUID)) 156 return NULL; 157 158 ptr += strlenW(ptr); 159 strcpyW(ptr, wszInstanceKeyName); 160 161 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, key_name, 0, KEY_READ, &ret) != ERROR_SUCCESS) { 162 WARN("Could not open %s\n", debugstr_w(key_name)); 163 return NULL; 164 } 165 166 return ret; 167 } 168 169 static HKEY open_special_category_key(const CLSID *clsid, BOOL create) 170 { 171 WCHAR key_name[sizeof(wszActiveMovieKey)/sizeof(WCHAR) + CHARS_IN_GUID-1]; 172 HKEY ret; 173 LONG res; 174 175 strcpyW(key_name, wszActiveMovieKey); 176 if (!StringFromGUID2(clsid, key_name + sizeof(wszActiveMovieKey)/sizeof(WCHAR)-1, CHARS_IN_GUID)) 177 return NULL; 178 179 if(create) 180 res = RegCreateKeyW(HKEY_CURRENT_USER, key_name, &ret); 181 else 182 res = RegOpenKeyExW(HKEY_CURRENT_USER, key_name, 0, KEY_READ, &ret); 183 if (res != ERROR_SUCCESS) { 184 WARN("Could not open %s\n", debugstr_w(key_name)); 185 return NULL; 186 } 187 188 return ret; 189 } 190 191 static void DEVENUM_ReadPinTypes(HKEY hkeyPinKey, REGFILTERPINS *rgPin) 192 { 193 HKEY hkeyTypes = NULL; 194 DWORD dwMajorTypes, i; 195 REGPINTYPES *lpMediaType = NULL; 196 DWORD dwMediaTypeSize = 0; 197 198 if (RegOpenKeyExW(hkeyPinKey, wszTypes, 0, KEY_READ, &hkeyTypes) != ERROR_SUCCESS) 199 return ; 200 201 if (RegQueryInfoKeyW(hkeyTypes, NULL, NULL, NULL, &dwMajorTypes, NULL, NULL, NULL, NULL, NULL, NULL, NULL) 202 != ERROR_SUCCESS) 203 { 204 RegCloseKey(hkeyTypes); 205 return ; 206 } 207 208 for (i = 0; i < dwMajorTypes; i++) 209 { 210 HKEY hkeyMajorType = NULL; 211 WCHAR wszMajorTypeName[64]; 212 DWORD cName = sizeof(wszMajorTypeName) / sizeof(WCHAR); 213 DWORD dwMinorTypes, i1; 214 215 if (RegEnumKeyExW(hkeyTypes, i, wszMajorTypeName, &cName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) continue; 216 217 if (RegOpenKeyExW(hkeyTypes, wszMajorTypeName, 0, KEY_READ, &hkeyMajorType) != ERROR_SUCCESS) continue; 218 219 if (RegQueryInfoKeyW(hkeyMajorType, NULL, NULL, NULL, &dwMinorTypes, NULL, NULL, NULL, NULL, NULL, NULL, NULL) 220 != ERROR_SUCCESS) 221 { 222 RegCloseKey(hkeyMajorType); 223 continue; 224 } 225 226 for (i1 = 0; i1 < dwMinorTypes; i1++) 227 { 228 WCHAR wszMinorTypeName[64]; 229 CLSID *clsMajorType = NULL, *clsMinorType = NULL; 230 HRESULT hr; 231 232 cName = sizeof(wszMinorTypeName) / sizeof(WCHAR); 233 if (RegEnumKeyExW(hkeyMajorType, i1, wszMinorTypeName, &cName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) continue; 234 235 clsMinorType = CoTaskMemAlloc(sizeof(CLSID)); 236 if (!clsMinorType) continue; 237 238 clsMajorType = CoTaskMemAlloc(sizeof(CLSID)); 239 if (!clsMajorType) goto error_cleanup_types; 240 241 hr = CLSIDFromString(wszMinorTypeName, clsMinorType); 242 if (FAILED(hr)) goto error_cleanup_types; 243 244 hr = CLSIDFromString(wszMajorTypeName, clsMajorType); 245 if (FAILED(hr)) goto error_cleanup_types; 246 247 if (rgPin->nMediaTypes == dwMediaTypeSize) 248 { 249 DWORD dwNewSize = dwMediaTypeSize + (dwMediaTypeSize < 2 ? 1 : dwMediaTypeSize / 2); 250 REGPINTYPES *lpNewMediaType; 251 252 lpNewMediaType = CoTaskMemRealloc(lpMediaType, sizeof(REGPINTYPES) * dwNewSize); 253 if (!lpNewMediaType) goto error_cleanup_types; 254 255 lpMediaType = lpNewMediaType; 256 dwMediaTypeSize = dwNewSize; 257 } 258 259 lpMediaType[rgPin->nMediaTypes].clsMajorType = clsMajorType; 260 lpMediaType[rgPin->nMediaTypes].clsMinorType = clsMinorType; 261 rgPin->nMediaTypes++; 262 continue; 263 264 error_cleanup_types: 265 266 CoTaskMemFree(clsMajorType); 267 CoTaskMemFree(clsMinorType); 268 } 269 270 RegCloseKey(hkeyMajorType); 271 } 272 273 RegCloseKey(hkeyTypes); 274 275 if (lpMediaType && !rgPin->nMediaTypes) 276 { 277 CoTaskMemFree(lpMediaType); 278 lpMediaType = NULL; 279 } 280 281 rgPin->lpMediaType = lpMediaType; 282 } 283 284 static void DEVENUM_ReadPins(HKEY hkeyFilterClass, REGFILTER2 *rgf2) 285 { 286 HKEY hkeyPins = NULL; 287 DWORD dwPinsSubkeys, i; 288 REGFILTERPINS *rgPins = NULL; 289 290 if (RegOpenKeyExW(hkeyFilterClass, wszPins, 0, KEY_READ, &hkeyPins) != ERROR_SUCCESS) 291 return ; 292 293 if (RegQueryInfoKeyW(hkeyPins, NULL, NULL, NULL, &dwPinsSubkeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL) 294 != ERROR_SUCCESS) 295 { 296 RegCloseKey(hkeyPins); 297 return ; 298 } 299 300 if (dwPinsSubkeys) 301 { 302 rgPins = CoTaskMemAlloc(sizeof(REGFILTERPINS) * dwPinsSubkeys); 303 if (!rgPins) 304 { 305 RegCloseKey(hkeyPins); 306 return ; 307 } 308 } 309 310 for (i = 0; i < dwPinsSubkeys; i++) 311 { 312 HKEY hkeyPinKey = NULL; 313 WCHAR wszPinName[MAX_PATH]; 314 DWORD cName = sizeof(wszPinName) / sizeof(WCHAR); 315 DWORD Type, cbData; 316 REGFILTERPINS *rgPin = &rgPins[rgf2->u.s1.cPins]; 317 LONG lRet; 318 319 rgPin->strName = NULL; 320 rgPin->clsConnectsToFilter = &GUID_NULL; 321 rgPin->strConnectsToPin = NULL; 322 rgPin->nMediaTypes = 0; 323 rgPin->lpMediaType = NULL; 324 325 if (RegEnumKeyExW(hkeyPins, i, wszPinName, &cName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) continue; 326 327 if (RegOpenKeyExW(hkeyPins, wszPinName, 0, KEY_READ, &hkeyPinKey) != ERROR_SUCCESS) continue; 328 329 rgPin->strName = CoTaskMemAlloc((strlenW(wszPinName) + 1) * sizeof(WCHAR)); 330 if (!rgPin->strName) goto error_cleanup; 331 332 strcpyW(rgPin->strName, wszPinName); 333 334 cbData = sizeof(rgPin->bMany); 335 lRet = RegQueryValueExW(hkeyPinKey, wszAllowedMany, NULL, &Type, (LPBYTE)&rgPin->bMany, &cbData); 336 if (lRet != ERROR_SUCCESS || Type != REG_DWORD) 337 goto error_cleanup; 338 339 cbData = sizeof(rgPin->bZero); 340 lRet = RegQueryValueExW(hkeyPinKey, wszAllowedZero, NULL, &Type, (LPBYTE)&rgPin->bZero, &cbData); 341 if (lRet != ERROR_SUCCESS || Type != REG_DWORD) 342 goto error_cleanup; 343 344 cbData = sizeof(rgPin->bOutput); 345 lRet = RegQueryValueExW(hkeyPinKey, wszDirection, NULL, &Type, (LPBYTE)&rgPin->bOutput, &cbData); 346 if (lRet != ERROR_SUCCESS || Type != REG_DWORD) 347 goto error_cleanup; 348 349 cbData = sizeof(rgPin->bRendered); 350 lRet = RegQueryValueExW(hkeyPinKey, wszIsRendered, NULL, &Type, (LPBYTE)&rgPin->bRendered, &cbData); 351 if (lRet != ERROR_SUCCESS || Type != REG_DWORD) 352 goto error_cleanup; 353 354 DEVENUM_ReadPinTypes(hkeyPinKey, rgPin); 355 356 ++rgf2->u.s1.cPins; 357 continue; 358 359 error_cleanup: 360 361 RegCloseKey(hkeyPinKey); 362 CoTaskMemFree(rgPin->strName); 363 } 364 365 RegCloseKey(hkeyPins); 366 367 if (rgPins && !rgf2->u.s1.cPins) 368 { 369 CoTaskMemFree(rgPins); 370 rgPins = NULL; 371 } 372 373 rgf2->u.s1.rgPins = rgPins; 374 } 375 376 static HRESULT DEVENUM_RegisterLegacyAmFilters(void) 377 { 378 HKEY hkeyFilter = NULL; 379 DWORD dwFilterSubkeys, i; 380 LONG lRet; 381 IFilterMapper2 *pMapper = NULL; 382 HRESULT hr; 383 384 hr = CoCreateInstance(&CLSID_FilterMapper2, NULL, CLSCTX_INPROC, 385 &IID_IFilterMapper2, (void **) &pMapper); 386 if (SUCCEEDED(hr)) 387 { 388 lRet = RegOpenKeyExW(HKEY_CLASSES_ROOT, wszFilterKeyName, 0, KEY_READ, &hkeyFilter); 389 hr = HRESULT_FROM_WIN32(lRet); 390 } 391 392 if (SUCCEEDED(hr)) 393 { 394 lRet = RegQueryInfoKeyW(hkeyFilter, NULL, NULL, NULL, &dwFilterSubkeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL); 395 hr = HRESULT_FROM_WIN32(lRet); 396 } 397 398 if (SUCCEEDED(hr)) 399 { 400 for (i = 0; i < dwFilterSubkeys; i++) 401 { 402 WCHAR wszFilterSubkeyName[64]; 403 DWORD cName = sizeof(wszFilterSubkeyName) / sizeof(WCHAR); 404 HKEY hkeyCategoryBaseKey; 405 WCHAR wszRegKey[MAX_PATH]; 406 HKEY hkeyInstance = NULL; 407 408 if (RegEnumKeyExW(hkeyFilter, i, wszFilterSubkeyName, &cName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) continue; 409 410 hr = DEVENUM_GetCategoryKey(&CLSID_LegacyAmFilterCategory, &hkeyCategoryBaseKey, wszRegKey, MAX_PATH); 411 if (FAILED(hr)) continue; 412 413 strcatW(wszRegKey, wszRegSeparator); 414 strcatW(wszRegKey, wszFilterSubkeyName); 415 416 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, wszRegKey, 0, KEY_READ, &hkeyInstance) == ERROR_SUCCESS) 417 { 418 RegCloseKey(hkeyInstance); 419 } 420 else 421 { 422 /* Filter is registered the IFilterMapper(1)-way in HKCR\Filter. Needs to be added to 423 * legacy am filter category. */ 424 HKEY hkeyFilterClass = NULL; 425 REGFILTER2 rgf2; 426 CLSID clsidFilter; 427 WCHAR wszFilterName[MAX_PATH]; 428 DWORD Type; 429 DWORD cbData; 430 HRESULT res; 431 IMoniker *pMoniker = NULL; 432 433 TRACE("Registering %s\n", debugstr_w(wszFilterSubkeyName)); 434 435 strcpyW(wszRegKey, clsid_keyname); 436 strcatW(wszRegKey, wszRegSeparator); 437 strcatW(wszRegKey, wszFilterSubkeyName); 438 439 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, wszRegKey, 0, KEY_READ, &hkeyFilterClass) != ERROR_SUCCESS) 440 continue; 441 442 rgf2.dwVersion = 1; 443 rgf2.dwMerit = 0; 444 rgf2.u.s1.cPins = 0; 445 rgf2.u.s1.rgPins = NULL; 446 447 cbData = sizeof(wszFilterName); 448 if (RegQueryValueExW(hkeyFilterClass, NULL, NULL, &Type, (LPBYTE)wszFilterName, &cbData) != ERROR_SUCCESS || 449 Type != REG_SZ) 450 goto cleanup; 451 452 cbData = sizeof(rgf2.dwMerit); 453 if (RegQueryValueExW(hkeyFilterClass, wszMeritName, NULL, &Type, (LPBYTE)&rgf2.dwMerit, &cbData) != ERROR_SUCCESS || 454 Type != REG_DWORD) 455 goto cleanup; 456 457 DEVENUM_ReadPins(hkeyFilterClass, &rgf2); 458 459 res = CLSIDFromString(wszFilterSubkeyName, &clsidFilter); 460 if (FAILED(res)) goto cleanup; 461 462 IFilterMapper2_RegisterFilter(pMapper, &clsidFilter, wszFilterName, &pMoniker, NULL, NULL, &rgf2); 463 464 if (pMoniker) 465 IMoniker_Release(pMoniker); 466 467 cleanup: 468 469 if (hkeyFilterClass) RegCloseKey(hkeyFilterClass); 470 471 if (rgf2.u.s1.rgPins) 472 { 473 UINT iPin; 474 475 for (iPin = 0; iPin < rgf2.u.s1.cPins; iPin++) 476 { 477 CoTaskMemFree(rgf2.u.s1.rgPins[iPin].strName); 478 479 if (rgf2.u.s1.rgPins[iPin].lpMediaType) 480 { 481 UINT iType; 482 483 for (iType = 0; iType < rgf2.u.s1.rgPins[iPin].nMediaTypes; iType++) 484 { 485 CoTaskMemFree((void*)rgf2.u.s1.rgPins[iPin].lpMediaType[iType].clsMajorType); 486 CoTaskMemFree((void*)rgf2.u.s1.rgPins[iPin].lpMediaType[iType].clsMinorType); 487 } 488 489 CoTaskMemFree((void*)rgf2.u.s1.rgPins[iPin].lpMediaType); 490 } 491 } 492 493 CoTaskMemFree((void*)rgf2.u.s1.rgPins); 494 } 495 } 496 } 497 } 498 499 if (hkeyFilter) RegCloseKey(hkeyFilter); 500 501 if (pMapper) 502 IFilterMapper2_Release(pMapper); 503 504 return S_OK; 505 } 506 507 /********************************************************************** 508 * DEVENUM_ICreateDevEnum_CreateClassEnumerator 509 */ 510 static HRESULT WINAPI DEVENUM_ICreateDevEnum_CreateClassEnumerator( 511 ICreateDevEnum * iface, 512 REFCLSID clsidDeviceClass, 513 IEnumMoniker **ppEnumMoniker, 514 DWORD dwFlags) 515 { 516 HKEY hkey, special_hkey = NULL; 517 HRESULT hr; 518 519 TRACE("(%p)->(%s, %p, %x)\n", iface, debugstr_guid(clsidDeviceClass), ppEnumMoniker, dwFlags); 520 521 if (!ppEnumMoniker) 522 return E_POINTER; 523 524 *ppEnumMoniker = NULL; 525 526 if (IsEqualGUID(clsidDeviceClass, &CLSID_LegacyAmFilterCategory)) 527 { 528 DEVENUM_RegisterLegacyAmFilters(); 529 } 530 531 if (IsSpecialCategory(clsidDeviceClass)) 532 { 533 hr = DEVENUM_CreateSpecialCategories(); 534 if (FAILED(hr)) 535 return hr; 536 537 special_hkey = open_special_category_key(clsidDeviceClass, FALSE); 538 if (!special_hkey) 539 { 540 ERR("Couldn't open registry key for special device: %s\n", 541 debugstr_guid(clsidDeviceClass)); 542 return S_FALSE; 543 } 544 } 545 546 hkey = open_category_key(clsidDeviceClass); 547 if (!hkey && !special_hkey) 548 { 549 FIXME("Category %s not found\n", debugstr_guid(clsidDeviceClass)); 550 return S_FALSE; 551 } 552 553 return DEVENUM_IEnumMoniker_Construct(hkey, special_hkey, ppEnumMoniker); 554 } 555 556 /********************************************************************** 557 * ICreateDevEnum_Vtbl 558 */ 559 static const ICreateDevEnumVtbl ICreateDevEnum_Vtbl = 560 { 561 DEVENUM_ICreateDevEnum_QueryInterface, 562 DEVENUM_ICreateDevEnum_AddRef, 563 DEVENUM_ICreateDevEnum_Release, 564 DEVENUM_ICreateDevEnum_CreateClassEnumerator, 565 }; 566 567 /********************************************************************** 568 * static CreateDevEnum instance 569 */ 570 ICreateDevEnum DEVENUM_CreateDevEnum = { &ICreateDevEnum_Vtbl }; 571 572 /********************************************************************** 573 * DEVENUM_CreateAMCategoryKey (INTERNAL) 574 * 575 * Creates a registry key for a category at HKEY_CURRENT_USER\Software\ 576 * Microsoft\ActiveMovie\devenum\{clsid} 577 */ 578 static HRESULT DEVENUM_CreateAMCategoryKey(const CLSID * clsidCategory) 579 { 580 WCHAR wszRegKey[MAX_PATH]; 581 HRESULT res = S_OK; 582 HKEY hkeyDummy = NULL; 583 584 strcpyW(wszRegKey, wszActiveMovieKey); 585 586 if (!StringFromGUID2(clsidCategory, wszRegKey + strlenW(wszRegKey), sizeof(wszRegKey)/sizeof(wszRegKey[0]) - strlenW(wszRegKey))) 587 res = E_INVALIDARG; 588 589 if (SUCCEEDED(res)) 590 { 591 LONG lRes = RegCreateKeyW(HKEY_CURRENT_USER, wszRegKey, &hkeyDummy); 592 res = HRESULT_FROM_WIN32(lRes); 593 } 594 595 if (hkeyDummy) 596 RegCloseKey(hkeyDummy); 597 598 if (FAILED(res)) 599 ERR("Failed to create key HKEY_CURRENT_USER\\%s\n", debugstr_w(wszRegKey)); 600 601 return res; 602 } 603 604 static void register_vfw_codecs(void) 605 { 606 WCHAR avico_clsid_str[CHARS_IN_GUID]; 607 HKEY basekey, key; 608 ICINFO icinfo; 609 DWORD i, res; 610 611 static const WCHAR CLSIDW[] = {'C','L','S','I','D',0}; 612 static const WCHAR FccHandlerW[] = {'F','c','c','H','a','n','d','l','e','r',0}; 613 static const WCHAR FriendlyNameW[] = {'F','r','i','e','n','d','l','y','N','a','m','e',0}; 614 615 StringFromGUID2(&CLSID_AVICo, avico_clsid_str, sizeof(avico_clsid_str)/sizeof(WCHAR)); 616 617 basekey = open_special_category_key(&CLSID_VideoCompressorCategory, TRUE); 618 if(!basekey) { 619 ERR("Could not create key\n"); 620 return; 621 } 622 623 for(i=0; ICInfo(FCC('v','i','d','c'), i, &icinfo); i++) { 624 WCHAR fcc_str[5] = {LOBYTE(LOWORD(icinfo.fccHandler)), HIBYTE(LOWORD(icinfo.fccHandler)), 625 LOBYTE(HIWORD(icinfo.fccHandler)), HIBYTE(HIWORD(icinfo.fccHandler))}; 626 627 res = RegCreateKeyW(basekey, fcc_str, &key); 628 if(res != ERROR_SUCCESS) 629 continue; 630 631 RegSetValueExW(key, CLSIDW, 0, REG_SZ, (const BYTE*)avico_clsid_str, sizeof(avico_clsid_str)); 632 RegSetValueExW(key, FccHandlerW, 0, REG_SZ, (const BYTE*)fcc_str, sizeof(fcc_str)); 633 RegSetValueExW(key, FriendlyNameW, 0, REG_SZ, (const BYTE*)icinfo.szName, (strlenW(icinfo.szName)+1)*sizeof(WCHAR)); 634 /* FIXME: Set ClassManagerFlags and FilterData values */ 635 636 RegCloseKey(key); 637 } 638 639 RegCloseKey(basekey); 640 } 641 642 static BOOL WINAPI acm_driver_callback(HACMDRIVERID hadid, DWORD_PTR user, DWORD support) 643 { 644 static const WCHAR CLSIDW[] = {'C','L','S','I','D',0}; 645 static const WCHAR AcmIdW[] = {'A','c','m','I','d',0}; 646 static const WCHAR FriendlyNameW[] = {'F','r','i','e','n','d','l','y','N','a','m','e',0}; 647 static const WCHAR fmtW[] = {'%','u','%','s',0}; 648 649 WCHAR acmwrapper_clsid_str[CHARS_IN_GUID], buffer[MAX_PATH]; 650 HKEY key, basekey = (HKEY) user; 651 ACMFORMATTAGDETAILSW format; 652 ACMDRIVERDETAILSW driver; 653 HACMDRIVER had; 654 DWORD i, res; 655 656 StringFromGUID2(&CLSID_ACMWrapper, acmwrapper_clsid_str, sizeof(acmwrapper_clsid_str)/sizeof(WCHAR)); 657 658 driver.cbStruct = sizeof(driver); 659 if (acmDriverDetailsW(hadid, &driver, 0) != MMSYSERR_NOERROR) 660 return TRUE; 661 662 if (acmDriverOpen(&had, hadid, 0) != MMSYSERR_NOERROR) 663 return TRUE; 664 665 for (i = 0; i < driver.cFormatTags; i++) 666 { 667 memset(&format, 0, sizeof(format)); 668 format.cbStruct = sizeof(format); 669 format.dwFormatTagIndex = i; 670 671 if (acmFormatTagDetailsW(had, &format, ACM_FORMATTAGDETAILSF_INDEX) != MMSYSERR_NOERROR) 672 continue; 673 674 snprintfW(buffer, sizeof(buffer)/sizeof(WCHAR), fmtW, format.dwFormatTag, format.szFormatTag); 675 676 res = RegCreateKeyW(basekey, buffer, &key); 677 if (res != ERROR_SUCCESS) continue; 678 679 RegSetValueExW(key, CLSIDW, 0, REG_SZ, (BYTE*)acmwrapper_clsid_str, sizeof(acmwrapper_clsid_str)); 680 RegSetValueExW(key, AcmIdW, 0, REG_DWORD, (BYTE*)&format.dwFormatTag, sizeof(DWORD)); 681 RegSetValueExW(key, FriendlyNameW, 0, REG_SZ, (BYTE*)format.szFormatTag, (strlenW(format.szFormatTag)+1)*sizeof(WCHAR)); 682 /* FIXME: Set FilterData values */ 683 684 RegCloseKey(key); 685 } 686 687 acmDriverClose(had, 0); 688 689 return TRUE; 690 } 691 692 static void register_acm_codecs(void) 693 { 694 HKEY basekey; 695 696 basekey = open_special_category_key(&CLSID_AudioCompressorCategory, TRUE); 697 if (!basekey) 698 { 699 ERR("Could not create key\n"); 700 return; 701 } 702 703 acmDriverEnum(acm_driver_callback, (DWORD_PTR)basekey, 0); 704 705 RegCloseKey(basekey); 706 } 707 708 static HANDLE DEVENUM_populate_handle; 709 static const WCHAR DEVENUM_populate_handle_nameW[] = 710 {'_','_','W','I','N','E','_', 711 'D','e','v','e','n','u','m','_', 712 'P','o','p','u','l','a','t','e',0}; 713 714 /********************************************************************** 715 * DEVENUM_CreateSpecialCategories (INTERNAL) 716 * 717 * Creates the keys in the registry for the dynamic categories 718 */ 719 static HRESULT DEVENUM_CreateSpecialCategories(void) 720 { 721 HRESULT res; 722 WCHAR szDSoundNameFormat[MAX_PATH + 1]; 723 WCHAR szDSoundName[MAX_PATH + 1]; 724 DWORD iDefaultDevice = -1; 725 UINT numDevs; 726 IFilterMapper2 * pMapper = NULL; 727 REGFILTER2 rf2; 728 REGFILTERPINS2 rfp2; 729 WCHAR path[MAX_PATH]; 730 HKEY basekey; 731 732 if (DEVENUM_populate_handle) 733 return S_OK; 734 DEVENUM_populate_handle = CreateEventW(NULL, TRUE, FALSE, DEVENUM_populate_handle_nameW); 735 if (GetLastError() == ERROR_ALREADY_EXISTS) 736 { 737 /* Webcams can take some time to scan if the driver is badly written and it enables them, 738 * so have a 10 s timeout here 739 */ 740 if (WaitForSingleObject(DEVENUM_populate_handle, 10000) == WAIT_TIMEOUT) 741 WARN("Waiting for object timed out\n"); 742 TRACE("No need to rescan\n"); 743 return S_OK; 744 } 745 TRACE("Scanning for devices\n"); 746 747 /* Since devices can change between session, for example because you just plugged in a webcam 748 * or switched from pulseaudio to alsa, delete all old devices first 749 */ 750 if (SUCCEEDED(DEVENUM_GetCategoryKey(&CLSID_AudioRendererCategory, &basekey, path, MAX_PATH))) 751 RegDeleteTreeW(basekey, path); 752 if (SUCCEEDED(DEVENUM_GetCategoryKey(&CLSID_AudioInputDeviceCategory, &basekey, path, MAX_PATH))) 753 RegDeleteTreeW(basekey, path); 754 if (SUCCEEDED(DEVENUM_GetCategoryKey(&CLSID_VideoInputDeviceCategory, &basekey, path, MAX_PATH))) 755 RegDeleteTreeW(basekey, path); 756 if (SUCCEEDED(DEVENUM_GetCategoryKey(&CLSID_MidiRendererCategory, &basekey, path, MAX_PATH))) 757 RegDeleteTreeW(basekey, path); 758 if (SUCCEEDED(DEVENUM_GetCategoryKey(&CLSID_VideoCompressorCategory, &basekey, path, MAX_PATH))) 759 RegDeleteTreeW(basekey, path); 760 if (SUCCEEDED(DEVENUM_GetCategoryKey(&CLSID_AudioCompressorCategory, &basekey, path, MAX_PATH))) 761 RegDeleteTreeW(basekey, path); 762 763 rf2.dwVersion = 2; 764 rf2.dwMerit = MERIT_PREFERRED; 765 rf2.u.s2.cPins2 = 1; 766 rf2.u.s2.rgPins2 = &rfp2; 767 rfp2.cInstances = 1; 768 rfp2.nMediums = 0; 769 rfp2.lpMedium = NULL; 770 rfp2.clsPinCategory = &IID_NULL; 771 772 if (!LoadStringW(DEVENUM_hInstance, IDS_DEVENUM_DS, szDSoundNameFormat, sizeof(szDSoundNameFormat)/sizeof(szDSoundNameFormat[0])-1)) 773 { 774 ERR("Couldn't get string resource (GetLastError() is %d)\n", GetLastError()); 775 return HRESULT_FROM_WIN32(GetLastError()); 776 } 777 778 res = CoCreateInstance(&CLSID_FilterMapper2, NULL, CLSCTX_INPROC, 779 &IID_IFilterMapper2, (void **) &pMapper); 780 /* 781 * Fill in info for devices 782 */ 783 if (SUCCEEDED(res)) 784 { 785 UINT i; 786 WAVEOUTCAPSW wocaps; 787 WAVEINCAPSW wicaps; 788 MIDIOUTCAPSW mocaps; 789 REGPINTYPES * pTypes; 790 IPropertyBag * pPropBag = NULL; 791 792 numDevs = waveOutGetNumDevs(); 793 794 res = DEVENUM_CreateAMCategoryKey(&CLSID_AudioRendererCategory); 795 if (FAILED(res)) /* can't register any devices in this category */ 796 numDevs = 0; 797 798 rfp2.dwFlags = REG_PINFLAG_B_RENDERER; 799 for (i = 0; i < numDevs; i++) 800 { 801 if (waveOutGetDevCapsW(i, &wocaps, sizeof(WAVEOUTCAPSW)) 802 == MMSYSERR_NOERROR) 803 { 804 IMoniker * pMoniker = NULL; 805 806 rfp2.nMediaTypes = 1; 807 pTypes = CoTaskMemAlloc(rfp2.nMediaTypes * sizeof(REGPINTYPES)); 808 if (!pTypes) 809 { 810 IFilterMapper2_Release(pMapper); 811 return E_OUTOFMEMORY; 812 } 813 /* FIXME: Native devenum seems to register a lot more types for 814 * DSound than we do. Not sure what purpose they serve */ 815 pTypes[0].clsMajorType = &MEDIATYPE_Audio; 816 pTypes[0].clsMinorType = &MEDIASUBTYPE_PCM; 817 818 rfp2.lpMediaType = pTypes; 819 820 res = IFilterMapper2_RegisterFilter(pMapper, 821 &CLSID_AudioRender, 822 wocaps.szPname, 823 &pMoniker, 824 &CLSID_AudioRendererCategory, 825 wocaps.szPname, 826 &rf2); 827 828 if (pMoniker) 829 { 830 VARIANT var; 831 832 V_VT(&var) = VT_I4; 833 V_I4(&var) = i; 834 res = IMoniker_BindToStorage(pMoniker, NULL, NULL, &IID_IPropertyBag, (LPVOID)&pPropBag); 835 if (SUCCEEDED(res)) 836 res = IPropertyBag_Write(pPropBag, wszWaveOutID, &var); 837 else 838 pPropBag = NULL; 839 840 V_VT(&var) = VT_LPWSTR; 841 V_BSTR(&var) = wocaps.szPname; 842 if (SUCCEEDED(res)) 843 res = IPropertyBag_Write(pPropBag, wszFriendlyName, &var); 844 if (pPropBag) 845 IPropertyBag_Release(pPropBag); 846 IMoniker_Release(pMoniker); 847 pMoniker = NULL; 848 } 849 850 wsprintfW(szDSoundName, szDSoundNameFormat, wocaps.szPname); 851 res = IFilterMapper2_RegisterFilter(pMapper, 852 &CLSID_DSoundRender, 853 szDSoundName, 854 &pMoniker, 855 &CLSID_AudioRendererCategory, 856 szDSoundName, 857 &rf2); 858 859 /* FIXME: do additional stuff with IMoniker here, depending on what RegisterFilter does */ 860 861 if (pMoniker) 862 IMoniker_Release(pMoniker); 863 864 if (i == iDefaultDevice) 865 { 866 FIXME("Default device\n"); 867 } 868 869 CoTaskMemFree(pTypes); 870 } 871 } 872 873 numDevs = waveInGetNumDevs(); 874 875 res = DEVENUM_CreateAMCategoryKey(&CLSID_AudioInputDeviceCategory); 876 if (FAILED(res)) /* can't register any devices in this category */ 877 numDevs = 0; 878 879 rfp2.dwFlags = REG_PINFLAG_B_OUTPUT; 880 for (i = 0; i < numDevs; i++) 881 { 882 if (waveInGetDevCapsW(i, &wicaps, sizeof(WAVEINCAPSW)) 883 == MMSYSERR_NOERROR) 884 { 885 IMoniker * pMoniker = NULL; 886 887 rfp2.nMediaTypes = 1; 888 pTypes = CoTaskMemAlloc(rfp2.nMediaTypes * sizeof(REGPINTYPES)); 889 if (!pTypes) 890 { 891 IFilterMapper2_Release(pMapper); 892 return E_OUTOFMEMORY; 893 } 894 895 /* FIXME: Not sure if these are correct */ 896 pTypes[0].clsMajorType = &MEDIATYPE_Audio; 897 pTypes[0].clsMinorType = &MEDIASUBTYPE_PCM; 898 899 rfp2.lpMediaType = pTypes; 900 901 res = IFilterMapper2_RegisterFilter(pMapper, 902 &CLSID_AudioRecord, 903 wicaps.szPname, 904 &pMoniker, 905 &CLSID_AudioInputDeviceCategory, 906 wicaps.szPname, 907 &rf2); 908 909 910 if (pMoniker) { 911 VARIANT var; 912 913 V_VT(&var) = VT_I4; 914 V_I4(&var) = i; 915 res = IMoniker_BindToStorage(pMoniker, NULL, NULL, &IID_IPropertyBag, (LPVOID)&pPropBag); 916 if (SUCCEEDED(res)) 917 res = IPropertyBag_Write(pPropBag, wszWaveInID, &var); 918 else 919 pPropBag = NULL; 920 921 V_VT(&var) = VT_LPWSTR; 922 V_BSTR(&var) = wicaps.szPname; 923 if (SUCCEEDED(res)) 924 res = IPropertyBag_Write(pPropBag, wszFriendlyName, &var); 925 926 if (pPropBag) 927 IPropertyBag_Release(pPropBag); 928 IMoniker_Release(pMoniker); 929 } 930 931 CoTaskMemFree(pTypes); 932 } 933 } 934 935 numDevs = midiOutGetNumDevs(); 936 937 res = DEVENUM_CreateAMCategoryKey(&CLSID_MidiRendererCategory); 938 if (FAILED(res)) /* can't register any devices in this category */ 939 numDevs = 0; 940 941 rfp2.dwFlags = REG_PINFLAG_B_RENDERER; 942 for (i = 0; i < numDevs; i++) 943 { 944 if (midiOutGetDevCapsW(i, &mocaps, sizeof(MIDIOUTCAPSW)) 945 == MMSYSERR_NOERROR) 946 { 947 IMoniker * pMoniker = NULL; 948 949 rfp2.nMediaTypes = 1; 950 pTypes = CoTaskMemAlloc(rfp2.nMediaTypes * sizeof(REGPINTYPES)); 951 if (!pTypes) 952 { 953 IFilterMapper2_Release(pMapper); 954 return E_OUTOFMEMORY; 955 } 956 957 /* FIXME: Not sure if these are correct */ 958 pTypes[0].clsMajorType = &MEDIATYPE_Midi; 959 pTypes[0].clsMinorType = &MEDIASUBTYPE_None; 960 961 rfp2.lpMediaType = pTypes; 962 963 res = IFilterMapper2_RegisterFilter(pMapper, 964 &CLSID_AVIMIDIRender, 965 mocaps.szPname, 966 &pMoniker, 967 &CLSID_MidiRendererCategory, 968 mocaps.szPname, 969 &rf2); 970 971 /* FIXME: do additional stuff with IMoniker here, depending on what RegisterFilter does */ 972 /* Native version sets MidiOutId */ 973 974 if (pMoniker) 975 IMoniker_Release(pMoniker); 976 977 if (i == iDefaultDevice) 978 { 979 FIXME("Default device\n"); 980 } 981 982 CoTaskMemFree(pTypes); 983 } 984 } 985 res = DEVENUM_CreateAMCategoryKey(&CLSID_VideoInputDeviceCategory); 986 if (SUCCEEDED(res)) 987 for (i = 0; i < 10; i++) 988 { 989 WCHAR szDeviceName[32], szDeviceVersion[32], szDevicePath[10]; 990 991 if (capGetDriverDescriptionW ((WORD) i, 992 szDeviceName, sizeof(szDeviceName)/sizeof(WCHAR), 993 szDeviceVersion, sizeof(szDeviceVersion)/sizeof(WCHAR))) 994 { 995 IMoniker * pMoniker = NULL; 996 WCHAR dprintf[] = { 'v','i','d','e','o','%','d',0 }; 997 snprintfW(szDevicePath, sizeof(szDevicePath)/sizeof(WCHAR), dprintf, i); 998 /* The above code prevents 1 device with a different ID overwriting another */ 999 1000 rfp2.nMediaTypes = 1; 1001 pTypes = CoTaskMemAlloc(rfp2.nMediaTypes * sizeof(REGPINTYPES)); 1002 if (!pTypes) { 1003 IFilterMapper2_Release(pMapper); 1004 return E_OUTOFMEMORY; 1005 } 1006 1007 pTypes[0].clsMajorType = &MEDIATYPE_Video; 1008 pTypes[0].clsMinorType = &MEDIASUBTYPE_None; 1009 1010 rfp2.lpMediaType = pTypes; 1011 1012 res = IFilterMapper2_RegisterFilter(pMapper, 1013 &CLSID_VfwCapture, 1014 szDeviceName, 1015 &pMoniker, 1016 &CLSID_VideoInputDeviceCategory, 1017 szDevicePath, 1018 &rf2); 1019 1020 if (pMoniker) { 1021 OLECHAR wszVfwIndex[] = { 'V','F','W','I','n','d','e','x',0 }; 1022 VARIANT var; 1023 V_VT(&var) = VT_I4; 1024 V_I4(&var) = i; 1025 res = IMoniker_BindToStorage(pMoniker, NULL, NULL, &IID_IPropertyBag, (LPVOID)&pPropBag); 1026 if (SUCCEEDED(res)) { 1027 res = IPropertyBag_Write(pPropBag, wszVfwIndex, &var); 1028 IPropertyBag_Release(pPropBag); 1029 } 1030 IMoniker_Release(pMoniker); 1031 } 1032 1033 if (i == iDefaultDevice) FIXME("Default device\n"); 1034 CoTaskMemFree(pTypes); 1035 } 1036 } 1037 } 1038 1039 if (pMapper) 1040 IFilterMapper2_Release(pMapper); 1041 1042 register_vfw_codecs(); 1043 register_acm_codecs(); 1044 1045 SetEvent(DEVENUM_populate_handle); 1046 return res; 1047 } 1048