[PATCH] shell32: fix two bugs in IQueryAssocations

Theodore Dubois tblodt at icloud.com
Sat Feb 13 17:19:23 CST 2016


Basically, returns the correct value for
ASSOCSTR_FRIENDLYDOCNAME and ASSOCSTR_DEFAULTICON
when passed a ProgID instead of a file extension.

Also adds some tests to make sure it works.

Signed-off-by: Theodore Dubois <tblodt at icloud.com>
---
 dlls/shell32/assoc.c       | 146 +++++++++++++++++++++++++--------------------
 dlls/shell32/tests/assoc.c |  71 +++++++++++++++++++++-
 2 files changed, 151 insertions(+), 66 deletions(-)

diff --git a/dlls/shell32/assoc.c b/dlls/shell32/assoc.c
index 90d00a8..2667ffe 100644
--- a/dlls/shell32/assoc.c
+++ b/dlls/shell32/assoc.c
@@ -63,6 +63,7 @@ typedef struct
   LONG ref;
   HKEY hkeySource;
   HKEY hkeyProgID;
+  LPWSTR pszAssocName;
 } IQueryAssociationsImpl;
 
 typedef struct
@@ -88,6 +89,8 @@ static inline struct enumassochandlers *impl_from_IEnumAssocHandlers(IEnumAssocH
   return CONTAINING_RECORD(iface, struct enumassochandlers, IEnumAssocHandlers_iface);
 }
 
+static HRESULT ASSOC_GetValue(HKEY hkey, const WCHAR *name, void **data, DWORD *data_size);
+
 /**************************************************************************
  *  IQueryAssociations_QueryInterface
  *
@@ -152,6 +155,7 @@ static ULONG WINAPI IQueryAssociations_fnRelease(IQueryAssociations *iface)
     TRACE("Destroying IQueryAssociations (%p)\n", This);
     RegCloseKey(This->hkeySource);
     RegCloseKey(This->hkeyProgID);
+    HeapFree(GetProcessHeap(), 0, (LPVOID)This->pszAssocName);
     SHFree(This);
   }
 
@@ -186,20 +190,30 @@ static HRESULT WINAPI IQueryAssociations_fnInit(
     LONG ret;
 
     TRACE("(%p)->(%d,%s,%p,%p)\n", iface,
-                                    cfFlags,
-                                    debugstr_w(pszAssoc),
-                                    hkeyProgid,
-                                    hWnd);
+                                   cfFlags,
+                                   debugstr_w(pszAssoc),
+                                   hkeyProgid,
+                                   hWnd);
     if (hWnd != NULL)
         FIXME("hwnd != NULL not supported\n");
     if (cfFlags != 0)
 	FIXME("unsupported flags: %x\n", cfFlags);
 
     RegCloseKey(This->hkeySource);
-    RegCloseKey(This->hkeyProgID);
+    if (This->hkeySource != This->hkeyProgID)
+        RegCloseKey(This->hkeyProgID);
     This->hkeySource = This->hkeyProgID = NULL;
+    HeapFree(GetProcessHeap(), 0, (LPVOID)This->pszAssocName);
+
+    /* If the process of initializing hkeyProgID fails, just return S_OK. That's what Windows does. */
     if (pszAssoc != NULL)
     {
+        WCHAR *progId;
+        HRESULT hr;
+
+        This->pszAssocName = HeapAlloc(GetProcessHeap(), 0, (strlenW(pszAssoc) + 10001) * sizeof(WCHAR));
+        strcpyW(This->pszAssocName, pszAssoc);
+
         ret = RegOpenKeyExW(HKEY_CLASSES_ROOT,
                             pszAssoc,
                             0,
@@ -207,22 +221,51 @@ static HRESULT WINAPI IQueryAssociations_fnInit(
                             &This->hkeySource);
         if (ret)
             return S_OK;
-        /* if this is not a prog id */
-        if ((*pszAssoc == '.') || (*pszAssoc == '{'))
+        /* if this is a progid */
+        if (*pszAssoc != '.' && *pszAssoc != '{')
         {
-            RegOpenKeyExW(This->hkeySource,
-                          szProgID,
-                          0,
-                          KEY_READ,
-                          &This->hkeyProgID);
-        }
-        else
             This->hkeyProgID = This->hkeySource;
+            return S_OK;
+        }
+
+        /* if it's not a progid, it's a file extension or clsid */
+        if (*pszAssoc == '.')
+        {
+            /* for a file extension, the progid is the default value */
+            hr = ASSOC_GetValue(This->hkeySource, NULL, (void**)&progId, NULL);
+            if (FAILED(hr))
+                return S_OK;
+        }
+        else /* if (*pszAssoc == '{') */
+        {
+            HKEY progIdKey;
+            /* for a clsid, the progid is the default value of the ProgID subkey */
+            ret = RegOpenKeyExW(This->hkeySource,
+                                szProgID,
+                                0,
+                                KEY_READ,
+                                &progIdKey);
+            if (ret != ERROR_SUCCESS)
+                return S_OK;
+            hr = ASSOC_GetValue(progIdKey, NULL, (void**)&progId, NULL);
+            if (FAILED(hr))
+                return S_OK;
+            RegCloseKey(progIdKey);
+        }
+        
+        /* open the actual progid key, the one with the shell subkey */
+        ret = RegOpenKeyExW(HKEY_CLASSES_ROOT,
+                            progId,
+                            0,
+                            KEY_READ,
+                            &This->hkeyProgID);
+        HeapFree(GetProcessHeap(), 0, progId);
+
         return S_OK;
     }
     else if (hkeyProgid != NULL)
     {
-        This->hkeyProgID = hkeyProgid;
+        This->hkeySource = This->hkeyProgID = hkeyProgid;
         return S_OK;
     }
     else
