From 3c48b07b4aeaf7bfae61c5e43382fd2165ae31bd Mon Sep 17 00:00:00 2001
From: Louis Lenders <xerox.xerox2000x@gmail.com>
Date: Fri, 12 Oct 2018 11:57:18 +0200
Subject: [PATCH] shell32: Improve semi-stub SHGetStockIconInfo, try find
 existing iconhandle

v3: try find handle to existing icon

this is the best I can come up with for now, on windows afaict all these icons mostly come from imegares.dll
so the attached patch is not really how windows does it

Fix for bug https://bugs.winehq.org/show_bug.cgi?id=45868

Signed-off-by: Louis Lenders <xerox.xerox2000x@gmail.com>
---
 dlls/shell32/iconcache.c       | 158 +++++++++++++++++++++++++++++++--
 dlls/shell32/tests/shelllink.c |  15 ++++
 2 files changed, 165 insertions(+), 8 deletions(-)

diff --git a/dlls/shell32/iconcache.c b/dlls/shell32/iconcache.c
index 6107e0c33b..da3e043d95 100644
--- a/dlls/shell32/iconcache.c
+++ b/dlls/shell32/iconcache.c
@@ -987,6 +987,119 @@ INT WINAPI SHGetIconOverlayIndexW(LPCWSTR pszIconPath, INT iIconIndex)
   return -1;
 }
 
