// SysIconUtils.cpp #include "StdAfx.h" #ifndef _UNICODE #include "../../../Common/StringConvert.h" #endif #include "../../../Windows/FileDir.h" #include "SysIconUtils.h" #if defined(__MINGW32__) || defined(__MINGW64__) #include #else #include #endif #ifndef _UNICODE extern bool g_IsNT; #endif CExtToIconMap g_Ext_to_Icon_Map; int Shell_GetFileInfo_SysIconIndex_for_CSIDL(int csidl) { LPITEMIDLIST pidl = NULL; SHGetSpecialFolderLocation(NULL, csidl, &pidl); if (pidl) { SHFILEINFO shFileInfo; shFileInfo.iIcon = -1; const DWORD_PTR res = SHGetFileInfo((LPCTSTR)(const void *)(pidl), FILE_ATTRIBUTE_DIRECTORY, &shFileInfo, sizeof(shFileInfo), SHGFI_PIDL | SHGFI_SYSICONINDEX); /* IMalloc *pMalloc; SHGetMalloc(&pMalloc); if (pMalloc) { pMalloc->Free(pidl); pMalloc->Release(); } */ // we use OLE2.dll function here CoTaskMemFree(pidl); if (res) return shFileInfo.iIcon; } return -1; } #ifndef _UNICODE Z7_DIAGNOSTIC_IGNORE_CAST_FUNCTION typedef DWORD_PTR (WINAPI * Func_SHGetFileInfoW)(LPCWSTR pszPath, DWORD attrib, SHFILEINFOW *psfi, UINT cbFileInfo, UINT uFlags); static struct C_SHGetFileInfo_Init { Func_SHGetFileInfoW f_SHGetFileInfoW; C_SHGetFileInfo_Init() { f_SHGetFileInfoW = Z7_GET_PROC_ADDRESS( Func_SHGetFileInfoW, ::GetModuleHandleW(L"shell32.dll"), "SHGetFileInfoW"); // f_SHGetFileInfoW = NULL; // for debug } } g_SHGetFileInfo_Init; #endif #ifdef _UNICODE #define My_SHGetFileInfoW SHGetFileInfoW #else static DWORD_PTR My_SHGetFileInfoW(LPCWSTR pszPath, DWORD attrib, SHFILEINFOW *psfi, UINT cbFileInfo, UINT uFlags) { if (!g_SHGetFileInfo_Init.f_SHGetFileInfoW) return 0; return g_SHGetFileInfo_Init.f_SHGetFileInfoW(pszPath, attrib, psfi, cbFileInfo, uFlags); } #endif DWORD_PTR Shell_GetFileInfo_SysIconIndex_for_Path_attrib_iconIndexRef( CFSTR path, DWORD attrib, int &iconIndex) { #ifndef _UNICODE if (!g_IsNT || !g_SHGetFileInfo_Init.f_SHGetFileInfoW) { SHFILEINFO shFileInfo; // ZeroMemory(&shFileInfo, sizeof(shFileInfo)); shFileInfo.iIcon = -1; // optional const DWORD_PTR res = ::SHGetFileInfo(fs2fas(path), attrib ? attrib : FILE_ATTRIBUTE_ARCHIVE, &shFileInfo, sizeof(shFileInfo), SHGFI_USEFILEATTRIBUTES | SHGFI_SYSICONINDEX); iconIndex = shFileInfo.iIcon; return res; } else #endif { SHFILEINFOW shFileInfo; // ZeroMemory(&shFileInfo, sizeof(shFileInfo)); shFileInfo.iIcon = -1; // optional const DWORD_PTR res = ::My_SHGetFileInfoW(fs2us(path), attrib ? attrib : FILE_ATTRIBUTE_ARCHIVE, &shFileInfo, sizeof(shFileInfo), SHGFI_USEFILEATTRIBUTES | SHGFI_SYSICONINDEX); // (shFileInfo.iIcon == 0) returned for unknown extensions and files without extension iconIndex = shFileInfo.iIcon; // we use SHGFI_USEFILEATTRIBUTES, and // (res != 0) is expected for main cases, even if there are no such file. // (res == 0) for path with kSuperPrefix "\\?\" // Also SHGFI_USEFILEATTRIBUTES still returns icon inside exe. // So we can use SHGFI_USEFILEATTRIBUTES for any case. // UString temp = fs2us(path); // for debug // UString tempName = temp.Ptr(temp.ReverseFind_PathSepar() + 1); // for debug // iconIndex = -1; // for debug return res; } } int Shell_GetFileInfo_SysIconIndex_for_Path(CFSTR path, DWORD attrib) { int iconIndex = -1; if (!Shell_GetFileInfo_SysIconIndex_for_Path_attrib_iconIndexRef( path, attrib, iconIndex)) iconIndex = -1; return iconIndex; } HRESULT Shell_GetFileInfo_SysIconIndex_for_Path_return_HRESULT( CFSTR path, DWORD attrib, Int32 *iconIndex) { *iconIndex = -1; int iconIndexTemp; if (Shell_GetFileInfo_SysIconIndex_for_Path_attrib_iconIndexRef( path, attrib, iconIndexTemp)) { *iconIndex = iconIndexTemp; return S_OK; } return GetLastError_noZero_HRESULT(); } /* DWORD_PTR Shell_GetFileInfo_SysIconIndex_for_Path(const UString &fileName, DWORD attrib, int &iconIndex, UString *typeName) { #ifndef _UNICODE if (!g_IsNT) { SHFILEINFO shFileInfo; shFileInfo.szTypeName[0] = 0; DWORD_PTR res = ::SHGetFileInfoA(GetSystemString(fileName), FILE_ATTRIBUTE_ARCHIVE | attrib, &shFileInfo, sizeof(shFileInfo), SHGFI_USEFILEATTRIBUTES | SHGFI_SYSICONINDEX | SHGFI_TYPENAME); if (typeName) *typeName = GetUnicodeString(shFileInfo.szTypeName); iconIndex = shFileInfo.iIcon; return res; } else #endif { SHFILEINFOW shFileInfo; shFileInfo.szTypeName[0] = 0; DWORD_PTR res = ::My_SHGetFileInfoW(fileName, FILE_ATTRIBUTE_ARCHIVE | attrib, &shFileInfo, sizeof(shFileInfo), SHGFI_USEFILEATTRIBUTES | SHGFI_SYSICONINDEX | SHGFI_TYPENAME); if (typeName) *typeName = shFileInfo.szTypeName; iconIndex = shFileInfo.iIcon; return res; } } */ static int FindInSorted_Attrib(const CRecordVector &vect, DWORD attrib, unsigned &insertPos) { unsigned left = 0, right = vect.Size(); while (left != right) { const unsigned mid = (left + right) / 2; const DWORD midAttrib = vect[mid].Attrib; if (attrib == midAttrib) return (int)mid; if (attrib < midAttrib) right = mid; else left = mid + 1; } insertPos = left; return -1; } static int FindInSorted_Ext(const CObjectVector &vect, const wchar_t *ext, unsigned &insertPos) { unsigned left = 0, right = vect.Size(); while (left != right) { const unsigned mid = (left + right) / 2; const int compare = MyStringCompareNoCase(ext, vect[mid].Ext); if (compare == 0) return (int)mid; if (compare < 0) right = mid; else left = mid + 1; } insertPos = left; return -1; } // bool DoItemAlwaysStart(const UString &name); int CExtToIconMap::GetIconIndex(DWORD attrib, const wchar_t *fileName /*, UString *typeName */) { int dotPos = -1; unsigned i; for (i = 0;; i++) { const wchar_t c = fileName[i]; if (c == 0) break; if (c == '.') dotPos = (int)i; // we don't need IS_PATH_SEPAR check, because (fileName) doesn't include path prefix. // if (IS_PATH_SEPAR(c) || c == ':') dotPos = -1; } /* if (MyStringCompareNoCase(fileName, L"$Recycle.Bin") == 0) { char s[256]; sprintf(s, "SPEC i = %3d, attr = %7x", _attribMap.Size(), attrib); OutputDebugStringA(s); OutputDebugStringW(fileName); } */ if ((attrib & FILE_ATTRIBUTE_DIRECTORY) || dotPos < 0) for (unsigned k = 0;; k++) { if (k >= 2) return -1; unsigned insertPos = 0; const int index = FindInSorted_Attrib(_attribMap, attrib, insertPos); if (index >= 0) { // if (typeName) *typeName = _attribMap[index].TypeName; return _attribMap[(unsigned)index].IconIndex; } CAttribIconPair pair; pair.IconIndex = Shell_GetFileInfo_SysIconIndex_for_Path( #ifdef UNDER_CE FTEXT("\\") #endif FTEXT("__DIR__") , attrib // , pair.TypeName ); if (_attribMap.Size() < (1u << 16) // we limit cache size || attrib < (1u << 15)) // we want to put all items with basic attribs to cache { /* char s[256]; sprintf(s, "i = %3d, attr = %7x", _attribMap.Size(), attrib); OutputDebugStringA(s); */ pair.Attrib = attrib; _attribMap.Insert(insertPos, pair); // if (typeName) *typeName = pair.TypeName; return pair.IconIndex; } if (pair.IconIndex >= 0) return pair.IconIndex; attrib = (attrib & FILE_ATTRIBUTE_DIRECTORY) ? FILE_ATTRIBUTE_DIRECTORY : FILE_ATTRIBUTE_ARCHIVE; } CObjectVector &map = (attrib & FILE_ATTRIBUTE_COMPRESSED) ? _extMap_Compressed : _extMap_Normal; const wchar_t *ext = fileName + dotPos + 1; unsigned insertPos = 0; const int index = FindInSorted_Ext(map, ext, insertPos); if (index >= 0) { const CExtIconPair &pa = map[index]; // if (typeName) *typeName = pa.TypeName; return pa.IconIndex; } for (i = 0;; i++) { const wchar_t c = ext[i]; if (c == 0) break; if (c < L'0' || c > L'9') break; } if (i != 0 && ext[i] == 0) { // Shell_GetFileInfo_SysIconIndex_for_Path is too slow for big number of split extensions: .001, .002, .003 if (!SplitIconIndex_Defined) { Shell_GetFileInfo_SysIconIndex_for_Path_attrib_iconIndexRef( #ifdef UNDER_CE FTEXT("\\") #endif FTEXT("__FILE__.001"), FILE_ATTRIBUTE_ARCHIVE, SplitIconIndex); SplitIconIndex_Defined = true; } return SplitIconIndex; } CExtIconPair pair; pair.Ext = ext; pair.IconIndex = Shell_GetFileInfo_SysIconIndex_for_Path( us2fs(fileName + dotPos), attrib & FILE_ATTRIBUTE_COMPRESSED ? FILE_ATTRIBUTE_ARCHIVE | FILE_ATTRIBUTE_COMPRESSED: FILE_ATTRIBUTE_ARCHIVE); if (map.Size() < (1u << 16) // we limit cache size // || DoItemAlwaysStart(fileName + dotPos) // we want some popular extensions in cache ) map.Insert(insertPos, pair); // if (typeName) *typeName = pair.TypeName; return pair.IconIndex; } HIMAGELIST Shell_Get_SysImageList_smallIcons(bool smallIcons) { SHFILEINFO shFileInfo; // shFileInfo.hIcon = NULL; // optional const DWORD_PTR res = SHGetFileInfo(TEXT(""), /* FILE_ATTRIBUTE_ARCHIVE | */ FILE_ATTRIBUTE_DIRECTORY, &shFileInfo, sizeof(shFileInfo), SHGFI_USEFILEATTRIBUTES | SHGFI_SYSICONINDEX | (smallIcons ? SHGFI_SMALLICON : SHGFI_LARGEICON)); #if 0 // (shFileInfo.hIcon == NULL), because we don't use SHGFI_ICON. // so DestroyIcon() is not required if (res && shFileInfo.hIcon) // unexpected DestroyIcon(shFileInfo.hIcon); #endif return (HIMAGELIST)res; }