1 // SysIconUtils.cpp
2
3 #include "StdAfx.h"
4
5 #ifndef _UNICODE
6 #include "../../../Common/StringConvert.h"
7 #endif
8
9 #include "../../../Windows/FileDir.h"
10
11 #include "SysIconUtils.h"
12
13 #if defined(__MINGW32__) || defined(__MINGW64__)
14 #include <shlobj.h>
15 #else
16 #include <ShlObj.h>
17 #endif
18
19 #ifndef _UNICODE
20 extern bool g_IsNT;
21 #endif
22
23 CExtToIconMap g_Ext_to_Icon_Map;
24
Shell_GetFileInfo_SysIconIndex_for_CSIDL(int csidl)25 int Shell_GetFileInfo_SysIconIndex_for_CSIDL(int csidl)
26 {
27 LPITEMIDLIST pidl = NULL;
28 SHGetSpecialFolderLocation(NULL, csidl, &pidl);
29 if (pidl)
30 {
31 SHFILEINFO shFileInfo;
32 shFileInfo.iIcon = -1;
33 const DWORD_PTR res = SHGetFileInfo((LPCTSTR)(const void *)(pidl),
34 FILE_ATTRIBUTE_DIRECTORY,
35 &shFileInfo, sizeof(shFileInfo),
36 SHGFI_PIDL | SHGFI_SYSICONINDEX);
37 /*
38 IMalloc *pMalloc;
39 SHGetMalloc(&pMalloc);
40 if (pMalloc)
41 {
42 pMalloc->Free(pidl);
43 pMalloc->Release();
44 }
45 */
46 // we use OLE2.dll function here
47 CoTaskMemFree(pidl);
48 if (res)
49 return shFileInfo.iIcon;
50 }
51 return -1;
52 }
53
54 #ifndef _UNICODE
55 Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION
56 typedef DWORD_PTR (WINAPI * Func_SHGetFileInfoW)(LPCWSTR pszPath, DWORD attrib, SHFILEINFOW *psfi, UINT cbFileInfo, UINT uFlags);
57
58 static struct C_SHGetFileInfo_Init
59 {
60 Func_SHGetFileInfoW f_SHGetFileInfoW;
C_SHGetFileInfo_InitC_SHGetFileInfo_Init61 C_SHGetFileInfo_Init()
62 {
63 f_SHGetFileInfoW = Z7_GET_PROC_ADDRESS(
64 Func_SHGetFileInfoW, ::GetModuleHandleW(L"shell32.dll"),
65 "SHGetFileInfoW");
66 // f_SHGetFileInfoW = NULL; // for debug
67 }
68 } g_SHGetFileInfo_Init;
69 #endif
70
71 #ifdef _UNICODE
72 #define My_SHGetFileInfoW SHGetFileInfoW
73 #else
My_SHGetFileInfoW(LPCWSTR pszPath,DWORD attrib,SHFILEINFOW * psfi,UINT cbFileInfo,UINT uFlags)74 static DWORD_PTR My_SHGetFileInfoW(LPCWSTR pszPath, DWORD attrib, SHFILEINFOW *psfi, UINT cbFileInfo, UINT uFlags)
75 {
76 if (!g_SHGetFileInfo_Init.f_SHGetFileInfoW)
77 return 0;
78 return g_SHGetFileInfo_Init.f_SHGetFileInfoW(pszPath, attrib, psfi, cbFileInfo, uFlags);
79 }
80 #endif
81
Shell_GetFileInfo_SysIconIndex_for_Path_attrib_iconIndexRef(CFSTR path,DWORD attrib,int & iconIndex)82 DWORD_PTR Shell_GetFileInfo_SysIconIndex_for_Path_attrib_iconIndexRef(
83 CFSTR path, DWORD attrib, int &iconIndex)
84 {
85 #ifndef _UNICODE
86 if (!g_IsNT || !g_SHGetFileInfo_Init.f_SHGetFileInfoW)
87 {
88 SHFILEINFO shFileInfo;
89 // ZeroMemory(&shFileInfo, sizeof(shFileInfo));
90 shFileInfo.iIcon = -1; // optional
91 const DWORD_PTR res = ::SHGetFileInfo(fs2fas(path),
92 attrib ? attrib : FILE_ATTRIBUTE_ARCHIVE,
93 &shFileInfo, sizeof(shFileInfo),
94 SHGFI_USEFILEATTRIBUTES | SHGFI_SYSICONINDEX);
95 iconIndex = shFileInfo.iIcon;
96 return res;
97 }
98 else
99 #endif
100 {
101 SHFILEINFOW shFileInfo;
102 // ZeroMemory(&shFileInfo, sizeof(shFileInfo));
103 shFileInfo.iIcon = -1; // optional
104 const DWORD_PTR res = ::My_SHGetFileInfoW(fs2us(path),
105 attrib ? attrib : FILE_ATTRIBUTE_ARCHIVE,
106 &shFileInfo, sizeof(shFileInfo),
107 SHGFI_USEFILEATTRIBUTES | SHGFI_SYSICONINDEX);
108 // (shFileInfo.iIcon == 0) returned for unknown extensions and files without extension
109 iconIndex = shFileInfo.iIcon;
110 // we use SHGFI_USEFILEATTRIBUTES, and
111 // (res != 0) is expected for main cases, even if there are no such file.
112 // (res == 0) for path with kSuperPrefix "\\?\"
113 // Also SHGFI_USEFILEATTRIBUTES still returns icon inside exe.
114 // So we can use SHGFI_USEFILEATTRIBUTES for any case.
115 // UString temp = fs2us(path); // for debug
116 // UString tempName = temp.Ptr(temp.ReverseFind_PathSepar() + 1); // for debug
117 // iconIndex = -1; // for debug
118 return res;
119 }
120 }
121
Shell_GetFileInfo_SysIconIndex_for_Path(CFSTR path,DWORD attrib)122 int Shell_GetFileInfo_SysIconIndex_for_Path(CFSTR path, DWORD attrib)
123 {
124 int iconIndex = -1;
125 if (!Shell_GetFileInfo_SysIconIndex_for_Path_attrib_iconIndexRef(
126 path, attrib, iconIndex))
127 iconIndex = -1;
128 return iconIndex;
129 }
130
131
Shell_GetFileInfo_SysIconIndex_for_Path_return_HRESULT(CFSTR path,DWORD attrib,Int32 * iconIndex)132 HRESULT Shell_GetFileInfo_SysIconIndex_for_Path_return_HRESULT(
133 CFSTR path, DWORD attrib, Int32 *iconIndex)
134 {
135 *iconIndex = -1;
136 int iconIndexTemp;
137 if (Shell_GetFileInfo_SysIconIndex_for_Path_attrib_iconIndexRef(
138 path, attrib, iconIndexTemp))
139 {
140 *iconIndex = iconIndexTemp;
141 return S_OK;
142 }
143 return GetLastError_noZero_HRESULT();
144 }
145
146 /*
147 DWORD_PTR Shell_GetFileInfo_SysIconIndex_for_Path(const UString &fileName, DWORD attrib, int &iconIndex, UString *typeName)
148 {
149 #ifndef _UNICODE
150 if (!g_IsNT)
151 {
152 SHFILEINFO shFileInfo;
153 shFileInfo.szTypeName[0] = 0;
154 DWORD_PTR res = ::SHGetFileInfoA(GetSystemString(fileName), FILE_ATTRIBUTE_ARCHIVE | attrib, &shFileInfo,
155 sizeof(shFileInfo), SHGFI_USEFILEATTRIBUTES | SHGFI_SYSICONINDEX | SHGFI_TYPENAME);
156 if (typeName)
157 *typeName = GetUnicodeString(shFileInfo.szTypeName);
158 iconIndex = shFileInfo.iIcon;
159 return res;
160 }
161 else
162 #endif
163 {
164 SHFILEINFOW shFileInfo;
165 shFileInfo.szTypeName[0] = 0;
166 DWORD_PTR res = ::My_SHGetFileInfoW(fileName, FILE_ATTRIBUTE_ARCHIVE | attrib, &shFileInfo,
167 sizeof(shFileInfo), SHGFI_USEFILEATTRIBUTES | SHGFI_SYSICONINDEX | SHGFI_TYPENAME);
168 if (typeName)
169 *typeName = shFileInfo.szTypeName;
170 iconIndex = shFileInfo.iIcon;
171 return res;
172 }
173 }
174 */
175
FindInSorted_Attrib(const CRecordVector<CAttribIconPair> & vect,DWORD attrib,unsigned & insertPos)176 static int FindInSorted_Attrib(const CRecordVector<CAttribIconPair> &vect, DWORD attrib, unsigned &insertPos)
177 {
178 unsigned left = 0, right = vect.Size();
179 while (left != right)
180 {
181 const unsigned mid = (left + right) / 2;
182 const DWORD midAttrib = vect[mid].Attrib;
183 if (attrib == midAttrib)
184 return (int)mid;
185 if (attrib < midAttrib)
186 right = mid;
187 else
188 left = mid + 1;
189 }
190 insertPos = left;
191 return -1;
192 }
193
FindInSorted_Ext(const CObjectVector<CExtIconPair> & vect,const wchar_t * ext,unsigned & insertPos)194 static int FindInSorted_Ext(const CObjectVector<CExtIconPair> &vect, const wchar_t *ext, unsigned &insertPos)
195 {
196 unsigned left = 0, right = vect.Size();
197 while (left != right)
198 {
199 const unsigned mid = (left + right) / 2;
200 const int compare = MyStringCompareNoCase(ext, vect[mid].Ext);
201 if (compare == 0)
202 return (int)mid;
203 if (compare < 0)
204 right = mid;
205 else
206 left = mid + 1;
207 }
208 insertPos = left;
209 return -1;
210 }
211
212
213 // bool DoItemAlwaysStart(const UString &name);
214
GetIconIndex(DWORD attrib,const wchar_t * fileName)215 int CExtToIconMap::GetIconIndex(DWORD attrib, const wchar_t *fileName /*, UString *typeName */)
216 {
217 int dotPos = -1;
218 unsigned i;
219 for (i = 0;; i++)
220 {
221 const wchar_t c = fileName[i];
222 if (c == 0)
223 break;
224 if (c == '.')
225 dotPos = (int)i;
226 // we don't need IS_PATH_SEPAR check, because (fileName) doesn't include path prefix.
227 // if (IS_PATH_SEPAR(c) || c == ':') dotPos = -1;
228 }
229
230 /*
231 if (MyStringCompareNoCase(fileName, L"$Recycle.Bin") == 0)
232 {
233 char s[256];
234 sprintf(s, "SPEC i = %3d, attr = %7x", _attribMap.Size(), attrib);
235 OutputDebugStringA(s);
236 OutputDebugStringW(fileName);
237 }
238 */
239
240 if ((attrib & FILE_ATTRIBUTE_DIRECTORY) || dotPos < 0)
241 for (unsigned k = 0;; k++)
242 {
243 if (k >= 2)
244 return -1;
245 unsigned insertPos = 0;
246 const int index = FindInSorted_Attrib(_attribMap, attrib, insertPos);
247 if (index >= 0)
248 {
249 // if (typeName) *typeName = _attribMap[index].TypeName;
250 return _attribMap[(unsigned)index].IconIndex;
251 }
252 CAttribIconPair pair;
253 pair.IconIndex = Shell_GetFileInfo_SysIconIndex_for_Path(
254 #ifdef UNDER_CE
255 FTEXT("\\")
256 #endif
257 FTEXT("__DIR__")
258 , attrib
259 // , pair.TypeName
260 );
261 if (_attribMap.Size() < (1u << 16) // we limit cache size
262 || attrib < (1u << 15)) // we want to put all items with basic attribs to cache
263 {
264 /*
265 char s[256];
266 sprintf(s, "i = %3d, attr = %7x", _attribMap.Size(), attrib);
267 OutputDebugStringA(s);
268 */
269 pair.Attrib = attrib;
270 _attribMap.Insert(insertPos, pair);
271 // if (typeName) *typeName = pair.TypeName;
272 return pair.IconIndex;
273 }
274 if (pair.IconIndex >= 0)
275 return pair.IconIndex;
276 attrib = (attrib & FILE_ATTRIBUTE_DIRECTORY) ?
277 FILE_ATTRIBUTE_DIRECTORY :
278 FILE_ATTRIBUTE_ARCHIVE;
279 }
280
281 CObjectVector<CExtIconPair> &map =
282 (attrib & FILE_ATTRIBUTE_COMPRESSED) ?
283 _extMap_Compressed : _extMap_Normal;
284 const wchar_t *ext = fileName + dotPos + 1;
285 unsigned insertPos = 0;
286 const int index = FindInSorted_Ext(map, ext, insertPos);
287 if (index >= 0)
288 {
289 const CExtIconPair &pa = map[index];
290 // if (typeName) *typeName = pa.TypeName;
291 return pa.IconIndex;
292 }
293
294 for (i = 0;; i++)
295 {
296 const wchar_t c = ext[i];
297 if (c == 0)
298 break;
299 if (c < L'0' || c > L'9')
300 break;
301 }
302 if (i != 0 && ext[i] == 0)
303 {
304 // Shell_GetFileInfo_SysIconIndex_for_Path is too slow for big number of split extensions: .001, .002, .003
305 if (!SplitIconIndex_Defined)
306 {
307 Shell_GetFileInfo_SysIconIndex_for_Path_attrib_iconIndexRef(
308 #ifdef UNDER_CE
309 FTEXT("\\")
310 #endif
311 FTEXT("__FILE__.001"), FILE_ATTRIBUTE_ARCHIVE, SplitIconIndex);
312 SplitIconIndex_Defined = true;
313 }
314 return SplitIconIndex;
315 }
316
317 CExtIconPair pair;
318 pair.Ext = ext;
319 pair.IconIndex = Shell_GetFileInfo_SysIconIndex_for_Path(
320 us2fs(fileName + dotPos),
321 attrib & FILE_ATTRIBUTE_COMPRESSED ?
322 FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_COMPRESSED:
323 FILE_ATTRIBUTE_ARCHIVE);
324 if (map.Size() < (1u << 16) // we limit cache size
325 // || DoItemAlwaysStart(fileName + dotPos) // we want some popular extensions in cache
326 )
327 map.Insert(insertPos, pair);
328 // if (typeName) *typeName = pair.TypeName;
329 return pair.IconIndex;
330 }
331
332
Shell_Get_SysImageList_smallIcons(bool smallIcons)333 HIMAGELIST Shell_Get_SysImageList_smallIcons(bool smallIcons)
334 {
335 SHFILEINFO shFileInfo;
336 // shFileInfo.hIcon = NULL; // optional
337 const DWORD_PTR res = SHGetFileInfo(TEXT(""),
338 /* FILE_ATTRIBUTE_ARCHIVE | */
339 FILE_ATTRIBUTE_DIRECTORY,
340 &shFileInfo, sizeof(shFileInfo),
341 SHGFI_USEFILEATTRIBUTES |
342 SHGFI_SYSICONINDEX |
343 (smallIcons ? SHGFI_SMALLICON : SHGFI_LARGEICON));
344 #if 0
345 // (shFileInfo.hIcon == NULL), because we don't use SHGFI_ICON.
346 // so DestroyIcon() is not required
347 if (res && shFileInfo.hIcon) // unexpected
348 DestroyIcon(shFileInfo.hIcon);
349 #endif
350 return (HIMAGELIST)res;
351 }
352