Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1384 lines
42 KiB

  1. // routines for managing the icon cache tables, and file type tables.
  2. // Jan 95, ToddLa
  3. //
  4. // icon cache
  5. //
  6. // the icon cache is n ImageLists
  7. // and a table mapping a name/icon number/flags to a ImageList
  8. // index, the global hash table (pht==NULL) is used to hold
  9. // the names.
  10. //
  11. // AddToIconTable - associate a name/number/flags with a image index
  12. // SHLookupIconIndex - return a image index, given name/number/flags
  13. // RemoveFromIconTable - remove all entries with the given name
  14. // FlushIconCache - remove all entries.
  15. // GetFreeImageIndex - return a free ImageList index.
  16. //
  17. // the worst part about the whole icon cache design is that people
  18. // can add or lookup a image index (given a name/number/flags) but
  19. // they never have to release it. we never know if a ImageList index
  20. // is currently in use or not. this should be the first thing
  21. // fixed about the shell. currently we use a MRU type scheme when
  22. // we need to remove a entry from the icon cache, it is far from
  23. // perfect.
  24. //
  25. // file type cache
  26. //
  27. // the file type cache is a hash table with two DWORDs of extra data.
  28. // DWORD #0 holds flags, DWORD #1 holds a pointer to the name of
  29. // the class.
  30. //
  31. // LookupFileClass - given a file class (ie ".doc" or "Directory")
  32. // maps it to a DWORD of flags, return 0 if not found.
  33. //
  34. // AddFileClass - adds a class (and flags) to cache
  35. //
  36. // LookupFileClassName - given a file class, returns it name.
  37. // AddFileClassName - sets the name of a class.
  38. // FlushFileClass - removes all items in cache.
  39. //
  40. #include "shellprv.h"
  41. #pragma hdrstop
  42. #include "filetbl.h"
  43. #include "fstreex.h"
  44. #include <ntverp.h>
  45. #include "ovrlaymn.h"
  46. #include "dpa.h"
  47. typedef struct {
  48. DWORD cbSize; // size of this header.
  49. DWORD dwMagic; // magic number
  50. DWORD dwVersion; // version of this saved icon cache
  51. DWORD dwBuild; // windows build number
  52. DWORD dwNumIcons; // number of icons in cache
  53. DWORD dwColorRes; // color resolution of device at last save
  54. DWORD dwFlags; // ILC_* flags
  55. DWORD dwTimeSave; // icon time this file was saved
  56. DWORD dwTimeFlush; // icon time we last flushed.
  57. DWORD dwFreeImageCount;
  58. DWORD dwFreeEntryCount;
  59. SIZE rgsize[SHIL_COUNT]; // array of sizes of cached icons
  60. DWORD cImageLists; // equal to ARRAYSIZE(IC_HEAD.size)
  61. } IC_HEAD;
  62. #define ICONCACHE_MAGIC (TEXT('W') + (TEXT('i') << 8) + (TEXT('n') << 16) + (TEXT('4') << 24))
  63. #define ICONCACHE_VERSION 0x0505 // Unicode file names + lower case hash items + v6 imagelist
  64. typedef struct {
  65. LPCTSTR szName; // key: file name
  66. int iIconIndex; // key: icon index (or random DWORD for GIL_NOTFILE)
  67. UINT uFlags; // GIL_* flags
  68. int iILIndex; // data: system image list index
  69. UINT Access; // last access.
  70. } LOCATION_ENTRY;
  71. // LOCATION_ENTRY32 is the version of LOCATION_ENTRY that gets written to disk
  72. // It must be declared explicitly 32-bit for Win32/Win64 interop.
  73. typedef struct {
  74. DWORD dwszName; // (garbage in file)
  75. int iIconIndex; // key: icon index (or random DWORD for GIL_NOTFILE)
  76. UINT uFlags; // GIL_* flags
  77. int iILIndex; // data: system image list index
  78. UINT Access; // last access.
  79. } LOCATION_ENTRY32;
  80. //
  81. // MIN_FLUSH is the minimum time interval between flushing the icon cache
  82. // this number is in IconTime
  83. //
  84. #ifdef DEBUG
  85. #define MIN_FLUSH 60 // 60 == 1 min
  86. #else
  87. #define MIN_FLUSH 900 // 900 == 15min
  88. #endif
  89. // all file/icons in the location table are "time stamped"
  90. // each time they are accessed.
  91. //
  92. // this way we know the most important ones (MRU)
  93. //
  94. // when the icon cache get tooooo big we sort them all
  95. // and throw out the old ones.
  96. #define ICONTIME_ZERO 0
  97. // GetIconTime() returns the "clock" used to timestamp icons
  98. // in the icon table for MRU. the clock incrments once every 1024ms
  99. // (about once every second)
  100. #define GetIconTime() (g_dwIconTimeBase + (GetTickCount() >> 10))
  101. extern int g_ccIcon;
  102. TIMEVAR(LookupFileClass);
  103. TIMEVAR(AddFileClass);
  104. TIMEVAR(LookupFileClassName);
  105. TIMEVAR(AddFileClassName);
  106. TIMEVAR(LookupFileSCIDs);
  107. TIMEVAR(AddFileSCIDs);
  108. TIMEVAR(LookupIcon);
  109. TIMEVAR(RemoveIcon);
  110. TIMEVAR(AddIcon);
  111. TIMEVAR(IconFlush);
  112. DWORD g_dwIconTimeBase = ICONTIME_ZERO;
  113. DWORD g_dwIconTimeFlush = ICONTIME_ZERO;
  114. DWORD g_dwFreeImageCount = 0;
  115. DWORD g_dwFreeEntryCount = 0;
  116. CDSA<LOCATION_ENTRY> *g_pdsaLocationEntries = NULL;
  117. BOOL g_fDirtyIcons = FALSE;
  118. UINT g_iLastSysIcon = 0;
  119. typedef struct
  120. {
  121. PCTSTR pszClassName;
  122. DWORD dwFlags;
  123. PERCEIVED gen;
  124. UINT cSCID;
  125. SHCOLUMNID* ascid;
  126. } FILECLASSENTRY;
  127. // these GIL_ (GetIconLocation) flags are used when searching for a
  128. // match in the icon table. all other flags are ignored (when searching
  129. // for a match)
  130. //
  131. // NOTE! If you change this definition, you also have to update the
  132. // documentation for SHUpdateImage (since these are the bits that
  133. // SHUpdateImage uses, too)
  134. #define GIL_COMPARE (GIL_SIMULATEDOC | GIL_NOTFILENAME)
  135. void _InitIconOverlayIndices(void);
  136. BOOL _IconIndexInOverlayManager(int iILIndex);
  137. LOCATION_ENTRY* _LookupIcon(LPCTSTR pszName, int iIconIndex, UINT uFlags)
  138. {
  139. ASSERTCRITICAL
  140. TCHAR szLower[MAX_PATH];
  141. StrCpyN(szLower, pszName, ARRAYSIZE(szLower));
  142. CharLower(szLower);
  143. pszName = FindHashItem(NULL, szLower);
  144. LOCATION_ENTRY *pFound = NULL;
  145. if (pszName && g_pdsaLocationEntries)
  146. {
  147. LOCATION_ENTRY *p;
  148. int i, n = g_pdsaLocationEntries->GetItemCount();
  149. for (i = 0, p = g_pdsaLocationEntries->GetItemPtr(0); i < n; i++, p++)
  150. {
  151. if ((p->szName == pszName) &&
  152. ((UINT)(p->uFlags & GIL_COMPARE) == (uFlags & GIL_COMPARE)) &&
  153. (p->iIconIndex == iIconIndex))
  154. {
  155. p->Access = GetIconTime();
  156. pFound = p;
  157. break; // we are done
  158. }
  159. }
  160. }
  161. return pFound;
  162. }
  163. int LookupIconIndex(LPCTSTR pszName, int iIconIndex, UINT uFlags)
  164. {
  165. ASSERT(IS_VALID_STRING_PTR(pszName, -1));
  166. LPCTSTR pszRelativeName = PathFindFileName(pszName);
  167. if (lstrcmpi(pszRelativeName, TEXT("shell32.dll")) == 0)
  168. {
  169. // we want people to pass full paths in pszName, but shell32.dll is "special", since many callers
  170. // hardcode the short name, we will always use the short name for it.
  171. pszName = pszRelativeName;
  172. }
  173. ENTERCRITICAL;
  174. TIMESTART(LookupIcon);
  175. LOCATION_ENTRY *p = _LookupIcon(pszName, iIconIndex, uFlags);
  176. int iILIndex = p ? p->iILIndex : -1;
  177. TIMESTOP(LookupIcon);
  178. LEAVECRITICAL;
  179. return iILIndex;
  180. }
  181. STDAPI_(int) SHLookupIconIndex(LPCTSTR pszName, int iIconIndex, UINT uFlags)
  182. {
  183. return LookupIconIndex(pszName, iIconIndex, uFlags);
  184. }
  185. #ifdef UNICODE
  186. STDAPI_(int) SHLookupIconIndexA(LPCSTR pszName, int iIconIndex, UINT uFlags)
  187. {
  188. WCHAR wsz[MAX_PATH];
  189. SHAnsiToUnicode(pszName, wsz, ARRAYSIZE(wsz));
  190. return SHLookupIconIndex(wsz, iIconIndex, uFlags);
  191. }
  192. #else
  193. STDAPI_(int) SHLookupIconIndexW(LPCWSTR pszName, int iIconIndex, UINT uFlags)
  194. {
  195. char sz[MAX_PATH];
  196. SHUnicodeToAnsi(pszName, sz, ARRAYSIZE(sz));
  197. return SHLookupIconIndex(sz, iIconIndex, uFlags);
  198. }
  199. #endif
  200. // returns a free image index, or -1 if none
  201. int GetFreeImageIndex(void)
  202. {
  203. int iILIndex = -1;
  204. ASSERTCRITICAL
  205. if (g_dwFreeImageCount && g_pdsaLocationEntries)
  206. {
  207. LOCATION_ENTRY *p;
  208. int i, n = g_pdsaLocationEntries->GetItemCount();
  209. for (i = 0, p = g_pdsaLocationEntries->GetItemPtr(0); i < n; i++, p++)
  210. {
  211. if (p->szName == NULL && p->iILIndex != 0)
  212. {
  213. iILIndex = p->iILIndex; // get free index
  214. p->iILIndex = 0; // claim it.
  215. p->Access = ICONTIME_ZERO; // mark unused entry.
  216. g_dwFreeImageCount--;
  217. g_dwFreeEntryCount++;
  218. break;
  219. }
  220. }
  221. }
  222. return iILIndex;
  223. }
  224. int GetImageIndexUsage(int iILIndex)
  225. {
  226. int usage = 0;
  227. ASSERTCRITICAL
  228. if (g_pdsaLocationEntries)
  229. {
  230. LOCATION_ENTRY *p;
  231. int i, n = g_pdsaLocationEntries->GetItemCount();
  232. for (i = 0, p = g_pdsaLocationEntries->GetItemPtr(0); i < n; i++, p++)
  233. {
  234. if (p->iILIndex == iILIndex)
  235. {
  236. usage++;
  237. }
  238. }
  239. }
  240. return usage;
  241. }
  242. //
  243. // free specified icon table entry. If this makes a system image list index available
  244. // for reuse, check whether this index is cached by file class table. If it is, return
  245. // the image index and caller is responsible for updating file class table and display.
  246. // O/w return -1.
  247. //
  248. int _FreeEntry(LOCATION_ENTRY *p)
  249. {
  250. int iUsageCount;
  251. ASSERTCRITICAL
  252. TraceMsg(TF_IMAGE, "Icon cache DSA item ([\"%s\", %d], %x, %d, %x) is freed",
  253. p->szName, p->iIconIndex, p->uFlags, p->iILIndex, p->Access);
  254. g_fDirtyIcons = TRUE; // we need to save now.
  255. ASSERT(p->szName);
  256. DeleteHashItem(NULL, p->szName);
  257. p->szName = 0;
  258. iUsageCount = GetImageIndexUsage(p->iILIndex);
  259. if (iUsageCount > 1)
  260. {
  261. TraceMsg(TF_IMAGE, "Icon cache: count for %d was %d (is now minus 1)", p->iILIndex, iUsageCount);
  262. g_dwFreeEntryCount++;
  263. p->iILIndex = 0; // unused entry
  264. p->Access = ICONTIME_ZERO;
  265. }
  266. else
  267. {
  268. TraceMsg(TF_IMAGE, "Icon cache: count for %d was %d (is now free)", p->iILIndex, iUsageCount);
  269. g_dwFreeImageCount++;
  270. p->Access = ICONTIME_ZERO;
  271. if (IconIndexInFileClassTable(p->iILIndex) || _IconIndexInOverlayManager(p->iILIndex))
  272. {
  273. TraceMsg(TF_IMAGE, "Icon cache: system imagelist index %d is released for reuse", p->iILIndex);
  274. return p->iILIndex;
  275. }
  276. }
  277. return -1;
  278. }
  279. LOCATION_ENTRY *GetFreeEntry(void)
  280. {
  281. ASSERTCRITICAL
  282. if (g_dwFreeEntryCount && g_pdsaLocationEntries)
  283. {
  284. LOCATION_ENTRY *p;
  285. int i, n = g_pdsaLocationEntries->GetItemCount();
  286. for (i = 0, p = g_pdsaLocationEntries->GetItemPtr(0); i < n; i++, p++)
  287. {
  288. if (p->szName == NULL && p->iILIndex == 0)
  289. {
  290. g_dwFreeEntryCount--;
  291. return p;
  292. }
  293. }
  294. }
  295. return NULL;
  296. }
  297. // add a item the the cache
  298. //
  299. // lpszIconFile - filename to add
  300. // iIconIndex - icon index in file.
  301. // uFlags - flags
  302. // GIL_SIMULATEDOC - this is a simulated doc icon
  303. // GIL_NOTFILENAME - file is not a path/index that
  304. // ExtractIcon can deal with
  305. // iIndex - image index to use.
  306. //
  307. // returns:
  308. // image index for new entry.
  309. //
  310. // notes:
  311. // if the item already exists it is replaced.
  312. //
  313. HRESULT AddToIconTable(LPCTSTR pszName, int iIconIndex, UINT uFlags, int iILIndex)
  314. {
  315. HRESULT hr = E_FAIL;
  316. LPCTSTR pszRelativeName = PathFindFileName(pszName);
  317. if (lstrcmpi(pszRelativeName, TEXT("shell32.dll")) == 0)
  318. {
  319. // we want people to pass full paths in pszName, but shell32.dll is "special", since many callers
  320. // hardcode the short name, we will always use the short name for it.
  321. pszName = pszRelativeName;
  322. }
  323. if (pszName)
  324. {
  325. ENTERCRITICAL;
  326. TIMESTART(AddIcon);
  327. if (g_pdsaLocationEntries == NULL)
  328. {
  329. g_pdsaLocationEntries = CDSA_Create<LOCATION_ENTRY>(8);
  330. g_dwFreeEntryCount = 0;
  331. g_dwFreeImageCount = 0;
  332. g_dwIconTimeBase = 0;
  333. g_dwIconTimeBase = 0-GetIconTime();
  334. g_dwIconTimeFlush = 0;
  335. }
  336. if (g_pdsaLocationEntries)
  337. {
  338. g_fDirtyIcons = TRUE; // we need to save now.
  339. LOCATION_ENTRY *ple;
  340. if (0 == (uFlags & GIL_DONTCACHE))
  341. {
  342. ple = _LookupIcon(pszName, iIconIndex, uFlags);
  343. if (ple)
  344. {
  345. if (ple->iILIndex == iILIndex)
  346. {
  347. hr = S_FALSE; // We've already got this guy, no problem
  348. }
  349. else
  350. {
  351. AssertMsg(ple == NULL,TEXT("Don't call AddToIconTable with somebody who is already there!\n"));
  352. }
  353. }
  354. }
  355. if (FAILED(hr))
  356. {
  357. TCHAR szLower[MAX_PATH];
  358. lstrcpyn(szLower, pszName, ARRAYSIZE(szLower));
  359. CharLower(szLower);
  360. pszName = AddHashItem(NULL, szLower);
  361. if (pszName)
  362. {
  363. LOCATION_ENTRY le;
  364. le.szName = pszName;
  365. le.iIconIndex = iIconIndex;
  366. le.iILIndex = iILIndex;
  367. le.uFlags = uFlags;
  368. le.Access = GetIconTime();
  369. ple = GetFreeEntry();
  370. if (NULL != ple)
  371. {
  372. TraceMsg(TF_IMAGE, "Icon cache DSA item ([\"%s\", %d], %x, %d, %x) is added (unfreed)",
  373. le.szName, le.iIconIndex, le.uFlags, le.iILIndex, le.Access);
  374. *ple = le;
  375. hr = S_OK;
  376. }
  377. else
  378. {
  379. if (g_pdsaLocationEntries->AppendItem(&le) != -1)
  380. {
  381. TraceMsg(TF_IMAGE, "Icon cache DSA item ([\"%s\", %d], %x, %d, %x) is added",
  382. le.szName, le.iIconIndex, le.uFlags, le.iILIndex, le.Access);
  383. hr = S_OK;
  384. }
  385. }
  386. }
  387. }
  388. }
  389. TIMESTOP(AddIcon);
  390. LEAVECRITICAL;
  391. }
  392. return hr;
  393. }
  394. void RemoveFromIconTable(LPCTSTR pszName)
  395. {
  396. BOOL fUpdateFileClass = FALSE;
  397. ENTERCRITICAL;
  398. TIMESTART(RemoveIcon);
  399. LPCTSTR pszRelativeName = PathFindFileName(pszName);
  400. if (lstrcmpi(pszRelativeName, TEXT("shell32.dll")) == 0)
  401. {
  402. // we want people to pass full paths in pszName, but shell32.dll is "special", since many callers
  403. // hardcode the short name, we will always use the short name for it.
  404. pszName = pszRelativeName;
  405. }
  406. TCHAR szLower[MAX_PATH];
  407. StrCpyN(szLower, pszName, ARRAYSIZE(szLower));
  408. CharLower(szLower);
  409. pszName = FindHashItem(NULL, szLower);
  410. if (pszName && g_pdsaLocationEntries)
  411. {
  412. TraceMsg(TF_IMAGE, "IconCache: flush \"%s\"", pszName);
  413. LOCATION_ENTRY *p;
  414. UINT i, n = g_pdsaLocationEntries->GetItemCount();
  415. for (i = 0, p = g_pdsaLocationEntries->GetItemPtr(0); i < n; i++, p++)
  416. {
  417. if (p->szName == pszName && i > g_iLastSysIcon)
  418. {
  419. if (-1 != _FreeEntry(p))
  420. fUpdateFileClass = TRUE;
  421. }
  422. }
  423. }
  424. TIMESTOP(RemoveIcon);
  425. LEAVECRITICAL;
  426. if (fUpdateFileClass)
  427. {
  428. TraceMsg(TF_IMAGE, "Icon cache deleted some class items, broadcasting SHCNE_UPDATEIMAGE");
  429. FlushFileClass();
  430. _InitIconOverlayIndices(); // Tell overlay manager to re-determine icon indices
  431. SHChangeNotify(SHCNE_UPDATEIMAGE, SHCNF_DWORD, (LPCVOID)-1, NULL);
  432. }
  433. return;
  434. }
  435. //
  436. // empties the icon cache
  437. //
  438. void FlushIconCache(void)
  439. {
  440. ENTERCRITICAL;
  441. if (g_pdsaLocationEntries)
  442. {
  443. LOCATION_ENTRY *p;
  444. int i, n = g_pdsaLocationEntries->GetItemCount();
  445. for (i = 0, p = g_pdsaLocationEntries->GetItemPtr(0); i < n; i++, p++)
  446. {
  447. if (p->szName)
  448. DeleteHashItem(NULL, p->szName);
  449. }
  450. g_pdsaLocationEntries->DeleteAllItems();
  451. g_dwFreeEntryCount = 0;
  452. g_dwFreeImageCount = 0;
  453. g_dwIconTimeBase = 0;
  454. g_dwIconTimeBase = 0-GetIconTime();
  455. g_dwIconTimeFlush = 0;
  456. g_fDirtyIcons = TRUE; // we need to save now.
  457. }
  458. LEAVECRITICAL;
  459. }
  460. //
  461. // if the icon cache is too big get rid of some old items.
  462. //
  463. // remember FlushIconCache() removes *all* items from the
  464. // icon table, and this function gets rid of *some* old items.
  465. //
  466. STDAPI_(void) IconCacheFlush(BOOL fForce)
  467. {
  468. int nuked = 0;
  469. ENTERCRITICAL;
  470. if (g_pdsaLocationEntries)
  471. {
  472. // conpute the time from the last flush call
  473. DWORD dt = GetIconTime() - g_dwIconTimeFlush;
  474. // compute the number of "active" table entries.
  475. int active = g_pdsaLocationEntries->GetItemCount() - g_dwFreeEntryCount - g_dwFreeImageCount;
  476. ASSERT(active >= 0);
  477. if (fForce || (dt > MIN_FLUSH && active >= g_MaxIcons))
  478. {
  479. TraceMsg(TF_IMAGE, "_IconCacheFlush: removing all items older than %d. %d icons in cache", dt/2, active);
  480. LOCATION_ENTRY *p;
  481. UINT i, n = g_pdsaLocationEntries->GetItemCount();
  482. for (i = 0, p = g_pdsaLocationEntries->GetItemPtr(0); i < n; i++, p++)
  483. {
  484. if (i <= g_iLastSysIcon)
  485. continue;
  486. if (p->szName)
  487. {
  488. TraceMsg(TF_IMAGE, "_IconCacheFlush: \"%s,%d\" old enough? %d v %d", p->szName, p->iIconIndex, g_dwIconTimeFlush + dt/2, p->Access);
  489. }
  490. if (p->szName && p->Access < (g_dwIconTimeFlush + dt/2))
  491. {
  492. nuked++;
  493. _FreeEntry(p);
  494. }
  495. }
  496. if (nuked > 0)
  497. {
  498. g_dwIconTimeFlush = GetIconTime();
  499. g_fDirtyIcons = TRUE; // we need to save now.
  500. }
  501. }
  502. }
  503. LEAVECRITICAL;
  504. if (nuked > 0)
  505. {
  506. FlushFileClass();
  507. _InitIconOverlayIndices(); // Tell overlay manager to re-determine icon indices
  508. SHChangeNotify(SHCNE_UPDATEIMAGE, SHCNF_DWORD, (LPCVOID)-1, NULL);
  509. }
  510. }
  511. #ifdef DEBUG
  512. void _IconCacheDump()
  513. {
  514. TCHAR szBuffer[MAX_PATH];
  515. ENTERCRITICAL;
  516. if (g_pdsaLocationEntries && _IsSHILInited() && (g_dwDumpFlags & DF_ICONCACHE))
  517. {
  518. int cItems = g_pdsaLocationEntries->GetItemCount();
  519. TraceMsg(TF_IMAGE, "Icon cache: %d icons (%d free)", cItems, g_dwFreeEntryCount);
  520. TraceMsg(TF_IMAGE, "Icon cache: %d images (%d free)", _GetSHILImageCount(), g_dwFreeImageCount);
  521. for (int i = 0; i < cItems; i++)
  522. {
  523. LOCATION_ENTRY *pLocEntry = g_pdsaLocationEntries->GetItemPtr(i);
  524. if (pLocEntry->szName)
  525. GetHashItemName(NULL, pLocEntry->szName, szBuffer, ARRAYSIZE(szBuffer));
  526. else
  527. StrCpyN(szBuffer, TEXT("(free)"), ARRAYSIZE(szBuffer));
  528. TraceMsg(TF_ALWAYS, "%s;%d%s%s\timage=%d access=%d",
  529. (LPTSTR)szBuffer,
  530. pLocEntry->iIconIndex,
  531. ((pLocEntry->uFlags & GIL_SIMULATEDOC) ? TEXT(" doc"):TEXT("")),
  532. ((pLocEntry->uFlags & GIL_NOTFILENAME) ? TEXT(" not file"):TEXT("")),
  533. pLocEntry->iILIndex, pLocEntry->Access);
  534. }
  535. }
  536. LEAVECRITICAL;
  537. }
  538. #endif
  539. DWORD GetBuildNumber()
  540. {
  541. // Need to use DLL version as we are updating this dll plus others and
  542. // we need the cache to be invalidated as we may change the icons...
  543. return VER_PRODUCTVERSION_DW;
  544. }
  545. #ifdef _WIN64
  546. //
  547. // ps - stream to which to save
  548. // hda - DSA of LOCATION_ENTRY structures
  549. // cle - count of LOCATION_ENTRY32's to write
  550. //
  551. // The structures are stored as LOCATION_ENTRY32 on disk.
  552. //
  553. HRESULT _IconCacheWriteLocations(IStream *pstm, HDSA hdsa, int cle)
  554. {
  555. HRESULT hr = E_OUTOFMEMORY;
  556. // Convert from LOCATION_ENTRY to LOCATION_ENTRY32, then write out
  557. // the LOCATION_ENTRY32 structures.
  558. LOCATION_ENTRY32 *rgle32 = (LOCATION_ENTRY32*)LocalAlloc(LPTR, cle * sizeof(LOCATION_ENTRY32));
  559. if (rgle32)
  560. {
  561. LOCATION_ENTRY *rgle = (LOCATION_ENTRY*)DSA_GetItemPtr(hdsa, 0);
  562. for (int i = 0; i < cle; i++)
  563. {
  564. rgle32[i].iIconIndex = rgle[i].iIconIndex;
  565. rgle32[i].uFlags = rgle[i].uFlags;
  566. rgle32[i].iILIndex = rgle[i].iILIndex;
  567. rgle32[i].Access = rgle[i].Access;
  568. }
  569. hr = IStream_Write(pstm, rgle32, cle * sizeof(LOCATION_ENTRY32));
  570. LocalFree(rgle32);
  571. }
  572. return hr;
  573. }
  574. #else
  575. __inline HRESULT _IconCacheWriteLocations(IStream *pstm, HDSA hdsa, int cle)
  576. {
  577. // LOCATION_ENTRY and LOCATION_ENTRY32 are the same, so we can
  578. // read straight into the DSA data block
  579. COMPILETIME_ASSERT(sizeof(LOCATION_ENTRY) == sizeof(LOCATION_ENTRY32));
  580. return IStream_Write(pstm, DSA_GetItemPtr(hdsa, 0), cle * sizeof(LOCATION_ENTRY));
  581. }
  582. #endif
  583. HRESULT GetIconCachePath(LPTSTR pszPath)
  584. {
  585. HRESULT hr = SHGetFolderPath(NULL, CSIDL_LOCAL_APPDATA | CSIDL_FLAG_CREATE, NULL, 0, pszPath);
  586. if (SUCCEEDED(hr))
  587. {
  588. if (!PathAppend(pszPath, TEXT("IconCache.db")))
  589. hr = E_FAIL;
  590. }
  591. return hr;
  592. }
  593. // TODO: Make this function compute the actual required size.
  594. ULONG _GetIconCacheSize()
  595. {
  596. // Set the initial size to 6MB to prevent excessive fragmentation on the disk
  597. ULONG uSize = 6*1024*1024;
  598. return uSize;
  599. }
  600. // persist the icon cache to a file
  601. STDAPI_(BOOL) IconCacheSave()
  602. {
  603. HRESULT hr = S_OK; // assume OK
  604. // if the icon cache is not dirty no need to save anything
  605. if (IsMainShellProcess() && g_pdsaLocationEntries && g_fDirtyIcons)
  606. {
  607. // if the icon cache is way too big dont save it.
  608. // reload g_MaxIcons in case the user set it before shutting down.
  609. QueryNewMaxIcons();
  610. if ((UINT)g_pdsaLocationEntries->GetItemCount() <= (UINT)g_MaxIcons)
  611. {
  612. TCHAR szPath[MAX_PATH];
  613. hr = GetIconCachePath(szPath);
  614. if (SUCCEEDED(hr))
  615. {
  616. IStream *pstm;
  617. hr = SHCreateStreamOnFileEx(szPath, STGM_CREATE | STGM_WRITE | STGM_SHARE_DENY_WRITE, FILE_ATTRIBUTE_HIDDEN, TRUE, NULL, &pstm);
  618. if (SUCCEEDED(hr))
  619. {
  620. ULARGE_INTEGER size;
  621. size.LowPart = _GetIconCacheSize();
  622. size.HighPart = 0;
  623. // Set the right size initially so that the file system gives us contigous space on the disk
  624. // This avoid fragmentation and improves our startup time.
  625. hr = pstm->SetSize(size);
  626. if (SUCCEEDED(hr))
  627. {
  628. ENTERCRITICAL;
  629. IC_HEAD ich = {0};
  630. // ich.cbSize, don't set this until we re-write the header
  631. ich.dwMagic = ICONCACHE_MAGIC;
  632. ich.dwVersion = ICONCACHE_VERSION;
  633. ich.dwNumIcons = GetSystemMetrics(SM_CLEANBOOT) ? 0 : g_pdsaLocationEntries->GetItemCount();
  634. ich.dwColorRes = GetCurColorRes();
  635. ich.dwFlags = g_ccIcon;
  636. ich.dwBuild = GetBuildNumber();
  637. ich.dwTimeSave = GetIconTime();
  638. ich.dwTimeFlush = g_dwIconTimeFlush;
  639. ich.dwFreeImageCount = g_dwFreeImageCount;
  640. ich.dwFreeEntryCount = g_dwFreeEntryCount;
  641. ich.cImageLists = ARRAYSIZE(g_rgshil);
  642. for (int i = 0; i < ARRAYSIZE(g_rgshil); i++)
  643. {
  644. ImageList_GetIconSize(g_rgshil[i].himl, (int*)&ich.rgsize[i].cx, (int*)&ich.rgsize[i].cy);
  645. }
  646. hr = IStream_Write(pstm, &ich, sizeof(ich));
  647. if (SUCCEEDED(hr))
  648. {
  649. // write out entries (assumes all entries are contigious in memory)
  650. hr = _IconCacheWriteLocations(pstm, *g_pdsaLocationEntries, ich.dwNumIcons);
  651. // write out the path names
  652. for (i = 0; SUCCEEDED(hr) && (i < (int)ich.dwNumIcons); i++)
  653. {
  654. TCHAR ach[MAX_PATH];
  655. LOCATION_ENTRY *p = g_pdsaLocationEntries->GetItemPtr(i);
  656. if (p->szName)
  657. GetHashItemName(NULL, p->szName, ach, ARRAYSIZE(ach));
  658. else
  659. ach[0] = 0;
  660. hr = Stream_WriteString(pstm, ach, TRUE);
  661. }
  662. // write out the imagelist of the icons
  663. for (i = 0; SUCCEEDED(hr) && (i < ARRAYSIZE(g_rgshil)); i++)
  664. {
  665. hr = ImageList_Write(g_rgshil[i].himl, pstm) ? S_OK : E_FAIL;
  666. }
  667. if (SUCCEEDED(hr))
  668. {
  669. hr = pstm->Commit(0);
  670. if (SUCCEEDED(hr))
  671. {
  672. // This is where the file pointer is at the end of the file.
  673. ULARGE_INTEGER liSize;
  674. if (SUCCEEDED(pstm->Seek(g_li0, STREAM_SEEK_CUR, &liSize)))
  675. {
  676. // Trim the file size now. Ignore the return code
  677. pstm->SetSize(liSize);
  678. }
  679. hr = pstm->Seek(g_li0, STREAM_SEEK_SET, NULL);
  680. if (SUCCEEDED(hr))
  681. {
  682. ich.cbSize = sizeof(ich); // not valid until this is set
  683. hr = IStream_Write(pstm, &ich, sizeof(ich));
  684. if (SUCCEEDED(hr))
  685. {
  686. g_fDirtyIcons = FALSE; // reset dirty state
  687. }
  688. }
  689. }
  690. }
  691. }
  692. pstm->Release();
  693. LEAVECRITICAL;
  694. }
  695. }
  696. }
  697. if (FAILED(hr))
  698. DeleteFile(szPath); // saving failed, cleanup
  699. }
  700. }
  701. return SUCCEEDED(hr);
  702. }
  703. #ifdef _WIN64
  704. //
  705. // ps - stream from which to load
  706. // hda - DSA of LOCATION_ENTRY structures
  707. // cle - count of LOCATION_ENTRY32's to read
  708. //
  709. // The structures are stored as LOCATION_ENTRY32 on disk.
  710. //
  711. HRESULT _IconCacheReadLocations(IStream *pstm, HDSA hdsa, int cle)
  712. {
  713. HRESULT hr = E_OUTOFMEMORY;
  714. // read into a scratch buffer, then convert
  715. // LOCATION_ENTRY32 into LOCATION_ENTRY.
  716. LOCATION_ENTRY32 *rgle32 = (LOCATION_ENTRY32*)LocalAlloc(LPTR, cle * sizeof(LOCATION_ENTRY32));
  717. if (rgle32)
  718. {
  719. hr = IStream_Read(pstm, rgle32, cle * sizeof(LOCATION_ENTRY32));
  720. if (SUCCEEDED(hr))
  721. {
  722. LOCATION_ENTRY *rgle = (LOCATION_ENTRY*)DSA_GetItemPtr(hdsa, 0);
  723. for (int i = 0; i < cle; i++)
  724. {
  725. rgle[i].iIconIndex = rgle32[i].iIconIndex;
  726. rgle[i].uFlags = rgle32[i].uFlags;
  727. rgle[i].iILIndex = rgle32[i].iILIndex;
  728. rgle[i].Access = rgle32[i].Access;
  729. }
  730. }
  731. LocalFree(rgle32);
  732. }
  733. return hr;
  734. }
  735. #else
  736. __inline HRESULT _IconCacheReadLocations(IStream *pstm, HDSA hdsa, int cle)
  737. {
  738. // LOCATION_ENTRY and LOCATION_ENTRY32 are the same, so we can
  739. // read straight into the DSA data block
  740. COMPILETIME_ASSERT(sizeof(LOCATION_ENTRY) == sizeof(LOCATION_ENTRY32));
  741. return IStream_Read(pstm, DSA_GetItemPtr(hdsa, 0), cle * sizeof(LOCATION_ENTRY));
  742. }
  743. #endif
  744. void _InitIconOverlayIndices(void)
  745. {
  746. IShellIconOverlayManager *psiom;
  747. if (SUCCEEDED(GetIconOverlayManager(&psiom)))
  748. {
  749. psiom->RefreshOverlayImages(SIOM_OVERLAYINDEX | SIOM_ICONINDEX);
  750. psiom->Release();
  751. }
  752. }
  753. BOOL _IconIndexInOverlayManager(int iILIndex)
  754. {
  755. BOOL fInOverlayManager = FALSE;
  756. ENTERCRITICAL;
  757. IShellIconOverlayManager *psiom;
  758. if (SUCCEEDED(GetIconOverlayManager(&psiom)))
  759. {
  760. int iOverlayIndex;
  761. if (SUCCEEDED(psiom->OverlayIndexFromImageIndex(iILIndex, &iOverlayIndex, FALSE)))
  762. {
  763. fInOverlayManager = TRUE;
  764. }
  765. psiom->Release();
  766. }
  767. LEAVECRITICAL;
  768. return fInOverlayManager;
  769. }
  770. BOOL _ReadImageLists(IStream *pstrm, HIMAGELIST rghiml[SHIL_COUNT], SIZE rgsize[SHIL_COUNT])
  771. {
  772. BOOL fSuccess = TRUE;
  773. for (int i = 0; fSuccess && i < ARRAYSIZE(g_rgshil); i++)
  774. {
  775. rghiml[i] = ImageList_Read(pstrm);
  776. if (rghiml[i])
  777. {
  778. // If we read the list from disk and it does not contain the
  779. // parallel mirrored list while we are on a mirrored system,
  780. // let's not use the cache in this case
  781. // Example of this is ARA/HEB MUI on US W2k
  782. if (IS_BIDI_LOCALIZED_SYSTEM() && !(ImageList_GetFlags(rghiml[i]) & ILC_MIRROR))
  783. {
  784. fSuccess = FALSE;
  785. }
  786. else
  787. {
  788. int cx, cy;
  789. ImageList_GetIconSize(rghiml[i], &cx, &cy);
  790. if (cx != rgsize[i].cx || cy != rgsize[i].cy)
  791. {
  792. fSuccess = FALSE;
  793. }
  794. }
  795. }
  796. else
  797. {
  798. fSuccess = FALSE;
  799. }
  800. }
  801. if (fSuccess == FALSE)
  802. {
  803. // free any imagelists we allocated
  804. for (i = 0; i < ARRAYSIZE(g_rgshil); i++)
  805. {
  806. if (rghiml[i])
  807. {
  808. ImageList_Destroy(rghiml[i]);
  809. rghiml[i] = NULL;
  810. }
  811. }
  812. }
  813. return fSuccess;
  814. }
  815. // psz and cch passed in for efficiency (avoid using another MAX_PATH stack buffer)
  816. BOOL _ReadLocationEntries(const IC_HEAD *pich, IStream *pstrm, CDSA<LOCATION_ENTRY> *pdsaTemp, LPTSTR psz, int cch)
  817. {
  818. LOCATION_ENTRY dummy;
  819. // grow the array out so we can read data into it
  820. if (pdsaTemp->SetItem(pich->dwNumIcons - 1, &dummy))
  821. {
  822. ASSERT(pdsaTemp->GetItemCount() == (int)pich->dwNumIcons);
  823. if (SUCCEEDED(_IconCacheReadLocations(pstrm, *pdsaTemp, pich->dwNumIcons)))
  824. {
  825. // read the paths, patching up the table with the hashitem info
  826. for (int i = 0; i < (int)pich->dwNumIcons; i++)
  827. {
  828. LOCATION_ENTRY *pLocation = pdsaTemp->GetItemPtr(i);
  829. if (SUCCEEDED(Stream_ReadString(pstrm, psz, cch, TRUE)) && *psz)
  830. pLocation->szName = AddHashItem(NULL, psz);
  831. else
  832. pLocation->szName = 0;
  833. }
  834. // restore the image lists
  835. return TRUE;
  836. }
  837. }
  838. return FALSE;
  839. }
  840. BOOL _ValidateIconCacheHeader(const IC_HEAD *pich, SIZE rgsize[SHIL_COUNT], UINT flags)
  841. {
  842. if (pich->cbSize == sizeof(*pich) &&
  843. pich->dwVersion == ICONCACHE_VERSION &&
  844. pich->dwMagic == ICONCACHE_MAGIC &&
  845. pich->dwBuild == GetBuildNumber() &&
  846. pich->dwFlags == (DWORD)flags &&
  847. pich->cImageLists == ARRAYSIZE(g_rgshil) &&
  848. (0 == memcmp(pich->rgsize, rgsize, sizeof(pich->rgsize))))
  849. {
  850. UINT cres = GetCurColorRes();
  851. // dont load a mono image list on a color device, and
  852. // dont load a color image list on a mono device, get it?
  853. if (pich->dwColorRes == 1 && cres != 1 ||
  854. pich->dwColorRes != 1 && cres == 1)
  855. {
  856. return FALSE;
  857. }
  858. else if (pich->dwNumIcons > (UINT)g_MaxIcons)
  859. {
  860. return FALSE;
  861. }
  862. return TRUE;
  863. }
  864. return FALSE;
  865. }
  866. void _SetNewGlobals(const IC_HEAD *pich, CDSA<LOCATION_ENTRY> *pdsaTemp, HIMAGELIST rghiml[SHIL_COUNT])
  867. {
  868. ASSERTCRITICAL;
  869. if (g_pdsaLocationEntries)
  870. {
  871. g_pdsaLocationEntries->Destroy();
  872. delete g_pdsaLocationEntries;
  873. }
  874. g_pdsaLocationEntries = pdsaTemp;
  875. for (int i = 0; i < ARRAYSIZE(g_rgshil); i++)
  876. {
  877. if (g_rgshil[i].himl)
  878. ImageList_Destroy(g_rgshil[i].himl);
  879. g_rgshil[i].himl = rghiml[i];
  880. }
  881. //
  882. // we want GetIconTime() to pick up
  883. // where it left off when we saved.
  884. //
  885. g_dwIconTimeBase = 0; // GetIconTime() uses g_dwIconTimeBase
  886. g_dwIconTimeBase = pich->dwTimeSave - GetIconTime();
  887. g_dwIconTimeFlush = pich->dwTimeFlush;
  888. g_dwFreeImageCount = pich->dwFreeImageCount;
  889. g_dwFreeEntryCount = pich->dwFreeEntryCount;
  890. g_fDirtyIcons = FALSE;
  891. }
  892. //
  893. // get the icon cache back from disk, it must be the requested size and
  894. // bitdepth or we will not use it.
  895. //
  896. STDAPI_(BOOL) IconCacheRestore(SIZE rgsize[SHIL_COUNT], UINT flags)
  897. {
  898. ASSERTCRITICAL;
  899. BOOL fSuccess = FALSE;
  900. if (!GetSystemMetrics(SM_CLEANBOOT))
  901. {
  902. TCHAR szPath[MAX_PATH];
  903. IStream *pstm;
  904. if (SUCCEEDED(GetIconCachePath(szPath)) &&
  905. SUCCEEDED(SHCreateStreamOnFile(szPath, STGM_READ | STGM_SHARE_DENY_WRITE, &pstm)))
  906. {
  907. IC_HEAD ich;
  908. if (SUCCEEDED(IStream_Read(pstm, &ich, sizeof(ich))) &&
  909. _ValidateIconCacheHeader(&ich, rgsize, flags))
  910. {
  911. CDSA<LOCATION_ENTRY> *pdsaTemp = CDSA_Create<LOCATION_ENTRY>(8);
  912. // load the icon table
  913. if (pdsaTemp)
  914. {
  915. HIMAGELIST rghiml[ARRAYSIZE(g_rgshil)] = {0};
  916. fSuccess = _ReadLocationEntries(&ich, pstm, pdsaTemp, szPath, ARRAYSIZE(szPath)) &&
  917. _ReadImageLists(pstm, rghiml, rgsize);
  918. if (fSuccess)
  919. {
  920. // Make it so, number one.
  921. _SetNewGlobals(&ich, pdsaTemp, rghiml);
  922. _InitIconOverlayIndices();
  923. }
  924. else
  925. {
  926. // failure, clean up
  927. pdsaTemp->Destroy();
  928. delete pdsaTemp;
  929. }
  930. }
  931. }
  932. pstm->Release();
  933. }
  934. }
  935. return fSuccess;
  936. }
  937. //------------------ file class table ------------------------
  938. HHASHTABLE g_hhtClass = NULL;
  939. BOOL InitFileClassTable(void)
  940. {
  941. ASSERTCRITICAL;
  942. if (!g_hhtClass)
  943. {
  944. if (!g_hhtClass)
  945. g_hhtClass = CreateHashItemTable(0, sizeof(FILECLASSENTRY));
  946. }
  947. return BOOLIFY(g_hhtClass);
  948. }
  949. void FlushFileClass(void)
  950. {
  951. ENTERCRITICAL;
  952. #ifdef DEBUG
  953. if (g_hhtClass != NULL)
  954. {
  955. DebugMsg(DM_TRACE, TEXT("Flushing file class table"));
  956. TIMEOUT(LookupFileClass);
  957. TIMEOUT(AddFileClass);
  958. TIMEOUT(LookupFileClassName);
  959. TIMEOUT(AddFileClassName);
  960. TIMEOUT(LookupFileSCIDs);
  961. TIMEOUT(AddFileSCIDs);
  962. TIMEOUT(LookupIcon);
  963. TIMEOUT(AddIcon);
  964. TIMEOUT(RemoveIcon);
  965. TIMEIN(LookupFileClass);
  966. TIMEIN(AddFileClass);
  967. TIMEIN(LookupFileClassName);
  968. TIMEIN(AddFileClassName);
  969. TIMEIN(LookupFileSCIDs);
  970. TIMEIN(AddFileSCIDs);
  971. TIMEIN(LookupIcon);
  972. TIMEIN(AddIcon);
  973. TIMEIN(RemoveIcon);
  974. DumpHashItemTable(g_hhtClass);
  975. }
  976. #endif
  977. if (g_hhtClass != NULL)
  978. {
  979. DestroyHashItemTable(g_hhtClass);
  980. g_hhtClass = NULL;
  981. }
  982. TraceMsg(TF_IMAGE, "Flushed class maps");
  983. LEAVECRITICAL;
  984. }
  985. DWORD LookupFileClass(LPCTSTR pszClass)
  986. {
  987. DWORD dw = 0;
  988. ENTERCRITICAL;
  989. TIMESTART(LookupFileClass);
  990. if (g_hhtClass && (NULL != (pszClass = FindHashItem(g_hhtClass, pszClass))))
  991. dw = ((FILECLASSENTRY*)GetHashItemDataPtr(g_hhtClass, pszClass))->dwFlags;
  992. TIMESTOP(LookupFileClass);
  993. LEAVECRITICAL;
  994. return dw;
  995. }
  996. void AddFileClass(LPCTSTR pszClass, DWORD dw)
  997. {
  998. ENTERCRITICAL;
  999. TIMESTART(AddFileClass);
  1000. // create a hsa table to keep the file class info in.
  1001. if (InitFileClassTable() && (NULL != (pszClass = AddHashItem(g_hhtClass, pszClass))))
  1002. ((FILECLASSENTRY*)GetHashItemDataPtr(g_hhtClass, pszClass))->dwFlags = dw;
  1003. TraceMsg(TF_IMAGE, "Mapped %s to image %d", pszClass, (dw & SHCF_ICON_INDEX));
  1004. TIMESTOP(AddFileClass);
  1005. LEAVECRITICAL;
  1006. return;
  1007. }
  1008. //======================================================================
  1009. typedef struct _IconIndexCountParam
  1010. {
  1011. int iILIndex; // hash item data
  1012. int cItems; // number of hash items found
  1013. } ICONINDEXCOUNTPARAM;
  1014. //======================================================================
  1015. void _IconIndexInFileClassTableCallback(HHASHTABLE hht, LPCTSTR sz, UINT usage, DWORD_PTR dwParam)
  1016. {
  1017. ICONINDEXCOUNTPARAM *lpParam = (ICONINDEXCOUNTPARAM *)dwParam;
  1018. FILECLASSENTRY* pfce = (FILECLASSENTRY*)GetHashItemDataPtr(hht, sz);
  1019. if (pfce && (pfce->dwFlags & SHCF_ICON_INDEX) == lpParam->iILIndex)
  1020. {
  1021. lpParam->cItems++;
  1022. }
  1023. }
  1024. //======================================================================
  1025. BOOL IconIndexInFileClassTable(int iILIndex)
  1026. {
  1027. ICONINDEXCOUNTPARAM param;
  1028. param.iILIndex = iILIndex;
  1029. param.cItems = 0;
  1030. ENTERCRITICAL;
  1031. if (g_hhtClass)
  1032. {
  1033. EnumHashItems(g_hhtClass, _IconIndexInFileClassTableCallback, (DWORD_PTR)&param);
  1034. }
  1035. LEAVECRITICAL;
  1036. return param.cItems;
  1037. }
  1038. LPCTSTR LookupFileClassName(LPCTSTR pszClass)
  1039. {
  1040. LPCTSTR pszClassName = NULL;
  1041. ASSERTCRITICAL
  1042. TIMESTART(LookupFileClassName);
  1043. if (g_hhtClass && (NULL != (pszClass = FindHashItem(g_hhtClass, pszClass))))
  1044. {
  1045. FILECLASSENTRY* pfce = (FILECLASSENTRY*)GetHashItemDataPtr(g_hhtClass, pszClass);
  1046. pszClassName = pfce->pszClassName;
  1047. }
  1048. TIMESTOP(LookupFileClassName);
  1049. return pszClassName;
  1050. }
  1051. // If the return value is greater than zero,
  1052. // it is up to the caller to free the array that is passed out.
  1053. // If the return value is zero, the value of papProps is undefined.
  1054. UINT LookupFileSCIDs(LPCTSTR pszClass, SHCOLUMNID *pascidOut[])
  1055. {
  1056. SHCOLUMNID *ascid = NULL;
  1057. UINT cCount = 0;
  1058. ASSERTCRITICAL
  1059. TIMESTART(LookupFileClassName);
  1060. if (g_hhtClass && (NULL != (pszClass = FindHashItem(g_hhtClass, pszClass))))
  1061. {
  1062. FILECLASSENTRY* pfce = (FILECLASSENTRY*)GetHashItemDataPtr(g_hhtClass, pszClass);
  1063. cCount = pfce->cSCID;
  1064. if (cCount > 0)
  1065. {
  1066. // Make a local copy of the scid array
  1067. ascid = (SHCOLUMNID*)LocalAlloc(LMEM_FIXED, sizeof(SHCOLUMNID) * cCount);
  1068. if (ascid)
  1069. CopyMemory(ascid, pfce->ascid, sizeof(SHCOLUMNID) * cCount);
  1070. else
  1071. cCount = 0;
  1072. }
  1073. }
  1074. TIMESTOP(LookupFileClassName);
  1075. *pascidOut = ascid;
  1076. return cCount;
  1077. }
  1078. LPCTSTR AddFileClassName(LPCTSTR pszClass, LPCTSTR pszClassName)
  1079. {
  1080. ASSERTCRITICAL
  1081. TIMESTART(AddFileClassName);
  1082. // create a hsa table to keep the file class info in.
  1083. if (InitFileClassTable() && (NULL != (pszClass = AddHashItem(g_hhtClass, pszClass))))
  1084. {
  1085. pszClassName = AddHashItem(g_hhtClass, pszClassName);
  1086. ((FILECLASSENTRY*)GetHashItemDataPtr(g_hhtClass, pszClass))->pszClassName = pszClassName;
  1087. }
  1088. else
  1089. {
  1090. pszClassName = NULL;
  1091. }
  1092. TIMESTOP(AddFileClassName);
  1093. return pszClassName;
  1094. }
  1095. // The array of SHCOLUMNIDs passed in is copied
  1096. void AddFileSCIDs(LPCTSTR pszClass, SHCOLUMNID ascidIn[], UINT cSCID)
  1097. {
  1098. ASSERTCRITICAL
  1099. TIMESTART(AddFileSCIDs);
  1100. if (InitFileClassTable() && (NULL != (pszClass = AddHashItem(g_hhtClass, pszClass))))
  1101. {
  1102. // Make a copy of the array.
  1103. SHCOLUMNID *ascid = (SHCOLUMNID*)LocalAlloc(LMEM_FIXED, sizeof(SHCOLUMNID) * cSCID);
  1104. if (ascid)
  1105. {
  1106. FILECLASSENTRY *pfce = (FILECLASSENTRY*)GetHashItemDataPtr(g_hhtClass, pszClass);
  1107. // Free any previous scid array first
  1108. if (pfce->ascid)
  1109. LocalFree(pfce->ascid);
  1110. // Note, we never free the last scid array -- freed on process exit.
  1111. pfce->ascid = ascid;
  1112. CopyMemory(ascid, ascidIn, cSCID * sizeof(SHCOLUMNID));
  1113. pfce->cSCID = cSCID;
  1114. }
  1115. }
  1116. TIMESTOP(AddFileSCIDs);
  1117. }
  1118. PERCEIVED LookupFilePerceivedType(LPCTSTR pszClass)
  1119. {
  1120. PERCEIVED gen = GEN_UNKNOWN;
  1121. ENTERCRITICAL;
  1122. TIMESTART(LookupFileClassName);
  1123. if (g_hhtClass && (NULL != (pszClass = FindHashItem(g_hhtClass, pszClass))))
  1124. {
  1125. FILECLASSENTRY* pfce = (FILECLASSENTRY*)GetHashItemDataPtr(g_hhtClass, pszClass);
  1126. gen = pfce->gen;
  1127. }
  1128. TIMESTOP(LookupFileClassName);
  1129. LEAVECRITICAL;
  1130. return gen;
  1131. }
  1132. void AddFilePerceivedType(LPCTSTR pszClass, PERCEIVED gen)
  1133. {
  1134. ENTERCRITICAL;
  1135. TIMESTART(AddFileClassName);
  1136. // create a hsa table to keep the file class info in.
  1137. if (InitFileClassTable() && (NULL != (pszClass = AddHashItem(g_hhtClass, pszClass))))
  1138. {
  1139. ((FILECLASSENTRY*)GetHashItemDataPtr(g_hhtClass, pszClass))->gen = gen;
  1140. }
  1141. TIMESTOP(AddFileClassName);
  1142. LEAVECRITICAL;
  1143. }