Reactos
1/*
2 * PROJECT: ReactOS API Tests
3 * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later)
4 * PURPOSE: Tests for ExtractIconEx routine
5 * COPYRIGHT: Copyright 2019 George Bișoc (george.bisoc@reactos.org)
6 * Copyright 2023 Doug Lyons (douglyons@douglyons.com)
7 */
8
9#include "shelltest.h"
10#include <stdio.h>
11
12EXTERN_C BOOL WINAPI SHAreIconsEqual(HICON hIcon1, HICON hIcon2);
13
14static void SafeDestroyIcon(HICON hIco)
15{
16 if (hIco)
17 DestroyIcon(hIco);
18}
19
20static UINT GetIcoSize(HICON hIco)
21{
22 ICONINFO info;
23 if (!GetIconInfo(hIco, &info))
24 return 0;
25
26 BITMAP bm;
27 if (!GetObject(info.hbmColor ? info.hbmColor : info.hbmMask, sizeof(bm), &bm))
28 bm.bmWidth = 0;
29 DeleteObject(info.hbmMask);
30 DeleteObject(info.hbmColor);
31 return bm.bmWidth;
32}
33
34typedef struct
35{
36 PCWSTR pszFilePath;
37 UINT nIcons;
38} EXTRACTICONTEST;
39
40BOOL FileExists(LPCSTR FileName)
41{
42 FILE *fp = NULL;
43 bool exists = FALSE;
44
45 fp = fopen(FileName, "r");
46 if (fp != NULL)
47 {
48 exists = TRUE;
49 fclose(fp);
50 }
51 return exists;
52}
53
54BOOL ResourceToFile(INT i, LPCSTR FileName)
55{
56 FILE *fout;
57 HGLOBAL hData;
58 HRSRC hRes;
59 LPVOID lpResLock;
60 UINT iSize;
61
62 if (FileExists(FileName))
63 {
64 skip("'%s' already exists. Exiting now\n", FileName);
65 return FALSE;
66 }
67
68 hRes = FindResourceW(NULL, MAKEINTRESOURCEW(i), MAKEINTRESOURCEW(RT_RCDATA));
69 if (hRes == NULL)
70 {
71 skip("Could not locate resource (%d). Exiting now\n", i);
72 return FALSE;
73 }
74
75 iSize = SizeofResource(NULL, hRes);
76
77 hData = LoadResource(NULL, hRes);
78 if (hData == NULL)
79 {
80 skip("Could not load resource (%d). Exiting now\n", i);
81 return FALSE;
82 }
83
84 // Lock the resource into global memory.
85 lpResLock = LockResource(hData);
86 if (lpResLock == NULL)
87 {
88 skip("Could not lock resource (%d). Exiting now\n", i);
89 return FALSE;
90 }
91
92 fout = fopen(FileName, "wb");
93 fwrite(lpResLock, iSize, 1, fout);
94 fclose(fout);
95 return TRUE;
96}
97
98EXTRACTICONTEST IconTests[] =
99{
100 /* Executable file with icon */
101 {L"%SystemRoot%\\System32\\cmd.exe", 1},
102
103 /* Executable file without icon */
104 {L"%SystemRoot%\\System32\\autochk.exe", 0},
105
106 /* Non-existing files */
107 {L"%SystemRoot%\\non-existent-file.sdf", 0},
108
109 /* Multiple icons in the same ICO file (6 icons)
110 * Per MS: If the file is an .ico file, the return value is 1. */
111 {L"sysicon.ico", 1},
112
113 /* ICO file with both normal and PNG icons */
114 {L"ROS.ico", (UINT)(GetNTVersion() >= _WIN32_WINNT_VISTA ? 1 : 0)}
115};
116
117VOID RunExtractIconTest(EXTRACTICONTEST *Test)
118{
119 UINT nReturnedIcons, nExtractedIcons;
120
121 /* Check count of icons returned */
122 nReturnedIcons = ExtractIconExW(Test->pszFilePath, -1, NULL, NULL, 0);
123 ok(nReturnedIcons == Test->nIcons, "ExtractIconExW(L\"%S\"): Expects %u icons, got %u\n", Test->pszFilePath, Test->nIcons, nReturnedIcons);
124
125 /* Check if the 0th icon can be extracted successfully */
126 nExtractedIcons = ExtractIconExW(Test->pszFilePath, 0, NULL, NULL, 1);
127 ok(nExtractedIcons == Test->nIcons, "ExtractIconExW(L\"%S\"): Expects %u icons, got %u\n", Test->pszFilePath, Test->nIcons, nExtractedIcons);
128}
129
130START_TEST(ExtractIconEx)
131{
132 UINT i;
133 CHAR FileName[2][13] = { "ROS.ico", "sysicon.ico" };
134 EXTRACTICONTEST explorer_exe = {L"%SystemRoot%\\explorer.exe", 0};
135
136 if (!ResourceToFile(2, FileName[0]))
137 return;
138 if (!ResourceToFile(3, FileName[1]))
139 return;
140
141 /* Run normal tests */
142 for (i = 0; i < _countof(IconTests); ++i)
143 RunExtractIconTest(&IconTests[i]);
144
145 /* Run special case checks */
146 switch (GetNTVersion())
147 {
148 case _WIN32_WINNT_WS03:
149 explorer_exe.nIcons = 18;
150 break;
151 case _WIN32_WINNT_VISTA:
152 explorer_exe.nIcons = 23;
153 break;
154 case _WIN32_WINNT_WIN7:
155 explorer_exe.nIcons = 25;
156 break;
157 case _WIN32_WINNT_WIN8:
158 case _WIN32_WINNT_WINBLUE:
159 explorer_exe.nIcons = 24;
160 break;
161 case _WIN32_WINNT_WIN10:
162 explorer_exe.nIcons = 28;
163 break;
164 }
165
166 if (explorer_exe.nIcons)
167 RunExtractIconTest(&explorer_exe);
168 else
169 skip("Unknown NT Version: 0x%lX\n", GetNTVersion());
170
171 DeleteFileA(FileName[0]);
172 DeleteFileA(FileName[1]);
173}
174
175static HRESULT SHDEI(LPCWSTR pszIconFile, int Index = 0, UINT GIL = 0, UINT Size = 0)
176{
177 HICON hIco = NULL;
178 HRESULT hr = SHDefExtractIcon(pszIconFile, Index, GIL, &hIco, NULL, Size);
179 if (hr == S_OK)
180 {
181 hr = GetIcoSize(hIco);
182 SafeDestroyIcon(hIco);
183 }
184 return hr;
185}
186
187START_TEST(SHDefExtractIcon)
188{
189 HRESULT hr;
190 int SysBigIconSize = GetSystemMetrics(SM_CXICON);
191
192 // Modern Windows requires the system image list to be initialized for GIL_SIMULATEDOC to work!
193 SHFILEINFOW shfi;
194 SHGetFileInfoW(L"x", 0, &shfi, sizeof(shfi), SHGFI_SYSICONINDEX | SHGFI_USEFILEATTRIBUTES);
195
196 WCHAR path[MAX_PATH];
197 GetSystemDirectoryW(path, _countof(path));
198 PathAppendW(path, L"user32.dll");
199 int index = 1;
200
201 ok(SHDEI(path, index, 0, 0) == SysBigIconSize, "0 size must match GetSystemMetrics\n");
202 ok(SHDEI(path, index, 0, SysBigIconSize * 2) == SysBigIconSize * 2, "Resize failed\n");
203
204 HICON hIcoLarge, hIcoSmall;
205 if (SHDefExtractIcon(path, index, 0, &hIcoLarge, &hIcoSmall, 0) != S_OK)
206 hIcoLarge = hIcoSmall = NULL;
207 ok(hIcoLarge && hIcoSmall && !SHAreIconsEqual(hIcoLarge, hIcoSmall), "Large+Small failed\n");
208 SafeDestroyIcon(hIcoLarge);
209 SafeDestroyIcon(hIcoSmall);
210
211 static const int sizes[] = { 0, SysBigIconSize * 2 };
212 for (UINT i = 0; i < _countof(sizes); ++i)
213 {
214 HICON hIcoNormal, hIcoSimDoc;
215 if (FAILED(hr = SHDefExtractIcon(path, index, 0, &hIcoNormal, NULL, sizes[i])))
216 hIcoNormal = NULL;
217 if (FAILED(hr = SHDefExtractIcon(path, index, GIL_SIMULATEDOC, &hIcoSimDoc, NULL, sizes[i])))
218 hIcoSimDoc = NULL;
219 ok(hIcoNormal && hIcoSimDoc && !SHAreIconsEqual(hIcoNormal, hIcoSimDoc), "GIL_SIMULATEDOC failed\n");
220 SafeDestroyIcon(hIcoNormal);
221 SafeDestroyIcon(hIcoSimDoc);
222 }
223
224 GetTempPathW(_countof(path), path);
225 GetTempFileNameW(path, L"TEST", 0, path);
226 ok(SHDEI(path) == S_FALSE, "Empty file should return S_FALSE\n");
227 HANDLE hFile = CreateFileW(path, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, OPEN_EXISTING, 0, NULL);
228 if (hFile != INVALID_HANDLE_VALUE)
229 {
230 DWORD io;
231 WriteFile(hFile, "!", 1, &io, NULL);
232 CloseHandle(hFile);
233 ok(SHDEI(path) == S_FALSE, "File without icons should return S_FALSE\n");
234 }
235 DeleteFile(path);
236}