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 */
DEVENUM_ICreateDevEnum_QueryInterface(ICreateDevEnum * iface,REFIID riid,void ** ppv)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 */
DEVENUM_ICreateDevEnum_AddRef(ICreateDevEnum * iface)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 */
DEVENUM_ICreateDevEnum_Release(ICreateDevEnum * iface)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
register_codec(const GUID * class,const WCHAR * name,const GUID * clsid,const WCHAR * friendly_name,IPropertyBag ** ret)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
DEVENUM_ReadPinTypes(HKEY hkeyPinKey,REGFILTERPINS2 * rgPin)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
DEVENUM_ReadPins(HKEY hkeyFilterClass,REGFILTER2 * rgf2)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
free_regfilter2(REGFILTER2 * rgf)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
write_filter_data(IPropertyBag * prop_bag,REGFILTER2 * rgf)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
register_legacy_filters(void)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
register_dsound_devices(GUID * guid,const WCHAR * desc,const WCHAR * module,void * context)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
register_waveout_devices(void)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
register_wavein_devices(void)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
register_midiout_devices(void)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
register_vfw_codecs(void)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
register_avicap_devices(void)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 */
DEVENUM_ICreateDevEnum_CreateClassEnumerator(ICreateDevEnum * iface,REFCLSID class,IEnumMoniker ** out,DWORD flags)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(®ister_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 */
DEVENUM_CreateAMCategoryKey(const CLSID * clsidCategory)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