+/****************************************************************************
+ * For SHGetStockIconInfo
+ */
+typedef struct {
+    SHSTOCKICONID id;
+    DWORD iconid;
+} SI_ENTRY;
+
+static const SI_ENTRY si_table[] =
+{
+    [0]   = { SIID_DOCNOASSOC, IDI_SHELL_FILE},
+    [1]   = { SIID_DOCASSOC, IDI_SHELL_DOCUMENT},
+    [2]   = { SIID_APPLICATION, IDI_SHELL_WINDOW},
+    [3]   = { SIID_FOLDER, IDI_SHELL_FOLDER},
+    [4]   = { SIID_FOLDEROPEN, IDI_SHELL_FOLDER_OPEN},
+    [5]   = { SIID_DRIVE525, 0},
+    [6]   = { SIID_DRIVE35, 0},
+    [7]   = { SIID_DRIVERREMOVE, 0},
+    [8]   = { SIID_DRIVERFIXED, IDI_SHELL_DRIVE},
+    [9]   = { SIID_DRIVERNET, IDI_SHELL_NETDRIVE},
+    [10]  = { SIID_DRIVERNETDISABLE, IDI_SHELL_NETDRIVE2},
+    [11]  = { SIID_DRIVERCD, IDI_SHELL_OPTICAL_DRIVE},
+    [12]  = { SIID_DRIVERRAM, IDI_SHELL_RAMDISK},
+    [13]  = { SIID_WORLD, 0},
+    /* Missing: 14 */
+    [15]  = { SIID_SERVER, 0},
+    [16]  = { SIID_PRINTER, IDI_SHELL_PRINT},
+    [17]  = { SIID_MYNETWORK, 0},
+    /* Missing: 18 - 21 */
+    [22]  = { SIID_FIND, 0},
+    [23]  = { SIID_HELP, IDI_SHELL_HELP},
+    /* Missing: 24 - 27 */
+    [28]  = {SIID_SHARE, 0},
+    [29]  = {SIID_LINK, 0},
+    [30]  = {SIID_SLOWFILE, 0},
+    [31]  = {SIID_RECYCLER, IDI_SHELL_TRASH_FOLDER},
+    [32]  = {SIID_RECYCLERFULL, IDI_SHELL_FULL_RECYCLE_BIN},
+    /* Missing: 33 - 39 */
+    [40]  = {SIID_MEDIACDAUDIO, 0},
+    /* Missing: 41 - 46 */
+    [47]  = {SIID_LOCK, IDI_SHELL_PASSWORDS},
+    /* Missing: 48 */
+    [49]  = {SIID_AUTOLIST, 0},
+    [50]  = {SIID_PRINTERNET, 0},
+    [51]  = {SIID_SERVERSHARE, 0},
+    [52]  = {SIID_PRINTERFAX, 0},
+    [53]  = {SIID_PRINTERFAXNET, 0},
+    [54]  = {SIID_PRINTERFILE, 0},
+    [55]  = {SIID_STACK, 0},
+    [56]  = {SIID_MEDIASVCD, 0},
+    [57]  = {SIID_STUFFEDFOLDER, 0},
+    [58]  = {SIID_DRIVEUNKNOWN, 0},
+    [59]  = {SIID_DRIVEDVD, 0},
+    [60]  = {SIID_MEDIADVD, 0},
+    [61]  = {SIID_MEDIADVDRAM, 0},
+    [62]  = {SIID_MEDIADVDRW, 0},
+    [63]  = {SIID_MEDIADVDR, 0},
+    [64]  = {SIID_MEDIADVDROM, 0},
+    [65]  = {SIID_MEDIACDAUDIOPLUS, 0},
+    [66]  = {SIID_MEDIACDRW, 0},
+    [67]  = {SIID_MEDIACDR, 0},
+    [68]  = {SIID_MEDIACDBURN, 0},
+    [69]  = {SIID_MEDIABLANKCD, 0},
+    [70]  = {SIID_MEDIACDROM, 0},
+    [71]  = {SIID_AUDIOFILES, IDI_SHELL_AUDIO_FILE},
+    [72]  = {SIID_IMAGEFILES, IDI_SHELL_IMAGE_FILE},
+    [73]  = {SIID_VIDEOFILES, IDI_SHELL_VIDEO_FILE},
+    [74]  = {SIID_MIXEDFILES, 0},
+    [75]  = {SIID_FOLDERBACK, 0},
+    [76]  = {SIID_FOLDERFRONT, 0},
+    [77]  = {SIID_SHIELD, 0},
+    [78]  = {SIID_WARNING, 0},
+    [79]  = {SIID_INFO, 0},
+    [80]  = {SIID_ERROR, 0},
+    [81]  = {SIID_KEY, 0},
+    [82]  = {SIID_SOFTWARE, 0},
+    [83]  = {SIID_RENAME, IDI_SHELL_RENAME},
+    [84]  = {SIID_DELETE, IDI_SHELL_CONFIRM_DELETE},
+    [85]  = {SIID_MEDIAAUDIODVD, 0},
+    [86]  = {SIID_MEDIAMOVIEDVD, 0},
+    [87]  = {SIID_MEDIAENHANCEDCD, 0},
+    [88]  = {SIID_MEDIAENHANCEDDVD, 0},
+    [89]  = {SIID_MEDIAHDDVD, 0},
+    [90]  = {SIID_MEDIABLUERAY, 0},
+    [91]  = {SIID_MEDIAVCD, 0},
+    [92]  = {SIID_MEDIADVDPLUSR, 0},
+    [93]  = {SIID_MEDIADVDPLUSRW, 0},
+    [94]  = {SIID_DESKTOPPC, IDI_SHELL_MY_COMPUTER},
+    [95]  = {SIID_MOBILEPC, 0},
+    [96]  = {SIID_USERS, IDI_SHELL_USERS},
+    [97]  = {SIID_MEDIASMARTMEDIA, 0},
+    [98]  = {SIID_MEDIACOMPACTFLASH, 0},
+    [99]  = {SIID_DEVICECELLPHONE, 0},
+    [100] = {SIID_DEVICECAMERA, 0},
+    [101] = {SIID_DEVICEVIDEOCAMERA, 0},
+    [102] = {SIID_DEVICEAUDIOPLAYER, 0},
+    [103] = {SIID_NETWORKCONNECT, 0},
+    [104] = {SIID_INTERNET, IDI_SHELL_WEB_BROWSER},
+    [105] = {SIID_ZIPFILE, 0},
+    [106] = {SIID_SETTINGS, IDI_SHELL_SETTINGS},
+    /* Missing: 107 - 131 */
+    [132] = {SIID_DRIVEHDDVD, 0},
+    [133] = {SIID_DRIVEBD, 0},
+    [134] = {SIID_MEDIAHDDVDROM, 0},
+    [135] = {SIID_MEDIAHDDVDR, 0},
+    [136] = {SIID_MEDIAHDDVDRAM, 0},
+    [137] = {SIID_MEDIABDROM, 0},
+    [138] = {SIID_MEDIABDR, 0},
+    [139] = {SIID_MEDIABDRE, 0},
+    [140] = {SIID_CLUSTEREDDRIVE, 0}
+    /* Missing: 141 - 180  and  SIID_MAX_ICONS = 181*/
+ };
+
 /****************************************************************************
  * SHGetStockIconInfo [SHELL32.@]
  *
@@ -1004,25 +1117,54 @@ INT WINAPI SHGetIconOverlayIndexW(LPCWSTR pszIconPath, INT iIconIndex)
  */
 HRESULT WINAPI SHGetStockIconInfo(SHSTOCKICONID id, UINT flags, SHSTOCKICONINFO *sii)
 {
-    static const WCHAR shell32dll[] = {'\\','s','h','e','l','l','3','2','.','d','l','l',0};
+    static const WCHAR shell32dllW[] = {'s','h','e','l','l','3','2','.','d','l','l',0};
+    static const WCHAR slashW[] = {'\\',0};
+    HMODULE hmod;
 
-    FIXME("(%d, 0x%x, %p) semi-stub\n", id, flags, sii);
+    TRACE("(%d, 0x%x, %p)\n", id, flags, sii);
     if ((id < 0) || (id >= SIID_MAX_ICONS) || !sii || (sii->cbSize != sizeof(SHSTOCKICONINFO))) {
         return E_INVALIDARG;
     }
 
     GetSystemDirectoryW(sii->szPath, MAX_PATH);
+    lstrcatW(sii->szPath, slashW);
+    lstrcatW(sii->szPath, shell32dllW);
+
+    sii->hIcon = NULL;
+    sii->iSysImageIndex = -1;
 
-    /* no icons defined: use default */
-    sii->iIcon = -IDI_SHELL_FILE;
-    lstrcatW(sii->szPath, shell32dll);
+    /* this is not how windows does it, on windows picked mostly from imageres.dll !*/
+    if (si_table[id].iconid)
+        sii->iIcon = sii->iSysImageIndex - si_table[id].id;
+    else
+    {
+        FIXME("Couldn`t find SIID %d, returning default values (IDI_SHELL_FILE)\n", id);
+        sii->iIcon = sii->iSysImageIndex - IDI_SHELL_FILE;
+    }
+
+    if (flags & SHGSI_ICON)
+    {
+        flags &= ~SHGSI_ICON;
+
+        hmod = GetModuleHandleW(shell32dllW);
+        if (hmod)
+        {
+            if (si_table[id].iconid)
+                sii->hIcon = LoadIconW(hmod, MAKEINTRESOURCEW(si_table[id].iconid));
+            else
+                sii->hIcon = LoadIconW(hmod, MAKEINTRESOURCEW(IDI_SHELL_FILE));
+        }
+
+        if (!sii->hIcon)
+        {
+            ERR("failed to get an icon handle\n");
+            return E_INVALIDARG;
+        }
+    }
 
     if (flags)
         FIXME("flags 0x%x not implemented\n", flags);
 
-    sii->hIcon = NULL;
-    sii->iSysImageIndex = -1;
-
     TRACE("%3d: returning %s (%d)\n", id, debugstr_w(sii->szPath), sii->iIcon);
 
     return S_OK;
diff --git a/dlls/shell32/tests/shelllink.c b/dlls/shell32/tests/shelllink.c
index 3bfd9cbe38..e7e3dac987 100644
--- a/dlls/shell32/tests/shelllink.c
+++ b/dlls/shell32/tests/shelllink.c
@@ -1139,6 +1139,21 @@ static void test_SHGetStockIconInfo(void)
     /* there is a NULL check for the struct  */
     hr = pSHGetStockIconInfo(SIID_FOLDER, SHGSI_ICONLOCATION, NULL);
     ok(hr == E_INVALIDARG, "NULL: got 0x%x\n", hr);
+
+    for(i = 0; i < 140; i++)  /* highest on wvista, i > 140 gives E_INVALIDARG, win7 can go higher */
+    {
+        memset(buffer, 0, sizeof(buffer));
+        sii->cbSize = sizeof(SHSTOCKICONINFO);
+        hr = pSHGetStockIconInfo(i, SHGSI_ICON | SHGSI_SMALLICON, sii);
+        ok(hr == S_OK, "got 0x%x (expected S_OK)\n", hr);
+        ok(sii->hIcon != NULL, "got NULL, expected an icon handle\n");
+        ok(sii->iIcon != 0, "got unexpected 0 for SIID %d\n", i); /* howto find out exact sii->iIcon value??? */
+        ok(sii->iSysImageIndex == -1, "got %d (expected -1)\n", sii->iSysImageIndex);
+        ok(DestroyIcon(sii->hIcon), "DestroyIcon failed\n");
+        if (winetest_debug > 1)
+            trace("%3d: got iSysImageIndex %3d, iIcon %3d and %s\n", i, sii->iSysImageIndex,
+            sii->iIcon, wine_dbgstr_w(sii->szPath));
+    }
 }
 
 static void test_SHExtractIcons(void)
-- 
2.19.1

