1 /* 2 * Some unit tests for devenum 3 * 4 * Copyright (C) 2012 Christian Costa 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 21 #define COBJMACROS 22 23 #include <stdio.h> 24 25 #include "wine/test.h" 26 #include "initguid.h" 27 #include "ole2.h" 28 #include "strmif.h" 29 #include "uuids.h" 30 #include "vfwmsgs.h" 31 #include "mmsystem.h" 32 #include "dsound.h" 33 #include "mmddk.h" 34 #include "vfw.h" 35 36 DEFINE_GUID(GUID_NULL,0,0,0,0,0,0,0,0,0,0,0); 37 38 static const WCHAR friendly_name[] = {'F','r','i','e','n','d','l','y','N','a','m','e',0}; 39 static const WCHAR fcc_handlerW[] = {'F','c','c','H','a','n','d','l','e','r',0}; 40 static const WCHAR deviceW[] = {'@','d','e','v','i','c','e',':',0}; 41 static const WCHAR clsidW[] = {'C','L','S','I','D',0}; 42 static const WCHAR waveW[] = {'w','a','v','e',':',0}; 43 static const WCHAR mrleW[] = {'m','r','l','e',0}; 44 static const WCHAR swW[] = {'s','w',':',0}; 45 static const WCHAR cmW[] = {'c','m',':',0}; 46 static const WCHAR backslashW[] = {'\\',0}; 47 48 static inline WCHAR *strchrW( const WCHAR *str, WCHAR ch ) 49 { 50 do { if (*str == ch) return (WCHAR *)str; } while (*str++); 51 return NULL; 52 } 53 54 static inline int strncmpW( const WCHAR *str1, const WCHAR *str2, int n ) 55 { 56 if (n <= 0) return 0; 57 while ((--n > 0) && *str1 && (*str1 == *str2)) { str1++; str2++; } 58 return *str1 - *str2; 59 } 60 61 static void test_devenum(IBindCtx *bind_ctx) 62 { 63 IEnumMoniker *enum_cat, *enum_moniker; 64 ICreateDevEnum* create_devenum; 65 IPropertyBag *prop_bag; 66 IMoniker *moniker; 67 BOOL have_mrle = FALSE; 68 GUID cat_guid, clsid; 69 VARIANT var; 70 HRESULT hr; 71 72 hr = CoCreateInstance(&CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC, 73 &IID_ICreateDevEnum, (LPVOID*)&create_devenum); 74 ok(hr == S_OK, "Failed to create devenum: %#x\n", hr); 75 76 hr = ICreateDevEnum_CreateClassEnumerator(create_devenum, &CLSID_ActiveMovieCategories, &enum_cat, 0); 77 ok(hr == S_OK, "Failed to enum categories: %#x\n", hr); 78 79 while (IEnumMoniker_Next(enum_cat, 1, &moniker, NULL) == S_OK) 80 { 81 hr = IMoniker_BindToStorage(moniker, bind_ctx, NULL, &IID_IPropertyBag, (void **)&prop_bag); 82 ok(hr == S_OK, "IMoniker_BindToStorage failed: %#x\n", hr); 83 84 VariantInit(&var); 85 hr = IPropertyBag_Read(prop_bag, friendly_name, &var, NULL); 86 ok(hr == S_OK, "Failed to read FriendlyName: %#x\n", hr); 87 88 if (winetest_debug > 1) 89 trace("%s:\n", wine_dbgstr_w(V_BSTR(&var))); 90 91 VariantClear(&var); 92 hr = IPropertyBag_Read(prop_bag, clsidW, &var, NULL); 93 ok(hr == S_OK, "Failed to read CLSID: %#x\n", hr); 94 95 hr = CLSIDFromString(V_BSTR(&var), &cat_guid); 96 ok(hr == S_OK, "got %#x\n", hr); 97 98 IPropertyBag_Release(prop_bag); 99 IMoniker_Release(moniker); 100 101 hr = ICreateDevEnum_CreateClassEnumerator(create_devenum, &cat_guid, &enum_moniker, 0); 102 ok(SUCCEEDED(hr), "Failed to enum devices: %#x\n", hr); 103 104 if (hr == S_OK) 105 { 106 while (IEnumMoniker_Next(enum_moniker, 1, &moniker, NULL) == S_OK) 107 { 108 hr = IMoniker_GetClassID(moniker, NULL); 109 ok(hr == E_INVALIDARG, "IMoniker_GetClassID should failed %x\n", hr); 110 111 hr = IMoniker_GetClassID(moniker, &clsid); 112 ok(hr == S_OK, "IMoniker_GetClassID failed with error %x\n", hr); 113 ok(IsEqualGUID(&clsid, &CLSID_CDeviceMoniker), 114 "Expected CLSID_CDeviceMoniker got %s\n", wine_dbgstr_guid(&clsid)); 115 116 VariantInit(&var); 117 hr = IMoniker_BindToStorage(moniker, bind_ctx, NULL, &IID_IPropertyBag, (LPVOID*)&prop_bag); 118 ok(hr == S_OK, "IMoniker_BindToStorage failed with error %x\n", hr); 119 120 hr = IPropertyBag_Read(prop_bag, friendly_name, &var, NULL); 121 ok(hr == S_OK, "IPropertyBag_Read failed: %#x\n", hr); 122 123 if (winetest_debug > 1) 124 trace(" %s\n", wine_dbgstr_w(V_BSTR(&var))); 125 126 if (IsEqualGUID(&CLSID_VideoCompressorCategory, &cat_guid)) { 127 /* Test well known compressor to ensure that we really enumerate codecs */ 128 hr = IPropertyBag_Read(prop_bag, fcc_handlerW, &var, NULL); 129 if (SUCCEEDED(hr)) { 130 ok(V_VT(&var) == VT_BSTR, "V_VT(var) = %d\n", V_VT(&var)); 131 if(!lstrcmpW(V_BSTR(&var), mrleW)) 132 have_mrle = TRUE; 133 VariantClear(&var); 134 } 135 } 136 137 hr = IMoniker_BindToObject(moniker, bind_ctx, NULL, &IID_IUnknown, NULL); 138 ok(hr == E_POINTER, "got %#x\n", hr); 139 140 IPropertyBag_Release(prop_bag); 141 IMoniker_Release(moniker); 142 } 143 IEnumMoniker_Release(enum_moniker); 144 } 145 } 146 147 ICreateDevEnum_Release(create_devenum); 148 149 /* 64-bit windows are missing mrle codec */ 150 if(sizeof(void*) == 4) 151 ok(have_mrle, "mrle codec not found\n"); 152 } 153 static void test_moniker_isequal(void) 154 { 155 HRESULT res; 156 ICreateDevEnum *create_devenum = NULL; 157 IEnumMoniker *enum_moniker0 = NULL, *enum_moniker1 = NULL; 158 IMoniker *moniker0 = NULL, *moniker1 = NULL; 159 160 res = CoCreateInstance(&CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC, 161 &IID_ICreateDevEnum, (LPVOID*)&create_devenum); 162 if (FAILED(res)) 163 { 164 skip("Cannot create SystemDeviceEnum object (%x)\n", res); 165 return; 166 } 167 168 res = ICreateDevEnum_CreateClassEnumerator(create_devenum, &CLSID_LegacyAmFilterCategory, &enum_moniker0, 0); 169 ok(SUCCEEDED(res), "Cannot create enum moniker (res = %x)\n", res); 170 if (SUCCEEDED(res)) 171 { 172 if (IEnumMoniker_Next(enum_moniker0, 1, &moniker0, NULL) == S_OK && 173 IEnumMoniker_Next(enum_moniker0, 1, &moniker1, NULL) == S_OK) 174 { 175 res = IMoniker_IsEqual(moniker0, moniker1); 176 ok(res == S_FALSE, "IMoniker_IsEqual should fail (res = %x)\n", res); 177 178 res = IMoniker_IsEqual(moniker1, moniker0); 179 ok(res == S_FALSE, "IMoniker_IsEqual should fail (res = %x)\n", res); 180 181 IMoniker_Release(moniker0); 182 IMoniker_Release(moniker1); 183 } 184 else 185 skip("Cannot get moniker for testing.\n"); 186 } 187 IEnumMoniker_Release(enum_moniker0); 188 189 res = ICreateDevEnum_CreateClassEnumerator(create_devenum, &CLSID_LegacyAmFilterCategory, &enum_moniker0, 0); 190 ok(SUCCEEDED(res), "Cannot create enum moniker (res = %x)\n", res); 191 res = ICreateDevEnum_CreateClassEnumerator(create_devenum, &CLSID_AudioRendererCategory, &enum_moniker1, 0); 192 ok(SUCCEEDED(res), "Cannot create enum moniker (res = %x)\n", res); 193 if (SUCCEEDED(res)) 194 { 195 if (IEnumMoniker_Next(enum_moniker0, 1, &moniker0, NULL) == S_OK && 196 IEnumMoniker_Next(enum_moniker1, 1, &moniker1, NULL) == S_OK) 197 { 198 res = IMoniker_IsEqual(moniker0, moniker1); 199 ok(res == S_FALSE, "IMoniker_IsEqual should failed (res = %x)\n", res); 200 201 res = IMoniker_IsEqual(moniker1, moniker0); 202 ok(res == S_FALSE, "IMoniker_IsEqual should failed (res = %x)\n", res); 203 204 IMoniker_Release(moniker0); 205 IMoniker_Release(moniker1); 206 } 207 else 208 skip("Cannot get moniker for testing.\n"); 209 } 210 IEnumMoniker_Release(enum_moniker0); 211 IEnumMoniker_Release(enum_moniker1); 212 213 res = ICreateDevEnum_CreateClassEnumerator(create_devenum, &CLSID_LegacyAmFilterCategory, &enum_moniker0, 0); 214 ok(SUCCEEDED(res), "Cannot create enum moniker (res = %x)\n", res); 215 res = ICreateDevEnum_CreateClassEnumerator(create_devenum, &CLSID_LegacyAmFilterCategory, &enum_moniker1, 0); 216 ok(SUCCEEDED(res), "Cannot create enum moniker (res = %x)\n", res); 217 if (SUCCEEDED(res)) 218 { 219 if (IEnumMoniker_Next(enum_moniker0, 1, &moniker0, NULL) == S_OK && 220 IEnumMoniker_Next(enum_moniker1, 1, &moniker1, NULL) == S_OK) 221 { 222 res = IMoniker_IsEqual(moniker0, moniker1); 223 ok(res == S_OK, "IMoniker_IsEqual failed (res = %x)\n", res); 224 225 res = IMoniker_IsEqual(moniker1, moniker0); 226 ok(res == S_OK, "IMoniker_IsEqual failed (res = %x)\n", res); 227 228 IMoniker_Release(moniker0); 229 IMoniker_Release(moniker1); 230 } 231 else 232 skip("Cannot get moniker for testing.\n"); 233 } 234 IEnumMoniker_Release(enum_moniker0); 235 IEnumMoniker_Release(enum_moniker1); 236 237 ICreateDevEnum_Release(create_devenum); 238 239 return; 240 } 241 242 static BOOL find_moniker(const GUID *class, IMoniker *needle) 243 { 244 ICreateDevEnum *devenum; 245 IEnumMoniker *enum_mon; 246 IMoniker *mon; 247 BOOL found = FALSE; 248 249 CoCreateInstance(&CLSID_SystemDeviceEnum, NULL, CLSCTX_INPROC, &IID_ICreateDevEnum, (void **)&devenum); 250 ICreateDevEnum_CreateClassEnumerator(devenum, class, &enum_mon, 0); 251 while (!found && IEnumMoniker_Next(enum_mon, 1, &mon, NULL) == S_OK) 252 { 253 if (IMoniker_IsEqual(mon, needle) == S_OK) 254 found = TRUE; 255 256 IMoniker_Release(mon); 257 } 258 259 IEnumMoniker_Release(enum_mon); 260 ICreateDevEnum_Release(devenum); 261 return found; 262 } 263 264 DEFINE_GUID(CLSID_TestFilter, 0xdeadbeef,0xcf51,0x43e6,0xb6,0xc5,0x29,0x9e,0xa8,0xb6,0xb5,0x91); 265 266 static void test_register_filter(void) 267 { 268 static const WCHAR name[] = {'d','e','v','e','n','u','m',' ','t','e','s','t',0}; 269 IFilterMapper2 *mapper2; 270 IMoniker *mon = NULL; 271 REGFILTER2 rgf2 = {0}; 272 HRESULT hr; 273 274 hr = CoCreateInstance(&CLSID_FilterMapper2, NULL, CLSCTX_INPROC, &IID_IFilterMapper2, (void **)&mapper2); 275 ok(hr == S_OK, "Failed to create FilterMapper2: %#x\n", hr); 276 277 rgf2.dwVersion = 2; 278 rgf2.dwMerit = MERIT_UNLIKELY; 279 S2(U(rgf2)).cPins2 = 0; 280 281 hr = IFilterMapper2_RegisterFilter(mapper2, &CLSID_TestFilter, name, &mon, NULL, NULL, &rgf2); 282 if (hr == E_ACCESSDENIED) 283 { 284 skip("Not enough permissions to register filters\n"); 285 IFilterMapper2_Release(mapper2); 286 return; 287 } 288 ok(hr == S_OK, "RegisterFilter failed: %#x\n", hr); 289 290 ok(find_moniker(&CLSID_LegacyAmFilterCategory, mon), "filter should be registered\n"); 291 292 hr = IFilterMapper2_UnregisterFilter(mapper2, NULL, NULL, &CLSID_TestFilter); 293 ok(hr == S_OK, "UnregisterFilter failed: %#x\n", hr); 294 295 ok(!find_moniker(&CLSID_LegacyAmFilterCategory, mon), "filter should not be registered\n"); 296 IMoniker_Release(mon); 297 298 mon = NULL; 299 hr = IFilterMapper2_RegisterFilter(mapper2, &CLSID_TestFilter, name, &mon, &CLSID_AudioRendererCategory, NULL, &rgf2); 300 ok(hr == S_OK, "RegisterFilter failed: %#x\n", hr); 301 302 ok(find_moniker(&CLSID_AudioRendererCategory, mon), "filter should be registered\n"); 303 304 hr = IFilterMapper2_UnregisterFilter(mapper2, &CLSID_AudioRendererCategory, NULL, &CLSID_TestFilter); 305 ok(hr == S_OK, "UnregisterFilter failed: %#x\n", hr); 306 307 ok(!find_moniker(&CLSID_AudioRendererCategory, mon), "filter should not be registered\n"); 308 IMoniker_Release(mon); 309 310 IFilterMapper2_Release(mapper2); 311 } 312 313 static IMoniker *check_display_name_(int line, IParseDisplayName *parser, WCHAR *buffer) 314 { 315 IMoniker *mon; 316 ULONG eaten; 317 HRESULT hr; 318 WCHAR *str; 319 320 hr = IParseDisplayName_ParseDisplayName(parser, NULL, buffer, &eaten, &mon); 321 ok_(__FILE__, line)(hr == S_OK, "ParseDisplayName failed: %#x\n", hr); 322 323 hr = IMoniker_GetDisplayName(mon, NULL, NULL, &str); 324 ok_(__FILE__, line)(hr == S_OK, "GetDisplayName failed: %#x\n", hr); 325 ok_(__FILE__, line)(!lstrcmpW(str, buffer), "got %s\n", wine_dbgstr_w(str)); 326 327 CoTaskMemFree(str); 328 329 return mon; 330 } 331 #define check_display_name(parser, buffer) check_display_name_(__LINE__, parser, buffer) 332 333 static void test_directshow_filter(void) 334 { 335 static const WCHAR instanceW[] = {'\\','I','n','s','t','a','n','c','e',0}; 336 static const WCHAR clsidW[] = {'C','L','S','I','D','\\',0}; 337 static WCHAR testW[] = {'\\','t','e','s','t',0}; 338 IParseDisplayName *parser; 339 IPropertyBag *prop_bag; 340 IMoniker *mon; 341 WCHAR buffer[200]; 342 LRESULT res; 343 VARIANT var; 344 HRESULT hr; 345 346 /* Test ParseDisplayName and GetDisplayName */ 347 hr = CoCreateInstance(&CLSID_CDeviceMoniker, NULL, CLSCTX_INPROC, &IID_IParseDisplayName, (void **)&parser); 348 ok(hr == S_OK, "Failed to create ParseDisplayName: %#x\n", hr); 349 350 lstrcpyW(buffer, deviceW); 351 lstrcatW(buffer, swW); 352 StringFromGUID2(&CLSID_AudioRendererCategory, buffer + lstrlenW(buffer), CHARS_IN_GUID); 353 lstrcatW(buffer, testW); 354 mon = check_display_name(parser, buffer); 355 356 /* Test writing and reading from the property bag */ 357 ok(!find_moniker(&CLSID_AudioRendererCategory, mon), "filter should not be registered\n"); 358 359 hr = IMoniker_BindToStorage(mon, NULL, NULL, &IID_IPropertyBag, (void **)&prop_bag); 360 ok(hr == S_OK, "BindToStorage failed: %#x\n", hr); 361 362 VariantInit(&var); 363 hr = IPropertyBag_Read(prop_bag, friendly_name, &var, NULL); 364 ok(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "got %#x\n", hr); 365 366 /* writing causes the key to be created */ 367 V_VT(&var) = VT_BSTR; 368 V_BSTR(&var) = SysAllocString(testW); 369 hr = IPropertyBag_Write(prop_bag, friendly_name, &var); 370 if (hr != E_ACCESSDENIED) 371 { 372 ok(hr == S_OK, "Write failed: %#x\n", hr); 373 374 ok(find_moniker(&CLSID_AudioRendererCategory, mon), "filter should be registered\n"); 375 376 VariantClear(&var); 377 hr = IPropertyBag_Read(prop_bag, friendly_name, &var, NULL); 378 ok(hr == S_OK, "Read failed: %#x\n", hr); 379 ok(!lstrcmpW(V_BSTR(&var), testW), "got %s\n", wine_dbgstr_w(V_BSTR(&var))); 380 381 IMoniker_Release(mon); 382 383 /* devenum doesn't give us a way to unregister�we have to do that manually */ 384 lstrcpyW(buffer, clsidW); 385 StringFromGUID2(&CLSID_AudioRendererCategory, buffer + lstrlenW(buffer), CHARS_IN_GUID); 386 lstrcatW(buffer, instanceW); 387 lstrcatW(buffer, testW); 388 res = RegDeleteKeyW(HKEY_CLASSES_ROOT, buffer); 389 ok(!res, "RegDeleteKey failed: %lu\n", res); 390 } 391 392 VariantClear(&var); 393 IPropertyBag_Release(prop_bag); 394 395 /* name can be anything */ 396 397 lstrcpyW(buffer, deviceW); 398 lstrcatW(buffer, swW); 399 lstrcatW(buffer, testW+1); 400 mon = check_display_name(parser, buffer); 401 402 hr = IMoniker_BindToStorage(mon, NULL, NULL, &IID_IPropertyBag, (void **)&prop_bag); 403 ok(hr == S_OK, "BindToStorage failed: %#x\n", hr); 404 405 VariantClear(&var); 406 hr = IPropertyBag_Read(prop_bag, friendly_name, &var, NULL); 407 ok(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "got %#x\n", hr); 408 409 V_VT(&var) = VT_BSTR; 410 V_BSTR(&var) = SysAllocString(testW); 411 hr = IPropertyBag_Write(prop_bag, friendly_name, &var); 412 if (hr != E_ACCESSDENIED) 413 { 414 ok(hr == S_OK, "Write failed: %#x\n", hr); 415 416 VariantClear(&var); 417 hr = IPropertyBag_Read(prop_bag, friendly_name, &var, NULL); 418 ok(hr == S_OK, "Read failed: %#x\n", hr); 419 ok(!lstrcmpW(V_BSTR(&var), testW), "got %s\n", wine_dbgstr_w(V_BSTR(&var))); 420 421 IMoniker_Release(mon); 422 423 /* vista+ stores it inside the Instance key */ 424 RegDeleteKeyA(HKEY_CLASSES_ROOT, "CLSID\\test\\Instance"); 425 426 res = RegDeleteKeyA(HKEY_CLASSES_ROOT, "CLSID\\test"); 427 ok(!res, "RegDeleteKey failed: %lu\n", res); 428 } 429 430 VariantClear(&var); 431 IPropertyBag_Release(prop_bag); 432 IParseDisplayName_Release(parser); 433 } 434 435 static void test_codec(void) 436 { 437 static WCHAR testW[] = {'\\','t','e','s','t',0}; 438 IParseDisplayName *parser; 439 IPropertyBag *prop_bag; 440 IMoniker *mon; 441 WCHAR buffer[200]; 442 VARIANT var; 443 HRESULT hr; 444 445 /* Test ParseDisplayName and GetDisplayName */ 446 hr = CoCreateInstance(&CLSID_CDeviceMoniker, NULL, CLSCTX_INPROC, &IID_IParseDisplayName, (void **)&parser); 447 ok(hr == S_OK, "Failed to create ParseDisplayName: %#x\n", hr); 448 449 lstrcpyW(buffer, deviceW); 450 lstrcatW(buffer, cmW); 451 StringFromGUID2(&CLSID_AudioRendererCategory, buffer + lstrlenW(buffer), CHARS_IN_GUID); 452 lstrcatW(buffer, testW); 453 mon = check_display_name(parser, buffer); 454 455 /* Test writing and reading from the property bag */ 456 ok(!find_moniker(&CLSID_AudioRendererCategory, mon), "codec should not be registered\n"); 457 458 hr = IMoniker_BindToStorage(mon, NULL, NULL, &IID_IPropertyBag, (void **)&prop_bag); 459 ok(hr == S_OK, "BindToStorage failed: %#x\n", hr); 460 461 VariantInit(&var); 462 hr = IPropertyBag_Read(prop_bag, friendly_name, &var, NULL); 463 ok(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "got %#x\n", hr); 464 465 V_VT(&var) = VT_BSTR; 466 V_BSTR(&var) = SysAllocString(testW); 467 hr = IPropertyBag_Write(prop_bag, friendly_name, &var); 468 ok(hr == S_OK, "Write failed: %#x\n", hr); 469 470 VariantClear(&var); 471 hr = IPropertyBag_Read(prop_bag, friendly_name, &var, NULL); 472 ok(hr == S_OK, "Read failed: %#x\n", hr); 473 ok(!lstrcmpW(V_BSTR(&var), testW), "got %s\n", wine_dbgstr_w(V_BSTR(&var))); 474 475 /* unlike DirectShow filters, these are automatically generated, so 476 * enumerating them will destroy the key */ 477 ok(!find_moniker(&CLSID_AudioRendererCategory, mon), "codec should not be registered\n"); 478 479 hr = IPropertyBag_Read(prop_bag, friendly_name, &var, NULL); 480 ok(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "got %#x\n", hr); 481 482 IPropertyBag_Release(prop_bag); 483 IMoniker_Release(mon); 484 485 IParseDisplayName_Release(parser); 486 } 487 488 static void test_legacy_filter(void) 489 { 490 static const WCHAR nameW[] = {'t','e','s','t',0}; 491 IParseDisplayName *parser; 492 IPropertyBag *prop_bag; 493 IFilterMapper *mapper; 494 IMoniker *mon; 495 WCHAR buffer[200]; 496 VARIANT var; 497 HRESULT hr; 498 499 hr = CoCreateInstance(&CLSID_CDeviceMoniker, NULL, CLSCTX_INPROC, &IID_IParseDisplayName, (void **)&parser); 500 ok(hr == S_OK, "Failed to create ParseDisplayName: %#x\n", hr); 501 502 hr = CoCreateInstance(&CLSID_FilterMapper2, NULL, CLSCTX_INPROC, &IID_IFilterMapper, (void **)&mapper); 503 ok(hr == S_OK, "Failed to create FilterMapper: %#x\n", hr); 504 505 hr = IFilterMapper_RegisterFilter(mapper, CLSID_TestFilter, nameW, 0xdeadbeef); 506 if (hr == VFW_E_BAD_KEY) 507 { 508 win_skip("not enough permissions to register filters\n"); 509 goto end; 510 } 511 ok(hr == S_OK, "RegisterFilter failed: %#x\n", hr); 512 513 lstrcpyW(buffer, deviceW); 514 lstrcatW(buffer, cmW); 515 StringFromGUID2(&CLSID_LegacyAmFilterCategory, buffer + lstrlenW(buffer), CHARS_IN_GUID); 516 lstrcatW(buffer, backslashW); 517 StringFromGUID2(&CLSID_TestFilter, buffer + lstrlenW(buffer), CHARS_IN_GUID); 518 519 mon = check_display_name(parser, buffer); 520 ok(find_moniker(&CLSID_LegacyAmFilterCategory, mon), "filter should be registered\n"); 521 522 hr = IMoniker_BindToStorage(mon, NULL, NULL, &IID_IPropertyBag, (void **)&prop_bag); 523 ok(hr == S_OK, "BindToStorage failed: %#x\n", hr); 524 525 VariantInit(&var); 526 hr = IPropertyBag_Read(prop_bag, friendly_name, &var, NULL); 527 ok(hr == S_OK, "Read failed: %#x\n", hr); 528 529 StringFromGUID2(&CLSID_TestFilter, buffer, CHARS_IN_GUID); 530 ok(!lstrcmpW(buffer, V_BSTR(&var)), "expected %s, got %s\n", 531 wine_dbgstr_w(buffer), wine_dbgstr_w(V_BSTR(&var))); 532 533 VariantClear(&var); 534 hr = IPropertyBag_Read(prop_bag, clsidW, &var, NULL); 535 ok(hr == S_OK, "Read failed: %#x\n", hr); 536 ok(!lstrcmpW(buffer, V_BSTR(&var)), "expected %s, got %s\n", 537 wine_dbgstr_w(buffer), wine_dbgstr_w(V_BSTR(&var))); 538 539 IPropertyBag_Release(prop_bag); 540 541 hr = IFilterMapper_UnregisterFilter(mapper, CLSID_TestFilter); 542 ok(hr == S_OK, "UnregisterFilter failed: %#x\n", hr); 543 544 ok(!find_moniker(&CLSID_LegacyAmFilterCategory, mon), "filter should not be registered\n"); 545 IMoniker_Release(mon); 546 547 end: 548 IFilterMapper_Release(mapper); 549 IParseDisplayName_Release(parser); 550 } 551 552 static BOOL CALLBACK test_dsound(GUID *guid, const WCHAR *desc, const WCHAR *module, void *context) 553 { 554 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}; 555 static const WCHAR directsoundW[] = {'D','i','r','e','c','t','S','o','u','n','d',':',' ',0}; 556 static const WCHAR dsguidW[] = {'D','S','G','u','i','d',0}; 557 IParseDisplayName *parser; 558 IPropertyBag *prop_bag; 559 IMoniker *mon; 560 WCHAR buffer[200]; 561 WCHAR name[200]; 562 VARIANT var; 563 HRESULT hr; 564 565 if (guid) 566 { 567 lstrcpyW(name, directsoundW); 568 lstrcatW(name, desc); 569 } 570 else 571 { 572 lstrcpyW(name, defaultW); 573 guid = (GUID *)&GUID_NULL; 574 } 575 576 hr = CoCreateInstance(&CLSID_CDeviceMoniker, NULL, CLSCTX_INPROC, &IID_IParseDisplayName, (void **)&parser); 577 ok(hr == S_OK, "Failed to create ParseDisplayName: %#x\n", hr); 578 579 lstrcpyW(buffer, deviceW); 580 lstrcatW(buffer, cmW); 581 StringFromGUID2(&CLSID_AudioRendererCategory, buffer + lstrlenW(buffer), CHARS_IN_GUID); 582 lstrcatW(buffer, backslashW); 583 lstrcatW(buffer, name); 584 585 mon = check_display_name(parser, buffer); 586 587 hr = IMoniker_BindToStorage(mon, NULL, NULL, &IID_IPropertyBag, (void **)&prop_bag); 588 ok(hr == S_OK, "BindToStorage failed: %#x\n", hr); 589 590 VariantInit(&var); 591 hr = IPropertyBag_Read(prop_bag, friendly_name, &var, NULL); 592 if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) 593 { 594 /* Win8+ uses the GUID instead of the device name */ 595 IPropertyBag_Release(prop_bag); 596 IMoniker_Release(mon); 597 598 lstrcpyW(buffer, deviceW); 599 lstrcatW(buffer, cmW); 600 StringFromGUID2(&CLSID_AudioRendererCategory, buffer + lstrlenW(buffer), CHARS_IN_GUID); 601 lstrcatW(buffer, backslashW); 602 lstrcatW(buffer, directsoundW); 603 StringFromGUID2(guid, buffer + lstrlenW(buffer) - 1, CHARS_IN_GUID); 604 605 mon = check_display_name(parser, buffer); 606 607 hr = IMoniker_BindToStorage(mon, NULL, NULL, &IID_IPropertyBag, (void **)&prop_bag); 608 ok(hr == S_OK, "BindToStorage failed: %#x\n", hr); 609 610 VariantInit(&var); 611 hr = IPropertyBag_Read(prop_bag, friendly_name, &var, NULL); 612 } 613 ok(hr == S_OK, "Read failed: %#x\n", hr); 614 615 ok(!lstrcmpW(name, V_BSTR(&var)), "expected %s, got %s\n", 616 wine_dbgstr_w(name), wine_dbgstr_w(V_BSTR(&var))); 617 618 VariantClear(&var); 619 hr = IPropertyBag_Read(prop_bag, clsidW, &var, NULL); 620 ok(hr == S_OK, "Read failed: %#x\n", hr); 621 622 StringFromGUID2(&CLSID_DSoundRender, buffer, CHARS_IN_GUID); 623 ok(!lstrcmpW(buffer, V_BSTR(&var)), "expected %s, got %s\n", 624 wine_dbgstr_w(buffer), wine_dbgstr_w(V_BSTR(&var))); 625 626 VariantClear(&var); 627 hr = IPropertyBag_Read(prop_bag, dsguidW, &var, NULL); 628 ok(hr == S_OK, "Read failed: %#x\n", hr); 629 630 StringFromGUID2(guid, buffer, CHARS_IN_GUID); 631 ok(!lstrcmpW(buffer, V_BSTR(&var)), "expected %s, got %s\n", 632 wine_dbgstr_w(buffer), wine_dbgstr_w(V_BSTR(&var))); 633 634 IPropertyBag_Release(prop_bag); 635 IMoniker_Release(mon); 636 IParseDisplayName_Release(parser); 637 return TRUE; 638 } 639 640 static void test_waveout(void) 641 { 642 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}; 643 static const WCHAR waveoutidW[] = {'W','a','v','e','O','u','t','I','d',0}; 644 IParseDisplayName *parser; 645 IPropertyBag *prop_bag; 646 IMoniker *mon; 647 WCHAR endpoint[200]; 648 WAVEOUTCAPSW caps; 649 WCHAR buffer[200]; 650 const WCHAR *name; 651 MMRESULT mmr; 652 int count, i; 653 VARIANT var; 654 HRESULT hr; 655 656 hr = CoCreateInstance(&CLSID_CDeviceMoniker, NULL, CLSCTX_INPROC, &IID_IParseDisplayName, (void **)&parser); 657 ok(hr == S_OK, "Failed to create ParseDisplayName: %#x\n", hr); 658 659 count = waveOutGetNumDevs(); 660 661 for (i = -1; i < count; i++) 662 { 663 waveOutGetDevCapsW(i, &caps, sizeof(caps)); 664 665 if (i == -1) /* WAVE_MAPPER */ 666 name = defaultW; 667 else 668 name = caps.szPname; 669 670 lstrcpyW(buffer, deviceW); 671 lstrcatW(buffer, cmW); 672 StringFromGUID2(&CLSID_AudioRendererCategory, buffer + lstrlenW(buffer), CHARS_IN_GUID); 673 lstrcatW(buffer, backslashW); 674 lstrcatW(buffer, name); 675 676 mon = check_display_name(parser, buffer); 677 678 hr = IMoniker_BindToStorage(mon, NULL, NULL, &IID_IPropertyBag, (void **)&prop_bag); 679 ok(hr == S_OK, "BindToStorage failed: %#x\n", hr); 680 681 VariantInit(&var); 682 hr = IPropertyBag_Read(prop_bag, friendly_name, &var, NULL); 683 if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) 684 { 685 IPropertyBag_Release(prop_bag); 686 IMoniker_Release(mon); 687 688 /* Win8+ uses the endpoint GUID instead of the device name */ 689 mmr = waveOutMessage((HWAVEOUT)(DWORD_PTR) i, DRV_QUERYFUNCTIONINSTANCEID, 690 (DWORD_PTR) endpoint, sizeof(endpoint)); 691 ok(!mmr, "waveOutMessage failed: %u\n", mmr); 692 693 lstrcpyW(buffer, deviceW); 694 lstrcatW(buffer, cmW); 695 StringFromGUID2(&CLSID_AudioRendererCategory, buffer + lstrlenW(buffer), CHARS_IN_GUID); 696 lstrcatW(buffer, backslashW); 697 lstrcatW(buffer, waveW); 698 lstrcatW(buffer, strchrW(endpoint, '}') + 2); 699 700 mon = check_display_name(parser, buffer); 701 702 hr = IMoniker_BindToStorage(mon, NULL, NULL, &IID_IPropertyBag, (void **)&prop_bag); 703 ok(hr == S_OK, "BindToStorage failed: %#x\n", hr); 704 705 hr = IPropertyBag_Read(prop_bag, friendly_name, &var, NULL); 706 } 707 ok(hr == S_OK, "Read failed: %#x\n", hr); 708 709 ok(!strncmpW(name, V_BSTR(&var), lstrlenW(name)), "expected %s, got %s\n", 710 wine_dbgstr_w(name), wine_dbgstr_w(V_BSTR(&var))); 711 712 VariantClear(&var); 713 hr = IPropertyBag_Read(prop_bag, clsidW, &var, NULL); 714 ok(hr == S_OK, "Read failed: %#x\n", hr); 715 716 StringFromGUID2(&CLSID_AudioRender, buffer, CHARS_IN_GUID); 717 ok(!lstrcmpW(buffer, V_BSTR(&var)), "expected %s, got %s\n", 718 wine_dbgstr_w(buffer), wine_dbgstr_w(V_BSTR(&var))); 719 720 VariantClear(&var); 721 hr = IPropertyBag_Read(prop_bag, waveoutidW, &var, NULL); 722 ok(hr == S_OK, "Read failed: %#x\n", hr); 723 724 ok(V_I4(&var) == i, "expected %d, got %d\n", i, V_I4(&var)); 725 726 IPropertyBag_Release(prop_bag); 727 IMoniker_Release(mon); 728 } 729 730 IParseDisplayName_Release(parser); 731 } 732 733 static void test_wavein(void) 734 { 735 static const WCHAR waveinidW[] = {'W','a','v','e','I','n','I','d',0}; 736 IParseDisplayName *parser; 737 IPropertyBag *prop_bag; 738 IMoniker *mon; 739 WCHAR endpoint[200]; 740 WCHAR buffer[200]; 741 WAVEINCAPSW caps; 742 MMRESULT mmr; 743 int count, i; 744 VARIANT var; 745 HRESULT hr; 746 747 hr = CoCreateInstance(&CLSID_CDeviceMoniker, NULL, CLSCTX_INPROC, &IID_IParseDisplayName, (void **)&parser); 748 ok(hr == S_OK, "Failed to create ParseDisplayName: %#x\n", hr); 749 750 count = waveInGetNumDevs(); 751 752 for (i = 0; i < count; i++) 753 { 754 waveInGetDevCapsW(i, &caps, sizeof(caps)); 755 756 lstrcpyW(buffer, deviceW); 757 lstrcatW(buffer, cmW); 758 StringFromGUID2(&CLSID_AudioInputDeviceCategory, buffer + lstrlenW(buffer), CHARS_IN_GUID); 759 lstrcatW(buffer, backslashW); 760 lstrcatW(buffer, caps.szPname); 761 762 mon = check_display_name(parser, buffer); 763 764 hr = IMoniker_BindToStorage(mon, NULL, NULL, &IID_IPropertyBag, (void **)&prop_bag); 765 ok(hr == S_OK, "BindToStorage failed: %#x\n", hr); 766 767 VariantInit(&var); 768 hr = IPropertyBag_Read(prop_bag, friendly_name, &var, NULL); 769 if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) 770 { 771 IPropertyBag_Release(prop_bag); 772 IMoniker_Release(mon); 773 774 /* Win8+ uses the endpoint GUID instead of the device name */ 775 mmr = waveInMessage((HWAVEIN)(DWORD_PTR) i, DRV_QUERYFUNCTIONINSTANCEID, 776 (DWORD_PTR) endpoint, sizeof(endpoint)); 777 ok(!mmr, "waveInMessage failed: %u\n", mmr); 778 779 lstrcpyW(buffer, deviceW); 780 lstrcatW(buffer, cmW); 781 StringFromGUID2(&CLSID_AudioInputDeviceCategory, buffer + lstrlenW(buffer), CHARS_IN_GUID); 782 lstrcatW(buffer, backslashW); 783 lstrcatW(buffer, waveW); 784 lstrcatW(buffer, strchrW(endpoint, '}') + 2); 785 786 mon = check_display_name(parser, buffer); 787 788 hr = IMoniker_BindToStorage(mon, NULL, NULL, &IID_IPropertyBag, (void **)&prop_bag); 789 ok(hr == S_OK, "BindToStorage failed: %#x\n", hr); 790 791 hr = IPropertyBag_Read(prop_bag, friendly_name, &var, NULL); 792 } 793 ok(hr == S_OK, "Read failed: %#x\n", hr); 794 795 ok(!strncmpW(caps.szPname, V_BSTR(&var), lstrlenW(caps.szPname)), "expected %s, got %s\n", 796 wine_dbgstr_w(caps.szPname), wine_dbgstr_w(V_BSTR(&var))); 797 798 VariantClear(&var); 799 hr = IPropertyBag_Read(prop_bag, clsidW, &var, NULL); 800 ok(hr == S_OK, "Read failed: %#x\n", hr); 801 802 StringFromGUID2(&CLSID_AudioRecord, buffer, CHARS_IN_GUID); 803 ok(!lstrcmpW(buffer, V_BSTR(&var)), "expected %s, got %s\n", 804 wine_dbgstr_w(buffer), wine_dbgstr_w(V_BSTR(&var))); 805 806 VariantClear(&var); 807 hr = IPropertyBag_Read(prop_bag, waveinidW, &var, NULL); 808 ok(hr == S_OK, "Read failed: %#x\n", hr); 809 810 ok(V_I4(&var) == i, "expected %d, got %d\n", i, V_I4(&var)); 811 812 IPropertyBag_Release(prop_bag); 813 IMoniker_Release(mon); 814 } 815 816 IParseDisplayName_Release(parser); 817 } 818 819 static void test_midiout(void) 820 { 821 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}; 822 static const WCHAR midioutidW[] = {'M','i','d','i','O','u','t','I','d',0}; 823 IParseDisplayName *parser; 824 IPropertyBag *prop_bag; 825 IMoniker *mon; 826 MIDIOUTCAPSW caps; 827 WCHAR buffer[200]; 828 const WCHAR *name; 829 int count, i; 830 VARIANT var; 831 HRESULT hr; 832 833 hr = CoCreateInstance(&CLSID_CDeviceMoniker, NULL, CLSCTX_INPROC, &IID_IParseDisplayName, (void **)&parser); 834 ok(hr == S_OK, "Failed to create ParseDisplayName: %#x\n", hr); 835 836 count = midiOutGetNumDevs(); 837 838 for (i = -1; i < count; i++) 839 { 840 midiOutGetDevCapsW(i, &caps, sizeof(caps)); 841 842 if (i == -1) /* MIDI_MAPPER */ 843 name = defaultW; 844 else 845 name = caps.szPname; 846 847 lstrcpyW(buffer, deviceW); 848 lstrcatW(buffer, cmW); 849 StringFromGUID2(&CLSID_MidiRendererCategory, buffer + lstrlenW(buffer), CHARS_IN_GUID); 850 lstrcatW(buffer, backslashW); 851 lstrcatW(buffer, name); 852 853 mon = check_display_name(parser, buffer); 854 855 hr = IMoniker_BindToStorage(mon, NULL, NULL, &IID_IPropertyBag, (void **)&prop_bag); 856 ok(hr == S_OK, "BindToStorage failed: %#x\n", hr); 857 858 VariantInit(&var); 859 hr = IPropertyBag_Read(prop_bag, friendly_name, &var, NULL); 860 ok(hr == S_OK, "Read failed: %#x\n", hr); 861 862 ok(!lstrcmpW(name, V_BSTR(&var)), "expected %s, got %s\n", 863 wine_dbgstr_w(name), wine_dbgstr_w(V_BSTR(&var))); 864 865 VariantClear(&var); 866 hr = IPropertyBag_Read(prop_bag, clsidW, &var, NULL); 867 ok(hr == S_OK, "Read failed: %#x\n", hr); 868 869 StringFromGUID2(&CLSID_AVIMIDIRender, buffer, CHARS_IN_GUID); 870 ok(!lstrcmpW(buffer, V_BSTR(&var)), "expected %s, got %s\n", 871 wine_dbgstr_w(buffer), wine_dbgstr_w(V_BSTR(&var))); 872 873 VariantClear(&var); 874 hr = IPropertyBag_Read(prop_bag, midioutidW, &var, NULL); 875 ok(hr == S_OK, "Read failed: %#x\n", hr); 876 877 ok(V_I4(&var) == i, "expected %d, got %d\n", i, V_I4(&var)); 878 879 IPropertyBag_Release(prop_bag); 880 IMoniker_Release(mon); 881 } 882 883 IParseDisplayName_Release(parser); 884 } 885 886 static void test_vfw(void) 887 { 888 static const WCHAR fcchandlerW[] = {'F','c','c','H','a','n','d','l','e','r',0}; 889 IParseDisplayName *parser; 890 IPropertyBag *prop_bag; 891 IMoniker *mon; 892 WCHAR buffer[200]; 893 ICINFO info; 894 VARIANT var; 895 HRESULT hr; 896 int i = 0; 897 HIC hic; 898 899 if (broken(sizeof(void *) == 8)) 900 { 901 win_skip("VFW codecs are not enumerated on 64-bit Windows\n"); 902 return; 903 } 904 905 hr = CoCreateInstance(&CLSID_CDeviceMoniker, NULL, CLSCTX_INPROC, &IID_IParseDisplayName, (void **)&parser); 906 ok(hr == S_OK, "Failed to create ParseDisplayName: %#x\n", hr); 907 908 while (ICInfo(ICTYPE_VIDEO, i++, &info)) 909 { 910 WCHAR name[5] = {LOBYTE(LOWORD(info.fccHandler)), HIBYTE(LOWORD(info.fccHandler)), 911 LOBYTE(HIWORD(info.fccHandler)), HIBYTE(HIWORD(info.fccHandler))}; 912 913 hic = ICOpen(ICTYPE_VIDEO, info.fccHandler, ICMODE_QUERY); 914 ICGetInfo(hic, &info, sizeof(info)); 915 ICClose(hic); 916 917 lstrcpyW(buffer, deviceW); 918 lstrcatW(buffer, cmW); 919 StringFromGUID2(&CLSID_VideoCompressorCategory, buffer + lstrlenW(buffer), CHARS_IN_GUID); 920 lstrcatW(buffer, backslashW); 921 lstrcatW(buffer, name); 922 923 mon = check_display_name(parser, buffer); 924 925 hr = IMoniker_BindToStorage(mon, NULL, NULL, &IID_IPropertyBag, (void **)&prop_bag); 926 ok(hr == S_OK, "BindToStorage failed: %#x\n", hr); 927 928 VariantInit(&var); 929 hr = IPropertyBag_Read(prop_bag, friendly_name, &var, NULL); 930 ok(hr == S_OK, "Read failed: %#x\n", hr); 931 932 ok(!lstrcmpW(info.szDescription, V_BSTR(&var)), "expected %s, got %s\n", 933 wine_dbgstr_w(info.szDescription), wine_dbgstr_w(V_BSTR(&var))); 934 935 VariantClear(&var); 936 hr = IPropertyBag_Read(prop_bag, clsidW, &var, NULL); 937 ok(hr == S_OK, "Read failed: %#x\n", hr); 938 939 StringFromGUID2(&CLSID_AVICo, buffer, CHARS_IN_GUID); 940 ok(!lstrcmpW(buffer, V_BSTR(&var)), "expected %s, got %s\n", 941 wine_dbgstr_w(buffer), wine_dbgstr_w(V_BSTR(&var))); 942 943 VariantClear(&var); 944 hr = IPropertyBag_Read(prop_bag, fcchandlerW, &var, NULL); 945 ok(hr == S_OK, "Read failed: %#x\n", hr); 946 ok(!lstrcmpW(name, V_BSTR(&var)), "expected %s, got %s\n", 947 wine_dbgstr_w(name), wine_dbgstr_w(V_BSTR(&var))); 948 949 IPropertyBag_Release(prop_bag); 950 IMoniker_Release(mon); 951 } 952 953 IParseDisplayName_Release(parser); 954 } 955 956 START_TEST(devenum) 957 { 958 IBindCtx *bind_ctx = NULL; 959 HRESULT hr; 960 961 CoInitialize(NULL); 962 963 test_devenum(NULL); 964 965 /* IBindCtx is allowed in IMoniker_BindToStorage (IMediaCatMoniker_BindToStorage) */ 966 hr = CreateBindCtx(0, &bind_ctx); 967 ok(hr == S_OK, "Cannot create BindCtx: (res = 0x%x)\n", hr); 968 if (bind_ctx) { 969 test_devenum(bind_ctx); 970 IBindCtx_Release(bind_ctx); 971 } 972 973 test_moniker_isequal(); 974 test_register_filter(); 975 test_directshow_filter(); 976 test_codec(); 977 978 test_legacy_filter(); 979 hr = DirectSoundEnumerateW(test_dsound, NULL); 980 ok(hr == S_OK, "got %#x\n", hr); 981 test_waveout(); 982 test_wavein(); 983 test_midiout(); 984 test_vfw(); 985 986 CoUninitialize(); 987 } 988