1381 lines
41 KiB
C++
1381 lines
41 KiB
C++
// 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];
|
|
lstrcpy(szLower, pszName);
|
|
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];
|
|
lstrcpy(szLower, pszName);
|
|
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];
|
|
lstrcpy(szLower, pszName);
|
|
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
|
|
lstrcpy(szBuffer, TEXT("(free)"));
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|