From f5c531bdd10ea10c78a74e45beb9009dadf70c09 Mon Sep 17 00:00:00 2001 From: Zebediah Figura Date: Tue, 6 Mar 2018 20:57:08 -0600 Subject: [PATCH] devenum: More correctly handle device types. Signed-off-by: Zebediah Figura Signed-off-by: Alexandre Julliard --- dlls/devenum/createdevenum.c | 134 +++++------------------------ dlls/devenum/devenum_private.h | 23 +++-- dlls/devenum/mediacatenum.c | 102 +++++++++++++--------- dlls/devenum/parsedisplayname.c | 42 ++++++--- dlls/devenum/tests/devenum.c | 145 +++++++++++++++++++++++++++++++- 5 files changed, 268 insertions(+), 178 deletions(-) diff --git a/dlls/devenum/createdevenum.c b/dlls/devenum/createdevenum.c index 17d98507684..7cfdbfff14d 100644 --- a/dlls/devenum/createdevenum.c +++ b/dlls/devenum/createdevenum.c @@ -38,13 +38,7 @@ WINE_DEFAULT_DEBUG_CHANNEL(devenum); extern HINSTANCE DEVENUM_hInstance; -const WCHAR wszInstanceKeyName[] ={'\\','I','n','s','t','a','n','c','e',0}; - static const WCHAR wszRegSeparator[] = {'\\', 0 }; -static const WCHAR wszActiveMovieKey[] = {'S','o','f','t','w','a','r','e','\\', - 'M','i','c','r','o','s','o','f','t','\\', - 'A','c','t','i','v','e','M','o','v','i','e','\\', - 'd','e','v','e','n','u','m','\\',0}; static const WCHAR wszFilterKeyName[] = {'F','i','l','t','e','r',0}; static const WCHAR wszMeritName[] = {'M','e','r','i','t',0}; static const WCHAR wszPins[] = {'P','i','n','s',0}; @@ -58,7 +52,7 @@ static const WCHAR wszWaveInID[] = {'W','a','v','e','I','n','I','D',0}; static const WCHAR wszWaveOutID[] = {'W','a','v','e','O','u','t','I','D',0}; static ULONG WINAPI DEVENUM_ICreateDevEnum_AddRef(ICreateDevEnum * iface); -static HRESULT DEVENUM_CreateSpecialCategories(void); +static HRESULT register_codecs(void); /********************************************************************** * DEVENUM_ICreateDevEnum_QueryInterface (also IUnknown) @@ -108,63 +102,6 @@ static ULONG WINAPI DEVENUM_ICreateDevEnum_Release(ICreateDevEnum * iface) return 1; /* non-heap based object */ } -static BOOL IsSpecialCategory(const CLSID *clsid) -{ - return IsEqualGUID(clsid, &CLSID_AudioRendererCategory) || - IsEqualGUID(clsid, &CLSID_AudioInputDeviceCategory) || - IsEqualGUID(clsid, &CLSID_VideoInputDeviceCategory) || - IsEqualGUID(clsid, &CLSID_VideoCompressorCategory) || - IsEqualGUID(clsid, &CLSID_MidiRendererCategory); -} - -HRESULT DEVENUM_GetCategoryKey(REFCLSID clsidDeviceClass, HKEY *pBaseKey, WCHAR *wszRegKeyName, UINT maxLen) -{ - if (IsSpecialCategory(clsidDeviceClass)) - { - *pBaseKey = HKEY_CURRENT_USER; - strcpyW(wszRegKeyName, wszActiveMovieKey); - - if (!StringFromGUID2(clsidDeviceClass, wszRegKeyName + strlenW(wszRegKeyName), maxLen - strlenW(wszRegKeyName))) - return E_OUTOFMEMORY; - } - else - { - *pBaseKey = HKEY_CLASSES_ROOT; - strcpyW(wszRegKeyName, clsid_keyname); - strcatW(wszRegKeyName, wszRegSeparator); - - if (!StringFromGUID2(clsidDeviceClass, wszRegKeyName + CLSID_STR_LEN, maxLen - CLSID_STR_LEN)) - return E_OUTOFMEMORY; - - strcatW(wszRegKeyName, wszInstanceKeyName); - } - - return S_OK; -} - -static HKEY open_category_key(const CLSID *clsid) -{ - WCHAR key_name[sizeof(wszInstanceKeyName)/sizeof(WCHAR) + CHARS_IN_GUID-1 + 6 /* strlen("CLSID\") */], *ptr; - HKEY ret; - - strcpyW(key_name, clsid_keyname); - ptr = key_name + strlenW(key_name); - *ptr++ = '\\'; - - if (!StringFromGUID2(clsid, ptr, CHARS_IN_GUID)) - return NULL; - - ptr += strlenW(ptr); - strcpyW(ptr, wszInstanceKeyName); - - if (RegOpenKeyExW(HKEY_CLASSES_ROOT, key_name, 0, KEY_READ, &ret) != ERROR_SUCCESS) { - WARN("Could not open %s\n", debugstr_w(key_name)); - return NULL; - } - - return ret; -} - static HKEY open_special_category_key(const CLSID *clsid, BOOL create) { WCHAR key_name[sizeof(wszActiveMovieKey)/sizeof(WCHAR) + CHARS_IN_GUID-1]; @@ -400,14 +337,13 @@ static HRESULT DEVENUM_RegisterLegacyAmFilters(void) { WCHAR wszFilterSubkeyName[64]; DWORD cName = sizeof(wszFilterSubkeyName) / sizeof(WCHAR); - HKEY hkeyCategoryBaseKey; WCHAR wszRegKey[MAX_PATH]; HKEY hkeyInstance = NULL; if (RegEnumKeyExW(hkeyFilter, i, wszFilterSubkeyName, &cName, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) continue; - hr = DEVENUM_GetCategoryKey(&CLSID_LegacyAmFilterCategory, &hkeyCategoryBaseKey, wszRegKey, MAX_PATH); - if (FAILED(hr)) continue; + strcpyW(wszRegKey, wszActiveMovieKey); + StringFromGUID2(&CLSID_LegacyAmFilterCategory, wszRegKey + strlenW(wszRegKey), CHARS_IN_GUID); strcatW(wszRegKey, wszRegSeparator); strcatW(wszRegKey, wszFilterSubkeyName); @@ -512,9 +448,6 @@ static HRESULT WINAPI DEVENUM_ICreateDevEnum_CreateClassEnumerator( IEnumMoniker **ppEnumMoniker, DWORD dwFlags) { - HKEY hkey, special_hkey = NULL; - HRESULT hr; - TRACE("(%p)->(%s, %p, %x)\n", iface, debugstr_guid(clsidDeviceClass), ppEnumMoniker, dwFlags); if (!ppEnumMoniker) @@ -522,34 +455,10 @@ static HRESULT WINAPI DEVENUM_ICreateDevEnum_CreateClassEnumerator( *ppEnumMoniker = NULL; - if (IsEqualGUID(clsidDeviceClass, &CLSID_LegacyAmFilterCategory)) - { - DEVENUM_RegisterLegacyAmFilters(); - } + register_codecs(); + DEVENUM_RegisterLegacyAmFilters(); - if (IsSpecialCategory(clsidDeviceClass)) - { - hr = DEVENUM_CreateSpecialCategories(); - if (FAILED(hr)) - return hr; - - special_hkey = open_special_category_key(clsidDeviceClass, FALSE); - if (!special_hkey) - { - ERR("Couldn't open registry key for special device: %s\n", - debugstr_guid(clsidDeviceClass)); - return S_FALSE; - } - } - - hkey = open_category_key(clsidDeviceClass); - if (!hkey && !special_hkey) - { - FIXME("Category %s not found\n", debugstr_guid(clsidDeviceClass)); - return S_FALSE; - } - - return DEVENUM_IEnumMoniker_Construct(hkey, special_hkey, ppEnumMoniker); + return create_EnumMoniker(clsidDeviceClass, ppEnumMoniker); } /********************************************************************** @@ -644,22 +553,17 @@ static const WCHAR DEVENUM_populate_handle_nameW[] = 'D','e','v','e','n','u','m','_', 'P','o','p','u','l','a','t','e',0}; -/********************************************************************** - * DEVENUM_CreateSpecialCategories (INTERNAL) - * - * Creates the keys in the registry for the dynamic categories - */ -static HRESULT DEVENUM_CreateSpecialCategories(void) +static HRESULT register_codecs(void) { HRESULT res; WCHAR szDSoundNameFormat[MAX_PATH + 1]; WCHAR szDSoundName[MAX_PATH + 1]; + WCHAR class[CHARS_IN_GUID]; DWORD iDefaultDevice = -1; UINT numDevs; IFilterMapper2 * pMapper = NULL; REGFILTER2 rf2; REGFILTERPINS2 rfp2; - WCHAR path[MAX_PATH]; HKEY basekey; if (DEVENUM_populate_handle) @@ -680,16 +584,18 @@ static HRESULT DEVENUM_CreateSpecialCategories(void) /* Since devices can change between session, for example because you just plugged in a webcam * or switched from pulseaudio to alsa, delete all old devices first */ - if (SUCCEEDED(DEVENUM_GetCategoryKey(&CLSID_AudioRendererCategory, &basekey, path, MAX_PATH))) - RegDeleteTreeW(basekey, path); - if (SUCCEEDED(DEVENUM_GetCategoryKey(&CLSID_AudioInputDeviceCategory, &basekey, path, MAX_PATH))) - RegDeleteTreeW(basekey, path); - if (SUCCEEDED(DEVENUM_GetCategoryKey(&CLSID_VideoInputDeviceCategory, &basekey, path, MAX_PATH))) - RegDeleteTreeW(basekey, path); - if (SUCCEEDED(DEVENUM_GetCategoryKey(&CLSID_MidiRendererCategory, &basekey, path, MAX_PATH))) - RegDeleteTreeW(basekey, path); - if (SUCCEEDED(DEVENUM_GetCategoryKey(&CLSID_VideoCompressorCategory, &basekey, path, MAX_PATH))) - RegDeleteTreeW(basekey, path); + RegOpenKeyW(HKEY_CURRENT_USER, wszActiveMovieKey, &basekey); + StringFromGUID2(&CLSID_AudioRendererCategory, class, CHARS_IN_GUID); + RegDeleteTreeW(basekey, class); + StringFromGUID2(&CLSID_AudioInputDeviceCategory, class, CHARS_IN_GUID); + RegDeleteTreeW(basekey, class); + StringFromGUID2(&CLSID_VideoInputDeviceCategory, class, CHARS_IN_GUID); + RegDeleteTreeW(basekey, class); + StringFromGUID2(&CLSID_MidiRendererCategory, class, CHARS_IN_GUID); + RegDeleteTreeW(basekey, class); + StringFromGUID2(&CLSID_VideoCompressorCategory, class, CHARS_IN_GUID); + RegDeleteTreeW(basekey, class); + RegCloseKey(basekey); rf2.dwVersion = 2; rf2.dwMerit = MERIT_PREFERRED; diff --git a/dlls/devenum/devenum_private.h b/dlls/devenum/devenum_private.h index 347aebb9081..bcc5085492e 100644 --- a/dlls/devenum/devenum_private.h +++ b/dlls/devenum/devenum_private.h @@ -60,6 +60,12 @@ typedef struct IClassFactory IClassFactory_iface; } ClassFactoryImpl; +enum device_type +{ + DEVICE_FILTER, + DEVICE_CODEC, +}; + typedef struct { IMoniker IMoniker_iface; @@ -68,23 +74,24 @@ typedef struct } MediaCatMoniker; MediaCatMoniker * DEVENUM_IMediaCatMoniker_Construct(void) DECLSPEC_HIDDEN; -HRESULT DEVENUM_IEnumMoniker_Construct(HKEY hkey, HKEY special_hkey, IEnumMoniker ** ppEnumMoniker) DECLSPEC_HIDDEN; +HRESULT create_EnumMoniker(REFCLSID class, IEnumMoniker **enum_mon) DECLSPEC_HIDDEN; extern ClassFactoryImpl DEVENUM_ClassFactory DECLSPEC_HIDDEN; extern ICreateDevEnum DEVENUM_CreateDevEnum DECLSPEC_HIDDEN; extern IParseDisplayName DEVENUM_ParseDisplayName DECLSPEC_HIDDEN; -/********************************************************************** - * Private helper function to get AM filter category key location - */ -HRESULT DEVENUM_GetCategoryKey(REFCLSID clsidDeviceClass, HKEY *pBaseKey, WCHAR *wszRegKeyName, UINT maxLen) DECLSPEC_HIDDEN; - /********************************************************************** * Global string constant declarations */ + +static const WCHAR clsidW[] = {'C','L','S','I','D','\\',0}; +static const WCHAR instanceW[] = {'\\','I','n','s','t','a','n','c','e',0}; +static const WCHAR wszActiveMovieKey[] = {'S','o','f','t','w','a','r','e','\\', + 'M','i','c','r','o','s','o','f','t','\\', + 'A','c','t','i','v','e','M','o','v','i','e','\\', + 'd','e','v','e','n','u','m','\\',0}; + extern const WCHAR clsid_keyname[6] DECLSPEC_HIDDEN; -extern const WCHAR wszInstanceKeyName[] DECLSPEC_HIDDEN; -#define CLSID_STR_LEN (sizeof(clsid_keyname) / sizeof(WCHAR)) /********************************************************************** * Resource IDs diff --git a/dlls/devenum/mediacatenum.c b/dlls/devenum/mediacatenum.c index 4edc4e96223..583ca7dbba8 100644 --- a/dlls/devenum/mediacatenum.c +++ b/dlls/devenum/mediacatenum.c @@ -34,10 +34,10 @@ typedef struct { IEnumMoniker IEnumMoniker_iface; LONG ref; - DWORD index; - DWORD subkey_cnt; - HKEY hkey; - HKEY special_hkey; + HKEY sw_key; + DWORD sw_index; + HKEY cm_key; + DWORD cm_index; } EnumMonikerImpl; typedef struct @@ -748,9 +748,8 @@ static ULONG WINAPI DEVENUM_IEnumMoniker_Release(IEnumMoniker *iface) if (!ref) { - if(This->special_hkey) - RegCloseKey(This->special_hkey); - RegCloseKey(This->hkey); + RegCloseKey(This->sw_key); + RegCloseKey(This->cm_key); CoTaskMemFree(This); DEVENUM_UnlockModule(); return 0; @@ -766,37 +765,42 @@ static HRESULT WINAPI DEVENUM_IEnumMoniker_Next(IEnumMoniker *iface, ULONG celt, LONG res; ULONG fetched = 0; MediaCatMoniker * pMoniker; + HKEY hkey; TRACE("(%p)->(%d, %p, %p)\n", iface, celt, rgelt, pceltFetched); while (fetched < celt) { - if(This->index+fetched < This->subkey_cnt) - res = RegEnumKeyW(This->hkey, This->index+fetched, buffer, sizeof(buffer) / sizeof(WCHAR)); - else if(This->special_hkey) - res = RegEnumKeyW(This->special_hkey, This->index+fetched-This->subkey_cnt, buffer, sizeof(buffer) / sizeof(WCHAR)); + /* FIXME: try PNP devices and DMOs first */ + + /* try DirectShow filters */ + if (!(res = RegEnumKeyW(This->sw_key, This->sw_index, buffer, sizeof(buffer)/sizeof(WCHAR)))) + { + This->sw_index++; + if ((res = RegOpenKeyExW(This->sw_key, buffer, 0, KEY_QUERY_VALUE, &hkey))) + break; + } + /* then try codecs */ + else if (!(res = RegEnumKeyW(This->cm_key, This->cm_index, buffer, sizeof(buffer)/sizeof(WCHAR)))) + { + This->cm_index++; + + if ((res = RegOpenKeyExW(This->cm_key, buffer, 0, KEY_QUERY_VALUE, &hkey))) + break; + } else break; - if (res != ERROR_SUCCESS) - { - break; - } + pMoniker = DEVENUM_IMediaCatMoniker_Construct(); if (!pMoniker) return E_OUTOFMEMORY; - if (RegOpenKeyW(This->index+fetched < This->subkey_cnt ? This->hkey : This->special_hkey, - buffer, &pMoniker->hkey) != ERROR_SUCCESS) - { - IMoniker_Release(&pMoniker->IMoniker_iface); - break; - } + pMoniker->hkey = hkey; + rgelt[fetched] = &pMoniker->IMoniker_iface; fetched++; } - This->index += fetched; - TRACE("-- fetched %d\n", fetched); if (pceltFetched) @@ -811,21 +815,26 @@ static HRESULT WINAPI DEVENUM_IEnumMoniker_Next(IEnumMoniker *iface, ULONG celt, static HRESULT WINAPI DEVENUM_IEnumMoniker_Skip(IEnumMoniker *iface, ULONG celt) { EnumMonikerImpl *This = impl_from_IEnumMoniker(iface); - DWORD special_subkeys = 0; TRACE("(%p)->(%d)\n", iface, celt); - /* Before incrementing, check if there are any more values to run through. - Some programs use the Skip() function to get the number of devices */ - if(This->special_hkey) - RegQueryInfoKeyW(This->special_hkey, NULL, NULL, NULL, &special_subkeys, NULL, NULL, NULL, NULL, NULL, NULL, NULL); - - if((This->index + celt) >= This->subkey_cnt + special_subkeys) + while (celt--) { - return S_FALSE; - } + /* FIXME: try PNP devices and DMOs first */ - This->index += celt; + /* try DirectShow filters */ + if (RegEnumKeyW(This->sw_key, This->sw_index, NULL, 0) != ERROR_NO_MORE_ITEMS) + { + This->sw_index++; + } + /* then try codecs */ + else if (RegEnumKeyW(This->cm_key, This->cm_index, NULL, 0) != ERROR_NO_MORE_ITEMS) + { + This->cm_index++; + } + else + return S_FALSE; + } return S_OK; } @@ -836,7 +845,8 @@ static HRESULT WINAPI DEVENUM_IEnumMoniker_Reset(IEnumMoniker *iface) TRACE("(%p)->()\n", iface); - This->index = 0; + This->sw_index = 0; + This->cm_index = 0; return S_OK; } @@ -862,24 +872,32 @@ static const IEnumMonikerVtbl IEnumMoniker_Vtbl = DEVENUM_IEnumMoniker_Clone }; -HRESULT DEVENUM_IEnumMoniker_Construct(HKEY hkey, HKEY special_hkey, IEnumMoniker ** ppEnumMoniker) +HRESULT create_EnumMoniker(REFCLSID class, IEnumMoniker **ppEnumMoniker) { EnumMonikerImpl * pEnumMoniker = CoTaskMemAlloc(sizeof(EnumMonikerImpl)); + WCHAR buffer[78]; + if (!pEnumMoniker) return E_OUTOFMEMORY; pEnumMoniker->IEnumMoniker_iface.lpVtbl = &IEnumMoniker_Vtbl; pEnumMoniker->ref = 1; - pEnumMoniker->index = 0; - pEnumMoniker->hkey = hkey; - pEnumMoniker->special_hkey = special_hkey; + pEnumMoniker->sw_index = 0; + pEnumMoniker->cm_index = 0; + + strcpyW(buffer, clsidW); + StringFromGUID2(class, buffer + strlenW(buffer), CHARS_IN_GUID); + strcatW(buffer, instanceW); + if (RegOpenKeyExW(HKEY_CLASSES_ROOT, buffer, 0, KEY_ENUMERATE_SUB_KEYS, &pEnumMoniker->sw_key)) + pEnumMoniker->sw_key = NULL; + + strcpyW(buffer, wszActiveMovieKey); + StringFromGUID2(class, buffer + strlenW(buffer), CHARS_IN_GUID); + if (RegOpenKeyExW(HKEY_CURRENT_USER, buffer, 0, KEY_ENUMERATE_SUB_KEYS, &pEnumMoniker->cm_key)) + pEnumMoniker->cm_key = NULL; *ppEnumMoniker = &pEnumMoniker->IEnumMoniker_iface; - if(RegQueryInfoKeyW(pEnumMoniker->hkey, NULL, NULL, NULL, &pEnumMoniker->subkey_cnt, NULL, NULL, NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS) - pEnumMoniker->subkey_cnt = 0; - - DEVENUM_LockModule(); return S_OK; diff --git a/dlls/devenum/parsedisplayname.c b/dlls/devenum/parsedisplayname.c index 77ea1ecdcb8..b5a3951eece 100644 --- a/dlls/devenum/parsedisplayname.c +++ b/dlls/devenum/parsedisplayname.c @@ -82,10 +82,12 @@ static HRESULT WINAPI DEVENUM_IParseDisplayName_ParseDisplayName(IParseDisplayNa LPOLESTR pszClass = NULL; MediaCatMoniker * pMoniker = NULL; CLSID clsidDevice; - HRESULT res = S_OK; + HRESULT hr = S_OK; WCHAR wszRegKeyName[MAX_PATH]; + enum device_type type; HKEY hbasekey; int classlen; + LONG res; static const WCHAR wszRegSeparator[] = {'\\', 0 }; TRACE("(%p, %s, %p, %p)\n", pbc, debugstr_w(pszDisplayName), pchEaten, ppmkOut); @@ -94,6 +96,25 @@ static HRESULT WINAPI DEVENUM_IParseDisplayName_ParseDisplayName(IParseDisplayNa if (pchEaten) *pchEaten = strlenW(pszDisplayName); + pszDisplayName = strchrW(pszDisplayName, ':') + 1; + if (pszDisplayName[0] == 's' && pszDisplayName[1] == 'w' && pszDisplayName[2] == ':') + { + type = DEVICE_FILTER; + if ((res = RegOpenKeyExW(HKEY_CLASSES_ROOT, clsidW, 0, 0, &hbasekey))) + return HRESULT_FROM_WIN32(res); + } + else if (pszDisplayName[0] == 'c' && pszDisplayName[1] == 'm' && pszDisplayName[2] == ':') + { + type = DEVICE_CODEC; + if ((res = RegOpenKeyExW(HKEY_CURRENT_USER, wszActiveMovieKey, 0, 0, &hbasekey))) + return HRESULT_FROM_WIN32(res); + } + else + { + FIXME("unhandled device type %s\n", debugstr_w(pszDisplayName+1)); + return MK_E_SYNTAX; + } + pszDisplayName = strchrW(pszDisplayName, '{'); pszBetween = strchrW(pszDisplayName, '}') + 2; @@ -110,35 +131,32 @@ static HRESULT WINAPI DEVENUM_IParseDisplayName_ParseDisplayName(IParseDisplayNa TRACE("Device CLSID: %s\n", debugstr_w(pszClass)); - res = CLSIDFromString(pszClass, &clsidDevice); + hr = CLSIDFromString(pszClass, &clsidDevice); - if (SUCCEEDED(res)) - { - res = DEVENUM_GetCategoryKey(&clsidDevice, &hbasekey, wszRegKeyName, MAX_PATH); - } - - if (SUCCEEDED(res)) + if (SUCCEEDED(hr)) { pMoniker = DEVENUM_IMediaCatMoniker_Construct(); if (pMoniker) { + strcpyW(wszRegKeyName, pszClass); + if (type == DEVICE_FILTER) + strcatW(wszRegKeyName, instanceW); strcatW(wszRegKeyName, wszRegSeparator); strcatW(wszRegKeyName, pszBetween); - if (RegCreateKeyW(hbasekey, wszRegKeyName, &pMoniker->hkey) == ERROR_SUCCESS) *ppmkOut = &pMoniker->IMoniker_iface; else { IMoniker_Release(&pMoniker->IMoniker_iface); - res = MK_E_NOOBJECT; + hr = MK_E_NOOBJECT; } } } CoTaskMemFree(pszClass); - TRACE("-- returning: %x\n", res); - return res; + TRACE("-- returning: %x\n", hr); + return hr; } /********************************************************************** diff --git a/dlls/devenum/tests/devenum.c b/dlls/devenum/tests/devenum.c index 79189f3526f..c53f909c17b 100644 --- a/dlls/devenum/tests/devenum.c +++ b/dlls/devenum/tests/devenum.c @@ -274,16 +274,155 @@ static void test_register_filter(void) ok(find_moniker(&CLSID_AudioRendererCategory, mon), "filter should be registered\n"); hr = IFilterMapper2_UnregisterFilter(mapper2, &CLSID_AudioRendererCategory, NULL, &CLSID_TestFilter); -todo_wine ok(hr == S_OK, "UnregisterFilter failed: %#x\n", hr); -todo_wine ok(!find_moniker(&CLSID_AudioRendererCategory, mon), "filter should not be registered\n"); IMoniker_Release(mon); IFilterMapper2_Release(mapper2); } +static IMoniker *check_display_name_(int line, IParseDisplayName *parser, WCHAR *buffer) +{ + IMoniker *mon; + ULONG eaten; + HRESULT hr; + WCHAR *str; + + hr = IParseDisplayName_ParseDisplayName(parser, NULL, buffer, &eaten, &mon); + ok_(__FILE__, line)(hr == S_OK, "ParseDisplayName failed: %#x\n", hr); + + hr = IMoniker_GetDisplayName(mon, NULL, NULL, &str); +todo_wine { + ok_(__FILE__, line)(hr == S_OK, "GetDisplayName failed: %#x\n", hr); + ok_(__FILE__, line)(!lstrcmpW(str, buffer), "got %s\n", wine_dbgstr_w(str)); +} + + CoTaskMemFree(str); + + return mon; +} +#define check_display_name(parser, buffer) check_display_name_(__LINE__, parser, buffer) + +static void test_directshow_filter(void) +{ + static const WCHAR deviceW[] = {'@','d','e','v','i','c','e',':','s','w',':',0}; + static const WCHAR instanceW[] = {'\\','I','n','s','t','a','n','c','e',0}; + static const WCHAR clsidW[] = {'C','L','S','I','D','\\',0}; + static WCHAR testW[] = {'\\','t','e','s','t',0}; + IParseDisplayName *parser; + IPropertyBag *prop_bag; + IMoniker *mon; + WCHAR buffer[200]; + LRESULT res; + VARIANT var; + HRESULT hr; + + /* Test ParseDisplayName and GetDisplayName */ + hr = CoCreateInstance(&CLSID_CDeviceMoniker, NULL, CLSCTX_INPROC, &IID_IParseDisplayName, (void **)&parser); + ok(hr == S_OK, "Failed to create ParseDisplayName: %#x\n", hr); + + lstrcpyW(buffer, deviceW); + StringFromGUID2(&CLSID_AudioRendererCategory, buffer + lstrlenW(buffer), CHARS_IN_GUID); + lstrcatW(buffer, testW); + mon = check_display_name(parser, buffer); + + /* Test writing and reading from the property bag */ + ok(!find_moniker(&CLSID_AudioRendererCategory, mon), "filter should not be registered\n"); + + hr = IMoniker_BindToStorage(mon, NULL, NULL, &IID_IPropertyBag, (void **)&prop_bag); + ok(hr == S_OK, "BindToStorage failed: %#x\n", hr); + + VariantInit(&var); + hr = IPropertyBag_Read(prop_bag, friendly_name, &var, NULL); + ok(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "got %#x\n", hr); + + /* writing causes the key to be created */ + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = SysAllocString(testW); + hr = IPropertyBag_Write(prop_bag, friendly_name, &var); + if (hr != E_ACCESSDENIED) + { + ok(hr == S_OK, "Write failed: %#x\n", hr); + + ok(find_moniker(&CLSID_AudioRendererCategory, mon), "filter should be registered\n"); + + VariantClear(&var); + hr = IPropertyBag_Read(prop_bag, friendly_name, &var, NULL); + ok(hr == S_OK, "Read failed: %#x\n", hr); + ok(!lstrcmpW(V_BSTR(&var), testW), "got %s\n", wine_dbgstr_w(V_BSTR(&var))); + + IMoniker_Release(mon); + + /* devenum doesn't give us a way to unregister—we have to do that manually */ + lstrcpyW(buffer, clsidW); + StringFromGUID2(&CLSID_AudioRendererCategory, buffer + lstrlenW(buffer), CHARS_IN_GUID); + lstrcatW(buffer, instanceW); + lstrcatW(buffer, testW); + res = RegDeleteKeyW(HKEY_CLASSES_ROOT, buffer); + ok(!res, "RegDeleteKey failed: %lu\n", res); + } + + VariantClear(&var); + IPropertyBag_Release(prop_bag); + IParseDisplayName_Release(parser); +} + +static void test_codec(void) +{ + static const WCHAR deviceW[] = {'@','d','e','v','i','c','e',':','c','m',':',0}; + static WCHAR testW[] = {'\\','t','e','s','t',0}; + IParseDisplayName *parser; + IPropertyBag *prop_bag; + IMoniker *mon; + WCHAR buffer[200]; + VARIANT var; + HRESULT hr; + + /* Test ParseDisplayName and GetDisplayName */ + hr = CoCreateInstance(&CLSID_CDeviceMoniker, NULL, CLSCTX_INPROC, &IID_IParseDisplayName, (void **)&parser); + ok(hr == S_OK, "Failed to create ParseDisplayName: %#x\n", hr); + + lstrcpyW(buffer, deviceW); + StringFromGUID2(&CLSID_AudioRendererCategory, buffer + lstrlenW(buffer), CHARS_IN_GUID); + lstrcatW(buffer, testW); + mon = check_display_name(parser, buffer); + + /* Test writing and reading from the property bag */ + ok(!find_moniker(&CLSID_AudioRendererCategory, mon), "codec should not be registered\n"); + + hr = IMoniker_BindToStorage(mon, NULL, NULL, &IID_IPropertyBag, (void **)&prop_bag); + ok(hr == S_OK, "BindToStorage failed: %#x\n", hr); + + VariantInit(&var); + hr = IPropertyBag_Read(prop_bag, friendly_name, &var, NULL); + ok(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "got %#x\n", hr); + + V_VT(&var) = VT_BSTR; + V_BSTR(&var) = SysAllocString(testW); + hr = IPropertyBag_Write(prop_bag, friendly_name, &var); + ok(hr == S_OK, "Write failed: %#x\n", hr); + + VariantClear(&var); + hr = IPropertyBag_Read(prop_bag, friendly_name, &var, NULL); + ok(hr == S_OK, "Read failed: %#x\n", hr); + ok(!lstrcmpW(V_BSTR(&var), testW), "got %s\n", wine_dbgstr_w(V_BSTR(&var))); + + /* unlike DirectShow filters, these are automatically generated, so + * enumerating them will destroy the key */ +todo_wine + ok(!find_moniker(&CLSID_AudioRendererCategory, mon), "codec should not be registered\n"); + + hr = IPropertyBag_Read(prop_bag, friendly_name, &var, NULL); +todo_wine + ok(hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND), "got %#x\n", hr); + + IPropertyBag_Release(prop_bag); + IMoniker_Release(mon); + + IParseDisplayName_Release(parser); +} + START_TEST(devenum) { IBindCtx *bind_ctx = NULL; @@ -303,6 +442,8 @@ START_TEST(devenum) test_moniker_isequal(); test_register_filter(); + test_directshow_filter(); + test_codec(); CoUninitialize(); }