1 /*
2  * PROJECT:     ReactOS API tests
3  * LICENSE:     GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
4  * PURPOSE:     Test for SHCreateFileExtractIconW
5  * COPYRIGHT:   Copyright 2017,2018 Mark Jansen (mark.jansen@reactos.org)
6  */
7 
8 #include "shelltest.h"
9 
10 #include <wincon.h>
11 #include <wingdi.h>
12 #include <stdio.h>
13 #include <shellutils.h>
14 
15 HRESULT (STDAPICALLTYPE *pSHCreateFileExtractIconW)(LPCWSTR pszFile, DWORD dwFileAttributes, REFIID riid, void **ppv);
16 
17 struct TestData
18 {
19     const WCHAR* Name;
20     DWORD dwFlags;
21 };
22 
23 static TestData IconTests[] =
24 {
25     { L"xxx.zip", FILE_ATTRIBUTE_NORMAL },
26     { L"xxx.zip", FILE_ATTRIBUTE_DIRECTORY },
27     { L"xxx.exe", FILE_ATTRIBUTE_NORMAL },
28     { L"xxx.exe", FILE_ATTRIBUTE_DIRECTORY },
29     { L"xxx.dll", FILE_ATTRIBUTE_NORMAL },
30     { L"xxx.dll", FILE_ATTRIBUTE_DIRECTORY },
31     { L"xxx.txt", FILE_ATTRIBUTE_NORMAL },
32     { L"xxx.txt", FILE_ATTRIBUTE_DIRECTORY },
33     { NULL, FILE_ATTRIBUTE_NORMAL },
34     { NULL, FILE_ATTRIBUTE_DIRECTORY },
35 };
36 
37 struct TestIID
38 {
39     const GUID* IID;
40     HRESULT ExpectedCreate;
41     HRESULT ExpectedQueryInterface;
42 };
43 
44 static TestIID InterfaceTests[] =
45 {
46     { &IID_IDefaultExtractIconInit, E_NOINTERFACE, E_NOINTERFACE },
47     { &IID_IExtractIconW, S_OK, S_OK },
48     { &IID_IExtractIconA, S_OK, S_OK },
49     { &IID_IPersist, E_NOINTERFACE, E_NOINTERFACE },
50     { &IID_IPersistFile, E_NOINTERFACE, E_NOINTERFACE },
51 };
52 
53 
ExtractOneBitmap(HBITMAP hbm,CComHeapPtr<BYTE> & data,DWORD & size)54 static void ExtractOneBitmap(HBITMAP hbm, CComHeapPtr<BYTE>& data, DWORD& size)
55 {
56     HDC hdc = CreateCompatibleDC(NULL);
57     HGDIOBJ obj = SelectObject(hdc, hbm);
58 
59     CComHeapPtr<BITMAPINFO> pInfoBM;
60 
61     pInfoBM.AllocateBytes(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
62     memset(pInfoBM, 0, sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
63     pInfoBM->bmiHeader.biSize = sizeof(pInfoBM->bmiHeader);
64     if (!GetDIBits(hdc, hbm, 0, 0, NULL, pInfoBM, DIB_RGB_COLORS))
65         return;
66 
67     size = pInfoBM->bmiHeader.biSizeImage;
68     data.Allocate(size);
69     GetDIBits(hdc, hbm, 0, pInfoBM->bmiHeader.biHeight, data, pInfoBM, DIB_RGB_COLORS);
70 
71     SelectObject(hdc, obj);
72     DeleteDC(hdc);
73 }
74 
GetIconData(HICON icon,CComHeapPtr<BYTE> & colorData,DWORD & colorSize,CComHeapPtr<BYTE> & maskData,DWORD & maskSize)75 static bool GetIconData(HICON icon, CComHeapPtr<BYTE>& colorData, DWORD& colorSize, CComHeapPtr<BYTE>& maskData, DWORD& maskSize)
76 {
77     ICONINFO iconinfo;
78 
79     if (!GetIconInfo(icon, &iconinfo))
80         return false;
81 
82     ExtractOneBitmap(iconinfo.hbmColor, colorData, colorSize);
83     ExtractOneBitmap(iconinfo.hbmMask, maskData, maskSize);
84 
85     DeleteObject(iconinfo.hbmColor);
86     DeleteObject(iconinfo.hbmMask);
87 
88     return true;
89 }
90 
91 
START_TEST(SHCreateFileExtractIconW)92 START_TEST(SHCreateFileExtractIconW)
93 {
94     WCHAR CurrentModule[MAX_PATH];
95     HMODULE shell32 = LoadLibraryA("shell32.dll");
96     HICON myIcon;
97     pSHCreateFileExtractIconW = (HRESULT (__stdcall *)(LPCWSTR, DWORD, REFIID, void **))GetProcAddress(shell32, "SHCreateFileExtractIconW");
98 
99     /* Show that icons returned are always the same */
100     UINT tryFlags[4] = { 0, GIL_FORSHORTCUT, GIL_OPENICON };
101 
102     CoInitialize(NULL);
103 
104     GetModuleFileNameW(NULL, CurrentModule, _countof(CurrentModule));
105     {
106         SHFILEINFOW shfi;
107         ULONG_PTR firet = SHGetFileInfoW(CurrentModule, 0, &shfi, sizeof(shfi), SHGFI_ICON);
108         myIcon = shfi.hIcon;
109         if (!firet)
110         {
111             skip("Unable to get my own icon\n");
112             return;
113         }
114     }
115 
116     if (!pSHCreateFileExtractIconW)
117     {
118         skip("SHCreateFileExtractIconW not available\n");
119         return;
120     }
121 
122     for (size_t n = 0; n < _countof(InterfaceTests); ++n)
123     {
124         {
125             CComPtr<IUnknown> spUnknown;
126             HRESULT hr = pSHCreateFileExtractIconW(L"test.txt", FILE_ATTRIBUTE_NORMAL, *InterfaceTests[n].IID, (void**)&spUnknown);
127             ok(hr == InterfaceTests[n].ExpectedCreate, "Expected hr to be 0x%lx, was 0x%lx for %u\n", InterfaceTests[n].ExpectedCreate, hr, n);
128         }
129 
130         {
131             CComPtr<IUnknown> spUnknown, spUnknown2;
132             HRESULT hr = pSHCreateFileExtractIconW(L"test.txt", FILE_ATTRIBUTE_NORMAL, IID_PPV_ARG(IUnknown, &spUnknown));
133             ok(hr == S_OK, "Expected hr to be S_OK, was 0x%lx for %u\n", hr, n);
134 
135             hr = spUnknown->QueryInterface(*InterfaceTests[n].IID, (void**)&spUnknown2);
136             ok(hr == InterfaceTests[n].ExpectedQueryInterface, "Expected hr to be 0x%lx, was 0x%lx for %u\n", InterfaceTests[n].ExpectedQueryInterface, hr, n);
137         }
138     }
139 
140     for (size_t n = 0; n < _countof(IconTests); ++n)
141     {
142         TestData& cur = IconTests[n];
143         bool useMyIcon = false;
144 
145         if (cur.Name == NULL)
146         {
147             cur.Name = CurrentModule;
148             useMyIcon = true;
149         }
150 
151         CComPtr<IExtractIconW> spExtract;
152         HRESULT hr = pSHCreateFileExtractIconW(cur.Name, cur.dwFlags, IID_PPV_ARG(IExtractIconW, &spExtract));
153         ok(hr == S_OK, "Expected hr to be S_OK, was 0x%lx for %S(%lx)\n", hr, cur.Name, cur.dwFlags);
154 
155         if (!SUCCEEDED(hr))
156             continue;
157 
158         /* Show that GIL_DEFAULTICON does not work. */
159         {
160             int ilIndex = -1;
161             UINT wFlags = 0xdeaddead;
162             WCHAR Buffer[MAX_PATH];
163 
164             hr = spExtract->GetIconLocation(GIL_DEFAULTICON, Buffer, _countof(Buffer), &ilIndex, &wFlags);
165             ok(hr == S_FALSE, "Expected hr to be S_FALSE, was 0x%lx for %S(0x%lx)\n", hr, cur.Name, cur.dwFlags);
166         }
167 
168 
169         for (UINT idFlags = 0; idFlags < _countof(tryFlags); ++idFlags)
170         {
171             int ilIndex = -1;
172             UINT wFlags = 0xdeaddead;
173             WCHAR Buffer[MAX_PATH];
174 
175             hr = spExtract->GetIconLocation(tryFlags[idFlags], Buffer, _countof(Buffer), &ilIndex, &wFlags);
176             ok(hr == S_OK, "Expected hr to be S_OK, was 0x%lx for %S(0x%lx,0x%x)\n", hr, cur.Name, cur.dwFlags, tryFlags[idFlags]);
177             if (!SUCCEEDED(hr))
178                 continue;
179 
180             ok(wFlags & (GIL_NOTFILENAME|GIL_PERCLASS), "Expected GIL_NOTFILENAME|GIL_PERCLASS to be set for %S(0x%lx,0x%x)\n", cur.Name, cur.dwFlags, tryFlags[idFlags]);
181             ok(!wcscmp(Buffer, L"*"), "Expected '*', was '%S' for %S(0x%lx,0x%x)\n", Buffer, cur.Name, cur.dwFlags, tryFlags[idFlags]);
182 
183             HICON ico;
184             hr = spExtract->Extract(Buffer, ilIndex, &ico, NULL, 0);
185 
186             /* Visualize the icon extracted for whoever is stepping through this code. */
187             HWND console = GetConsoleWindow();
188             SendMessage(console, WM_SETICON, ICON_BIG, (LPARAM)ico);
189             SendMessage(console, WM_SETICON, ICON_SMALL, (LPARAM)ico);
190 
191             CComHeapPtr<BYTE> colorData, maskData;
192             DWORD colorSize = 0, maskSize = 0;
193 
194             GetIconData(ico, colorData, colorSize, maskData, maskSize);
195 
196             if (!colorSize || !maskSize)
197                 continue;
198 
199             SHFILEINFOW shfi;
200             ULONG_PTR firet = SHGetFileInfoW(cur.Name, cur.dwFlags, &shfi, sizeof(shfi), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON | SHGFI_SYSICONINDEX);
201 
202             if (!firet)
203                 continue;
204 
205             ok(shfi.iIcon == ilIndex, "Expected ilIndex to be 0%x, was 0x%x for %S(0x%lx,0x%x)\n", shfi.iIcon, ilIndex, cur.Name, cur.dwFlags, tryFlags[idFlags]);
206 
207 
208             CComHeapPtr<BYTE> colorDataRef, maskDataRef;
209             DWORD colorSizeRef = 0, maskSizeRef = 0;
210             GetIconData(shfi.hIcon, colorDataRef, colorSizeRef, maskDataRef, maskSizeRef);
211 
212             ok(colorSizeRef == colorSize, "Expected %lu, was %lu for %S(0x%lx,0x%x)\n", colorSizeRef, colorSize, cur.Name, cur.dwFlags, tryFlags[idFlags]);
213             ok(maskSizeRef == maskSize, "Expected %lu, was %lu for %S(0x%lx,0x%x)\n", maskSizeRef, maskSize, cur.Name, cur.dwFlags, tryFlags[idFlags]);
214 
215             if (colorSizeRef == colorSize)
216             {
217                 ok(!memcmp(colorData, colorDataRef, colorSize), "Expected equal colorData for %S(0x%lx,0x%x)\n", cur.Name, cur.dwFlags, tryFlags[idFlags]);
218             }
219 
220             if (maskSizeRef == maskSize)
221             {
222                 ok(!memcmp(maskData, maskDataRef, maskSize), "Expected equal maskData for %S(0x%lx,0x%x)\n", cur.Name, cur.dwFlags, tryFlags[idFlags]);
223             }
224 
225             if (useMyIcon)
226             {
227                 colorDataRef.Free();
228                 maskDataRef.Free();
229                 colorSizeRef = maskSizeRef = 0;
230                 GetIconData(myIcon, colorDataRef, colorSizeRef, maskDataRef, maskSizeRef);
231 
232                 ok(colorSizeRef == colorSize, "Expected %lu, was %lu for %S(0x%lx,0x%x)\n", colorSizeRef, colorSize, cur.Name, cur.dwFlags, tryFlags[idFlags]);
233                 ok(maskSizeRef == maskSize, "Expected %lu, was %lu for %S(0x%lx,0x%x)\n", maskSizeRef, maskSize, cur.Name, cur.dwFlags, tryFlags[idFlags]);
234 
235                 if (colorSizeRef == colorSize)
236                 {
237                     /* In case requested filetype does not match, the exe icon is not used! */
238                     if (cur.dwFlags == FILE_ATTRIBUTE_DIRECTORY)
239                     {
240                         ok(memcmp(colorData, colorDataRef, colorSize), "Expected colorData to be changed for %S(0x%lx,0x%x)\n", cur.Name, cur.dwFlags, tryFlags[idFlags]);
241                     }
242                     else
243                     {
244                         ok(!memcmp(colorData, colorDataRef, colorSize), "Expected equal colorData for %S(0x%lx,0x%x)\n", cur.Name, cur.dwFlags, tryFlags[idFlags]);
245                     }
246                 }
247 
248                 // Mask is not reliable for some reason
249                 //if (maskSizeRef == maskSize)
250                 //{
251                 //    ok(!memcmp(maskData, maskDataRef, maskSize), "Expected equal maskData for %S(0x%lx,0x%lx)\n", cur.Name, cur.dwFlags);
252                 //}
253             }
254         }
255     }
256 }
257