[SHELL32] Finally fix the icon location path (un)expansion for shell links, and expla...
authorHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Sun, 7 Oct 2018 22:42:02 +0000 (00:42 +0200)
committerHermès Bélusca-Maïto <hermes.belusca-maito@reactos.org>
Sun, 7 Oct 2018 22:50:38 +0000 (00:50 +0200)
CORE-14009 CORE-14982

dll/win32/shell32/CShellLink.cpp

index 92f6296..fa1adab 100644 (file)
 
 WINE_DEFAULT_DEBUG_CHANNEL(shell);
 
 
 WINE_DEFAULT_DEBUG_CHANNEL(shell);
 
+/*
+ * Allows to define whether or not Windows-compatible behaviour
+ * should be adopted when setting and retrieving icon location paths.
+ * See CShellLink::SetIconLocation(LPCWSTR pszIconPath, INT iIcon)
+ * for more details.
+ */
+#define ICON_LINK_WINDOWS_COMPAT
+
 #define SHLINK_LOCAL  0
 #define SHLINK_REMOTE 1
 
 #define SHLINK_LOCAL  0
 #define SHLINK_REMOTE 1
 
@@ -1715,7 +1723,7 @@ HRESULT STDMETHODCALLTYPE CShellLink::GetIconLocation(UINT uFlags, PWSTR pszIcon
     uFlags |= GIL_FORSHORTCUT;
 
     if (uFlags & GIL_DEFAULTICON)
     uFlags |= GIL_FORSHORTCUT;
 
     if (uFlags & GIL_DEFAULTICON)
-        return E_FAIL;
+        return S_FALSE;
 
     hr = GetIconLocation(pszIconFile, cchMax, piIndex);
     if (FAILED(hr) || pszIconFile[0] == UNICODE_NULL)
 
     hr = GetIconLocation(pszIconFile, cchMax, piIndex);
     if (FAILED(hr) || pszIconFile[0] == UNICODE_NULL)
@@ -1849,15 +1857,12 @@ BOOL PathFullyUnExpandEnvStringsW(
 HRESULT STDMETHODCALLTYPE CShellLink::SetIconLocation(LPCWSTR pszIconPath, INT iIcon)
 {
     HRESULT hr = E_FAIL;
 HRESULT STDMETHODCALLTYPE CShellLink::SetIconLocation(LPCWSTR pszIconPath, INT iIcon)
 {
     HRESULT hr = E_FAIL;
-    WCHAR szUnExpIconPath[MAX_PATH];
-    BOOL bSuccess;
+    WCHAR szIconPath[MAX_PATH];
 
     TRACE("(%p)->(path=%s iicon=%u)\n", this, debugstr_w(pszIconPath), iIcon);
 
     if (pszIconPath)
     {
 
     TRACE("(%p)->(path=%s iicon=%u)\n", this, debugstr_w(pszIconPath), iIcon);
 
     if (pszIconPath)
     {
-        /* Try to fully unexpand the icon path */
-
         /*
          * Check whether the user-given file path contains unexpanded
          * environment variables. If so, create a target environment block.
         /*
          * Check whether the user-given file path contains unexpanded
          * environment variables. If so, create a target environment block.
@@ -1868,16 +1873,35 @@ HRESULT STDMETHODCALLTYPE CShellLink::SetIconLocation(LPCWSTR pszIconPath, INT i
          * refer to the same place even if the would-be corresponding
          * environment variable could change).
          */
          * refer to the same place even if the would-be corresponding
          * environment variable could change).
          */
-        // FIXME: http://stackoverflow.com/questions/2976489/ishelllinkseticonlocation-translates-my-icon-path-into-program-files-which-i
-        // if (PathFullyUnExpandEnvStringsW(pszIconPath, szUnExpIconPath, _countof(szUnExpIconPath)))
-        bSuccess = PathUnExpandEnvStringsW(pszIconPath, szUnExpIconPath, _countof(szUnExpIconPath));
-        if (bSuccess && wcscmp(pszIconPath, szUnExpIconPath) != 0)
+#ifdef ICON_LINK_WINDOWS_COMPAT
+        /* Try to fully unexpand the icon path */
+        // if (PathFullyUnExpandEnvStringsW(pszIconPath, szIconPath, _countof(szIconPath)))
+        BOOL bSuccess = PathUnExpandEnvStringsW(pszIconPath, szIconPath, _countof(szIconPath));
+        if (bSuccess && wcscmp(pszIconPath, szIconPath) != 0)
+#else
+        /*
+         * In some situations, described in http://stackoverflow.com/questions/2976489/ishelllinkseticonlocation-translates-my-icon-path-into-program-files-which-i
+         * the result of PathUnExpandEnvStringsW() could be wrong, and instead
+         * one would have to store the actual provided icon location path, while
+         * creating an icon environment block ONLY if that path already contains
+         * environment variables. This is what the present case is trying to implement.
+         */
+        SHExpandEnvironmentStringsW(pszIconPath, szIconPath, _countof(szIconPath));
+        if (wcscmp(pszIconPath, szIconPath) != 0)
+#endif
         {
         {
-            /* Unexpansion succeeded, so we need an icon environment block */
+            /*
+             * The user-given file path contains unexpanded environment
+             * variables, so we need an icon environment block.
+             */
             EXP_SZ_LINK buffer;
             LPEXP_SZ_LINK pInfo;
 
             EXP_SZ_LINK buffer;
             LPEXP_SZ_LINK pInfo;
 
-            pszIconPath = szUnExpIconPath;
+#ifdef ICON_LINK_WINDOWS_COMPAT
+            /* Make pszIconPath point to the unexpanded path */
+            LPCWSTR pszOrgIconPath = pszIconPath;
+            pszIconPath = szIconPath;
+#endif
             pInfo = (LPEXP_SZ_LINK)SHFindDataBlock(m_pDBList, EXP_SZ_ICON_SIG);
             if (pInfo)
             {
             pInfo = (LPEXP_SZ_LINK)SHFindDataBlock(m_pDBList, EXP_SZ_ICON_SIG);
             if (pInfo)
             {
@@ -1902,8 +1926,8 @@ HRESULT STDMETHODCALLTYPE CShellLink::SetIconLocation(LPCWSTR pszIconPath, INT i
                 buffer.dwSignature = EXP_SZ_ICON_SIG;
             }
 
                 buffer.dwSignature = EXP_SZ_ICON_SIG;
             }
 
-            lstrcpynW(pInfo->szwTarget, szUnExpIconPath, _countof(pInfo->szwTarget));
-            WideCharToMultiByte(CP_ACP, 0, szUnExpIconPath, -1,
+            lstrcpynW(pInfo->szwTarget, pszIconPath, _countof(pInfo->szwTarget));
+            WideCharToMultiByte(CP_ACP, 0, pszIconPath, -1,
                                 pInfo->szTarget, _countof(pInfo->szTarget), NULL, NULL);
 
             hr = S_OK;
                                 pInfo->szTarget, _countof(pInfo->szTarget), NULL, NULL);
 
             hr = S_OK;
@@ -1911,16 +1935,31 @@ HRESULT STDMETHODCALLTYPE CShellLink::SetIconLocation(LPCWSTR pszIconPath, INT i
                 hr = AddDataBlock(pInfo);
             if (hr == S_OK)
                 m_Header.dwFlags |= SLDF_HAS_EXP_ICON_SZ;
                 hr = AddDataBlock(pInfo);
             if (hr == S_OK)
                 m_Header.dwFlags |= SLDF_HAS_EXP_ICON_SZ;
+
+#ifdef ICON_LINK_WINDOWS_COMPAT
+            /* Set pszIconPath back to the original one */
+            pszIconPath = pszOrgIconPath;
+#else
+            /* Now, make pszIconPath point to the expanded path */
+            pszIconPath = szIconPath;
+#endif
         }
         else
         {
         }
         else
         {
-            /* Unexpansion failed, so we need to remove any icon environment block */
+            /*
+             * The user-given file path does not contain unexpanded environment
+             * variables, so we need to remove any icon environment block.
+             */
             m_Header.dwFlags &= ~SLDF_HAS_EXP_ICON_SZ;
             RemoveDataBlock(EXP_SZ_ICON_SIG);
             m_Header.dwFlags &= ~SLDF_HAS_EXP_ICON_SZ;
             RemoveDataBlock(EXP_SZ_ICON_SIG);
+
+            /* pszIconPath points to the user path */
         }
     }
 
         }
     }
 
-    /* Store the original icon path location (this one may contain unexpanded environment strings) */
+#ifdef ICON_LINK_WINDOWS_COMPAT
+    /* Store the original icon path location (may contain unexpanded environment strings) */
+#endif
     if (pszIconPath)
     {
         m_Header.dwFlags &= ~SLDF_HAS_ICONLOCATION;
     if (pszIconPath)
     {
         m_Header.dwFlags &= ~SLDF_HAS_ICONLOCATION;
@@ -2404,7 +2443,7 @@ HRESULT STDMETHODCALLTYPE CShellLink::SetPath(LPCWSTR pszFile)
             if (hr == S_OK)
                 m_Header.dwFlags |= SLDF_HAS_EXP_SZ;
 
             if (hr == S_OK)
                 m_Header.dwFlags |= SLDF_HAS_EXP_SZ;
 
-            /* Now, make pszFile point to the expanded buffer */
+            /* Now, make pszFile point to the expanded path */
             pszFile = szPath;
         }
         else
             pszFile = szPath;
         }
         else