1181 lines
34 KiB
C++
1181 lines
34 KiB
C++
#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 (RegOpenKey(HKEY_CURRENT_USER, REGSTR_PATH_METRICS, &hkey) == 0)
|
|
{
|
|
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[10];
|
|
TCHAR ach[MAX_PATH];
|
|
DWORD cb = sizeof(ach);
|
|
|
|
wsprintf(val, TEXT("%d"), i);
|
|
|
|
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[10], szBuf[MAX_PATH];
|
|
DWORD cb = sizeof(szBuf);
|
|
|
|
wsprintf(szRegPath, TEXT("%d"), i);
|
|
|
|
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];
|
|
LONG cb = sizeof(szBuf);
|
|
|
|
SHStringFromGUID(*c_rgCLSID[i].pclsid, szCLSID, ARRAYSIZE(szCLSID));
|
|
wsprintf(szRegPath, TEXT("CLSID\\%s\\DefaultIcon"), szCLSID);
|
|
|
|
if (SHRegQueryValue(HKEY_CLASSES_ROOT, szRegPath, 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];
|
|
LONG cb = sizeof(szBuf);
|
|
|
|
wsprintf(szRegPath, TEXT("%s\\DefaultIcon"), c_rgProgID[i].pszProgID);
|
|
|
|
if (SHRegQueryValue(HKEY_CLASSES_ROOT, szRegPath, szBuf, &cb) == ERROR_SUCCESS &&
|
|
StrStrI(szBuf, TEXT("cool.dll")))
|
|
{
|
|
RegSetString(HKEY_CLASSES_ROOT, szRegPath, c_rgProgID[i].pszIcon);
|
|
}
|
|
}
|
|
|
|
FlushIconCache();
|
|
}
|