[PATCH v2 3/6] devenum: More correctly handle device types.

Zebediah Figura z.figura12 at gmail.com
Tue Mar 6 20:57:08 CST 2018


Signed-off-by: Zebediah Figura <z.figura12 at gmail.com>
---
A devenum device comes in one of four types: DirectShow filter, DMO,
audio or video codec, or PNP device. Each of these is identified in a
slightly different way.

* A DirectShow filter (sw) is identified by a subkey under the CLSID
  key. They're normally registered through
  IFilterMapper2:RegisterFilter. devenum doesn't touch these keys, but
  rather just reads from them; it translates every key/value pair
  directly into an IPropertyBag property.

* An audio or video codec (cm) is identified by a subkey under the
  HKCU\Software\Microsoft\ActiveMovie\devenum key. This key is
  populated by devenum from a variety of sources, including the
  "legacy" HKCR\Filters key and the Drivers32 key. After being
  populated, it's read in the same way as the DirectShow filters
  above.

* A DMO (dmo) is identified by its CLSID and its category GUID, in
  that order. These aren't read in the same way as the above; rather,
  the key/value pairs are translated from the DMO storage format into
  the devenum format.

* A PNP device (pnp) is identified by a path in the global namespace,
  i.e. beginning with '\\?\'. devenum read these from the registry at
  the path CurrentControlSet\Control\DeviceClasses\{category
  guid}\##?#Root#SYSTEM#0000#{category guid}, and the properties for
  the IPropertyBag are given by the subkey "Device Parameters".

Previously, the code treated filters as "normal" and codecs as
"special", and assumed the difference lay in which category a device
was registered under. As the tests show, however, devenum will
enumerate all of the available device types for any given category.

v2: fix test failures.

 dlls/devenum/createdevenum.c    | 134 ++++++-------------------------------
 dlls/devenum/devenum_private.h  |  23 ++++---
 dlls/devenum/mediacatenum.c     | 100 +++++++++++++++------------
 dlls/devenum/parsedisplayname.c |  42 ++++++++----
 dlls/devenum/tests/devenum.c    | 145 +++++++++++++++++++++++++++++++++++++++-
 5 files changed, 267 insertions(+), 177 deletions(-)

diff --git a/dlls/devenum/createdevenum.c b/dlls/devenum/createdevenum.c
index 17d9850..7cfdbff 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();
-    }
-
-    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;
-         }
-    }
+    register_codecs();
+    DEVENUM_RegisterLegacyAmFilters();
 
-    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 347aebb..bcc5085 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 4edc4e9..583ca7d 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));
-        else
-            break;
-        if (res != ERROR_SUCCESS)
+        /* FIXME: try PNP devices and DMOs first */
+
+        /* try DirectShow filters */
+        if (!(res = RegEnumKeyW(This->sw_key, This->sw_index, buffer, sizeof(buffer)/sizeof(WCHAR))))
         {
-            break;
+            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;
+
         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,23 +872,31 @@ 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;
 
-    *ppEnumMoniker = &pEnumMoniker->IEnumMoniker_iface;
+    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;
 
-    if(RegQueryInfoKeyW(pEnumMoniker->hkey, NULL, NULL, NULL, &pEnumMoniker->subkey_cnt, NULL, NULL, NULL, NULL, NULL, NULL, NULL) != ERROR_SUCCESS)
-        pEnumMoniker->subkey_cnt = 0;
+    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;
 
     DEVENUM_LockModule();
 
diff --git a/dlls/devenum/parsedisplayname.c b/dlls/devenum/parsedisplayname.c
index 77ea1ec..b5a3951 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 79189f3..c53f909 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();
 }
-- 
2.7.4




More information about the wine-devel mailing list