From 31c90330238c8c840872e82963437e26cdf82c93 Mon Sep 17 00:00:00 2001 From: Michael Jung Date: Wed, 26 Oct 2005 12:05:59 +0000 Subject: [PATCH] - Try LoadLibrary/LoadImage to extract icons from wine's built-in dlls if SearchPath fails to find the requested file in PrivateExtractIcons. - Some unit tests for PrivateExtractIcons. --- dlls/user/exticon.c | 101 +++++++++++++++++++++++++++++++++++++++++++++ dlls/user/tests/resource.c | 38 +++++++++++++++++ 2 files changed, 139 insertions(+) diff --git a/dlls/user/exticon.c b/dlls/user/exticon.c index 7b447367229..c53438554ef 100644 --- a/dlls/user/exticon.c +++ b/dlls/user/exticon.c @@ -235,6 +235,81 @@ static BYTE * ICO_GetIconDirectory( LPBYTE peimage, LPicoICONDIR* lplpiID, ULONG return 0; } +/****************************************************************************** + * struct extract_icons_state [internal] + * + * Used to carry params and state through the iterations of EnumResourceNames in ICO_ExtractIconExW + */ +struct extract_icons_state { + INT nBaseIndex; /* Zero based index or negated integer id of the first icon to extract. */ + UINT nIcons; /* Number of icons to extract. */ + UINT cxDesired; /* Desired horizontal size in pixels (Might be two sizes in HI/LOWORD). */ + UINT cyDesired; /* Desired vertical size in pixels (Might be two sizes in HI/LOWORD). */ + UINT fuLoad; /* Flags passed to LoadImage. */ + INT cIter; /* Iteration counter. */ + HICON *pahIcon; /* Pointer to an array where the icon handles will be stored. */ + UINT *paIconId; /* Pointer to an array where the icon identifiers will be stored. */ +}; + +/****************************************************************************** + * extract_icons_callback [internal] + * + * Callback function of type ENUMRESNAMEPROC for EnumResourceNames. Used in + * ICO_ExtractIconsExW. + */ +static BOOL CALLBACK extract_icons_callback(HMODULE hModule, LPCWSTR pwszType, LPWSTR pwszName, + LONG_PTR lParam) +{ + struct extract_icons_state *pState = (struct extract_icons_state *)lParam; + INT idCurrent = (INT)pwszName; + + /* If the handle array pointer is NULL, we just count the number of icons in the module. */ + if (!pState->pahIcon) { + pState->cIter++; + return TRUE; + } + + /* If we didn't already start extracting icons (cIter == 0), we look if the current + * icon is the one we should start with. That's the case if nBaseIndex is negative and + * it's absolute value matches the current icon's identifier. Or if nBaseIndex is positive + * and we already ommited the first nBaseIndex icons. */ + if (pState->cIter == 0) { + if (pState->nBaseIndex < 0) { + if (!IS_INTRESOURCE(pwszName) || idCurrent != -pState->nBaseIndex) { + return TRUE; + } + } else if (pState->nBaseIndex > 0) { + pState->nBaseIndex--; + return TRUE; + } + } + + if (pState->cIter < pState->nIcons) { + /* Load the current icon with the size given in the LOWORD of cx/cyDesired */ + pState->pahIcon[pState->cIter] = + LoadImageW(hModule, pwszName, IMAGE_ICON, LOWORD(pState->cxDesired), + LOWORD(pState->cyDesired), pState->fuLoad); + /* FIXME: The resource's identifier (that is not idCurrent, which is the + * identifier of the icon directory) should be stored in paIconId, but I don't + * know how to query for it. */ + if (pState->paIconId) pState->paIconId[pState->cIter] = 0; + pState->cIter++; + + /* If there is a second desired size given in the HIWORDs of cx/cyDesired, load too. + * There seems to be an off-by-one error here, since pState->cIter might now be equal + * to pState->nIcons, but this is in fact how it works on windows. */ + if (HIWORD(pState->cxDesired) && HIWORD(pState->cyDesired)) { + pState->pahIcon[pState->cIter] = + LoadImageW(hModule, pwszName, IMAGE_ICON, HIWORD(pState->cxDesired), + HIWORD(pState->cyDesired), pState->fuLoad); + if (pState->paIconId) pState->paIconId[pState->cIter] = 0; + pState->cIter++; + } + } + + return pState->cIter < pState->nIcons; +} + /************************************************************************* * ICO_ExtractIconExW [internal] * @@ -274,8 +349,34 @@ static UINT ICO_ExtractIconExW( dwSearchReturn = SearchPathW(NULL, lpszExeFileName, NULL, sizeof(szExePath) / sizeof(szExePath[0]), szExePath, NULL); if ((dwSearchReturn == 0) || (dwSearchReturn > sizeof(szExePath) / sizeof(szExePath[0]))) { + /* This is very wine specific: If the user tries to extract icon's from system dlls, + * wine's build-in *.dll.so's will not be found (Even if they would be found it wouldn't + * work, since they are not in PE-format). Thus, if the SearchPath call fails, we try + * to load the file with LoadLibary and extract the icons with LoadImage (via + * EnumResourceNames). + */ + struct extract_icons_state state; + HMODULE hDllSo = LoadLibraryW(lpszExeFileName); + + if (!hDllSo) { WARN("File %s not found or path too long\n", debugstr_w(lpszExeFileName)); return -1; + } + + state.nBaseIndex = nIconIndex; + state.nIcons = nIcons; + state.cxDesired = cxDesired; + state.cyDesired = cyDesired; + state.fuLoad = flags; + state.cIter = 0; + state.pahIcon = RetPtr; + state.paIconId = pIconId; + + EnumResourceNamesW(hDllSo, MAKEINTRESOURCEW(RT_GROUP_ICON), extract_icons_callback, + (LONG_PTR)&state); + FreeLibrary(hDllSo); + + return state.cIter; } hFile = CreateFileW(szExePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0); diff --git a/dlls/user/tests/resource.c b/dlls/user/tests/resource.c index acb24ece46e..893d52ed11c 100644 --- a/dlls/user/tests/resource.c +++ b/dlls/user/tests/resource.c @@ -23,6 +23,16 @@ #include "wine/test.h" +static UINT (WINAPI *pPrivateExtractIconsA)(LPCTSTR, int, int, int, HICON *, UINT *, UINT, UINT) = NULL; + +static void init_function_pointers(void) +{ + HMODULE hmod = GetModuleHandleA("user32.dll"); + if (hmod) { + pPrivateExtractIconsA = (void*)GetProcAddress(hmod, "PrivateExtractIconsA"); + } +} + static void test_LoadStringA (void) { HINSTANCE hInst = GetModuleHandle (NULL); @@ -247,9 +257,37 @@ static void test_accel2(void) ok( DestroyAcceleratorTable( hac ), "destroy failed\n"); } +static void test_PrivateExtractIcons(void) { + CONST CHAR szShell32Dll[] = "shell32.dll"; + HICON ahIcon[256]; + UINT aIconId[256]; + UINT cIcons, cIcons2; + + if (!pPrivateExtractIconsA) return; + + cIcons = pPrivateExtractIconsA(szShell32Dll, 0, 16, 16, NULL, NULL, 0, 0); + cIcons2 = pPrivateExtractIconsA(szShell32Dll, 4, MAKELONG(32,16), MAKELONG(32,16), + NULL, NULL, 256, 0); + ok((cIcons == cIcons2) && (cIcons > 0), + "Icon count should be independent of requested icon sizes and base icon index! " + "(cIcons=%d, cIcons2=%d)\n", cIcons, cIcons2); + + cIcons = pPrivateExtractIconsA(szShell32Dll, 0, 16, 16, ahIcon, aIconId, 0, 0); + ok(cIcons == 0, "Zero icons requested, got cIcons=%d\n", cIcons); + + cIcons = pPrivateExtractIconsA(szShell32Dll, 0, 16, 16, ahIcon, aIconId, 3, 0); + ok(cIcons == 3, "Three icons requested got cIcons=%d\n", cIcons); + + cIcons = pPrivateExtractIconsA(szShell32Dll, 0, MAKELONG(32,16), MAKELONG(32,16), + ahIcon, aIconId, 3, 0); + ok(cIcons == 4, "Three icons requested, four expected, got cIcons=%d\n", cIcons); +} + START_TEST(resource) { + init_function_pointers(); test_LoadStringA (); test_accel1(); test_accel2(); + test_PrivateExtractIcons(); } -- 2.11.4.GIT