1f0005049bde50c01cd4d96ca74470ebe7e5bde5
[reactos.git] / dll / directx / wine / devenum / createdevenum.c
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 }