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