server: Add a helper for queuing pipe message.
[wine.git] / dlls / devenum / createdevenum.c
blob6884ffc6cae1a2450ee479af568fd5b688736595
1 /*
2 * ICreateDevEnum implementation for DEVENUM.dll
4 * Copyright (C) 2002 Robert Shearman
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.
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.
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
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
26 #define NONAMELESSSTRUCT
27 #define NONAMELESSUNION
29 #include "devenum_private.h"
30 #include "vfw.h"
31 #include "aviriff.h"
32 #include "dsound.h"
34 #include "wine/debug.h"
35 #include "wine/unicode.h"
36 #include "wine/heap.h"
37 #include "mmddk.h"
39 #include "initguid.h"
40 #include "fil_data.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(devenum);
44 extern HINSTANCE DEVENUM_hInstance;
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 wszWaveInID[] = {'W','a','v','e','I','n','I','D',0};
56 static const WCHAR wszFilterData[] = {'F','i','l','t','e','r','D','a','t','a',0};
58 static ULONG WINAPI DEVENUM_ICreateDevEnum_AddRef(ICreateDevEnum * iface);
59 static HRESULT register_codecs(void);
60 static HRESULT DEVENUM_CreateAMCategoryKey(const CLSID * clsidCategory);
62 /**********************************************************************
63 * DEVENUM_ICreateDevEnum_QueryInterface (also IUnknown)
65 static HRESULT WINAPI DEVENUM_ICreateDevEnum_QueryInterface(ICreateDevEnum *iface, REFIID riid,
66 void **ppv)
68 TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
70 if (!ppv)
71 return E_POINTER;
73 if (IsEqualGUID(riid, &IID_IUnknown) ||
74 IsEqualGUID(riid, &IID_ICreateDevEnum))
76 *ppv = iface;
77 DEVENUM_ICreateDevEnum_AddRef(iface);
78 return S_OK;
81 FIXME("- no interface IID: %s\n", debugstr_guid(riid));
82 *ppv = NULL;
83 return E_NOINTERFACE;
86 /**********************************************************************
87 * DEVENUM_ICreateDevEnum_AddRef (also IUnknown)
89 static ULONG WINAPI DEVENUM_ICreateDevEnum_AddRef(ICreateDevEnum * iface)
91 TRACE("\n");
93 DEVENUM_LockModule();
95 return 2; /* non-heap based object */
98 /**********************************************************************
99 * DEVENUM_ICreateDevEnum_Release (also IUnknown)
101 static ULONG WINAPI DEVENUM_ICreateDevEnum_Release(ICreateDevEnum * iface)
103 TRACE("\n");
105 DEVENUM_UnlockModule();
107 return 1; /* non-heap based object */
110 static HKEY open_special_category_key(const CLSID *clsid, BOOL create)
112 WCHAR key_name[sizeof(wszActiveMovieKey)/sizeof(WCHAR) + CHARS_IN_GUID-1];
113 HKEY ret;
114 LONG res;
116 strcpyW(key_name, wszActiveMovieKey);
117 if (!StringFromGUID2(clsid, key_name + sizeof(wszActiveMovieKey)/sizeof(WCHAR)-1, CHARS_IN_GUID))
118 return NULL;
120 if(create)
121 res = RegCreateKeyW(HKEY_CURRENT_USER, key_name, &ret);
122 else
123 res = RegOpenKeyExW(HKEY_CURRENT_USER, key_name, 0, KEY_READ, &ret);
124 if (res != ERROR_SUCCESS) {
125 WARN("Could not open %s\n", debugstr_w(key_name));
126 return NULL;
129 return ret;
132 static HRESULT register_codec(const CLSID *class, const WCHAR *name, IMoniker **ret)
134 static const WCHAR deviceW[] = {'@','d','e','v','i','c','e',':','c','m',':',0};
135 IParseDisplayName *parser;
136 WCHAR *buffer;
137 ULONG eaten;
138 HRESULT hr;
140 hr = CoCreateInstance(&CLSID_CDeviceMoniker, NULL, CLSCTX_INPROC, &IID_IParseDisplayName, (void **)&parser);
141 if (FAILED(hr))
142 return hr;
144 buffer = heap_alloc((strlenW(deviceW) + CHARS_IN_GUID + strlenW(name) + 1) * sizeof(WCHAR));
145 if (!buffer)
147 IParseDisplayName_Release(parser);
148 return E_OUTOFMEMORY;
151 strcpyW(buffer, deviceW);
152 StringFromGUID2(class, buffer + strlenW(buffer), CHARS_IN_GUID);
153 strcatW(buffer, backslashW);
154 strcatW(buffer, name);
156 hr = IParseDisplayName_ParseDisplayName(parser, NULL, buffer, &eaten, ret);
157 IParseDisplayName_Release(parser);
158 heap_free(buffer);
159 return hr;
162 static void DEVENUM_ReadPinTypes(HKEY hkeyPinKey, REGFILTERPINS2 *rgPin)
164 HKEY hkeyTypes = NULL;
165 DWORD dwMajorTypes, i;
166 REGPINTYPES *lpMediaType = NULL;
167 DWORD dwMediaTypeSize = 0;
169 if (RegOpenKeyExW(hkeyPinKey, wszTypes, 0, KEY_READ, &hkeyTypes) != ERROR_SUCCESS)
170 return ;
172 if (RegQueryInfoKeyW(hkeyTypes, NULL, NULL, NULL, &dwMajorTypes, NULL, NULL, NULL, NULL, NULL, NULL, NULL)
173 != ERROR_SUCCESS)
175 RegCloseKey(hkeyTypes);
176 return ;
179 for (i = 0; i < dwMajorTypes; i++)
181 HKEY hkeyMajorType = NULL;
182 WCHAR wszMajorTypeName[64];
183 DWORD cName = sizeof(wszMajorTypeName) / sizeof(WCHAR);
184 DWORD dwMinorTypes, i1;
186 if (RegEnumKeyExW(hkeyTypes, i, wszMajorTypeName, &cName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) continue;
188 if (RegOpenKeyExW(hkeyTypes, wszMajorTypeName, 0, KEY_READ, &hkeyMajorType) != ERROR_SUCCESS) continue;
190 if (RegQueryInfoKeyW(hkeyMajorType, NULL, NULL, NULL, &dwMinorTypes, NULL, NULL, NULL, NULL, NULL, NULL, NULL)
191 != ERROR_SUCCESS)
193 RegCloseKey(hkeyMajorType);
194 continue;
197 for (i1 = 0; i1 < dwMinorTypes; i1++)
199 WCHAR wszMinorTypeName[64];
200 CLSID *clsMajorType = NULL, *clsMinorType = NULL;
201 HRESULT hr;
203 cName = sizeof(wszMinorTypeName) / sizeof(WCHAR);
204 if (RegEnumKeyExW(hkeyMajorType, i1, wszMinorTypeName, &cName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) continue;
206 clsMinorType = CoTaskMemAlloc(sizeof(CLSID));
207 if (!clsMinorType) continue;
209 clsMajorType = CoTaskMemAlloc(sizeof(CLSID));
210 if (!clsMajorType) goto error_cleanup_types;
212 hr = CLSIDFromString(wszMinorTypeName, clsMinorType);
213 if (FAILED(hr)) goto error_cleanup_types;
215 hr = CLSIDFromString(wszMajorTypeName, clsMajorType);
216 if (FAILED(hr)) goto error_cleanup_types;
218 if (rgPin->nMediaTypes == dwMediaTypeSize)
220 DWORD dwNewSize = dwMediaTypeSize + (dwMediaTypeSize < 2 ? 1 : dwMediaTypeSize / 2);
221 REGPINTYPES *lpNewMediaType;
223 lpNewMediaType = CoTaskMemRealloc(lpMediaType, sizeof(REGPINTYPES) * dwNewSize);
224 if (!lpNewMediaType) goto error_cleanup_types;
226 lpMediaType = lpNewMediaType;
227 dwMediaTypeSize = dwNewSize;
230 lpMediaType[rgPin->nMediaTypes].clsMajorType = clsMajorType;
231 lpMediaType[rgPin->nMediaTypes].clsMinorType = clsMinorType;
232 rgPin->nMediaTypes++;
233 continue;
235 error_cleanup_types:
237 CoTaskMemFree(clsMajorType);
238 CoTaskMemFree(clsMinorType);
241 RegCloseKey(hkeyMajorType);
244 RegCloseKey(hkeyTypes);
246 if (lpMediaType && !rgPin->nMediaTypes)
248 CoTaskMemFree(lpMediaType);
249 lpMediaType = NULL;
252 rgPin->lpMediaType = lpMediaType;
255 static void DEVENUM_ReadPins(HKEY hkeyFilterClass, REGFILTER2 *rgf2)
257 HKEY hkeyPins = NULL;
258 DWORD dwPinsSubkeys, i;
259 REGFILTERPINS2 *rgPins = NULL;
261 rgf2->dwVersion = 2;
262 rgf2->u.s2.cPins2 = 0;
263 rgf2->u.s2.rgPins2 = NULL;
265 if (RegOpenKeyExW(hkeyFilterClass, wszPins, 0, KEY_READ, &hkeyPins) != ERROR_SUCCESS)
266 return ;
268 if (RegQueryInfoKeyW(hkeyPins, NULL, NULL, NULL, &dwPinsSubkeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL)
269 != ERROR_SUCCESS)
271 RegCloseKey(hkeyPins);
272 return ;
275 if (dwPinsSubkeys)
277 rgPins = CoTaskMemAlloc(sizeof(REGFILTERPINS2) * dwPinsSubkeys);
278 if (!rgPins)
280 RegCloseKey(hkeyPins);
281 return ;
285 for (i = 0; i < dwPinsSubkeys; i++)
287 HKEY hkeyPinKey = NULL;
288 WCHAR wszPinName[MAX_PATH];
289 DWORD cName = sizeof(wszPinName) / sizeof(WCHAR);
290 REGFILTERPINS2 *rgPin = &rgPins[rgf2->u.s2.cPins2];
291 DWORD value, size, Type;
292 LONG lRet;
294 memset(rgPin, 0, sizeof(*rgPin));
296 if (RegEnumKeyExW(hkeyPins, i, wszPinName, &cName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) continue;
298 if (RegOpenKeyExW(hkeyPins, wszPinName, 0, KEY_READ, &hkeyPinKey) != ERROR_SUCCESS) continue;
300 size = sizeof(DWORD);
301 lRet = RegQueryValueExW(hkeyPinKey, wszAllowedMany, NULL, &Type, (BYTE *)&value, &size);
302 if (lRet != ERROR_SUCCESS || Type != REG_DWORD)
303 goto error_cleanup;
304 if (value)
305 rgPin->dwFlags |= REG_PINFLAG_B_MANY;
307 size = sizeof(DWORD);
308 lRet = RegQueryValueExW(hkeyPinKey, wszAllowedZero, NULL, &Type, (BYTE *)&value, &size);
309 if (lRet != ERROR_SUCCESS || Type != REG_DWORD)
310 goto error_cleanup;
311 if (value)
312 rgPin->dwFlags |= REG_PINFLAG_B_ZERO;
314 size = sizeof(DWORD);
315 lRet = RegQueryValueExW(hkeyPinKey, wszDirection, NULL, &Type, (BYTE *)&value, &size);
316 if (lRet != ERROR_SUCCESS || Type != REG_DWORD)
317 goto error_cleanup;
318 if (value)
319 rgPin->dwFlags |= REG_PINFLAG_B_OUTPUT;
322 size = sizeof(DWORD);
323 lRet = RegQueryValueExW(hkeyPinKey, wszIsRendered, NULL, &Type, (BYTE *)&value, &size);
324 if (lRet != ERROR_SUCCESS || Type != REG_DWORD)
325 goto error_cleanup;
326 if (value)
327 rgPin->dwFlags |= REG_PINFLAG_B_RENDERER;
329 DEVENUM_ReadPinTypes(hkeyPinKey, rgPin);
331 ++rgf2->u.s2.cPins2;
332 continue;
334 error_cleanup:
336 RegCloseKey(hkeyPinKey);
339 RegCloseKey(hkeyPins);
341 if (rgPins && !rgf2->u.s2.cPins2)
343 CoTaskMemFree(rgPins);
344 rgPins = NULL;
347 rgf2->u.s2.rgPins2 = rgPins;
350 static void free_regfilter2(REGFILTER2 *rgf)
352 if (rgf->u.s2.rgPins2)
354 UINT iPin;
356 for (iPin = 0; iPin < rgf->u.s2.cPins2; iPin++)
358 if (rgf->u.s2.rgPins2[iPin].lpMediaType)
360 UINT iType;
362 for (iType = 0; iType < rgf->u.s2.rgPins2[iPin].nMediaTypes; iType++)
364 CoTaskMemFree((void *)rgf->u.s2.rgPins2[iPin].lpMediaType[iType].clsMajorType);
365 CoTaskMemFree((void *)rgf->u.s2.rgPins2[iPin].lpMediaType[iType].clsMinorType);
368 CoTaskMemFree((void *)rgf->u.s2.rgPins2[iPin].lpMediaType);
372 CoTaskMemFree((void *)rgf->u.s2.rgPins2);
376 static void write_filter_data(IPropertyBag *prop_bag, REGFILTER2 *rgf)
378 IAMFilterData *fildata;
379 SAFEARRAYBOUND sabound;
380 BYTE *data, *array;
381 VARIANT var = {};
382 ULONG size;
383 HRESULT hr;
385 hr = CoCreateInstance(&CLSID_FilterMapper2, NULL, CLSCTX_INPROC, &IID_IAMFilterData, (void **)&fildata);
386 if (FAILED(hr)) goto cleanup;
388 hr = IAMFilterData_CreateFilterData(fildata, rgf, &data, &size);
389 if (FAILED(hr)) goto cleanup;
391 V_VT(&var) = VT_ARRAY | VT_UI1;
392 sabound.lLbound = 0;
393 sabound.cElements = size;
394 if (!(V_ARRAY(&var) = SafeArrayCreate(VT_UI1, 1, &sabound)))
395 goto cleanup;
396 hr = SafeArrayAccessData(V_ARRAY(&var), (void *)&array);
397 if (FAILED(hr)) goto cleanup;
399 memcpy(array, data, size);
400 hr = SafeArrayUnaccessData(V_ARRAY(&var));
401 if (FAILED(hr)) goto cleanup;
403 hr = IPropertyBag_Write(prop_bag, wszFilterData, &var);
404 if (FAILED(hr)) goto cleanup;
406 cleanup:
407 VariantClear(&var);
408 CoTaskMemFree(data);
409 IAMFilterData_Release(fildata);
412 static void register_legacy_filters(void)
414 HKEY hkeyFilter = NULL;
415 DWORD dwFilterSubkeys, i;
416 LONG lRet;
417 HRESULT hr;
419 lRet = RegOpenKeyExW(HKEY_CLASSES_ROOT, wszFilterKeyName, 0, KEY_READ, &hkeyFilter);
420 hr = HRESULT_FROM_WIN32(lRet);
422 if (SUCCEEDED(hr))
424 lRet = RegQueryInfoKeyW(hkeyFilter, NULL, NULL, NULL, &dwFilterSubkeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
425 hr = HRESULT_FROM_WIN32(lRet);
428 if (SUCCEEDED(hr))
429 hr = DEVENUM_CreateAMCategoryKey(&CLSID_LegacyAmFilterCategory);
431 if (SUCCEEDED(hr))
433 for (i = 0; i < dwFilterSubkeys; i++)
435 WCHAR wszFilterSubkeyName[64];
436 DWORD cName = sizeof(wszFilterSubkeyName) / sizeof(WCHAR);
437 IPropertyBag *prop_bag = NULL;
438 WCHAR wszRegKey[MAX_PATH];
439 HKEY classkey = NULL;
440 IMoniker *mon = NULL;
441 VARIANT var = {};
442 REGFILTER2 rgf2;
443 DWORD Type, len;
445 if (RegEnumKeyExW(hkeyFilter, i, wszFilterSubkeyName, &cName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) continue;
447 TRACE("Registering %s\n", debugstr_w(wszFilterSubkeyName));
449 strcpyW(wszRegKey, clsidW);
450 strcatW(wszRegKey, wszFilterSubkeyName);
452 if (RegOpenKeyExW(HKEY_CLASSES_ROOT, wszRegKey, 0, KEY_READ, &classkey) != ERROR_SUCCESS)
453 continue;
455 hr = register_codec(&CLSID_LegacyAmFilterCategory, wszFilterSubkeyName, &mon);
456 if (FAILED(hr)) goto cleanup;
458 hr = IMoniker_BindToStorage(mon, NULL, NULL, &IID_IPropertyBag, (void **)&prop_bag);
459 if (FAILED(hr)) goto cleanup;
461 /* write friendly name */
462 len = 0;
463 V_VT(&var) = VT_BSTR;
464 if (!RegQueryValueExW(classkey, NULL, NULL, &Type, NULL, &len))
466 WCHAR *friendlyname = heap_alloc(len);
467 if (!friendlyname)
468 goto cleanup;
469 RegQueryValueExW(classkey, NULL, NULL, &Type, (BYTE *)friendlyname, &len);
470 V_BSTR(&var) = SysAllocStringLen(friendlyname, len/sizeof(WCHAR));
471 heap_free(friendlyname);
473 else
474 V_BSTR(&var) = SysAllocString(wszFilterSubkeyName);
476 if (!V_BSTR(&var))
477 goto cleanup;
478 hr = IPropertyBag_Write(prop_bag, wszFriendlyName, &var);
479 if (FAILED(hr)) goto cleanup;
480 VariantClear(&var);
482 /* write clsid */
483 V_VT(&var) = VT_BSTR;
484 if (!(V_BSTR(&var) = SysAllocString(wszFilterSubkeyName)))
485 goto cleanup;
486 hr = IPropertyBag_Write(prop_bag, clsid_keyname, &var);
487 if (FAILED(hr)) goto cleanup;
488 VariantClear(&var);
490 /* write filter data */
491 rgf2.dwMerit = MERIT_NORMAL;
493 len = sizeof(rgf2.dwMerit);
494 RegQueryValueExW(classkey, wszMeritName, NULL, &Type, (BYTE *)&rgf2.dwMerit, &len);
496 DEVENUM_ReadPins(classkey, &rgf2);
498 write_filter_data(prop_bag, &rgf2);
500 cleanup:
501 if (prop_bag) IPropertyBag_Release(prop_bag);
502 if (mon) IMoniker_Release(mon);
503 RegCloseKey(classkey);
504 VariantClear(&var);
505 free_regfilter2(&rgf2);
509 if (hkeyFilter) RegCloseKey(hkeyFilter);
512 static BOOL CALLBACK register_dsound_devices(GUID *guid, const WCHAR *desc, const WCHAR *module, void *context)
514 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};
515 static const WCHAR directsoundW[] = {'D','i','r','e','c','t','S','o','u','n','d',':',' ',0};
516 static const WCHAR dsguidW[] = {'D','S','G','u','i','d',0};
517 IPropertyBag *prop_bag = NULL;
518 REGFILTERPINS2 rgpins = {0};
519 REGPINTYPES rgtypes = {0};
520 REGFILTER2 rgf = {0};
521 WCHAR clsid[CHARS_IN_GUID];
522 IMoniker *mon = NULL;
523 VARIANT var;
524 HRESULT hr;
526 hr = DEVENUM_CreateAMCategoryKey(&CLSID_AudioRendererCategory);
527 if (FAILED(hr)) goto cleanup;
529 V_VT(&var) = VT_BSTR;
530 if (guid)
532 WCHAR *name = heap_alloc(sizeof(defaultW) + strlenW(desc) * sizeof(WCHAR));
533 if (!name)
534 goto cleanup;
535 strcpyW(name, directsoundW);
536 strcatW(name, desc);
538 V_BSTR(&var) = SysAllocString(name);
539 heap_free(name);
541 else
542 V_BSTR(&var) = SysAllocString(defaultW);
544 if (!V_BSTR(&var))
545 goto cleanup;
547 hr = register_codec(&CLSID_AudioRendererCategory, V_BSTR(&var), &mon);
548 if (FAILED(hr)) goto cleanup;
550 hr = IMoniker_BindToStorage(mon, NULL, NULL, &IID_IPropertyBag, (void **)&prop_bag);
551 if (FAILED(hr)) goto cleanup;
553 /* write friendly name */
554 hr = IPropertyBag_Write(prop_bag, wszFriendlyName, &var);
555 if (FAILED(hr)) goto cleanup;
556 VariantClear(&var);
558 /* write clsid */
559 V_VT(&var) = VT_BSTR;
560 StringFromGUID2(&CLSID_DSoundRender, clsid, CHARS_IN_GUID);
561 if (!(V_BSTR(&var) = SysAllocString(clsid)))
562 goto cleanup;
563 hr = IPropertyBag_Write(prop_bag, clsid_keyname, &var);
564 if (FAILED(hr)) goto cleanup;
565 VariantClear(&var);
567 /* write filter data */
568 rgf.dwVersion = 2;
569 rgf.dwMerit = guid ? MERIT_DO_NOT_USE : MERIT_PREFERRED;
570 rgf.u.s2.cPins2 = 1;
571 rgf.u.s2.rgPins2 = &rgpins;
572 rgpins.dwFlags = REG_PINFLAG_B_RENDERER;
573 /* FIXME: native registers many more formats */
574 rgpins.nMediaTypes = 1;
575 rgpins.lpMediaType = &rgtypes;
576 rgtypes.clsMajorType = &MEDIATYPE_Audio;
577 rgtypes.clsMinorType = &MEDIASUBTYPE_PCM;
579 write_filter_data(prop_bag, &rgf);
581 /* write DSound guid */
582 V_VT(&var) = VT_BSTR;
583 StringFromGUID2(guid ? guid : &GUID_NULL, clsid, CHARS_IN_GUID);
584 if (!(V_BSTR(&var) = SysAllocString(clsid)))
585 goto cleanup;
586 hr = IPropertyBag_Write(prop_bag, dsguidW, &var);
587 if (FAILED(hr)) goto cleanup;
589 cleanup:
590 VariantClear(&var);
591 if (prop_bag) IPropertyBag_Release(prop_bag);
592 if (mon) IMoniker_Release(mon);
594 return TRUE;
597 /**********************************************************************
598 * DEVENUM_ICreateDevEnum_CreateClassEnumerator
600 static HRESULT WINAPI DEVENUM_ICreateDevEnum_CreateClassEnumerator(
601 ICreateDevEnum * iface,
602 REFCLSID clsidDeviceClass,
603 IEnumMoniker **ppEnumMoniker,
604 DWORD dwFlags)
606 HRESULT hr;
608 TRACE("(%p)->(%s, %p, %x)\n", iface, debugstr_guid(clsidDeviceClass), ppEnumMoniker, dwFlags);
610 if (!ppEnumMoniker)
611 return E_POINTER;
613 *ppEnumMoniker = NULL;
615 register_codecs();
616 register_legacy_filters();
617 hr = DirectSoundEnumerateW(&register_dsound_devices, NULL);
618 if (FAILED(hr)) return hr;
620 return create_EnumMoniker(clsidDeviceClass, ppEnumMoniker);
623 /**********************************************************************
624 * ICreateDevEnum_Vtbl
626 static const ICreateDevEnumVtbl ICreateDevEnum_Vtbl =
628 DEVENUM_ICreateDevEnum_QueryInterface,
629 DEVENUM_ICreateDevEnum_AddRef,
630 DEVENUM_ICreateDevEnum_Release,
631 DEVENUM_ICreateDevEnum_CreateClassEnumerator,
634 /**********************************************************************
635 * static CreateDevEnum instance
637 ICreateDevEnum DEVENUM_CreateDevEnum = { &ICreateDevEnum_Vtbl };
639 /**********************************************************************
640 * DEVENUM_CreateAMCategoryKey (INTERNAL)
642 * Creates a registry key for a category at HKEY_CURRENT_USER\Software\
643 * Microsoft\ActiveMovie\devenum\{clsid}
645 static HRESULT DEVENUM_CreateAMCategoryKey(const CLSID * clsidCategory)
647 WCHAR wszRegKey[MAX_PATH];
648 HRESULT res = S_OK;
649 HKEY hkeyDummy = NULL;
651 strcpyW(wszRegKey, wszActiveMovieKey);
653 if (!StringFromGUID2(clsidCategory, wszRegKey + strlenW(wszRegKey), sizeof(wszRegKey)/sizeof(wszRegKey[0]) - strlenW(wszRegKey)))
654 res = E_INVALIDARG;
656 if (SUCCEEDED(res))
658 LONG lRes = RegCreateKeyW(HKEY_CURRENT_USER, wszRegKey, &hkeyDummy);
659 res = HRESULT_FROM_WIN32(lRes);
662 if (hkeyDummy)
663 RegCloseKey(hkeyDummy);
665 if (FAILED(res))
666 ERR("Failed to create key HKEY_CURRENT_USER\\%s\n", debugstr_w(wszRegKey));
668 return res;
671 static void register_vfw_codecs(void)
673 WCHAR avico_clsid_str[CHARS_IN_GUID];
674 HKEY basekey, key;
675 ICINFO icinfo;
676 DWORD i, res;
678 static const WCHAR CLSIDW[] = {'C','L','S','I','D',0};
679 static const WCHAR FccHandlerW[] = {'F','c','c','H','a','n','d','l','e','r',0};
680 static const WCHAR FriendlyNameW[] = {'F','r','i','e','n','d','l','y','N','a','m','e',0};
682 StringFromGUID2(&CLSID_AVICo, avico_clsid_str, sizeof(avico_clsid_str)/sizeof(WCHAR));
684 basekey = open_special_category_key(&CLSID_VideoCompressorCategory, TRUE);
685 if(!basekey) {
686 ERR("Could not create key\n");
687 return;
690 for(i=0; ICInfo(FCC('v','i','d','c'), i, &icinfo); i++) {
691 WCHAR fcc_str[5] = {LOBYTE(LOWORD(icinfo.fccHandler)), HIBYTE(LOWORD(icinfo.fccHandler)),
692 LOBYTE(HIWORD(icinfo.fccHandler)), HIBYTE(HIWORD(icinfo.fccHandler))};
694 res = RegCreateKeyW(basekey, fcc_str, &key);
695 if(res != ERROR_SUCCESS)
696 continue;
698 RegSetValueExW(key, CLSIDW, 0, REG_SZ, (const BYTE*)avico_clsid_str, sizeof(avico_clsid_str));
699 RegSetValueExW(key, FccHandlerW, 0, REG_SZ, (const BYTE*)fcc_str, sizeof(fcc_str));
700 RegSetValueExW(key, FriendlyNameW, 0, REG_SZ, (const BYTE*)icinfo.szName, (strlenW(icinfo.szName)+1)*sizeof(WCHAR));
701 /* FIXME: Set ClassManagerFlags and FilterData values */
703 RegCloseKey(key);
706 RegCloseKey(basekey);
709 static HRESULT register_codecs(void)
711 HRESULT res;
712 WCHAR class[CHARS_IN_GUID];
713 DWORD iDefaultDevice = -1;
714 UINT numDevs;
715 IFilterMapper2 * pMapper = NULL;
716 REGFILTER2 rf2;
717 REGFILTERPINS2 rfp2;
718 HKEY basekey;
720 /* Since devices can change between session, for example because you just plugged in a webcam
721 * or switched from pulseaudio to alsa, delete all old devices first
723 RegOpenKeyW(HKEY_CURRENT_USER, wszActiveMovieKey, &basekey);
724 StringFromGUID2(&CLSID_LegacyAmFilterCategory, class, CHARS_IN_GUID);
725 RegDeleteTreeW(basekey, class);
726 StringFromGUID2(&CLSID_AudioRendererCategory, class, CHARS_IN_GUID);
727 RegDeleteTreeW(basekey, class);
728 StringFromGUID2(&CLSID_AudioInputDeviceCategory, class, CHARS_IN_GUID);
729 RegDeleteTreeW(basekey, class);
730 StringFromGUID2(&CLSID_VideoInputDeviceCategory, class, CHARS_IN_GUID);
731 RegDeleteTreeW(basekey, class);
732 StringFromGUID2(&CLSID_MidiRendererCategory, class, CHARS_IN_GUID);
733 RegDeleteTreeW(basekey, class);
734 StringFromGUID2(&CLSID_VideoCompressorCategory, class, CHARS_IN_GUID);
735 RegDeleteTreeW(basekey, class);
736 RegCloseKey(basekey);
738 rf2.dwVersion = 2;
739 rf2.dwMerit = MERIT_PREFERRED;
740 rf2.u.s2.cPins2 = 1;
741 rf2.u.s2.rgPins2 = &rfp2;
742 rfp2.cInstances = 1;
743 rfp2.nMediums = 0;
744 rfp2.lpMedium = NULL;
745 rfp2.clsPinCategory = &IID_NULL;
747 res = CoCreateInstance(&CLSID_FilterMapper2, NULL, CLSCTX_INPROC,
748 &IID_IFilterMapper2, (void **) &pMapper);
750 * Fill in info for devices
752 if (SUCCEEDED(res))
754 UINT i;
755 WAVEOUTCAPSW wocaps;
756 WAVEINCAPSW wicaps;
757 MIDIOUTCAPSW mocaps;
758 REGPINTYPES * pTypes;
759 IPropertyBag * pPropBag = NULL;
761 numDevs = waveOutGetNumDevs();
763 res = DEVENUM_CreateAMCategoryKey(&CLSID_AudioRendererCategory);
764 if (FAILED(res)) /* can't register any devices in this category */
765 numDevs = 0;
767 rfp2.dwFlags = REG_PINFLAG_B_RENDERER;
768 for (i = 0; i < numDevs; i++)
770 if (waveOutGetDevCapsW(i, &wocaps, sizeof(WAVEOUTCAPSW))
771 == MMSYSERR_NOERROR)
773 IMoniker * pMoniker = NULL;
775 rfp2.nMediaTypes = 1;
776 pTypes = CoTaskMemAlloc(rfp2.nMediaTypes * sizeof(REGPINTYPES));
777 if (!pTypes)
779 IFilterMapper2_Release(pMapper);
780 return E_OUTOFMEMORY;
782 /* FIXME: Native devenum seems to register a lot more types for
783 * DSound than we do. Not sure what purpose they serve */
784 pTypes[0].clsMajorType = &MEDIATYPE_Audio;
785 pTypes[0].clsMinorType = &MEDIASUBTYPE_PCM;
787 rfp2.lpMediaType = pTypes;
789 res = IFilterMapper2_RegisterFilter(pMapper,
790 &CLSID_AudioRender,
791 wocaps.szPname,
792 &pMoniker,
793 &CLSID_AudioRendererCategory,
794 wocaps.szPname,
795 &rf2);
797 /* FIXME: do additional stuff with IMoniker here, depending on what RegisterFilter does */
799 if (pMoniker)
800 IMoniker_Release(pMoniker);
802 if (i == iDefaultDevice)
804 FIXME("Default device\n");
807 CoTaskMemFree(pTypes);
811 numDevs = waveInGetNumDevs();
813 res = DEVENUM_CreateAMCategoryKey(&CLSID_AudioInputDeviceCategory);
814 if (FAILED(res)) /* can't register any devices in this category */
815 numDevs = 0;
817 rfp2.dwFlags = REG_PINFLAG_B_OUTPUT;
818 for (i = 0; i < numDevs; i++)
820 if (waveInGetDevCapsW(i, &wicaps, sizeof(WAVEINCAPSW))
821 == MMSYSERR_NOERROR)
823 IMoniker * pMoniker = NULL;
825 rfp2.nMediaTypes = 1;
826 pTypes = CoTaskMemAlloc(rfp2.nMediaTypes * sizeof(REGPINTYPES));
827 if (!pTypes)
829 IFilterMapper2_Release(pMapper);
830 return E_OUTOFMEMORY;
833 /* FIXME: Not sure if these are correct */
834 pTypes[0].clsMajorType = &MEDIATYPE_Audio;
835 pTypes[0].clsMinorType = &MEDIASUBTYPE_PCM;
837 rfp2.lpMediaType = pTypes;
839 res = IFilterMapper2_RegisterFilter(pMapper,
840 &CLSID_AudioRecord,
841 wicaps.szPname,
842 &pMoniker,
843 &CLSID_AudioInputDeviceCategory,
844 wicaps.szPname,
845 &rf2);
848 if (pMoniker) {
849 VARIANT var;
851 V_VT(&var) = VT_I4;
852 V_I4(&var) = i;
853 res = IMoniker_BindToStorage(pMoniker, NULL, NULL, &IID_IPropertyBag, (LPVOID)&pPropBag);
854 if (SUCCEEDED(res))
855 res = IPropertyBag_Write(pPropBag, wszWaveInID, &var);
856 else
857 pPropBag = NULL;
859 V_VT(&var) = VT_LPWSTR;
860 V_BSTR(&var) = wicaps.szPname;
861 if (SUCCEEDED(res))
862 res = IPropertyBag_Write(pPropBag, wszFriendlyName, &var);
864 if (pPropBag)
865 IPropertyBag_Release(pPropBag);
866 IMoniker_Release(pMoniker);
869 CoTaskMemFree(pTypes);
873 numDevs = midiOutGetNumDevs();
875 res = DEVENUM_CreateAMCategoryKey(&CLSID_MidiRendererCategory);
876 if (FAILED(res)) /* can't register any devices in this category */
877 numDevs = 0;
879 rfp2.dwFlags = REG_PINFLAG_B_RENDERER;
880 for (i = 0; i < numDevs; i++)
882 if (midiOutGetDevCapsW(i, &mocaps, sizeof(MIDIOUTCAPSW))
883 == MMSYSERR_NOERROR)
885 IMoniker * pMoniker = NULL;
887 rfp2.nMediaTypes = 1;
888 pTypes = CoTaskMemAlloc(rfp2.nMediaTypes * sizeof(REGPINTYPES));
889 if (!pTypes)
891 IFilterMapper2_Release(pMapper);
892 return E_OUTOFMEMORY;
895 /* FIXME: Not sure if these are correct */
896 pTypes[0].clsMajorType = &MEDIATYPE_Midi;
897 pTypes[0].clsMinorType = &MEDIASUBTYPE_None;
899 rfp2.lpMediaType = pTypes;
901 res = IFilterMapper2_RegisterFilter(pMapper,
902 &CLSID_AVIMIDIRender,
903 mocaps.szPname,
904 &pMoniker,
905 &CLSID_MidiRendererCategory,
906 mocaps.szPname,
907 &rf2);
909 /* FIXME: do additional stuff with IMoniker here, depending on what RegisterFilter does */
910 /* Native version sets MidiOutId */
912 if (pMoniker)
913 IMoniker_Release(pMoniker);
915 if (i == iDefaultDevice)
917 FIXME("Default device\n");
920 CoTaskMemFree(pTypes);
923 res = DEVENUM_CreateAMCategoryKey(&CLSID_VideoInputDeviceCategory);
924 if (SUCCEEDED(res))
925 for (i = 0; i < 10; i++)
927 WCHAR szDeviceName[32], szDeviceVersion[32], szDevicePath[10];
929 if (capGetDriverDescriptionW ((WORD) i,
930 szDeviceName, sizeof(szDeviceName)/sizeof(WCHAR),
931 szDeviceVersion, sizeof(szDeviceVersion)/sizeof(WCHAR)))
933 IMoniker * pMoniker = NULL;
934 WCHAR dprintf[] = { 'v','i','d','e','o','%','d',0 };
935 snprintfW(szDevicePath, sizeof(szDevicePath)/sizeof(WCHAR), dprintf, i);
936 /* The above code prevents 1 device with a different ID overwriting another */
938 rfp2.nMediaTypes = 1;
939 pTypes = CoTaskMemAlloc(rfp2.nMediaTypes * sizeof(REGPINTYPES));
940 if (!pTypes) {
941 IFilterMapper2_Release(pMapper);
942 return E_OUTOFMEMORY;
945 pTypes[0].clsMajorType = &MEDIATYPE_Video;
946 pTypes[0].clsMinorType = &MEDIASUBTYPE_None;
948 rfp2.lpMediaType = pTypes;
950 res = IFilterMapper2_RegisterFilter(pMapper,
951 &CLSID_VfwCapture,
952 szDeviceName,
953 &pMoniker,
954 &CLSID_VideoInputDeviceCategory,
955 szDevicePath,
956 &rf2);
958 if (pMoniker) {
959 OLECHAR wszVfwIndex[] = { 'V','F','W','I','n','d','e','x',0 };
960 VARIANT var;
961 V_VT(&var) = VT_I4;
962 V_I4(&var) = i;
963 res = IMoniker_BindToStorage(pMoniker, NULL, NULL, &IID_IPropertyBag, (LPVOID)&pPropBag);
964 if (SUCCEEDED(res)) {
965 res = IPropertyBag_Write(pPropBag, wszVfwIndex, &var);
966 IPropertyBag_Release(pPropBag);
968 IMoniker_Release(pMoniker);
971 if (i == iDefaultDevice) FIXME("Default device\n");
972 CoTaskMemFree(pTypes);
977 if (pMapper)
978 IFilterMapper2_Release(pMapper);
980 register_vfw_codecs();
982 return res;