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