@@ -239,7 +282,7 @@ static HRESULT ASSOC_GetValue(HKEY hkey, const WCHAR *name, void **data, DWORD *
     return HRESULT_FROM_WIN32(ret);
   if (!size)
     return E_FAIL;
-  *data = HeapAlloc(GetProcessHeap(), 0, size);
+  *data = HeapAlloc(GetProcessHeap(), 0, size + 100);
   if (!*data)
     return E_OUTOFMEMORY;
   ret = RegQueryValueExW(hkey, name, 0, NULL, (LPBYTE)*data, &size);
@@ -507,33 +550,16 @@ static HRESULT WINAPI IQueryAssociations_fnGetString(
 
     case ASSOCSTR_FRIENDLYDOCNAME:
     {
-      WCHAR *pszFileType;
-      DWORD ret;
-      DWORD size;
+      WCHAR *docName;
+
+      if (This->hkeyProgID == NULL) {
+
+      }
 
-      hr = ASSOC_GetValue(This->hkeySource, NULL, (void**)&pszFileType, NULL);
+      hr = ASSOC_GetValue(This->hkeyProgID, NULL, (void**)&docName, NULL);
       if (FAILED(hr))
         return hr;
-      size = 0;
-      ret = RegGetValueW(HKEY_CLASSES_ROOT, pszFileType, NULL, RRF_RT_REG_SZ, NULL, NULL, &size);
-      if (ret == ERROR_SUCCESS)
-      {
-        WCHAR *docName = HeapAlloc(GetProcessHeap(), 0, size);
-        if (docName)
-        {
-          ret = RegGetValueW(HKEY_CLASSES_ROOT, pszFileType, NULL, RRF_RT_REG_SZ, NULL, docName, &size);
-          if (ret == ERROR_SUCCESS)
-            hr = ASSOC_ReturnString(flags, pszOut, pcchOut, docName, strlenW(docName) + 1);
-          else
-            hr = HRESULT_FROM_WIN32(ret);
-          HeapFree(GetProcessHeap(), 0, docName);
-        }
-        else
-          hr = E_OUTOFMEMORY;
-      }
-      else
-        hr = HRESULT_FROM_WIN32(ret);
-      HeapFree(GetProcessHeap(), 0, pszFileType);
+      hr = ASSOC_ReturnString(flags, pszOut, pcchOut, docName, strlenW(docName) + 1);
       return hr;
     }
 
@@ -623,41 +649,31 @@ get_friendly_name_fail:
     case ASSOCSTR_DEFAULTICON:
     {
       static const WCHAR DefaultIconW[] = {'D','e','f','a','u','l','t','I','c','o','n',0};
-      WCHAR *pszFileType;
+      static const WCHAR defaultDefaultIcon[] = {'s','h','e','l','l','3','2','.','d','l','l',',','0',0};
       DWORD ret;
       DWORD size;
-      HKEY hkeyFile;
 
-      hr = ASSOC_GetValue(This->hkeySource, NULL, (void**)&pszFileType, NULL);
-      if (FAILED(hr))
-        return hr;
-      ret = RegOpenKeyExW(HKEY_CLASSES_ROOT, pszFileType, 0, KEY_READ, &hkeyFile);
+      size = 0;
+      ret = RegGetValueW(This->hkeyProgID, DefaultIconW, NULL, RRF_RT_REG_SZ, NULL, NULL, &size);
       if (ret == ERROR_SUCCESS)
       {
-        size = 0;
-        ret = RegGetValueW(hkeyFile, DefaultIconW, NULL, RRF_RT_REG_SZ, NULL, NULL, &size);
-        if (ret == ERROR_SUCCESS)
+        WCHAR *icon = HeapAlloc(GetProcessHeap(), 0, size);
+        if (icon)
         {
-          WCHAR *icon = HeapAlloc(GetProcessHeap(), 0, size);
-          if (icon)
-          {
-            ret = RegGetValueW(hkeyFile, DefaultIconW, NULL, RRF_RT_REG_SZ, NULL, icon, &size);
-            if (ret == ERROR_SUCCESS)
-              hr = ASSOC_ReturnString(flags, pszOut, pcchOut, icon, strlenW(icon) + 1);
-            else
-              hr = HRESULT_FROM_WIN32(ret);
-            HeapFree(GetProcessHeap(), 0, icon);
-          }
+          ret = RegGetValueW(This->hkeyProgID, DefaultIconW, NULL, RRF_RT_REG_SZ, NULL, icon, &size);
+          if (ret == ERROR_SUCCESS)
+            hr = ASSOC_ReturnString(flags, pszOut, pcchOut, icon, strlenW(icon) + 1);
           else
-            hr = E_OUTOFMEMORY;
+            hr = HRESULT_FROM_WIN32(ret);
+          HeapFree(GetProcessHeap(), 0, icon);
         }
         else
-          hr = HRESULT_FROM_WIN32(ret);
-        RegCloseKey(hkeyFile);
+          hr = E_OUTOFMEMORY;
+      }
+      else {
+          /* there is no DefaultIcon subkey or hkeyProgID is NULL, so return the default document icon */
+          hr = ASSOC_ReturnString(flags, pszOut, pcchOut, defaultDefaultIcon, strlenW(defaultDefaultIcon) + 1);
       }
-      else
-        hr = HRESULT_FROM_WIN32(ret);
-      HeapFree(GetProcessHeap(), 0, pszFileType);
       return hr;
     }
     case ASSOCSTR_SHELLEXTENSION:
diff --git a/dlls/shell32/tests/assoc.c b/dlls/shell32/tests/assoc.c
index 8aa2535..1ba4261 100644
--- a/dlls/shell32/tests/assoc.c
+++ b/dlls/shell32/tests/assoc.c
@@ -91,6 +91,8 @@ struct assoc_getstring_test
 };
 
 static const WCHAR httpW[] = {'h','t','t','p',0};
+static const WCHAR dot_urlW[] = {'.','u','r','l',0};
+static const WCHAR InternetShortcutW[] = {'I','n','t','e','r','n','e','t','S','h','o','r','t','c','u','t',0};
 static const WCHAR badW[] = {'b','a','d','b','a','d',0};
 
 static struct assoc_getstring_test getstring_tests[] =
@@ -135,7 +137,7 @@ static void test_IQueryAssociations_GetString(void)
         else
         {
             ok(hr == ptr->hr, "%d: GetString failed, 0x%08x\n", i, hr);
-            ok(len > ptr->len, "%d: got needed length %d\n", i, len);
+            ok(len >= ptr->len, "%d: got needed length %d\n", i, len);
         }
 
         /* even with ASSOCF_NOTRUNCATE it's null terminated */
@@ -152,6 +154,72 @@ static void test_IQueryAssociations_GetString(void)
     IQueryAssociations_Release(assoc);
 }
 
