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