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