+static void getstring_test(ASSOCF flags, LPCWSTR assocName, HKEY progIdKey, ASSOCSTR str, LPCWSTR extra,
+        HRESULT expected_hr, LPCWSTR expected_string) {
+    IQueryAssociations *assoc;
+    HRESULT hr;
+    WCHAR *buffer;
+    DWORD len;
+
+    hr = CoCreateInstance(&CLSID_QueryAssociations, NULL, CLSCTX_INPROC_SERVER, &IID_IQueryAssociations, (void*)&assoc);
+    ok(hr == S_OK, "failed to create IQueryAssociations, 0x%x\n", hr);
+    hr = IQueryAssociations_Init(assoc, flags, assocName, progIdKey, NULL);
+    ok(hr == S_OK, "IQueryAssociations::Init failed, 0x%x\n", hr);
+    
+    hr = IQueryAssociations_GetString(assoc, flags, str, extra, NULL, &len);
+    if (hr != S_FALSE)
+        ok(hr == expected_hr, "GetString returned 0x%x, expected 0x%x\n", hr, expected_hr);
+    buffer = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR));
+    ok(buffer != NULL, "out of memory\n");
+    hr = IQueryAssociations_GetString(assoc, flags, str, extra, buffer, &len);
+    ok(hr == expected_hr, "GetString returned 0x%x, expected 0x%x\n", hr, expected_hr);
+
+    if (expected_string) {
+        ok(lstrcmpW(buffer, expected_string) == 0, "GetString returned %s, expected %s\n",
+                wine_dbgstr_w(buffer), wine_dbgstr_w(expected_string));
+    }
+}
+
+static void test_IQueryAssociations_GetString_results(void) {
+    static WCHAR test_extensionW[] = {'.','t','e','s','t',0};
+    static WCHAR test_progidW[] = {'t','e','s','t','f','i','l','e',0};
+    static WCHAR DefaultIconW[] = {'D','e','f','a','u','l','t','I','c','o','n',0};
+    /* folder.ico, why not */
+    static WCHAR test_iconW[] = {'C',':','\\','w','i','n','d','o','w','s','\\','s','y','s','t','e','m','3','2','\\','s','h','e','l','l','3','2','.','d','l','l',',','1',0};
+    HKEY test_extension_key;
+    HKEY test_progid_key;
+    HKEY test_defaulticon_key;
+    LRESULT r;
+
+    /* request for default icon on extension with no progid should return default document icon
+       (exact string may change between versions of windows) */
+    r = RegCreateKeyExW(HKEY_CLASSES_ROOT, test_extensionW, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &test_extension_key, NULL);
+    ok(r == ERROR_SUCCESS, "RegCreateKeyExW(HKCR, \".test\") failed: 0x%lx\n", r);
+    getstring_test(0, test_extensionW, NULL, ASSOCSTR_DEFAULTICON, NULL, S_OK, NULL);
+
+    r = RegSetValueExW(test_extension_key, NULL, 0, REG_SZ, (PBYTE)test_progidW, sizeof(test_progidW));
+    ok(r == ERROR_SUCCESS, "RegSetValueExW(HKCR\\.test, NULL, \"testfile\") failed: 0x%lx\n", r);
+    getstring_test(0, test_extensionW, NULL, ASSOCSTR_DEFAULTICON, NULL, S_OK, NULL);
+
+    r = RegCreateKeyExW(HKEY_CLASSES_ROOT, test_progidW, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &test_progid_key, NULL);
+    ok(r == ERROR_SUCCESS, "RegCreateKeyExW(HKCR, \"testfile\") failed: 0x%lx\n", r);
+    getstring_test(0, test_extensionW, NULL, ASSOCSTR_DEFAULTICON, NULL, S_OK, NULL);
+    getstring_test(0, test_progidW, NULL, ASSOCSTR_DEFAULTICON, NULL, S_OK, NULL);
+    getstring_test(0, NULL, test_progid_key, ASSOCSTR_DEFAULTICON, NULL, S_OK, NULL);
+
+    /* adding information to the progid should return that information */
+    r = RegCreateKeyExW(test_progid_key, DefaultIconW, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &test_defaulticon_key, NULL);
+    ok(r == ERROR_SUCCESS, "RegCreateKeyExW(HKCR\\testfile\\DefaultIcon) failed: 0x%x\n", r);
+    r = RegSetValueExW(test_defaulticon_key, NULL, 0, REG_SZ, (PBYTE)test_iconW, sizeof(test_iconW));
+    ok(r == ERROR_SUCCESS, "RegSetValueExW(HKCR\\testfile\\DefaultIcon, NULL, \"folder.ico\") failed: 0x%lx\n", r);
+    getstring_test(0, test_extensionW, NULL, ASSOCSTR_DEFAULTICON, NULL, S_OK, test_iconW);
+    getstring_test(0, test_progidW, NULL, ASSOCSTR_DEFAULTICON, NULL, S_OK, test_iconW);
+    getstring_test(0, NULL, test_progid_key, ASSOCSTR_DEFAULTICON, NULL, S_OK, test_iconW);
+
+    //RegDeleteKeyExW(HKEY_CLASSES_ROOT, test_extensionW, KEY_WOW64_32KEY, 0);
+    //RegDeleteKeyExW(HKEY_CLASSES_ROOT, test_progidW, KEY_WOW64_32KEY, 0);
+}
+
 static void test_IQueryAssociations_Init(void)
 {
     IQueryAssociations *assoc;
@@ -229,6 +297,7 @@ START_TEST(assoc)
     if (hr == S_OK)
     {
         test_IQueryAssociations_QueryInterface();
+        test_IQueryAssociations_GetString_results();
         test_IQueryAssociations_GetString();
         test_IQueryAssociations_Init();
 
-- 
2.5.4 (Apple Git-61)




More information about the wine-patches mailing list