xref: /aosp_15_r20/external/lzma/CPP/7zip/UI/FileManager/SysIconUtils.cpp (revision f6dc9357d832569d4d1f5d24eacdb3935a1ae8e6)
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