|
|
// routines for managing the icon cache tables, and file type tables.
// Jan 95, ToddLa
//
// icon cache
//
// the icon cache is n ImageLists
// and a table mapping a name/icon number/flags to a ImageList
// index, the global hash table (pht==NULL) is used to hold
// the names.
//
// AddToIconTable - associate a name/number/flags with a image index
// SHLookupIconIndex - return a image index, given name/number/flags
// RemoveFromIconTable - remove all entries with the given name
// FlushIconCache - remove all entries.
// GetFreeImageIndex - return a free ImageList index.
//
// the worst part about the whole icon cache design is that people
// can add or lookup a image index (given a name/number/flags) but
// they never have to release it. we never know if a ImageList index
// is currently in use or not. this should be the first thing
// fixed about the shell. currently we use a MRU type scheme when
// we need to remove a entry from the icon cache, it is far from
// perfect.
//
// file type cache
//
// the file type cache is a hash table with two DWORDs of extra data.
// DWORD #0 holds flags, DWORD #1 holds a pointer to the name of
// the class.
//
// LookupFileClass - given a file class (ie ".doc" or "Directory")
// maps it to a DWORD of flags, return 0 if not found.
//
// AddFileClass - adds a class (and flags) to cache
//
// LookupFileClassName - given a file class, returns it name.
// AddFileClassName - sets the name of a class.
// FlushFileClass - removes all items in cache.
//
#include "shellprv.h"
#pragma hdrstop
#include "filetbl.h"
#include "fstreex.h"
#include <ntverp.h>
#include "ovrlaymn.h"
#include "dpa.h"
typedef struct { DWORD cbSize; // size of this header.
DWORD dwMagic; // magic number
DWORD dwVersion; // version of this saved icon cache
DWORD dwBuild; // windows build number
DWORD dwNumIcons; // number of icons in cache
DWORD dwColorRes; // color resolution of device at last save
DWORD dwFlags; // ILC_* flags
DWORD dwTimeSave; // icon time this file was saved
DWORD dwTimeFlush; // icon time we last flushed.
DWORD dwFreeImageCount; DWORD dwFreeEntryCount; SIZE rgsize[SHIL_COUNT]; // array of sizes of cached icons
DWORD cImageLists; // equal to ARRAYSIZE(IC_HEAD.size)
} IC_HEAD;
#define ICONCACHE_MAGIC (TEXT('W') + (TEXT('i') << 8) + (TEXT('n') << 16) + (TEXT('4') << 24))
#define ICONCACHE_VERSION 0x0505 // Unicode file names + lower case hash items + v6 imagelist
typedef struct { LPCTSTR szName; // key: file name
int iIconIndex; // key: icon index (or random DWORD for GIL_NOTFILE)
UINT uFlags; // GIL_* flags
int iILIndex; // data: system image list index
UINT Access; // last access.
} LOCATION_ENTRY;
// LOCATION_ENTRY32 is the version of LOCATION_ENTRY that gets written to disk
// It must be declared explicitly 32-bit for Win32/Win64 interop.
typedef struct { DWORD dwszName; // (garbage in file)
int iIconIndex; // key: icon index (or random DWORD for GIL_NOTFILE)
UINT uFlags; // GIL_* flags
int iILIndex; // data: system image list index
UINT Access; // last access.
} LOCATION_ENTRY32;
//
// MIN_FLUSH is the minimum time interval between flushing the icon cache
// this number is in IconTime
//
#ifdef DEBUG
#define MIN_FLUSH 60 // 60 == 1 min
#else
#define MIN_FLUSH 900 // 900 == 15min
#endif
// all file/icons in the location table are "time stamped"
// each time they are accessed.
//
// this way we know the most important ones (MRU)
//
// when the icon cache get tooooo big we sort them all
// and throw out the old ones.
#define ICONTIME_ZERO 0
// GetIconTime() returns the "clock" used to timestamp icons
// in the icon table for MRU. the clock incrments once every 1024ms
// (about once every second)
#define GetIconTime() (g_dwIconTimeBase + (GetTickCount() >> 10))
extern int g_ccIcon;
TIMEVAR(LookupFileClass); TIMEVAR(AddFileClass);
TIMEVAR(LookupFileClassName); TIMEVAR(AddFileClassName);
TIMEVAR(LookupFileSCIDs); TIMEVAR(AddFileSCIDs);
TIMEVAR(LookupIcon); TIMEVAR(RemoveIcon); TIMEVAR(AddIcon); TIMEVAR(IconFlush);
DWORD g_dwIconTimeBase = ICONTIME_ZERO; DWORD g_dwIconTimeFlush = ICONTIME_ZERO; DWORD g_dwFreeImageCount = 0; DWORD g_dwFreeEntryCount = 0;
CDSA<LOCATION_ENTRY> *g_pdsaLocationEntries = NULL; BOOL g_fDirtyIcons = FALSE; UINT g_iLastSysIcon = 0;
typedef struct { PCTSTR pszClassName; DWORD dwFlags; PERCEIVED gen; UINT cSCID; SHCOLUMNID* ascid; } FILECLASSENTRY;
// these GIL_ (GetIconLocation) flags are used when searching for a
// match in the icon table. all other flags are ignored (when searching
// for a match)
//
// NOTE! If you change this definition, you also have to update the
// documentation for SHUpdateImage (since these are the bits that
// SHUpdateImage uses, too)
#define GIL_COMPARE (GIL_SIMULATEDOC | GIL_NOTFILENAME)
void _InitIconOverlayIndices(void); BOOL _IconIndexInOverlayManager(int iILIndex);
LOCATION_ENTRY* _LookupIcon(LPCTSTR pszName, int iIconIndex, UINT uFlags) { ASSERTCRITICAL
TCHAR szLower[MAX_PATH]; StrCpyN(szLower, pszName, ARRAYSIZE(szLower)); CharLower(szLower);
pszName = FindHashItem(NULL, szLower);
LOCATION_ENTRY *pFound = NULL; if (pszName && g_pdsaLocationEntries) { LOCATION_ENTRY *p; int i, n = g_pdsaLocationEntries->GetItemCount(); for (i = 0, p = g_pdsaLocationEntries->GetItemPtr(0); i < n; i++, p++) { if ((p->szName == pszName) && ((UINT)(p->uFlags & GIL_COMPARE) == (uFlags & GIL_COMPARE)) && (p->iIconIndex == iIconIndex)) { p->Access = GetIconTime(); pFound = p; break; // we are done
} } } return pFound; }
int LookupIconIndex(LPCTSTR pszName, int iIconIndex, UINT uFlags) { ASSERT(IS_VALID_STRING_PTR(pszName, -1));
LPCTSTR pszRelativeName = PathFindFileName(pszName);
if (lstrcmpi(pszRelativeName, TEXT("shell32.dll")) == 0) { // we want people to pass full paths in pszName, but shell32.dll is "special", since many callers
// hardcode the short name, we will always use the short name for it.
pszName = pszRelativeName; }
ENTERCRITICAL; TIMESTART(LookupIcon);
LOCATION_ENTRY *p = _LookupIcon(pszName, iIconIndex, uFlags); int iILIndex = p ? p->iILIndex : -1;
TIMESTOP(LookupIcon); LEAVECRITICAL;
return iILIndex; }
STDAPI_(int) SHLookupIconIndex(LPCTSTR pszName, int iIconIndex, UINT uFlags) { return LookupIconIndex(pszName, iIconIndex, uFlags); }
#ifdef UNICODE
STDAPI_(int) SHLookupIconIndexA(LPCSTR pszName, int iIconIndex, UINT uFlags) { WCHAR wsz[MAX_PATH];
SHAnsiToUnicode(pszName, wsz, ARRAYSIZE(wsz)); return SHLookupIconIndex(wsz, iIconIndex, uFlags); }
#else
STDAPI_(int) SHLookupIconIndexW(LPCWSTR pszName, int iIconIndex, UINT uFlags) { char sz[MAX_PATH]; SHUnicodeToAnsi(pszName, sz, ARRAYSIZE(sz)); return SHLookupIconIndex(sz, iIconIndex, uFlags); }
#endif
// returns a free image index, or -1 if none
int GetFreeImageIndex(void) { int iILIndex = -1;
ASSERTCRITICAL
if (g_dwFreeImageCount && g_pdsaLocationEntries) { LOCATION_ENTRY *p; int i, n = g_pdsaLocationEntries->GetItemCount(); for (i = 0, p = g_pdsaLocationEntries->GetItemPtr(0); i < n; i++, p++) { if (p->szName == NULL && p->iILIndex != 0) { iILIndex = p->iILIndex; // get free index
p->iILIndex = 0; // claim it.
p->Access = ICONTIME_ZERO; // mark unused entry.
g_dwFreeImageCount--; g_dwFreeEntryCount++; break; } } }
return iILIndex; }
int GetImageIndexUsage(int iILIndex) { int usage = 0;
ASSERTCRITICAL
if (g_pdsaLocationEntries) { LOCATION_ENTRY *p; int i, n = g_pdsaLocationEntries->GetItemCount(); for (i = 0, p = g_pdsaLocationEntries->GetItemPtr(0); i < n; i++, p++) { if (p->iILIndex == iILIndex) { usage++; } } }
return usage; }
//
// free specified icon table entry. If this makes a system image list index available
// for reuse, check whether this index is cached by file class table. If it is, return
// the image index and caller is responsible for updating file class table and display.
// O/w return -1.
//
int _FreeEntry(LOCATION_ENTRY *p) { int iUsageCount;
ASSERTCRITICAL
TraceMsg(TF_IMAGE, "Icon cache DSA item ([\"%s\", %d], %x, %d, %x) is freed", p->szName, p->iIconIndex, p->uFlags, p->iILIndex, p->Access);
g_fDirtyIcons = TRUE; // we need to save now.
ASSERT(p->szName); DeleteHashItem(NULL, p->szName); p->szName = 0;
iUsageCount = GetImageIndexUsage(p->iILIndex); if (iUsageCount > 1) { TraceMsg(TF_IMAGE, "Icon cache: count for %d was %d (is now minus 1)", p->iILIndex, iUsageCount); g_dwFreeEntryCount++; p->iILIndex = 0; // unused entry
p->Access = ICONTIME_ZERO; } else { TraceMsg(TF_IMAGE, "Icon cache: count for %d was %d (is now free)", p->iILIndex, iUsageCount); g_dwFreeImageCount++; p->Access = ICONTIME_ZERO;
if (IconIndexInFileClassTable(p->iILIndex) || _IconIndexInOverlayManager(p->iILIndex)) { TraceMsg(TF_IMAGE, "Icon cache: system imagelist index %d is released for reuse", p->iILIndex); return p->iILIndex; } }
return -1; }
LOCATION_ENTRY *GetFreeEntry(void) { ASSERTCRITICAL
if (g_dwFreeEntryCount && g_pdsaLocationEntries) { LOCATION_ENTRY *p; int i, n = g_pdsaLocationEntries->GetItemCount(); for (i = 0, p = g_pdsaLocationEntries->GetItemPtr(0); i < n; i++, p++) { if (p->szName == NULL && p->iILIndex == 0) { g_dwFreeEntryCount--; return p; } } }
return NULL; }
// add a item the the cache
//
// lpszIconFile - filename to add
// iIconIndex - icon index in file.
// uFlags - flags
// GIL_SIMULATEDOC - this is a simulated doc icon
// GIL_NOTFILENAME - file is not a path/index that
// ExtractIcon can deal with
// iIndex - image index to use.
//
// returns:
// image index for new entry.
//
// notes:
// if the item already exists it is replaced.
//
HRESULT AddToIconTable(LPCTSTR pszName, int iIconIndex, UINT uFlags, int iILIndex) { HRESULT hr = E_FAIL; LPCTSTR pszRelativeName = PathFindFileName(pszName);
if (lstrcmpi(pszRelativeName, TEXT("shell32.dll")) == 0) { // we want people to pass full paths in pszName, but shell32.dll is "special", since many callers
// hardcode the short name, we will always use the short name for it.
pszName = pszRelativeName; }
if (pszName) { ENTERCRITICAL; TIMESTART(AddIcon);
if (g_pdsaLocationEntries == NULL) { g_pdsaLocationEntries = CDSA_Create<LOCATION_ENTRY>(8);
g_dwFreeEntryCount = 0; g_dwFreeImageCount = 0; g_dwIconTimeBase = 0; g_dwIconTimeBase = 0-GetIconTime(); g_dwIconTimeFlush = 0; }
if (g_pdsaLocationEntries) { g_fDirtyIcons = TRUE; // we need to save now.
LOCATION_ENTRY *ple;
if (0 == (uFlags & GIL_DONTCACHE)) { ple = _LookupIcon(pszName, iIconIndex, uFlags); if (ple) { if (ple->iILIndex == iILIndex) { hr = S_FALSE; // We've already got this guy, no problem
} else { AssertMsg(ple == NULL,TEXT("Don't call AddToIconTable with somebody who is already there!\n")); } } }
if (FAILED(hr)) { TCHAR szLower[MAX_PATH]; lstrcpyn(szLower, pszName, ARRAYSIZE(szLower)); CharLower(szLower); pszName = AddHashItem(NULL, szLower); if (pszName) { LOCATION_ENTRY le; le.szName = pszName; le.iIconIndex = iIconIndex; le.iILIndex = iILIndex; le.uFlags = uFlags; le.Access = GetIconTime();
ple = GetFreeEntry();
if (NULL != ple) { TraceMsg(TF_IMAGE, "Icon cache DSA item ([\"%s\", %d], %x, %d, %x) is added (unfreed)", le.szName, le.iIconIndex, le.uFlags, le.iILIndex, le.Access);
*ple = le; hr = S_OK; } else { if (g_pdsaLocationEntries->AppendItem(&le) != -1) { TraceMsg(TF_IMAGE, "Icon cache DSA item ([\"%s\", %d], %x, %d, %x) is added", le.szName, le.iIconIndex, le.uFlags, le.iILIndex, le.Access);
hr = S_OK; } } } } }
TIMESTOP(AddIcon); LEAVECRITICAL; }
return hr; }
void RemoveFromIconTable(LPCTSTR pszName) { BOOL fUpdateFileClass = FALSE;
ENTERCRITICAL; TIMESTART(RemoveIcon);
LPCTSTR pszRelativeName = PathFindFileName(pszName);
if (lstrcmpi(pszRelativeName, TEXT("shell32.dll")) == 0) { // we want people to pass full paths in pszName, but shell32.dll is "special", since many callers
// hardcode the short name, we will always use the short name for it.
pszName = pszRelativeName; }
TCHAR szLower[MAX_PATH]; StrCpyN(szLower, pszName, ARRAYSIZE(szLower)); CharLower(szLower); pszName = FindHashItem(NULL, szLower); if (pszName && g_pdsaLocationEntries) { TraceMsg(TF_IMAGE, "IconCache: flush \"%s\"", pszName); LOCATION_ENTRY *p; UINT i, n = g_pdsaLocationEntries->GetItemCount(); for (i = 0, p = g_pdsaLocationEntries->GetItemPtr(0); i < n; i++, p++) { if (p->szName == pszName && i > g_iLastSysIcon) { if (-1 != _FreeEntry(p)) fUpdateFileClass = TRUE; } } }
TIMESTOP(RemoveIcon); LEAVECRITICAL;
if (fUpdateFileClass) { TraceMsg(TF_IMAGE, "Icon cache deleted some class items, broadcasting SHCNE_UPDATEIMAGE");
FlushFileClass(); _InitIconOverlayIndices(); // Tell overlay manager to re-determine icon indices
SHChangeNotify(SHCNE_UPDATEIMAGE, SHCNF_DWORD, (LPCVOID)-1, NULL); }
return; }
//
// empties the icon cache
//
void FlushIconCache(void) { ENTERCRITICAL;
if (g_pdsaLocationEntries) { LOCATION_ENTRY *p; int i, n = g_pdsaLocationEntries->GetItemCount(); for (i = 0, p = g_pdsaLocationEntries->GetItemPtr(0); i < n; i++, p++) { if (p->szName) DeleteHashItem(NULL, p->szName); }
g_pdsaLocationEntries->DeleteAllItems(); g_dwFreeEntryCount = 0; g_dwFreeImageCount = 0; g_dwIconTimeBase = 0; g_dwIconTimeBase = 0-GetIconTime(); g_dwIconTimeFlush = 0; g_fDirtyIcons = TRUE; // we need to save now.
}
LEAVECRITICAL; }
//
// if the icon cache is too big get rid of some old items.
//
// remember FlushIconCache() removes *all* items from the
// icon table, and this function gets rid of *some* old items.
//
STDAPI_(void) IconCacheFlush(BOOL fForce) { int nuked = 0;
ENTERCRITICAL;
if (g_pdsaLocationEntries) { // conpute the time from the last flush call
DWORD dt = GetIconTime() - g_dwIconTimeFlush;
// compute the number of "active" table entries.
int active = g_pdsaLocationEntries->GetItemCount() - g_dwFreeEntryCount - g_dwFreeImageCount; ASSERT(active >= 0);
if (fForce || (dt > MIN_FLUSH && active >= g_MaxIcons)) { TraceMsg(TF_IMAGE, "_IconCacheFlush: removing all items older than %d. %d icons in cache", dt/2, active);
LOCATION_ENTRY *p; UINT i, n = g_pdsaLocationEntries->GetItemCount();
for (i = 0, p = g_pdsaLocationEntries->GetItemPtr(0); i < n; i++, p++) { if (i <= g_iLastSysIcon) continue;
if (p->szName) { TraceMsg(TF_IMAGE, "_IconCacheFlush: \"%s,%d\" old enough? %d v %d", p->szName, p->iIconIndex, g_dwIconTimeFlush + dt/2, p->Access); }
if (p->szName && p->Access < (g_dwIconTimeFlush + dt/2)) { nuked++; _FreeEntry(p); } }
if (nuked > 0) { g_dwIconTimeFlush = GetIconTime(); g_fDirtyIcons = TRUE; // we need to save now.
} } }
LEAVECRITICAL;
if (nuked > 0) { FlushFileClass(); _InitIconOverlayIndices(); // Tell overlay manager to re-determine icon indices
SHChangeNotify(SHCNE_UPDATEIMAGE, SHCNF_DWORD, (LPCVOID)-1, NULL); } }
#ifdef DEBUG
void _IconCacheDump() { TCHAR szBuffer[MAX_PATH];
ENTERCRITICAL; if (g_pdsaLocationEntries && _IsSHILInited() && (g_dwDumpFlags & DF_ICONCACHE)) { int cItems = g_pdsaLocationEntries->GetItemCount();
TraceMsg(TF_IMAGE, "Icon cache: %d icons (%d free)", cItems, g_dwFreeEntryCount); TraceMsg(TF_IMAGE, "Icon cache: %d images (%d free)", _GetSHILImageCount(), g_dwFreeImageCount);
for (int i = 0; i < cItems; i++) { LOCATION_ENTRY *pLocEntry = g_pdsaLocationEntries->GetItemPtr(i);
if (pLocEntry->szName) GetHashItemName(NULL, pLocEntry->szName, szBuffer, ARRAYSIZE(szBuffer)); else StrCpyN(szBuffer, TEXT("(free)"), ARRAYSIZE(szBuffer));
TraceMsg(TF_ALWAYS, "%s;%d%s%s\timage=%d access=%d", (LPTSTR)szBuffer, pLocEntry->iIconIndex, ((pLocEntry->uFlags & GIL_SIMULATEDOC) ? TEXT(" doc"):TEXT("")), ((pLocEntry->uFlags & GIL_NOTFILENAME) ? TEXT(" not file"):TEXT("")), pLocEntry->iILIndex, pLocEntry->Access); } } LEAVECRITICAL; } #endif
DWORD GetBuildNumber() { // Need to use DLL version as we are updating this dll plus others and
// we need the cache to be invalidated as we may change the icons...
return VER_PRODUCTVERSION_DW; }
#ifdef _WIN64
//
// ps - stream to which to save
// hda - DSA of LOCATION_ENTRY structures
// cle - count of LOCATION_ENTRY32's to write
//
// The structures are stored as LOCATION_ENTRY32 on disk.
//
HRESULT _IconCacheWriteLocations(IStream *pstm, HDSA hdsa, int cle) { HRESULT hr = E_OUTOFMEMORY;
// Convert from LOCATION_ENTRY to LOCATION_ENTRY32, then write out
// the LOCATION_ENTRY32 structures.
LOCATION_ENTRY32 *rgle32 = (LOCATION_ENTRY32*)LocalAlloc(LPTR, cle * sizeof(LOCATION_ENTRY32)); if (rgle32) { LOCATION_ENTRY *rgle = (LOCATION_ENTRY*)DSA_GetItemPtr(hdsa, 0); for (int i = 0; i < cle; i++) { rgle32[i].iIconIndex = rgle[i].iIconIndex; rgle32[i].uFlags = rgle[i].uFlags; rgle32[i].iILIndex = rgle[i].iILIndex; rgle32[i].Access = rgle[i].Access; }
hr = IStream_Write(pstm, rgle32, cle * sizeof(LOCATION_ENTRY32)); LocalFree(rgle32); } return hr; }
#else
__inline HRESULT _IconCacheWriteLocations(IStream *pstm, HDSA hdsa, int cle) { // LOCATION_ENTRY and LOCATION_ENTRY32 are the same, so we can
// read straight into the DSA data block
COMPILETIME_ASSERT(sizeof(LOCATION_ENTRY) == sizeof(LOCATION_ENTRY32)); return IStream_Write(pstm, DSA_GetItemPtr(hdsa, 0), cle * sizeof(LOCATION_ENTRY)); } #endif
HRESULT GetIconCachePath(LPTSTR pszPath) { HRESULT hr = SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, pszPath); if (SUCCEEDED(hr)) { if (!PathAppend(pszPath, TEXT("IconCache.db"))) hr = E_FAIL; } return hr; }
// TODO: Make this function compute the actual required size.
ULONG _GetIconCacheSize() { // Set the initial size to 6MB to prevent excessive fragmentation on the disk
ULONG uSize = 6*1024*1024;
return uSize; }
// persist the icon cache to a file
STDAPI_(BOOL) IconCacheSave() { HRESULT hr = S_OK; // assume OK
// if the icon cache is not dirty no need to save anything
if (IsMainShellProcess() && g_pdsaLocationEntries && g_fDirtyIcons) { // if the icon cache is way too big dont save it.
// reload g_MaxIcons in case the user set it before shutting down.
QueryNewMaxIcons(); if ((UINT)g_pdsaLocationEntries->GetItemCount() <= (UINT)g_MaxIcons) { TCHAR szPath[MAX_PATH]; hr = GetIconCachePath(szPath); if (SUCCEEDED(hr)) { IStream *pstm; hr = SHCreateStreamOnFileEx(szPath, STGM_CREATE | STGM_WRITE | STGM_SHARE_DENY_WRITE, FILE_ATTRIBUTE_HIDDEN, TRUE, NULL, &pstm); if (SUCCEEDED(hr)) { ULARGE_INTEGER size; size.LowPart = _GetIconCacheSize(); size.HighPart = 0; // Set the right size initially so that the file system gives us contigous space on the disk
// This avoid fragmentation and improves our startup time.
hr = pstm->SetSize(size); if (SUCCEEDED(hr)) { ENTERCRITICAL;
IC_HEAD ich = {0}; // ich.cbSize, don't set this until we re-write the header
ich.dwMagic = ICONCACHE_MAGIC; ich.dwVersion = ICONCACHE_VERSION; ich.dwNumIcons = GetSystemMetrics(SM_CLEANBOOT) ? 0 : g_pdsaLocationEntries->GetItemCount(); ich.dwColorRes = GetCurColorRes(); ich.dwFlags = g_ccIcon; ich.dwBuild = GetBuildNumber(); ich.dwTimeSave = GetIconTime(); ich.dwTimeFlush = g_dwIconTimeFlush; ich.dwFreeImageCount = g_dwFreeImageCount; ich.dwFreeEntryCount = g_dwFreeEntryCount; ich.cImageLists = ARRAYSIZE(g_rgshil);
for (int i = 0; i < ARRAYSIZE(g_rgshil); i++) { ImageList_GetIconSize(g_rgshil[i].himl, (int*)&ich.rgsize[i].cx, (int*)&ich.rgsize[i].cy); }
hr = IStream_Write(pstm, &ich, sizeof(ich)); if (SUCCEEDED(hr)) { // write out entries (assumes all entries are contigious in memory)
hr = _IconCacheWriteLocations(pstm, *g_pdsaLocationEntries, ich.dwNumIcons); // write out the path names
for (i = 0; SUCCEEDED(hr) && (i < (int)ich.dwNumIcons); i++) { TCHAR ach[MAX_PATH]; LOCATION_ENTRY *p = g_pdsaLocationEntries->GetItemPtr(i);
if (p->szName) GetHashItemName(NULL, p->szName, ach, ARRAYSIZE(ach)); else ach[0] = 0;
hr = Stream_WriteString(pstm, ach, TRUE); }
// write out the imagelist of the icons
for (i = 0; SUCCEEDED(hr) && (i < ARRAYSIZE(g_rgshil)); i++) { hr = ImageList_Write(g_rgshil[i].himl, pstm) ? S_OK : E_FAIL; }
if (SUCCEEDED(hr)) { hr = pstm->Commit(0); if (SUCCEEDED(hr)) { // This is where the file pointer is at the end of the file.
ULARGE_INTEGER liSize; if (SUCCEEDED(pstm->Seek(g_li0, STREAM_SEEK_CUR, &liSize))) { // Trim the file size now. Ignore the return code
pstm->SetSize(liSize); }
hr = pstm->Seek(g_li0, STREAM_SEEK_SET, NULL); if (SUCCEEDED(hr)) { ich.cbSize = sizeof(ich); // not valid until this is set
hr = IStream_Write(pstm, &ich, sizeof(ich)); if (SUCCEEDED(hr)) { g_fDirtyIcons = FALSE; // reset dirty state
} } } } } pstm->Release();
LEAVECRITICAL; } } } if (FAILED(hr)) DeleteFile(szPath); // saving failed, cleanup
} }
return SUCCEEDED(hr); }
#ifdef _WIN64
//
// ps - stream from which to load
// hda - DSA of LOCATION_ENTRY structures
// cle - count of LOCATION_ENTRY32's to read
//
// The structures are stored as LOCATION_ENTRY32 on disk.
//
HRESULT _IconCacheReadLocations(IStream *pstm, HDSA hdsa, int cle) { HRESULT hr = E_OUTOFMEMORY;
// read into a scratch buffer, then convert
// LOCATION_ENTRY32 into LOCATION_ENTRY.
LOCATION_ENTRY32 *rgle32 = (LOCATION_ENTRY32*)LocalAlloc(LPTR, cle * sizeof(LOCATION_ENTRY32)); if (rgle32) { hr = IStream_Read(pstm, rgle32, cle * sizeof(LOCATION_ENTRY32)); if (SUCCEEDED(hr)) { LOCATION_ENTRY *rgle = (LOCATION_ENTRY*)DSA_GetItemPtr(hdsa, 0); for (int i = 0; i < cle; i++) { rgle[i].iIconIndex = rgle32[i].iIconIndex; rgle[i].uFlags = rgle32[i].uFlags; rgle[i].iILIndex = rgle32[i].iILIndex; rgle[i].Access = rgle32[i].Access; } } LocalFree(rgle32); } return hr; }
#else
__inline HRESULT _IconCacheReadLocations(IStream *pstm, HDSA hdsa, int cle) { // LOCATION_ENTRY and LOCATION_ENTRY32 are the same, so we can
// read straight into the DSA data block
COMPILETIME_ASSERT(sizeof(LOCATION_ENTRY) == sizeof(LOCATION_ENTRY32)); return IStream_Read(pstm, DSA_GetItemPtr(hdsa, 0), cle * sizeof(LOCATION_ENTRY)); } #endif
void _InitIconOverlayIndices(void) { IShellIconOverlayManager *psiom;
if (SUCCEEDED(GetIconOverlayManager(&psiom))) { psiom->RefreshOverlayImages(SIOM_OVERLAYINDEX | SIOM_ICONINDEX); psiom->Release(); } }
BOOL _IconIndexInOverlayManager(int iILIndex) { BOOL fInOverlayManager = FALSE;
ENTERCRITICAL;
IShellIconOverlayManager *psiom;
if (SUCCEEDED(GetIconOverlayManager(&psiom))) { int iOverlayIndex;
if (SUCCEEDED(psiom->OverlayIndexFromImageIndex(iILIndex, &iOverlayIndex, FALSE))) { fInOverlayManager = TRUE; } psiom->Release(); }
LEAVECRITICAL;
return fInOverlayManager; }
BOOL _ReadImageLists(IStream *pstrm, HIMAGELIST rghiml[SHIL_COUNT], SIZE rgsize[SHIL_COUNT]) { BOOL fSuccess = TRUE; for (int i = 0; fSuccess && i < ARRAYSIZE(g_rgshil); i++) { rghiml[i] = ImageList_Read(pstrm); if (rghiml[i]) { // If we read the list from disk and it does not contain the
// parallel mirrored list while we are on a mirrored system,
// let's not use the cache in this case
// Example of this is ARA/HEB MUI on US W2k
if (IS_BIDI_LOCALIZED_SYSTEM() && !(ImageList_GetFlags(rghiml[i]) & ILC_MIRROR)) { fSuccess = FALSE; } else { int cx, cy; ImageList_GetIconSize(rghiml[i], &cx, &cy); if (cx != rgsize[i].cx || cy != rgsize[i].cy) { fSuccess = FALSE; } } } else { fSuccess = FALSE; } }
if (fSuccess == FALSE) { // free any imagelists we allocated
for (i = 0; i < ARRAYSIZE(g_rgshil); i++) { if (rghiml[i]) { ImageList_Destroy(rghiml[i]); rghiml[i] = NULL; } } }
return fSuccess; }
// psz and cch passed in for efficiency (avoid using another MAX_PATH stack buffer)
BOOL _ReadLocationEntries(const IC_HEAD *pich, IStream *pstrm, CDSA<LOCATION_ENTRY> *pdsaTemp, LPTSTR psz, int cch) { LOCATION_ENTRY dummy;
// grow the array out so we can read data into it
if (pdsaTemp->SetItem(pich->dwNumIcons - 1, &dummy)) { ASSERT(pdsaTemp->GetItemCount() == (int)pich->dwNumIcons); if (SUCCEEDED(_IconCacheReadLocations(pstrm, *pdsaTemp, pich->dwNumIcons))) { // read the paths, patching up the table with the hashitem info
for (int i = 0; i < (int)pich->dwNumIcons; i++) { LOCATION_ENTRY *pLocation = pdsaTemp->GetItemPtr(i);
if (SUCCEEDED(Stream_ReadString(pstrm, psz, cch, TRUE)) && *psz) pLocation->szName = AddHashItem(NULL, psz); else pLocation->szName = 0; } // restore the image lists
return TRUE; } } return FALSE; }
BOOL _ValidateIconCacheHeader(const IC_HEAD *pich, SIZE rgsize[SHIL_COUNT], UINT flags) { if (pich->cbSize == sizeof(*pich) && pich->dwVersion == ICONCACHE_VERSION && pich->dwMagic == ICONCACHE_MAGIC && pich->dwBuild == GetBuildNumber() && pich->dwFlags == (DWORD)flags && pich->cImageLists == ARRAYSIZE(g_rgshil) && (0 == memcmp(pich->rgsize, rgsize, sizeof(pich->rgsize)))) { UINT cres = GetCurColorRes();
// dont load a mono image list on a color device, and
// dont load a color image list on a mono device, get it?
if (pich->dwColorRes == 1 && cres != 1 || pich->dwColorRes != 1 && cres == 1) { return FALSE; } else if (pich->dwNumIcons > (UINT)g_MaxIcons) { return FALSE; } return TRUE; } return FALSE; }
void _SetNewGlobals(const IC_HEAD *pich, CDSA<LOCATION_ENTRY> *pdsaTemp, HIMAGELIST rghiml[SHIL_COUNT]) { ASSERTCRITICAL;
if (g_pdsaLocationEntries) { g_pdsaLocationEntries->Destroy(); delete g_pdsaLocationEntries; } g_pdsaLocationEntries = pdsaTemp;
for (int i = 0; i < ARRAYSIZE(g_rgshil); i++) { if (g_rgshil[i].himl) ImageList_Destroy(g_rgshil[i].himl); g_rgshil[i].himl = rghiml[i]; }
//
// we want GetIconTime() to pick up
// where it left off when we saved.
//
g_dwIconTimeBase = 0; // GetIconTime() uses g_dwIconTimeBase
g_dwIconTimeBase = pich->dwTimeSave - GetIconTime(); g_dwIconTimeFlush = pich->dwTimeFlush; g_dwFreeImageCount = pich->dwFreeImageCount; g_dwFreeEntryCount = pich->dwFreeEntryCount; g_fDirtyIcons = FALSE; }
//
// get the icon cache back from disk, it must be the requested size and
// bitdepth or we will not use it.
//
STDAPI_(BOOL) IconCacheRestore(SIZE rgsize[SHIL_COUNT], UINT flags) { ASSERTCRITICAL;
BOOL fSuccess = FALSE;
if (!GetSystemMetrics(SM_CLEANBOOT)) { TCHAR szPath[MAX_PATH];
IStream *pstm; if (SUCCEEDED(GetIconCachePath(szPath)) && SUCCEEDED(SHCreateStreamOnFile(szPath, STGM_READ | STGM_SHARE_DENY_WRITE, &pstm))) { IC_HEAD ich; if (SUCCEEDED(IStream_Read(pstm, &ich, sizeof(ich))) && _ValidateIconCacheHeader(&ich, rgsize, flags)) { CDSA<LOCATION_ENTRY> *pdsaTemp = CDSA_Create<LOCATION_ENTRY>(8);
// load the icon table
if (pdsaTemp) { HIMAGELIST rghiml[ARRAYSIZE(g_rgshil)] = {0};
fSuccess = _ReadLocationEntries(&ich, pstm, pdsaTemp, szPath, ARRAYSIZE(szPath)) && _ReadImageLists(pstm, rghiml, rgsize);
if (fSuccess) { // Make it so, number one.
_SetNewGlobals(&ich, pdsaTemp, rghiml); _InitIconOverlayIndices(); } else { // failure, clean up
pdsaTemp->Destroy(); delete pdsaTemp; } } } pstm->Release(); } }
return fSuccess; }
//------------------ file class table ------------------------
HHASHTABLE g_hhtClass = NULL;
BOOL InitFileClassTable(void) { ASSERTCRITICAL;
if (!g_hhtClass) { if (!g_hhtClass) g_hhtClass = CreateHashItemTable(0, sizeof(FILECLASSENTRY)); }
return BOOLIFY(g_hhtClass); } void FlushFileClass(void) { ENTERCRITICAL;
#ifdef DEBUG
if (g_hhtClass != NULL) { DebugMsg(DM_TRACE, TEXT("Flushing file class table")); TIMEOUT(LookupFileClass); TIMEOUT(AddFileClass); TIMEOUT(LookupFileClassName); TIMEOUT(AddFileClassName); TIMEOUT(LookupFileSCIDs); TIMEOUT(AddFileSCIDs); TIMEOUT(LookupIcon); TIMEOUT(AddIcon); TIMEOUT(RemoveIcon);
TIMEIN(LookupFileClass); TIMEIN(AddFileClass); TIMEIN(LookupFileClassName); TIMEIN(AddFileClassName); TIMEIN(LookupFileSCIDs); TIMEIN(AddFileSCIDs); TIMEIN(LookupIcon); TIMEIN(AddIcon); TIMEIN(RemoveIcon);
DumpHashItemTable(g_hhtClass); } #endif
if (g_hhtClass != NULL) { DestroyHashItemTable(g_hhtClass); g_hhtClass = NULL; }
TraceMsg(TF_IMAGE, "Flushed class maps");
LEAVECRITICAL; }
DWORD LookupFileClass(LPCTSTR pszClass) { DWORD dw = 0;
ENTERCRITICAL; TIMESTART(LookupFileClass); if (g_hhtClass && (NULL != (pszClass = FindHashItem(g_hhtClass, pszClass)))) dw = ((FILECLASSENTRY*)GetHashItemDataPtr(g_hhtClass, pszClass))->dwFlags;
TIMESTOP(LookupFileClass); LEAVECRITICAL;
return dw; }
void AddFileClass(LPCTSTR pszClass, DWORD dw) { ENTERCRITICAL; TIMESTART(AddFileClass);
// create a hsa table to keep the file class info in.
if (InitFileClassTable() && (NULL != (pszClass = AddHashItem(g_hhtClass, pszClass)))) ((FILECLASSENTRY*)GetHashItemDataPtr(g_hhtClass, pszClass))->dwFlags = dw;
TraceMsg(TF_IMAGE, "Mapped %s to image %d", pszClass, (dw & SHCF_ICON_INDEX));
TIMESTOP(AddFileClass); LEAVECRITICAL; return; }
//======================================================================
typedef struct _IconIndexCountParam { int iILIndex; // hash item data
int cItems; // number of hash items found
} ICONINDEXCOUNTPARAM;
//======================================================================
void _IconIndexInFileClassTableCallback(HHASHTABLE hht, LPCTSTR sz, UINT usage, DWORD_PTR dwParam) { ICONINDEXCOUNTPARAM *lpParam = (ICONINDEXCOUNTPARAM *)dwParam;
FILECLASSENTRY* pfce = (FILECLASSENTRY*)GetHashItemDataPtr(hht, sz);
if (pfce && (pfce->dwFlags & SHCF_ICON_INDEX) == lpParam->iILIndex) { lpParam->cItems++; } }
//======================================================================
BOOL IconIndexInFileClassTable(int iILIndex) { ICONINDEXCOUNTPARAM param;
param.iILIndex = iILIndex; param.cItems = 0;
ENTERCRITICAL;
if (g_hhtClass) { EnumHashItems(g_hhtClass, _IconIndexInFileClassTableCallback, (DWORD_PTR)¶m); }
LEAVECRITICAL;
return param.cItems; }
LPCTSTR LookupFileClassName(LPCTSTR pszClass) { LPCTSTR pszClassName = NULL;
ASSERTCRITICAL TIMESTART(LookupFileClassName);
if (g_hhtClass && (NULL != (pszClass = FindHashItem(g_hhtClass, pszClass)))) { FILECLASSENTRY* pfce = (FILECLASSENTRY*)GetHashItemDataPtr(g_hhtClass, pszClass); pszClassName = pfce->pszClassName; } TIMESTOP(LookupFileClassName);
return pszClassName; }
// If the return value is greater than zero,
// it is up to the caller to free the array that is passed out.
// If the return value is zero, the value of papProps is undefined.
UINT LookupFileSCIDs(LPCTSTR pszClass, SHCOLUMNID *pascidOut[]) { SHCOLUMNID *ascid = NULL; UINT cCount = 0;
ASSERTCRITICAL TIMESTART(LookupFileClassName);
if (g_hhtClass && (NULL != (pszClass = FindHashItem(g_hhtClass, pszClass)))) { FILECLASSENTRY* pfce = (FILECLASSENTRY*)GetHashItemDataPtr(g_hhtClass, pszClass); cCount = pfce->cSCID; if (cCount > 0) { // Make a local copy of the scid array
ascid = (SHCOLUMNID*)LocalAlloc(LMEM_FIXED, sizeof(SHCOLUMNID) * cCount); if (ascid) CopyMemory(ascid, pfce->ascid, sizeof(SHCOLUMNID) * cCount); else cCount = 0; } } TIMESTOP(LookupFileClassName);
*pascidOut = ascid; return cCount;
}
LPCTSTR AddFileClassName(LPCTSTR pszClass, LPCTSTR pszClassName) { ASSERTCRITICAL TIMESTART(AddFileClassName);
// create a hsa table to keep the file class info in.
if (InitFileClassTable() && (NULL != (pszClass = AddHashItem(g_hhtClass, pszClass)))) { pszClassName = AddHashItem(g_hhtClass, pszClassName); ((FILECLASSENTRY*)GetHashItemDataPtr(g_hhtClass, pszClass))->pszClassName = pszClassName; } else { pszClassName = NULL; }
TIMESTOP(AddFileClassName); return pszClassName; }
// The array of SHCOLUMNIDs passed in is copied
void AddFileSCIDs(LPCTSTR pszClass, SHCOLUMNID ascidIn[], UINT cSCID) { ASSERTCRITICAL TIMESTART(AddFileSCIDs);
if (InitFileClassTable() && (NULL != (pszClass = AddHashItem(g_hhtClass, pszClass)))) { // Make a copy of the array.
SHCOLUMNID *ascid = (SHCOLUMNID*)LocalAlloc(LMEM_FIXED, sizeof(SHCOLUMNID) * cSCID);
if (ascid) { FILECLASSENTRY *pfce = (FILECLASSENTRY*)GetHashItemDataPtr(g_hhtClass, pszClass); // Free any previous scid array first
if (pfce->ascid) LocalFree(pfce->ascid); // Note, we never free the last scid array -- freed on process exit.
pfce->ascid = ascid; CopyMemory(ascid, ascidIn, cSCID * sizeof(SHCOLUMNID)); pfce->cSCID = cSCID; } }
TIMESTOP(AddFileSCIDs); }
PERCEIVED LookupFilePerceivedType(LPCTSTR pszClass) { PERCEIVED gen = GEN_UNKNOWN;
ENTERCRITICAL; TIMESTART(LookupFileClassName);
if (g_hhtClass && (NULL != (pszClass = FindHashItem(g_hhtClass, pszClass)))) { FILECLASSENTRY* pfce = (FILECLASSENTRY*)GetHashItemDataPtr(g_hhtClass, pszClass); gen = pfce->gen; } TIMESTOP(LookupFileClassName); LEAVECRITICAL; return gen; }
void AddFilePerceivedType(LPCTSTR pszClass, PERCEIVED gen) { ENTERCRITICAL; TIMESTART(AddFileClassName);
// create a hsa table to keep the file class info in.
if (InitFileClassTable() && (NULL != (pszClass = AddHashItem(g_hhtClass, pszClass)))) { ((FILECLASSENTRY*)GetHashItemDataPtr(g_hhtClass, pszClass))->gen = gen; }
TIMESTOP(AddFileClassName); LEAVECRITICAL; }
|