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 Mark Jansen (mark.jansen@reactos.org)
6  */
7 
8 #include "shelltest.h"
9 
10 #include <wincon.h>
11 #include <wingdi.h>
12 
13 ULONG DbgPrint(PCH Format,...);
14 #include <shellutils.h>
15 
16 HRESULT (STDAPICALLTYPE *pSHCreateFileExtractIconW)(LPCWSTR pszFile, DWORD dwFileAttributes, REFIID riid, void **ppv);
17 
18 struct TestData
19 {
20     const WCHAR* Name;
21     DWORD dwFlags;
22 };
23 
24 static TestData IconTests[] =
25 {
26     { L"xxx.zip", FILE_ATTRIBUTE_NORMAL },
27     { L"xxx.zip", FILE_ATTRIBUTE_DIRECTORY },
28     { L"xxx.exe", FILE_ATTRIBUTE_NORMAL },
29     { L"xxx.exe", FILE_ATTRIBUTE_DIRECTORY },
30     { L"xxx.dll", FILE_ATTRIBUTE_NORMAL },
31     { L"xxx.dll", FILE_ATTRIBUTE_DIRECTORY },
32     { L"xxx.txt", FILE_ATTRIBUTE_NORMAL },
33     { L"xxx.txt", FILE_ATTRIBUTE_DIRECTORY },
34     { NULL, FILE_ATTRIBUTE_NORMAL },
35     { NULL, FILE_ATTRIBUTE_DIRECTORY },
36 };
37 
38 
39 static void ExtractOneBitmap(HBITMAP hbm, CComHeapPtr<BYTE>& data, DWORD& size)
40 {
41     HDC hdc = CreateCompatibleDC(NULL);
42     HGDIOBJ obj = SelectObject(hdc, hbm);
43 
44     CComHeapPtr<BITMAPINFO> pInfoBM;
45 
46     pInfoBM.AllocateBytes(sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
47     memset(pInfoBM, 0, sizeof(BITMAPINFOHEADER) + 256 * sizeof(RGBQUAD));
48     pInfoBM->bmiHeader.biSize = sizeof(pInfoBM->bmiHeader);
49     if (!GetDIBits(hdc, hbm, 0, 0, NULL, pInfoBM, DIB_RGB_COLORS))
50         return;
51 
52     size = pInfoBM->bmiHeader.biSizeImage;
53     data.Allocate(size);
54     GetDIBits(hdc, hbm, 0, pInfoBM->bmiHeader.biHeight, data, pInfoBM, DIB_RGB_COLORS);
55 
56     SelectObject(hdc, obj);
57     DeleteDC(hdc);
58 }
59 
60 static bool GetIconData(HICON icon, CComHeapPtr<BYTE>& colorData, DWORD& colorSize, CComHeapPtr<BYTE>& maskData, DWORD& maskSize)
61 {
62     ICONINFO iconinfo;
63 
64     if (!GetIconInfo(icon, &iconinfo))
65         return false;
66 
67     ExtractOneBitmap(iconinfo.hbmColor, colorData, colorSize);
68     ExtractOneBitmap(iconinfo.hbmMask, maskData, maskSize);
69 
70     DeleteObject(iconinfo.hbmColor);
71     DeleteObject(iconinfo.hbmMask);
72 
73     return true;
74 }
75 
76 
77 START_TEST(SHCreateFileExtractIconW)
78 {
79     WCHAR CurrentModule[MAX_PATH];
80     HMODULE shell32 = LoadLibraryA("shell32.dll");
81     HICON myIcon;
82     pSHCreateFileExtractIconW = (HRESULT (__stdcall *)(LPCWSTR, DWORD, REFIID, void **))GetProcAddress(shell32, "SHCreateFileExtractIconW");
83 
84     CoInitialize(NULL);
85 
86     GetModuleFileNameW(NULL, CurrentModule, _countof(CurrentModule));
87     {
88         SHFILEINFOW shfi;
89         ULONG_PTR firet = SHGetFileInfoW(CurrentModule, 0, &shfi, sizeof(shfi), SHGFI_ICON);
90         myIcon = shfi.hIcon;
91         if (!firet)
92         {
93             skip("Unable to get my own icon\n");
94             return;
95         }
96     }
97 
98     if (!pSHCreateFileExtractIconW)
99     {
100         skip("SHCreateFileExtractIconW not available\n");
101         return;
102     }
103 
104     for (size_t n = 0; n < _countof(IconTests); ++n)
105     {
106         TestData& cur = IconTests[n];
107         bool useMyIcon = false;
108 
109         if (cur.Name == NULL)
110         {
111             cur.Name = CurrentModule;
112             useMyIcon = true;
113         }
114 
115         CComPtr<IExtractIconW> spExtract;
116         HRESULT hr = pSHCreateFileExtractIconW(cur.Name, cur.dwFlags, IID_PPV_ARG(IExtractIconW, &spExtract));
117         ok(hr == S_OK, "Expected hr to be S_OK, was 0x%lx for %S(%lx)\n", hr, cur.Name, cur.dwFlags);
118 
119         if (!SUCCEEDED(hr))
120             continue;
121 
122         int ilIndex = -1;
123         UINT wFlags = 0xdeaddead;
124         WCHAR Buffer[MAX_PATH];
125 
126         hr = spExtract->GetIconLocation(0, Buffer, _countof(Buffer), &ilIndex, &wFlags);
127         ok(hr == S_OK, "Expected hr to be S_OK, was 0x%lx for %S(%lx)\n", hr, cur.Name, cur.dwFlags);
128         if (!SUCCEEDED(hr))
129             continue;
130 
131         ok(wFlags & (GIL_NOTFILENAME|GIL_PERCLASS), "Expected GIL_NOTFILENAME|GIL_PERCLASS to be set for %S(%lx)\n", cur.Name, cur.dwFlags);
132         ok(!wcscmp(Buffer, L"*"), "Expected '*', was '%S' for %S(%lx)\n", Buffer, cur.Name, cur.dwFlags);
133 
134         HICON ico;
135         hr = spExtract->Extract(Buffer, ilIndex, &ico, NULL, 0);
136 
137         /* Visualize the icon extracted for whoever is stepping through this code. */
138         HWND console = GetConsoleWindow();
139         SendMessage(console, WM_SETICON, ICON_BIG, (LPARAM)ico);
140         SendMessage(console, WM_SETICON, ICON_SMALL, (LPARAM)ico);
141 
142         CComHeapPtr<BYTE> colorData, maskData;
143         DWORD colorSize = 0, maskSize = 0;
144 
145         GetIconData(ico, colorData, colorSize, maskData, maskSize);
146 
147         if (!colorSize || !maskSize)
148             continue;
149 
150         SHFILEINFOW shfi;
151         ULONG_PTR firet = SHGetFileInfoW(cur.Name, cur.dwFlags, &shfi, sizeof(shfi), SHGFI_USEFILEATTRIBUTES | SHGFI_ICON);
152 
153         if (!firet)
154             continue;
155 
156         CComHeapPtr<BYTE> colorDataRef, maskDataRef;
157         DWORD colorSizeRef = 0, maskSizeRef = 0;
158         GetIconData(shfi.hIcon, colorDataRef, colorSizeRef, maskDataRef, maskSizeRef);
159 
160         ok(colorSizeRef == colorSize, "Expected %lu, was %lu for %S(%lx)\n", colorSizeRef, colorSize, cur.Name, cur.dwFlags);
161         ok(maskSizeRef == maskSize, "Expected %lu, was %lu for %S(%lx)\n", maskSizeRef, maskSize, cur.Name, cur.dwFlags);
162 
163         if (colorSizeRef == colorSize)
164         {
165             ok(!memcmp(colorData, colorDataRef, colorSize), "Expected equal colorData for %S(%lx)\n", cur.Name, cur.dwFlags);
166         }
167 
168         if (maskSizeRef == maskSize)
169         {
170             ok(!memcmp(maskData, maskDataRef, maskSize), "Expected equal maskData for %S(%lx)\n", cur.Name, cur.dwFlags);
171         }
172 
173         if (useMyIcon)
174         {
175             colorDataRef.Free();
176             maskDataRef.Free();
177             colorSizeRef = maskSizeRef = 0;
178             GetIconData(myIcon, colorDataRef, colorSizeRef, maskDataRef, maskSizeRef);
179 
180             ok(colorSizeRef == colorSize, "Expected %lu, was %lu for %S(%lx)\n", colorSizeRef, colorSize, cur.Name, cur.dwFlags);
181             ok(maskSizeRef == maskSize, "Expected %lu, was %lu for %S(%lx)\n", maskSizeRef, maskSize, cur.Name, cur.dwFlags);
182 
183             if (colorSizeRef == colorSize)
184             {
185                 /* Incase requested filetype does not match, the exe icon is not used! */
186                 if (cur.dwFlags == FILE_ATTRIBUTE_DIRECTORY)
187                 {
188                     ok(memcmp(colorData, colorDataRef, colorSize), "Expected colorData to be changed for %S(%lx)\n", cur.Name, cur.dwFlags);
189                 }
190                 else
191                 {
192                     ok(!memcmp(colorData, colorDataRef, colorSize), "Expected equal colorData for %S(%lx)\n", cur.Name, cur.dwFlags);
193                 }
194             }
195 
196             // Mask is not reliable for some reason
197             //if (maskSizeRef == maskSize)
198             //{
199             //    ok(!memcmp(maskData, maskDataRef, maskSize), "Expected equal maskData for %S(%lx)\n", cur.Name, cur.dwFlags);
200             //}
201         }
202     }
203 }
204