#include "shellprv.h" #pragma hdrstop #include "ovrlaymn.h" #include "fstreex.h" #include "filetbl.h" #include "ids.h" // REVIEW: More clean up should be done. BOOL _ShellImageListInit(UINT flags, BOOL fRestore); int g_ccIcon = 0; // color depth of ImageLists int g_MaxIcons = DEF_MAX_ICONS; // panic limit for icons in cache int g_lrFlags = 0; int g_ccIconDEBUG = -1; int g_resDEBUG = -1; int GetRegInt(HKEY hk, LPCTSTR szKey, int def) { TCHAR ach[20]; DWORD cb = sizeof(ach); if (ERROR_SUCCESS == SHQueryValueEx(hk, szKey, NULL, NULL, (LPBYTE)ach, &cb) && (ach[0] >= TEXT('0') && ach[0] <= TEXT('9'))) { return (int)StrToLong(ach); } else return def; } int _GetMetricsRegInt(LPCTSTR pszKey, int iDefault) { HKEY hkey; if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, REGSTR_PATH_METRICS, NULL, KEY_QUERY_VALUE, &hkey)) { iDefault = GetRegInt(hkey, pszKey, iDefault); RegCloseKey(hkey); } return iDefault; } typedef void (*PSIZECALLBACK)(SIZE *psize); void WINAPI _GetLargeIconSizeCB(SIZE *psize) { int cxIcon = GetSystemMetrics(SM_CXICON); // // get the user prefered icon size from the registry. // cxIcon = _GetMetricsRegInt(TEXT("Shell Icon Size"), cxIcon); psize->cx = psize->cy = cxIcon; } void WINAPI _GetSmallIconSizeCB(SIZE *psize) { int cxIcon = GetSystemMetrics(SM_CXICON)/2; // // get the user prefered icon size from the registry. // cxIcon = _GetMetricsRegInt(TEXT("Shell Small Icon Size"), cxIcon); psize->cx = psize->cy = cxIcon; } void WINAPI _GetSysSmallIconSizeCB(SIZE *psize) { psize->cx = GetSystemMetrics(SM_CXSMICON); psize->cy = GetSystemMetrics(SM_CYSMICON); } void WINAPI _GetXLIconSizeCB(SIZE *psize) { psize->cx = 3 * GetSystemMetrics(SM_CXICON) / 2; psize->cy = 3 * GetSystemMetrics(SM_CYICON) / 2; } static const PSIZECALLBACK c_rgSizeCB[SHIL_COUNT] = { _GetLargeIconSizeCB, // SHIL_LARGE _GetSmallIconSizeCB, // SHIL_SMALL _GetXLIconSizeCB, // SHIL_EXTRALARGE _GetSysSmallIconSizeCB, // SHIL_SYSSMALL }; EXTERN_C SHIMAGELIST g_rgshil[SHIL_COUNT] = {0}; BOOL _IsSHILInited() { #ifdef DEBUG for (int i = 0; i < ARRAYSIZE(g_rgshil); i++) { // If allocation of any one image list failed, all should be NULL. So // make sure they're either all NULL or all non-NULL. ASSERTMSG((g_rgshil[0].himl == NULL) == (g_rgshil[i].himl == NULL), "_IsSHILInited: g_rgshil is inconsistent. g_rgshil[0].himl %x, g_rgshil[%x].himl %x", g_rgshil[0].himl, i, g_rgshil[i].himl); } #endif return (g_rgshil[0].himl != NULL); } int _GetSHILImageCount() { #ifdef DEBUG for (int i = 0; i < ARRAYSIZE(g_rgshil); i++) { // If insertion of an image into one image list failed, insertion of an // image into all image lists should have failed. So make sure the image // counts are all the same. ASSERTMSG(ImageList_GetImageCount(g_rgshil[0].himl) == ImageList_GetImageCount(g_rgshil[i].himl), "_GetSHILImageCount: g_rgshil is inconsistent. image counts don't line up."); } #endif return ImageList_GetImageCount(g_rgshil[0].himl); } // // System imagelist - Don't change the order of this list. // If you need to add a new icon, add it to the end of the // array, and update shellp.h. // EXTERN_C UINT const c_SystemImageListIndexes[] = { IDI_DOCUMENT, IDI_DOCASSOC, IDI_APP, IDI_FOLDER, IDI_FOLDEROPEN, IDI_DRIVE525, IDI_DRIVE35, IDI_DRIVEREMOVE, IDI_DRIVEFIXED, IDI_DRIVENET, IDI_DRIVENETDISABLED, IDI_DRIVECD, IDI_DRIVERAM, IDI_WORLD, IDI_NETWORK, IDI_SERVER, IDI_PRINTER, IDI_MYNETWORK, IDI_GROUP, IDI_STPROGS, IDI_STDOCS, IDI_STSETNGS, IDI_STFIND, IDI_STHELP, IDI_STRUN, IDI_STSUSPEND, IDI_STEJECT, IDI_STSHUTD, IDI_SHARE, IDI_LINK, IDI_SLOWFILE, IDI_RECYCLER, IDI_RECYCLERFULL, IDI_RNA, IDI_DESKTOP, IDI_CPLFLD, IDI_STSPROGS, IDI_PRNFLD, IDI_STFONTS, IDI_STTASKBR, IDI_CDAUDIO, IDI_TREE, IDI_STCPROGS, IDI_STFAV, IDI_STLOGOFF, IDI_STFLDRPROP, IDI_WINUPDATE ,IDI_MU_SECURITY, IDI_MU_DISCONN }; // get g_MaxIcons from the registry, returning TRUE if it has changed BOOL QueryNewMaxIcons(void) { int MaxIcons = -1; HKEY hk = SHGetShellKey(SHELLKEY_HKLM_EXPLORER, NULL, FALSE); if (hk) { MaxIcons = GetRegInt(hk, TEXT("Max Cached Icons"), DEF_MAX_ICONS); RegCloseKey(hk); } if (MaxIcons < 0) MaxIcons = DEF_MAX_ICONS; int OldMaxIcons = InterlockedExchange((LONG*)&g_MaxIcons, MaxIcons); return (OldMaxIcons != MaxIcons); } // Initializes shared resources for Shell_GetIconIndex and others STDAPI_(BOOL) FileIconInit(BOOL fRestoreCache) { BOOL fNotify = FALSE; static int s_res = 32; QueryNewMaxIcons(); // in case the size of the icon cache has changed SIZE rgsize[ARRAYSIZE(g_rgshil)]; for (int i = 0; i < ARRAYSIZE(g_rgshil); i++) { c_rgSizeCB[i](&rgsize[i]); } // // get the user prefered color depth from the registry. // int ccIcon = _GetMetricsRegInt(TEXT("Shell Icon Bpp"), 0); g_ccIconDEBUG = ccIcon; int res = (int)GetCurColorRes(); g_resDEBUG = res; if (res == 0) res = s_res; s_res = res; if (ccIcon > res) ccIcon = 0; if (res >= 24) // Match User32. They will extract 32bpp icons in 24bpp. ccIcon = 32; if (res <= 8) ccIcon = 0; // wouldn't have worked anyway ENTERCRITICAL; // // if we already have a icon cache make sure it is the right size etc. // BOOL fHadCache = _IsSHILInited(); BOOL fCacheValid = fHadCache && (ccIcon == g_ccIcon); for (int i = 0; fCacheValid && i < ARRAYSIZE(g_rgshil); i++) { if (g_rgshil[i].size.cx != rgsize[i].cx || g_rgshil[i].size.cy != rgsize[i].cy) { fCacheValid = FALSE; } } if (!fCacheValid) { fNotify = fHadCache; FlushIconCache(); FlushFileClass(); // if we are the desktop process (explorer.exe), then force us to re-init the cache, so we get // the basic set of icons in the right order.... if (!fRestoreCache && _IsSHILInited() && IsWindowInProcess(GetShellWindow())) { fRestoreCache = TRUE; } for (int i = 0; i < ARRAYSIZE(g_rgshil); i++) { g_rgshil[i].size.cx = rgsize[i].cx; g_rgshil[i].size.cy = rgsize[i].cy; } g_ccIcon = ccIcon; if (res > 4 && g_ccIcon <= 4) g_lrFlags = LR_VGACOLOR; else g_lrFlags = 0; if (g_iLastSysIcon == 0) // Keep track of which icons are perm. { if (fRestoreCache) g_iLastSysIcon = II_LASTSYSICON; else g_iLastSysIcon = (II_OVERLAYLAST - II_OVERLAYFIRST) + 1; } // // if // 1) we already have the icon cache but want to flush and re-initialize it because of size/color depth change, or // 2) we don't have icon cache but want to initialize it, instead of restoring it from disk, or // 3) we failed to restore icon cache from disk // then, initialize the icon cache with c_SystemImageListIndexes // if (_IsSHILInited() || !fRestoreCache || !IconCacheRestore(rgsize, g_ccIcon)) { fCacheValid = _ShellImageListInit(g_ccIcon, fRestoreCache); } else { fCacheValid = TRUE; } } LEAVECRITICAL; if (fCacheValid && fNotify) { SHChangeNotify(SHCNE_UPDATEIMAGE, SHCNF_DWORD, (LPCVOID)-1, NULL); } return fCacheValid; } void _ShellImageListTerm() { ASSERTCRITICAL; for (int i = 0; i < ARRAYSIZE(g_rgshil); i++) { if (g_rgshil[i].himl) { ImageList_Destroy(g_rgshil[i].himl); g_rgshil[i].himl = NULL; } } } void FileIconTerm() { ENTERCRITICAL; _ShellImageListTerm(); LEAVECRITICAL; } void _DestroyIcons(HICON *phicons, int cIcons) { for (int i = 0; i < cIcons; i++) { if (phicons[i]) { DestroyIcon(phicons[i]); phicons[i] = NULL; } } } BOOL _ShellImageListInit(UINT flags, BOOL fRestore) { ASSERTCRITICAL; // // Check if we need to create a mirrored imagelist. [samera] // if (IS_BIDI_LOCALIZED_SYSTEM()) { flags |= ILC_MIRROR; } BOOL fFailedAlloc = FALSE; for (int i = 0; i < ARRAYSIZE(g_rgshil); i++) { if (g_rgshil[i].himl == NULL) { g_rgshil[i].himl = ImageList_Create(g_rgshil[i].size.cx, g_rgshil[i].size.cy, ILC_MASK|ILC_SHARED|flags, 0, 32); fFailedAlloc |= (g_rgshil[i].himl == NULL); } else { // set the flags incase the colour depth has changed... // ImageList_setFlags already calls ImageList_remove on success if (!ImageList_SetFlags(g_rgshil[i].himl, ILC_MASK|ILC_SHARED|flags)) { // Couldn't change flags; tough. At least remove them all. ImageList_Remove(g_rgshil[i].himl, -1); } ImageList_SetIconSize(g_rgshil[i].himl, g_rgshil[i].size.cx, g_rgshil[i].size.cy); } // set the bk colors to COLOR_WINDOW since this is what will // be used most of the time as the bk for these lists (cabinet, tray) // this avoids having to do ROPs when drawing, thus making it fast if (g_rgshil[i].himl) { ImageList_SetBkColor(g_rgshil[i].himl, GetSysColor(COLOR_WINDOW)); } } // If any imagelist allocation failed, fail the whole initialization if (fFailedAlloc) { _ShellImageListTerm(); return FALSE; } else { // Load all of the icons with fRestore == TRUE if (fRestore) { TCHAR szModule[MAX_PATH]; HKEY hkeyIcons; GetModuleFileName(HINST_THISDLL, szModule, ARRAYSIZE(szModule)); // WARNING: this code assumes that these icons are the first in // our RC file and are in this order and these indexes correspond // to the II_ constants in shell.h. hkeyIcons = SHGetShellKey(SHELLKEY_HKLM_EXPLORER, TEXT("Shell Icons"), FALSE); for (i = 0; i < ARRAYSIZE(c_SystemImageListIndexes); i++) { HICON rghicon[ARRAYSIZE(g_rgshil)] = {0}; // check to see if icon is overridden in the registry if (hkeyIcons) { TCHAR val[12]; TCHAR ach[MAX_PATH]; DWORD cb = sizeof(ach); StringCchPrintf(val, ARRAYSIZE(val), TEXT("%d"), i); // ok to truncate ach[0] = 0; SHQueryValueEx(hkeyIcons, val, NULL, NULL, (LPBYTE)ach, &cb); if (ach[0]) { int iIcon = PathParseIconLocation(ach); for (int j = 0; j < ARRAYSIZE(g_rgshil); j++) { ExtractIcons(ach, iIcon, g_rgshil[j].size.cx, g_rgshil[j].size.cy, &rghicon[j], NULL, 1, g_lrFlags); } } } // if we got a large icon, run with that for everyone. otherwise fall back to loadimage. if (rghicon[SHIL_LARGE] == NULL) { for (int j = 0; j < ARRAYSIZE(rghicon); j++) { if (rghicon[j] == NULL) { rghicon[j] = (HICON)LoadImage(HINST_THISDLL, MAKEINTRESOURCE(c_SystemImageListIndexes[i]), IMAGE_ICON, g_rgshil[j].size.cx, g_rgshil[j].size.cy, g_lrFlags); } } } int iIndex = SHAddIconsToCache(rghicon, szModule, i, 0); ASSERT(iIndex == i || iIndex == -1); // assume index _DestroyIcons(rghicon, ARRAYSIZE(rghicon)); if (iIndex == -1) { fFailedAlloc = TRUE; break; } } if (hkeyIcons) RegCloseKey(hkeyIcons); if (fFailedAlloc) { FlushIconCache(); _ShellImageListTerm(); return FALSE; } } // // Refresh the overlay image so that the overlays are added to the imaglist. // GetIconOverlayManager() will initialize the overlay manager if necessary. // IShellIconOverlayManager *psiom; if (SUCCEEDED(GetIconOverlayManager(&psiom))) { psiom->RefreshOverlayImages(SIOM_OVERLAYINDEX | SIOM_ICONINDEX); psiom->Release(); } return TRUE; } } // get a hold of the system image lists BOOL WINAPI Shell_GetImageLists(HIMAGELIST *phiml, HIMAGELIST *phimlSmall) { if (!_IsSHILInited()) { FileIconInit(FALSE); // make sure they are created and the right size. if (!_IsSHILInited()) return FALSE; } if (phiml) *phiml = g_rgshil[SHIL_LARGE].himl; if (phimlSmall) *phimlSmall = g_rgshil[SHIL_SMALL].himl; return TRUE; } HRESULT SHGetImageList(int iImageList, REFIID riid, void **ppvObj) { HRESULT hr = E_OUTOFMEMORY; if (!_IsSHILInited()) { FileIconInit(FALSE); // make sure they are created and the right size. if (!_IsSHILInited()) return hr; } ENTERCRITICAL; if (iImageList >=0 && iImageList < ARRAYSIZE(g_rgshil)) { hr = HIMAGELIST_QueryInterface(g_rgshil[iImageList].himl, riid, ppvObj); } else { hr = E_INVALIDARG; } LEAVECRITICAL; return hr; } void WINAPI Shell_SysColorChange(void) { COLORREF clrWindow = GetSysColor(COLOR_WINDOW); ENTERCRITICAL; for (int i = 0; i < ARRAYSIZE(g_rgshil); i++) { ImageList_SetBkColor(g_rgshil[i].himl, clrWindow); } LEAVECRITICAL; } // simulate the document icon by crunching a copy of an icon and putting it in the // middle of our default document icon, then add it to the passsed image list // // in: // hIcon icon to use as a basis for the simulation // // returns: // hicon HBITMAP CreateDIB(HDC h, WORD depth, int cx, int cy, RGBQUAD** pprgb) { BITMAPINFO bi = {0}; bi.bmiHeader.biSize = sizeof(bi.bmiHeader); bi.bmiHeader.biWidth = cx; bi.bmiHeader.biHeight = cy; bi.bmiHeader.biPlanes = 1; bi.bmiHeader.biBitCount = depth; bi.bmiHeader.biCompression = BI_RGB; return CreateDIBSection(h, &bi, DIB_RGB_COLORS, (void**)pprgb, NULL, 0); } BOOL HasAlpha(RGBQUAD* prgb, int cx, int cy) { int iTotal = cx * cy; for (int i = 0; i < iTotal; i++) { if (prgb[i].rgbReserved != 0) return TRUE; } return FALSE; } void DorkAlpha(RGBQUAD* prgb, int x, int y, int cx, int cy, int cxTotal) { for (int dy = y; dy < (cy + y); dy++) { for (int dx = x; dx < (cx + x); dx++) { prgb[dx + dy * cxTotal].rgbReserved = 255; } } } HICON SimulateDocIcon(HIMAGELIST himl, HICON hIcon, int cx, int cy) { if (himl == NULL || hIcon == NULL) return NULL; HDC hdc = GetDC(NULL); if (hdc) { RGBQUAD* prgb; // If the display is in 24 or 32bpp mode, we may have alpha icons, so we'll need to create a dib section BOOL fAlphaIcon = (GetDeviceCaps(hdc, BITSPIXEL) >= 24)? TRUE: FALSE; HBITMAP hbmColor; if (fAlphaIcon) { hbmColor = CreateDIB(hdc, 32, cx, cy, &prgb); } else { hbmColor = CreateCompatibleBitmap(hdc, cx, cy); } if (hbmColor) { HBITMAP hbmMask = CreateBitmap(cx, cy, 1, 1, NULL); if (hbmMask) { HDC hdcMem = CreateCompatibleDC(hdc); if (hdcMem) { HBITMAP hbmT = (HBITMAP)SelectObject(hdcMem, hbmMask); UINT iIndex = Shell_GetCachedImageIndex(c_szShell32Dll, II_DOCNOASSOC, 0); ImageList_Draw(himl, iIndex, hdcMem, 0, 0, ILD_MASK); SelectObject(hdcMem, hbmColor); ImageList_DrawEx(himl, iIndex, hdcMem, 0, 0, 0, 0, RGB(0,0,0), CLR_DEFAULT, ILD_NORMAL); // Check to see if the parent has alpha. If so, we'll have to dork with the child's alpha later on. BOOL fParentHasAlpha = fAlphaIcon?HasAlpha(prgb, cx, cy):FALSE; HDC hdcMemChild = CreateCompatibleDC(hdcMem); if (hdcMemChild) { // Notes: // First: create a 24bpp Dibsection. We want to merge the alpha channel into the final image, // not preserve it. // Second: The document icon has "Goo" in it. We remove this goo by blitting white into it, then // merging the child bitmap HBITMAP hbmp = CreateDIB(hdc, 24, cx/2 + 2, cy/2 + 2, NULL); if (hbmp) { HBITMAP hbmpOld = (HBITMAP)SelectObject(hdcMemChild, hbmp); RECT rc; rc.left = 0; rc.top = 0; rc.right = cx/2 + 3; // Extra space to remove goo in the document icon rc.bottom = cy/2 + 3; // Fill with white. NOTE: don't use PatBlt because it actually adds an alpha channel! SHFillRectClr(hdcMemChild, &rc, RGB(255,255,255)); DrawIconEx(hdcMemChild, 1, 1, hIcon, cx/2, cy/2, 0, NULL, DI_NORMAL); BitBlt(hdcMem, cx/4-1, cy/4-1, cx/2+3, cy/2+3, hdcMemChild, 0, 0, SRCCOPY); SelectObject(hdcMemChild, hbmpOld); DeleteObject(hbmp); } DeleteDC(hdcMemChild); } if (fParentHasAlpha) { // If the parent had alpha, we need to bring the child alpha to opaqe DorkAlpha(prgb, cx/4, cy/4, cx/2, cy/2, cx); } SelectBitmap(hdcMem, hbmT); DeleteDC(hdcMem); } ICONINFO ii = {0}; ii.fIcon = TRUE; ii.hbmColor = hbmColor; ii.hbmMask = hbmMask; hIcon = CreateIconIndirect(&ii); DeleteObject(hbmMask); } DeleteObject(hbmColor); } ReleaseDC(NULL, hdc); } return hIcon; } // Check if the same number of images is present in all of the image lists. // If any of the imagelists have less icons than the others, fill the imagelist // in with the document icon to make them all consistent. // // Eg: WebZip v3.80 and v4.00 queries for the large and small image lists, // and adds 2 icons to it. However, it doesn't know to add these icons to the // newer image lists. Hence, the image lists are out of sync, and later on, // the wrong icon appears in their treeview. // // Allaire Homesite 4.5 does the same thing. void CheckConsistencyOfImageLists(void) { // This has to be done under the critical section to avoid race conditions. // Otherwise, if another thread is adding icons to the image list, // we will think it is corrupted when in fact it is just fine, and // then our attempts to repair it will corrupt it! ASSERTCRITICAL; int i, iMax = 0, iImageListsCounts[ARRAYSIZE(g_rgshil)]; BOOL bIdentical = TRUE; // Loop through all the image lists getting: // // 1) the image count for each list // 2) Compare the count against the count of the first (large) // imagelist to see if there are any differences. // 3) Determine the max number of images (in a single list) across all the image lists for (i = 0; i < ARRAYSIZE(g_rgshil); i++) { iImageListsCounts[i] = ImageList_GetImageCount (g_rgshil[i].himl); if (iImageListsCounts[i] != iImageListsCounts[0]) { bIdentical = FALSE; } if (iImageListsCounts[i] > iMax) { iMax = iImageListsCounts[i]; } } if (bIdentical) { return; } // For each imagelist, add the document icon as filler to bring it upto iMax in size for (i = 0; i < ARRAYSIZE(g_rgshil); i++) { if (iImageListsCounts[i] < iMax) { HICON hIcon = (HICON) LoadImage (HINST_THISDLL, MAKEINTRESOURCE(IDI_DOCUMENT), IMAGE_ICON, g_rgshil[i].size.cx, g_rgshil[i].size.cy, LR_DEFAULTCOLOR); if (hIcon) { while (iImageListsCounts[i] < iMax) { ImageList_ReplaceIcon (g_rgshil[i].himl, -1, hIcon); iImageListsCounts[i]++; } DestroyIcon (hIcon); } } } } // add icons to the system imagelist (icon cache) and put the location // in the location cache // // in: // hIcon, hIconSmall the icons, hIconSmall can be NULL // pszIconPath locations (for location cache) // iIconIndex index in pszIconPath (for location cache) // uIconFlags GIL_ flags (for location cahce) // returns: // location in system image list // int SHAddIconsToCache(HICON rghicon[SHIL_COUNT], LPCTSTR pszIconPath, int iIconIndex, UINT uIconFlags) { int iImage = -1; if (!_IsSHILInited()) { FileIconInit(FALSE); // make sure they are created and the right size. if (!_IsSHILInited()) return iImage; } // // NOTE: user should call SHLookupIconIndex or RemoveFromIconTable first to make sure // it isn't already in shell icon cache, or use Shell_GetCachedImageIndex to add icons to // the cache. Adding the same icon to icon cache several times may cause shell to flash. // if (!(uIconFlags & GIL_DONTCACHE)) { iImage = LookupIconIndex(pszIconPath, iIconIndex, uIconFlags); if (-1 != iImage) { return iImage; } } HICON rghiconT[ARRAYSIZE(g_rgshil)] = {0}; BOOL fFailure = FALSE; int i; for (i = 0; i < ARRAYSIZE(g_rgshil); i++) { if (rghicon == NULL) { SHDefExtractIcon(pszIconPath, iIconIndex, uIconFlags, &rghiconT[i], NULL, g_rgshil[i].size.cx); } else { if (rghicon[i]) { rghiconT[i] = rghicon[i]; } else { rghiconT[i] = rghicon[SHIL_LARGE]; } } if (rghiconT[i] == NULL) { fFailure = TRUE; break; } } ENTERCRITICAL; // test again in case there was a race between the test at the top and the // icon loading code. if (!(uIconFlags & GIL_DONTCACHE)) { iImage = LookupIconIndex(pszIconPath, iIconIndex, uIconFlags); } if (!fFailure && _IsSHILInited() && (-1 == iImage)) { // still not in the table so we CheckConsistencyOfImageLists(); int iImageFree = GetFreeImageIndex(); TraceMsg(TF_IMAGE, "FreeImageIndex = %d", iImageFree); for (i = 0; i < ARRAYSIZE(g_rgshil); i++) { int iImageT = ImageList_ReplaceIcon(g_rgshil[i].himl, iImageFree, rghiconT[i]); TraceMsg(TF_IMAGE, "ImageList_ReplaceIcon(%d) returned = %d", i, iImageT); if (iImageT < 0) { // failure -- break and undo changes break; } else { ASSERT(iImage == -1 || iImage == iImageT); iImage = iImageT; } } if (i < ARRAYSIZE(g_rgshil)) { // failure if (iImageFree == -1) { // only remove it if it was added at the end otherwise all the // index's above iImage will change. // ImageList_ReplaceIcon should only fail on the end anyway. for (int j = 0; j < i; j++) { ImageList_Remove(g_rgshil[j].himl, iImage); } } iImage = -1; } else { // success ASSERT(iImage >= 0); AddToIconTable(pszIconPath, iIconIndex, uIconFlags, iImage); } } LEAVECRITICAL; if (rghicon == NULL) { // destroy the icons we allocated _DestroyIcons(rghiconT, ARRAYSIZE(rghiconT)); } return iImage; } // // default handler to extract a icon from a file // // supports GIL_SIMULATEDOC // // returns S_OK if success // returns S_FALSE if the file has no icons (or not the asked for icon) // returns E_FAIL for files on a slow link. // returns E_FAIL if cant access the file // // LOWORD(nIconSize) = normal icon size // HIWORD(nIconSize) = smal icon size // STDAPI SHDefExtractIcon(LPCTSTR pszIconFile, int iIndex, UINT uFlags, HICON *phiconLarge, HICON *phiconSmall, UINT nIconSize) { HICON hIcons[2] = {0, 0}; UINT u; #ifdef DEBUG TCHAR ach[128]; GetModuleFileName(HINST_THISDLL, ach, ARRAYSIZE(ach)); if (lstrcmpi(pszIconFile, ach) == 0 && iIndex >= 0) { TraceMsg(TF_WARNING, "Re-extracting %d from SHELL32.DLL", iIndex); } #endif HIMAGELIST himlLarge, himlSmall; Shell_GetImageLists(&himlLarge, &himlSmall); // // get the icon from the file // if (PathIsSlow(pszIconFile, -1)) { DebugMsg(DM_TRACE, TEXT("not extracting icon from '%s' because of slow link"), pszIconFile); return E_FAIL; } #ifdef XXDEBUG TraceMsg(TF_ALWAYS, "Extracting icon %d from %s.", iIndex, pszIconFile); Sleep(500); #endif // // nIconSize == 0 means use the default size. // Backup is passing nIconSize == 1 need to support them too. // if (nIconSize <= 2) nIconSize = MAKELONG(g_cxIcon, g_cxSmIcon); if (uFlags & GIL_SIMULATEDOC) { HICON hIconSmall; u = ExtractIcons(pszIconFile, iIndex, g_cxSmIcon, g_cySmIcon, &hIconSmall, NULL, 1, g_lrFlags); if (u == -1) return E_FAIL; hIcons[0] = SimulateDocIcon(himlLarge, hIconSmall, g_cxIcon, g_cyIcon); hIcons[1] = SimulateDocIcon(himlSmall, hIconSmall, g_cxSmIcon, g_cySmIcon); if (hIconSmall) DestroyIcon(hIconSmall); } else { u = ExtractIcons(pszIconFile, iIndex, nIconSize, nIconSize, hIcons, NULL, 2, g_lrFlags); if (-1 == u) return E_FAIL; #ifdef DEBUG if (0 == u) { TraceMsg(TF_WARNING, "Failed to extract icon %d from %s.", iIndex, pszIconFile); } #endif } if (phiconLarge) *phiconLarge = hIcons[0]; else if (hIcons[0]) DestroyIcon(hIcons[0]); if (phiconSmall) *phiconSmall = hIcons[1]; else if (hIcons[1]) DestroyIcon(hIcons[1]); return u == 0 ? S_FALSE : S_OK; } #ifdef UNICODE STDAPI SHDefExtractIconA(LPCSTR pszIconFile, int iIndex, UINT uFlags, HICON *phiconLarge, HICON *phiconSmall, UINT nIconSize) { HRESULT hr = E_INVALIDARG; if (IS_VALID_STRING_PTRA(pszIconFile, -1)) { WCHAR wsz[MAX_PATH]; SHAnsiToUnicode(pszIconFile, wsz, ARRAYSIZE(wsz)); hr = SHDefExtractIcon(wsz, iIndex, uFlags, phiconLarge, phiconSmall, nIconSize); } return hr; } #else STDAPI SHDefExtractIconW(LPCWSTR pszIconFile, int iIndex, UINT uFlags, HICON *phiconLarge, HICON *phiconSmall, UINT nIconSize) { HRESULT hr = E_INVALIDARG; if (IS_VALID_STRING_PTRW(pszIconFile, -1)) { char sz[MAX_PATH]; SHUnicodeToAnsi(pszIconFile, sz, ARRAYSIZE(sz)); hr = SHDefExtractIcon(sz, iIndex, uFlags, phiconLarge, phiconSmall, nIconSize); } return hr; } #endif // // in: // pszIconPath file to get icon from (eg. cabinet.exe) // iIconIndex icon index in pszIconPath to get // uIconFlags GIL_ values indicating simulate doc icon, etc. int WINAPI Shell_GetCachedImageIndex(LPCTSTR pszIconPath, int iIconIndex, UINT uIconFlags) { // lots of random codepaths from APIs end up here before init if (!_IsSHILInited()) { FileIconInit(FALSE); if (!_IsSHILInited()) { return -1; } } int iImageIndex = LookupIconIndex(pszIconPath, iIconIndex, uIconFlags); if (iImageIndex == -1) { iImageIndex = SHAddIconsToCache(NULL, pszIconPath, iIconIndex, uIconFlags); } return iImageIndex; } STDAPI_(void) FixPlusIcons() { // nuke all of the shell internal icons HKEY hkeyIcons = SHGetShellKey(SHELLKEY_HKLM_EXPLORER, TEXT("Shell Icons"), FALSE); if (hkeyIcons) { for (int i = 0; i < ARRAYSIZE(c_SystemImageListIndexes); i++) { TCHAR szRegPath[12], szBuf[MAX_PATH]; DWORD cb = sizeof(szBuf); StringCchPrintf(szRegPath, ARRAYSIZE(szRegPath), TEXT("%d"), i); // ok to truncate if (SHQueryValueEx(hkeyIcons, szRegPath, NULL, NULL, (LPBYTE)szBuf, &cb) == ERROR_SUCCESS && StrStrI(szBuf, TEXT("cool.dll"))) { RegDeleteValue(hkeyIcons, szRegPath); } } RegCloseKey(hkeyIcons); } static const struct { const CLSID* pclsid; LPCTSTR pszIcon; } c_rgCLSID[] = { { &CLSID_NetworkPlaces, TEXT("shell32.dll,17") }, { &CLSID_ControlPanel, TEXT("shell32.dll,-137") }, { &CLSID_Printers, TEXT("shell32.dll,-138") }, { &CLSID_MyComputer, TEXT("explorer.exe,0") }, { &CLSID_Remote, TEXT("rnaui.dll,0") }, { &CLSID_CFonts, TEXT("fontext.dll,-101") }, { &CLSID_RecycleBin, NULL }, { &CLSID_Briefcase, NULL }, }; for (int i = 0; i < ARRAYSIZE(c_rgCLSID); i++) { TCHAR szCLSID[64], szRegPath[128], szBuf[MAX_PATH]; DWORD cb = sizeof(szBuf); SHStringFromGUID(*c_rgCLSID[i].pclsid, szCLSID, ARRAYSIZE(szCLSID)); StringCchPrintf(szRegPath, ARRAYSIZE(szRegPath), TEXT("CLSID\\%s\\DefaultIcon"), szCLSID); // should fit always 64 + 18 < 128 if (SHRegGetValue(HKEY_CLASSES_ROOT, szRegPath, NULL, SRRF_RT_REG_SZ, NULL, szBuf, &cb) == ERROR_SUCCESS && StrStrI(szBuf, TEXT("cool.dll"))) { if (IsEqualGUID(*c_rgCLSID[i].pclsid, CLSID_RecycleBin)) { RegSetValueString(HKEY_CLASSES_ROOT, szRegPath, TEXT("Empty"), TEXT("shell32.dll,31")); RegSetValueString(HKEY_CLASSES_ROOT, szRegPath, TEXT("Full"), TEXT("shell32.dll,32")); if (StrStr(szBuf, TEXT("20"))) RegSetString(HKEY_CLASSES_ROOT, szRegPath, TEXT("shell32.dll,31")); // empty else RegSetString(HKEY_CLASSES_ROOT, szRegPath, TEXT("shell32.dll,32")); // full } else { if (c_rgCLSID[i].pszIcon) RegSetString(HKEY_CLASSES_ROOT, szRegPath, c_rgCLSID[i].pszIcon); else RegDeleteValue(HKEY_CLASSES_ROOT, szRegPath); } } } static const struct { LPCTSTR pszProgID; LPCTSTR pszIcon; } c_rgProgID[] = { { TEXT("Folder"), TEXT("shell32.dll,3") }, { TEXT("Directory"),TEXT("shell32.dll,3") }, { TEXT("Drive"), TEXT("shell32.dll,8") }, { TEXT("drvfile"), TEXT("shell32.dll,-154") }, { TEXT("vxdfile"), TEXT("shell32.dll,-154") }, { TEXT("dllfile"), TEXT("shell32.dll,-154") }, { TEXT("sysfile"), TEXT("shell32.dll,-154") }, { TEXT("txtfile"), TEXT("shell32.dll,-152") }, { TEXT("inifile"), TEXT("shell32.dll,-151") }, { TEXT("inffile"), TEXT("shell32.dll,-151") }, }; for (i = 0; i < ARRAYSIZE(c_rgProgID); i++) { TCHAR szRegPath[128], szBuf[MAX_PATH]; DWORD cb = sizeof(szBuf); HRESULT hr = StringCchPrintf(szRegPath, ARRAYSIZE(szRegPath), TEXT("%s\\DefaultIcon"), c_rgProgID[i].pszProgID); if (SUCCEEDED(hr)) { if (SHRegGetValue(HKEY_CLASSES_ROOT, szRegPath, NULL, SRRF_RT_REG_SZ, NULL, szBuf, &cb) == ERROR_SUCCESS && StrStrI(szBuf, TEXT("cool.dll"))) { RegSetString(HKEY_CLASSES_ROOT, szRegPath, c_rgProgID[i].pszIcon); } } } FlushIconCache(); }