1 /*
2  * PROJECT:         ReactOS api tests
3  * LICENSE:         LGPLv2.1+ - See COPYING.LIB in the top level directory
4  * PURPOSE:         Test for PrivateExtractIcons
5  * PROGRAMMER:      Hermes Belusca-Maito
6  *                  Doug Lyons <douglyons@douglyons.com>
7  */
8 
9 #include "precomp.h"
10 #include <stdio.h>
11 
12 BOOL FileExists(PCWSTR FileName)
13 {
14     DWORD Attribute = GetFileAttributesW(FileName);
15 
16     return (Attribute != INVALID_FILE_ATTRIBUTES &&
17             !(Attribute & FILE_ATTRIBUTE_DIRECTORY));
18 }
19 
20 BOOL ResourceToFile(INT i, PCWSTR FileName)
21 {
22     FILE *fout;
23     HGLOBAL hData;
24     HRSRC hRes;
25     PVOID pResLock;
26     UINT iSize;
27 
28     if (FileExists(FileName))
29     {
30         /* We should only be using %temp% paths, so deleting here should be OK */
31         printf("Deleting '%S' that already exists.\n", FileName);
32         DeleteFileW(FileName);
33     }
34 
35     hRes = FindResourceW(NULL, MAKEINTRESOURCEW(i), MAKEINTRESOURCEW(RT_RCDATA));
36     if (hRes == NULL)
37     {
38         skip("Could not locate resource (%d). Exiting now\n", i);
39         return FALSE;
40     }
41 
42     iSize = SizeofResource(NULL, hRes);
43 
44     hData = LoadResource(NULL, hRes);
45     if (hData == NULL)
46     {
47         skip("Could not load resource (%d). Exiting now\n", i);
48         return FALSE;
49     }
50 
51     // Lock the resource into global memory.
52     pResLock = LockResource(hData);
53     if (pResLock == NULL)
54     {
55         skip("Could not lock resource (%d). Exiting now\n", i);
56         return FALSE;
57     }
58 
59     fout = _wfopen(FileName, L"wb");
60     fwrite(pResLock, iSize, 1, fout);
61     fclose(fout);
62     return TRUE;
63 }
64 
65 static struct
66 {
67     PCWSTR FilePath;
68     UINT cIcons;        // Return value of the first icon group extracted (should be 1 if no error)
69     UINT cTotalIcons;   // Return value of total icon groups in file
70     BOOL bhIconValid;   // Whether or not the returned icon handle is not NULL.
71 } IconTests[] =
72 {
73     /* Executables with just one icon group */
74     {L"notepad.exe", 1, 1, TRUE},
75     {L"%SystemRoot%\\System32\\cmd.exe", 1, 1, TRUE},
76 
77     /* Executable without icon groups */
78     {L"%SystemRoot%\\System32\\autochk.exe", 0, 0, FALSE},
79 
80     /* Existing file (shell32 has 233 icon groups in ReactOS only) */
81     {L"%SystemRoot%\\System32\\shell32.dll", 1, 233, TRUE},
82 
83     /* Non-existing files */
84     {L"%SystemRoot%\\non-existent-file.sdf", 0xFFFFFFFF, 0, FALSE},
85 
86     /* Executable with 18 icon groups */
87     {L"%SystemRoot%\\explorer.exe", 1, 18, TRUE},
88 
89     /* Icon group file containing 6 icons */
90     {L"%temp%\\sysicon.ico", 1, 1, TRUE},
91 
92     /* Icon group file containing one PNG icon and one normal icon */
93     {L"%temp%\\ROS.ico", 1, 1, TRUE},
94 
95     /* Executable file with bad 'Icon Group' but good 'Icons'.
96      * Windows explorer shows the program's icon correctly in WinXP/Win2K3
97      * but Windows 7 shows only a default icon. This is analogous
98      * to EXE's generated by older Watcom C/C++ versions. */
99     {L"%temp%\\cpimg2e.exe", 1, 1, TRUE},
100 };
101 
102 static struct
103 {
104     PCWSTR FileName;
105     INT ResourceId;
106 } IconFiles[] =
107 {
108     {L"%temp%\\ROS.ico", IDR_ICONS_PNG},
109     {L"%temp%\\sysicon.ico", IDR_ICONS_NORMAL},
110     {L"%temp%\\cpimg2e.exe", IDR_EXE_NORMAL}
111 };
112 
113 START_TEST(PrivateExtractIcons)
114 {
115     HICON ahIcon;
116     UINT i, aIconId, cIcons, cIcoTotal;
117     WCHAR PathBuffer[MAX_PATH];
118 
119     /* Extract icons */
120     for (i = 0; i < _countof(IconFiles); ++i)
121     {
122         ExpandEnvironmentStringsW(IconFiles[i].FileName, PathBuffer, _countof(PathBuffer));
123 
124         if (!ResourceToFile(IconFiles[i].ResourceId, PathBuffer))
125             goto Cleanup;
126     }
127 
128     for (i = 0; i < _countof(IconTests); ++i)
129     {
130         /* Get total number of icon groups in file.
131          * None of the hard numbers in the function matter since we have
132          * two NULLs for the Icon Handle and Count to be set. */
133         cIcoTotal = PrivateExtractIconsW(IconTests[i].FilePath, 0, 16, 16, NULL, NULL, 0, 0);
134         ok((i == 3 ?
135               cIcoTotal > 232 && cIcoTotal < 240 :    /* shell32 case: ROS has 233, W2K2SP2 has 239 icon groups. */
136               cIcoTotal == IconTests[i].cTotalIcons),
137            "PrivateExtractIconsW(%u): "
138            "got %u, expected %u\n", i, cIcoTotal, IconTests[i].cTotalIcons);
139 
140         /* Always test extraction of the FIRST icon (index 0) */
141         ahIcon = (HICON)UlongToHandle(0xdeadbeef);
142         aIconId = 0xdeadbeef;
143         cIcons = PrivateExtractIconsW(IconTests[i].FilePath, 0, 16, 16, &ahIcon, &aIconId, 1, 0);
144         ok(cIcons == IconTests[i].cIcons, "PrivateExtractIconsW(%u): got %u, expected %u\n", i, cIcons, IconTests[i].cIcons);
145         ok(ahIcon != (HICON)UlongToHandle(0xdeadbeef), "PrivateExtractIconsW(%u): icon not set\n", i);
146         ok((IconTests[i].bhIconValid && ahIcon) || (!IconTests[i].bhIconValid && !ahIcon),
147             "PrivateExtractIconsW(%u): icon expected to be %s, but got 0x%p\n",
148             i, IconTests[i].bhIconValid ? "valid" : "not valid", ahIcon);
149         if (cIcons == 0xFFFFFFFF)
150         {
151             ok(aIconId == 0xdeadbeef,
152                "PrivateExtractIconsW(%u): id should not be set to 0x%x\n",
153                i, aIconId);
154         }
155         else
156         {
157             ok(aIconId != 0xdeadbeef, "PrivateExtractIconsW(%u): id not set\n", i);
158         }
159         if (ahIcon && ahIcon != (HICON)UlongToHandle(0xdeadbeef))
160             DestroyIcon(ahIcon);
161     }
162 
163 Cleanup:
164     for (i = 0; i < _countof(IconFiles); ++i)
165     {
166         ExpandEnvironmentStringsW(IconFiles[i].FileName, PathBuffer, _countof(PathBuffer));
167         DeleteFileW(PathBuffer);
168     }
169 }
170