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