mirror of
https://github.com/Open-Shell/Open-Shell-Menu.git
synced 2026-04-11 17:37:22 +10:00
When `Invert Metro icon color` is enabled `Open-Shell` needs to distinguish full color icons from monochromatic ones. Color icons are displayed normally. Monochromatic ones are displayed inverted (original background becomes transparent and foreground will get Metro accent color). Metro icon is loaded from Metro app resources (usually PNG image) as bitmap with premultiplied alpha channel. This causes monochromatic image to essentially become grayscale (because RGB values are multiplied with alpha channel value). Function `DetectGrayscaleImage` is used to distinguish such images. Unfortunatelly if original image is grayscale (such as new Windows Terminal icon) it is recognized as monochromatic and thus inverted. To prevent this we will take into account also alpha channel in `DetectGrayscaleImage`. In monochromatic images alpha channel value will be the same as the rest of channel values. For color images alpha channel value will be different than other channel values. Fixes #364.
3816 lines
112 KiB
C++
3816 lines
112 KiB
C++
// Classic Shell (c) 2009-2017, Ivo Beltchev
|
|
// Open-Shell (c) 2017-2018, The Open-Shell Team
|
|
// Confidential information of Ivo Beltchev. Not for disclosure or distribution without prior written consent from the author
|
|
|
|
#include "stdafx.h"
|
|
#include "ItemManager.h"
|
|
#include "MetroLinkManager.h"
|
|
#include "FNVHash.h"
|
|
#include "Settings.h"
|
|
#include "SettingsUI.h"
|
|
#include "Translations.h"
|
|
#include "ResourceHelper.h"
|
|
#include "MenuContainer.h"
|
|
#include "LogManager.h"
|
|
#include "StartMenuDLL.h"
|
|
#include "resource.h"
|
|
#include <propkey.h>
|
|
#include <CommonControls.h>
|
|
#include <wincodec.h>
|
|
#include <algorithm>
|
|
|
|
//#define DISABLE_CACHE
|
|
//#define FORCE_ALL_NEW
|
|
|
|
#ifdef BUILD_SETUP
|
|
#undef DISABLE_CACHE
|
|
#undef FORCE_ALL_NEW
|
|
#endif
|
|
|
|
#ifdef _DEBUG
|
|
_declspec(thread) int CItemManager::RWLock::g_LockState[CItemManager::RWLOCK_COUNT]; // 0 - none, 1 - read, 2 - write
|
|
#endif
|
|
|
|
static bool g_bInvertMetroIcons;
|
|
|
|
const int MAX_FOLDER_LEVELS=10; // don't go more than 10 levels deep
|
|
const int REFRESH_DELAY=5000;
|
|
const int CACHE_FILE_VERSION=2;
|
|
|
|
PROPERTYKEY PKEY_MetroIcon={{0x86D40B4D, 0x9069, 0x443C, {0x81, 0x9A, 0x2A, 0x54, 0x09, 0x0D, 0xCC, 0xEC}}, 2};
|
|
|
|
// app ID resolver interface as described here: http://www.binrand.com/post/1510934-out-using-system-using-system-collections-generic-using-system.html
|
|
interface IApplicationResolver: public IUnknown
|
|
{
|
|
STDMETHOD(GetAppIDForShortcut)( IShellItem *psi, LPWSTR *ppszAppID );
|
|
// .... we don't care about the rest of the methods ....
|
|
};
|
|
|
|
GUID CLSID_ApplicationResolver={0x660b90c8,0x73a9,0x4b58,{0x8c,0xae,0x35,0x5b,0x7f,0x55,0x34,0x1b}};
|
|
// different IIDs for Win8 and Win8: http://a-whiter.livejournal.com/1266.html
|
|
GUID IID_IApplicationResolver7={0x46a6eeff,0x908e,0x4dc6,{0x92,0xa6,0x64,0xbe,0x91,0x77,0xb4,0x1c}};
|
|
GUID IID_IApplicationResolver8={0xde25675a,0x72de,0x44b4,{0x93,0x73,0x05,0x17,0x04,0x50,0xc1,0x40}};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
interface IResourceContext;
|
|
|
|
const GUID IID_IResourceMap={0x6e21e72b, 0xb9b0, 0x42ae, {0xa6, 0x86, 0x98, 0x3c, 0xf7, 0x84, 0xed, 0xcd}};
|
|
interface IResourceMap : public IUnknown
|
|
{
|
|
virtual HRESULT STDMETHODCALLTYPE GetUri(const wchar_t **pUri ) = 0;
|
|
virtual HRESULT STDMETHODCALLTYPE GetSubtree(const wchar_t *propName, IResourceMap **pSubTree ) = 0;
|
|
virtual HRESULT STDMETHODCALLTYPE GetString( const wchar_t *propName, wchar_t *pString ) = 0;
|
|
virtual HRESULT STDMETHODCALLTYPE GetStringForContext( IResourceContext *pContext, const wchar_t *propName, wchar_t *pString ) = 0;
|
|
virtual HRESULT STDMETHODCALLTYPE GetFilePath( const wchar_t *propName, wchar_t **pPath ) = 0;
|
|
virtual HRESULT STDMETHODCALLTYPE GetFilePathForContext( IResourceContext *pContext, const wchar_t *propName, wchar_t **pPath ) = 0;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
enum RESOURCE_SCALE
|
|
{
|
|
RES_SCALE_100=0,
|
|
RES_SCALE_140=1,
|
|
RES_SCALE_180=2,
|
|
RES_SCALE_80 =3,
|
|
};
|
|
|
|
const GUID IID_ResourceContext={0xe3c22b30, 0x8502, 0x4b2f, {0x91, 0x33, 0x55, 0x96, 0x74, 0x58, 0x7e, 0x51}};
|
|
interface IResourceContext : public IUnknown
|
|
{
|
|
virtual HRESULT STDMETHODCALLTYPE GetLanguage( void ) = 0;
|
|
virtual HRESULT STDMETHODCALLTYPE GetHomeRegion( wchar_t *pRegion ) = 0;
|
|
virtual HRESULT STDMETHODCALLTYPE GetLayoutDirection( enum RESOURCE_LAYOUT_DIRECTION *pDirection ) = 0;
|
|
virtual HRESULT STDMETHODCALLTYPE GetTargetSize( WORD *pSize ) = 0;
|
|
virtual HRESULT STDMETHODCALLTYPE GetScale( RESOURCE_SCALE *pScale ) = 0;
|
|
virtual HRESULT STDMETHODCALLTYPE GetContrast( enum RESOURCE_CONTRAST *pContrast ) = 0;
|
|
virtual HRESULT STDMETHODCALLTYPE GetAlternateForm( wchar_t *pForm ) = 0;
|
|
virtual HRESULT STDMETHODCALLTYPE GetQualifierValue( const wchar_t *name, wchar_t *pValue ) = 0;
|
|
virtual HRESULT STDMETHODCALLTYPE SetLanguage( const wchar_t *language ) = 0;
|
|
virtual HRESULT STDMETHODCALLTYPE SetHomeRegion( const wchar_t *region ) = 0;
|
|
virtual HRESULT STDMETHODCALLTYPE SetLayoutDirection( enum RESOURCE_LAYOUT_DIRECTION direction ) = 0;
|
|
virtual HRESULT STDMETHODCALLTYPE SetTargetSize( WORD size ) = 0;
|
|
virtual HRESULT STDMETHODCALLTYPE SetScale( RESOURCE_SCALE scale ) = 0;
|
|
virtual HRESULT STDMETHODCALLTYPE SetContrast( void ) = 0;
|
|
virtual HRESULT STDMETHODCALLTYPE SetAlternateForm( const wchar_t *form ) = 0;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
const CLSID CLSID_ResourceManager={0xdbce7e40, 0x7345, 0x439d, {0xb1, 0x2c, 0x11, 0x4a, 0x11, 0x81, 0x9a, 0x09}};
|
|
MIDL_INTERFACE("130a2f65-2be7-4309-9a58-a9052ff2b61c")
|
|
IResourceManager : public IUnknown
|
|
{
|
|
public:
|
|
virtual HRESULT STDMETHODCALLTYPE Initialize( void ) = 0;
|
|
virtual HRESULT STDMETHODCALLTYPE InitializeForCurrentApplication( void ) = 0;
|
|
virtual HRESULT STDMETHODCALLTYPE InitializeForPackage( const wchar_t *name ) = 0;
|
|
virtual HRESULT STDMETHODCALLTYPE InitializeForFile( const wchar_t *fname ) = 0;
|
|
virtual HRESULT STDMETHODCALLTYPE GetMainResourceMap( REFIID riid, void **ppvObject ) = 0;
|
|
virtual HRESULT STDMETHODCALLTYPE GetResourceMap( void ) = 0;
|
|
virtual HRESULT STDMETHODCALLTYPE GetDefaultContext( REFIID riid, void **ppvObject ) = 0;
|
|
virtual HRESULT STDMETHODCALLTYPE GetReference( void ) = 0;
|
|
};
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
extern PROPERTYKEY PKEY_MetroPackagePath;
|
|
extern PROPERTYKEY PKEY_MetroPackageName;
|
|
|
|
static CComPtr<IApplicationResolver> g_pAppResolver;
|
|
|
|
// Creates the app id resolver object
|
|
static void CreateAppResolver( void )
|
|
{
|
|
if (GetWinVersion()>=WIN_VER_WIN7)
|
|
{
|
|
CComPtr<IUnknown> pUnknown;
|
|
pUnknown.CoCreateInstance(CLSID_ApplicationResolver);
|
|
if (GetWinVersion()==WIN_VER_WIN7)
|
|
g_pAppResolver=CComQIPtr<IApplicationResolver,&IID_IApplicationResolver7>(pUnknown);
|
|
else
|
|
g_pAppResolver=CComQIPtr<IApplicationResolver,&IID_IApplicationResolver8>(pUnknown);
|
|
}
|
|
}
|
|
|
|
static bool DetectGrayscaleImage( const unsigned int *bits, int stride, int width, int height )
|
|
{
|
|
int transparent=0;
|
|
for (int y=0;y<height;y++,bits+=stride)
|
|
{
|
|
for (int x=0;x<width;x++)
|
|
{
|
|
unsigned int pixel=bits[x];
|
|
int a=(pixel>>24)&255;
|
|
int r=(pixel>>16)&255;
|
|
int g=(pixel>>8)&255;
|
|
int b=(pixel)&255;
|
|
if (abs(a-r)>2 || abs(r-g)>2 || abs(r-b)>2 || abs(g-b)>2)
|
|
return false; // found colored pixel
|
|
if (!(pixel&0xFF000000))
|
|
transparent++;
|
|
}
|
|
}
|
|
if ((transparent*100)/(width*height)<5)
|
|
return false; // less than 5% transparent pixels
|
|
return true;
|
|
}
|
|
|
|
static void CreateMonochromeImage( unsigned int *bits, int stride, int width, int height, DWORD metroColor )
|
|
{
|
|
int r0=(metroColor)&255;
|
|
int g0=(metroColor>>8)&255;
|
|
int b0=(metroColor>>16)&255;
|
|
for (int y=0;y<height;y++,bits+=stride)
|
|
{
|
|
for (int x=0;x<width;x++)
|
|
{
|
|
unsigned int &pixel=bits[x];
|
|
int a=pixel>>24;
|
|
int r=(r0*a)/255;
|
|
int g=(g0*a)/255;
|
|
int b=(b0*a)/255;
|
|
pixel=(a<<24)|(r<<16)|(g<<8)|b;
|
|
}
|
|
}
|
|
}
|
|
|
|
static HBITMAP BitmapFromMetroIcon( HICON hIcon, int bitmapSize, int iconSize, DWORD metroColor, bool bDestroyIcon=true )
|
|
{
|
|
ICONINFO info;
|
|
BITMAP bmpInfo;
|
|
GetIconInfo(hIcon,&info);
|
|
AddTrackedObject(info.hbmColor);
|
|
AddTrackedObject(info.hbmMask);
|
|
if (info.hbmColor)
|
|
{
|
|
GetObject(info.hbmColor,sizeof(bmpInfo),&bmpInfo);
|
|
iconSize=bmpInfo.bmWidth;
|
|
if (iconSize>bitmapSize)
|
|
iconSize=bitmapSize;
|
|
}
|
|
if (info.hbmColor) DeleteObject(info.hbmColor);
|
|
if (info.hbmMask) DeleteObject(info.hbmMask);
|
|
|
|
BITMAPINFO bi={0};
|
|
bi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
|
|
bi.bmiHeader.biWidth=bi.bmiHeader.biHeight=bitmapSize;
|
|
bi.bmiHeader.biPlanes=1;
|
|
bi.bmiHeader.biBitCount=32;
|
|
RECT rc={0,0,bitmapSize,bitmapSize};
|
|
|
|
HDC hdc=CreateCompatibleDC(NULL);
|
|
unsigned int *bits;
|
|
HBITMAP bmp=CreateDIBSection(hdc,&bi,DIB_RGB_COLORS,(void**)&bits,NULL,0);
|
|
HGDIOBJ bmp0=SelectObject(hdc,bmp);
|
|
int offset=(bitmapSize-iconSize)/2;
|
|
bool bInvert=g_bInvertMetroIcons;
|
|
if (g_bInvertMetroIcons)
|
|
{
|
|
FillRect(hdc,&rc,(HBRUSH)GetStockObject(BLACK_BRUSH));
|
|
DrawIconEx(hdc,offset,offset,hIcon,iconSize,iconSize,0,NULL,DI_NORMAL);
|
|
SelectObject(hdc,bmp0);
|
|
unsigned int *bits2=bits+offset*(bitmapSize+1);
|
|
if (DetectGrayscaleImage(bits2,bitmapSize,iconSize,iconSize))
|
|
{
|
|
CreateMonochromeImage(bits2,bitmapSize,iconSize,iconSize,metroColor);
|
|
}
|
|
else
|
|
{
|
|
SelectObject(hdc,bmp);
|
|
bInvert=false;
|
|
}
|
|
}
|
|
|
|
if (!bInvert)
|
|
{
|
|
SetDCBrushColor(hdc,metroColor&0xFFFFFF);
|
|
FillRect(hdc,&rc,(HBRUSH)GetStockObject(DC_BRUSH));
|
|
DrawIconEx(hdc,offset,offset,hIcon,iconSize,iconSize,0,NULL,DI_NORMAL);
|
|
SelectObject(hdc,bmp0);
|
|
int n=bitmapSize*bitmapSize;
|
|
for (int i=0;i<n;i++)
|
|
bits[i]|=0xFF000000;
|
|
}
|
|
DeleteDC(hdc);
|
|
|
|
if (bDestroyIcon) DestroyIcon(hIcon);
|
|
return bmp;
|
|
}
|
|
|
|
static HBITMAP BitmapFromMetroBitmap( HBITMAP hBitmap, int bitmapSize, DWORD metroColor, bool bDestroyBitmap=true )
|
|
{
|
|
BITMAP info;
|
|
GetObject(hBitmap,sizeof(info),&info);
|
|
bool bGrayscale=DetectGrayscaleImage((const unsigned int*)info.bmBits,info.bmWidth,info.bmWidth,info.bmHeight);
|
|
|
|
BITMAPINFO bi={0};
|
|
bi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
|
|
bi.bmiHeader.biWidth=bi.bmiHeader.biHeight=bitmapSize;
|
|
bi.bmiHeader.biPlanes=1;
|
|
bi.bmiHeader.biBitCount=32;
|
|
RECT rc={0,0,bitmapSize,bitmapSize};
|
|
|
|
HDC hdc=CreateCompatibleDC(NULL);
|
|
HDC hsrc=CreateCompatibleDC(NULL);
|
|
unsigned int *bits;
|
|
HBITMAP bmp=CreateDIBSection(hdc,&bi,DIB_RGB_COLORS,(void**)&bits,NULL,0);
|
|
HGDIOBJ bmp0=SelectObject(hdc,bmp);
|
|
HGDIOBJ bmp02=SelectObject(hsrc,hBitmap);
|
|
int offset=(bitmapSize-info.bmWidth)/2;
|
|
bool bInvert=g_bInvertMetroIcons;
|
|
if (g_bInvertMetroIcons && bGrayscale)
|
|
{
|
|
FillRect(hdc,&rc,(HBRUSH)GetStockObject(BLACK_BRUSH));
|
|
BitBlt(hdc,offset,offset,info.bmWidth,info.bmHeight,hsrc,0,0,SRCCOPY);
|
|
SelectObject(hdc,bmp0);
|
|
unsigned int *bits2=bits+offset*(bitmapSize+1);
|
|
CreateMonochromeImage(bits2,bitmapSize,info.bmWidth,info.bmHeight,metroColor);
|
|
}
|
|
else
|
|
{
|
|
if (metroColor!=0xFFFFFFFF && bGrayscale)
|
|
{
|
|
SetDCBrushColor(hdc,metroColor&0xFFFFFF);
|
|
FillRect(hdc,&rc,(HBRUSH)GetStockObject(DC_BRUSH));
|
|
BLENDFUNCTION func={AC_SRC_OVER,0,255,AC_SRC_ALPHA};
|
|
AlphaBlend(hdc,offset,offset,info.bmWidth,info.bmHeight,hsrc,0,0,info.bmWidth,info.bmHeight,func);
|
|
SelectObject(hdc,bmp0);
|
|
|
|
int n=bitmapSize*bitmapSize;
|
|
for (int i=0;i<n;i++)
|
|
bits[i]|=0xFF000000;
|
|
}
|
|
else
|
|
{
|
|
int n=bitmapSize*bitmapSize;
|
|
for (int i=0;i<n;i++)
|
|
bits[i]=0;
|
|
BitBlt(hdc,offset,offset,info.bmWidth,info.bmHeight,hsrc,0,0,SRCCOPY);
|
|
}
|
|
}
|
|
SelectObject(hsrc,bmp02);
|
|
DeleteDC(hdc);
|
|
DeleteDC(hsrc);
|
|
|
|
if (bDestroyBitmap) DeleteObject(hBitmap);
|
|
return bmp;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
static HBITMAP LoadMetroBitmap0( const wchar_t *path, int bitmapSize, DWORD metroColor )
|
|
{
|
|
int iconSize=g_bInvertMetroIcons?bitmapSize:(bitmapSize-2);
|
|
SIZE size={-iconSize,iconSize};
|
|
HBITMAP hBitmap=LoadImageFile(path,&size,true,true,NULL);
|
|
if (hBitmap)
|
|
{
|
|
if (metroColor==0xFFFFFFFF)
|
|
return hBitmap;
|
|
return BitmapFromMetroBitmap(hBitmap,bitmapSize,metroColor);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
static bool ParseMetroBitmapLocation( wchar_t *location )
|
|
{
|
|
CharUpper(location);
|
|
while (1)
|
|
{
|
|
wchar_t *scale=wcswcs(location,L"SCALE-");
|
|
if (!scale) return false;
|
|
if (scale==location || scale[-1]=='\\' || scale[-1]=='.')
|
|
{
|
|
scale+=6;
|
|
if (scale[0]>='0' && scale[0]<='9' && scale[1]>='0' && scale[1]<='9')
|
|
{
|
|
int digits=2;
|
|
while (scale[digits]>='0' && scale[digits]<='9')
|
|
digits++;
|
|
scale[0]='%';
|
|
scale[1]='d';
|
|
if (digits>2)
|
|
memmove(scale+2,scale+digits,Strlen(scale+digits)*2+2);
|
|
return true;
|
|
}
|
|
}
|
|
else
|
|
scale+=6;
|
|
location=scale;
|
|
}
|
|
}
|
|
|
|
static int g_MetroIconScales[]={80,100,140,180}; // 24, 30, 42, 54 pixels
|
|
|
|
static HBITMAP LoadMetroBitmap( const wchar_t *location, int bitmapSize, DWORD metroColor )
|
|
{
|
|
// pick the best sized icon
|
|
int start;
|
|
if (bitmapSize<=26)
|
|
start=0;
|
|
else if (bitmapSize<=32)
|
|
start=1;
|
|
else if (bitmapSize<=44)
|
|
start=2;
|
|
else
|
|
start=3;
|
|
wchar_t path[_MAX_PATH];
|
|
bool bFound=false;
|
|
for (int i=start;i<_countof(g_MetroIconScales);i++) // correct size or bigger (will scale down)
|
|
{
|
|
Sprintf(path,_countof(path),location,g_MetroIconScales[i]);
|
|
if (GetFileAttributes(path)!=INVALID_FILE_ATTRIBUTES)
|
|
{
|
|
bFound=true;
|
|
break;
|
|
}
|
|
}
|
|
if (!bFound)
|
|
{
|
|
for (int i=start-1;i>=0;i--) // smaller (will scale up)
|
|
{
|
|
Sprintf(path,_countof(path),location,g_MetroIconScales[i]);
|
|
if (GetFileAttributes(path)!=INVALID_FILE_ATTRIBUTES)
|
|
{
|
|
bFound=true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (bFound)
|
|
{
|
|
SIZE size={2-bitmapSize,bitmapSize-2};
|
|
HBITMAP hBitmap=LoadImageFile(path,&size,true,true,NULL);
|
|
if (hBitmap)
|
|
return BitmapFromMetroBitmap(hBitmap,bitmapSize,metroColor);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
static bool ParseMetroBitmapLocation2( wchar_t *location )
|
|
{
|
|
CharUpper(location);
|
|
wchar_t *png=wcswcs(location,L".PNG-");
|
|
if (!png) return false;
|
|
Strcpy(png,100,L".TARGETSIZE-%d.PNG");
|
|
return true;
|
|
}
|
|
|
|
static int g_MetroIconSizes2[]={16,20,24,32,40,48,64};
|
|
|
|
static HBITMAP LoadMetroBitmap2( const wchar_t *location, int bitmapSize, DWORD metroColor )
|
|
{
|
|
// pick the best sized icon
|
|
int start=-1;
|
|
for (int i=0;i<_countof(g_MetroIconSizes2);i++)
|
|
{
|
|
if (g_MetroIconSizes2[i]>=bitmapSize)
|
|
break;
|
|
start++;
|
|
}
|
|
if (start<0) start=0;
|
|
wchar_t path[_MAX_PATH];
|
|
int iconSize=0;
|
|
for (int i=start;i<_countof(g_MetroIconSizes2);i++) // correct size or bigger (will scale down)
|
|
{
|
|
Sprintf(path,_countof(path),location,g_MetroIconSizes2[i]);
|
|
if (GetFileAttributes(path)!=INVALID_FILE_ATTRIBUTES)
|
|
{
|
|
iconSize=g_MetroIconSizes2[i];
|
|
break;
|
|
}
|
|
}
|
|
if (!iconSize)
|
|
{
|
|
for (int i=start-1;i>=0;i--) // smaller (will scale up)
|
|
{
|
|
Sprintf(path,_countof(path),location,g_MetroIconSizes2[i]);
|
|
if (GetFileAttributes(path)!=INVALID_FILE_ATTRIBUTES)
|
|
{
|
|
iconSize=g_MetroIconSizes2[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (iconSize)
|
|
{
|
|
if (g_bInvertMetroIcons)
|
|
{
|
|
if (iconSize>bitmapSize)
|
|
iconSize=bitmapSize;
|
|
}
|
|
else
|
|
{
|
|
if (iconSize>bitmapSize-2)
|
|
iconSize=bitmapSize-2;
|
|
}
|
|
SIZE size={iconSize,iconSize};
|
|
HBITMAP hBitmap=LoadImageFile(path,&size,true,true,NULL);
|
|
if (hBitmap)
|
|
return BitmapFromMetroBitmap(hBitmap,bitmapSize,metroColor);
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
void CItemManager::LoadIconData::Init( void )
|
|
{
|
|
m_IconSizes[0]=SMALL_ICON_SIZE;
|
|
m_IconSizes[1]=LARGE_ICON_SIZE;
|
|
m_IconSizes[2]=EXTRA_LARGE_ICON_SIZE;
|
|
m_IconSizes[3]=SMALL_ICON_SIZE-2;
|
|
m_IconSizes[4]=LARGE_ICON_SIZE-2;
|
|
m_IconSizes[5]=EXTRA_LARGE_ICON_SIZE-2;
|
|
|
|
for (int i=0;i<_countof(m_TempLists);i++)
|
|
{
|
|
m_TempLists[i]=ImageList_Create(m_IconSizes[i],m_IconSizes[i],ILC_COLOR32,0,1);
|
|
if (m_TempLists[i])
|
|
{
|
|
ImageList_SetImageCount(m_TempLists[i],1);
|
|
HIMAGELIST_QueryInterface(m_TempLists[i],IID_IImageList2,(void**)&m_pTempLists[i]);
|
|
}
|
|
}
|
|
m_pFactory.CoCreateInstance(CLSID_WICImagingFactory);
|
|
}
|
|
|
|
void CItemManager::LoadIconData::Close( void )
|
|
{
|
|
for (int i=0;i<_countof(m_TempLists);i++)
|
|
{
|
|
m_pTempLists[i]=NULL;
|
|
if (m_TempLists[i])
|
|
ImageList_Destroy(m_TempLists[i]);
|
|
}
|
|
m_pFactory=NULL;
|
|
}
|
|
|
|
int CItemManager::SMALL_ICON_SIZE;
|
|
int CItemManager::LARGE_ICON_SIZE;
|
|
int CItemManager::EXTRA_LARGE_ICON_SIZE=64;
|
|
int CItemManager::s_DPI;
|
|
int CItemManager::s_DPIOverride;
|
|
|
|
CItemManager g_ItemManager;
|
|
|
|
CItemManager::CItemManager( void )
|
|
{
|
|
m_bInitialized=false;
|
|
|
|
memset(m_CriticalSections,0,sizeof(m_CriticalSections));
|
|
memset(m_CriticalSectionOwners,0,sizeof(m_CriticalSectionOwners));
|
|
m_StartEvent=m_WorkEvent=m_ExitEvent=m_DoneEvent=m_PreloadItemsThread=m_RefreshInfoThread=m_SaveCacheThread=NULL;
|
|
m_MainThreadId=m_PreloadItemsThreadId=m_RefreshInfoThreadId=0;
|
|
m_DefaultSmallIcon=m_DefaultLargeIcon=m_DefaultExtraLargeIcon=NULL;
|
|
m_bHasNewPrograms[0]=m_bHasNewPrograms[1]=m_bHasNewApps[0]=m_bHasNewApps[1]=m_bPreloadIcons=m_bPreloadFavorites=false;
|
|
m_LoadingStage=LOAD_STOPPED;
|
|
m_LastCacheSave=0;
|
|
m_TransientHash=1;
|
|
}
|
|
|
|
CItemManager::~CItemManager( void )
|
|
{
|
|
Close();
|
|
}
|
|
|
|
void CItemManager::Init( void )
|
|
{
|
|
m_MainThreadId=GetCurrentThreadId();
|
|
|
|
{
|
|
// get the DPI setting
|
|
HDC hdc=GetDC(NULL);
|
|
s_DPI=GetDeviceCaps(hdc,LOGPIXELSY);
|
|
ReleaseDC(NULL,hdc);
|
|
s_DPIOverride=GetSettingInt(L"OverrideDPI");
|
|
if (s_DPIOverride!=0)
|
|
{
|
|
if (s_DPIOverride<96) s_DPIOverride=96;
|
|
if (s_DPIOverride>480) s_DPIOverride=480;
|
|
}
|
|
}
|
|
|
|
SMALL_ICON_SIZE=GetSettingInt(L"SmallIconSize");
|
|
LARGE_ICON_SIZE=GetSettingInt(L"LargeIconSize");
|
|
m_OldSysAccentColor=GetSystemAccentColor();
|
|
g_bInvertMetroIcons=GetSettingBool(L"InvertMetroIcons");
|
|
m_bOldInvertIcons=g_bInvertMetroIcons;
|
|
m_LoadIconData[0].Init();
|
|
|
|
bool bRTL=IsLanguageRTL();
|
|
|
|
CComString pPath;
|
|
if (SUCCEEDED(ShGetKnownFolderPath(FOLDERID_StartMenu,&pPath)))
|
|
{
|
|
m_RootStartMenu1=pPath;
|
|
m_RootStartMenu1+=L"\\";
|
|
StringUpper(m_RootStartMenu1);
|
|
}
|
|
pPath.Clear();
|
|
if (SUCCEEDED(ShGetKnownFolderPath(FOLDERID_CommonStartMenu,&pPath)))
|
|
{
|
|
m_RootStartMenu2=pPath;
|
|
m_RootStartMenu2+=L"\\";
|
|
StringUpper(m_RootStartMenu2);
|
|
}
|
|
pPath.Clear();
|
|
if (SUCCEEDED(ShGetKnownFolderPath(FOLDERID_CommonPrograms,&pPath)))
|
|
{
|
|
m_RootCommonPrograms=pPath;
|
|
m_RootCommonPrograms+=L"\\";
|
|
StringUpper(m_RootCommonPrograms);
|
|
}
|
|
pPath.Clear();
|
|
if (SUCCEEDED(ShGetKnownFolderPath(FOLDERID_Desktop,&pPath)))
|
|
{
|
|
m_RootDesktop=pPath;
|
|
m_RootDesktop+=L"\\";
|
|
StringUpper(m_RootDesktop);
|
|
}
|
|
pPath.Clear();
|
|
|
|
m_RootGames=L"::{ED228FDF-9EA8-4870-83B1-96B02CFE0D52}\\";
|
|
wchar_t text[_MAX_PATH];
|
|
Strcpy(text,_countof(text),START_MENU_PINNED_ROOT L"\\");
|
|
DoEnvironmentSubst(text,_countof(text));
|
|
m_RootStartMenu3=text;
|
|
StringUpper(m_RootStartMenu3);
|
|
Strcpy(text,_countof(text),TASKBAR_PINNED_ROOT L"\\");
|
|
DoEnvironmentSubst(text,_countof(text));
|
|
m_RootTaskbar=text;
|
|
StringUpper(m_RootTaskbar);
|
|
Strcpy(text,_countof(text),METRO_APP_ROOT L"\\");
|
|
DoEnvironmentSubst(text,_countof(text));
|
|
m_RootMetro=text;
|
|
StringUpper(m_RootMetro);
|
|
|
|
for (int i=0;i<=SHIL_LAST;i++)
|
|
{
|
|
CComPtr<IImageList> pList;
|
|
if (SUCCEEDED(SHGetImageList(i,IID_IImageList,(void**)&pList)))
|
|
{
|
|
int width, height;
|
|
pList->GetIconSize(&width,&height);
|
|
m_ListSizes.push_back(std::pair<int,int>(width,i));
|
|
}
|
|
}
|
|
std::sort(m_ListSizes.begin(),m_ListSizes.end());
|
|
|
|
CreateDefaultIcons();
|
|
LoadCacheFile();
|
|
|
|
ItemInfo &item=m_ItemInfos.insert(std::pair<unsigned int,ItemInfo>(0,ItemInfo()))->second;
|
|
item.bIconOnly=true;
|
|
item.smallIcon=m_DefaultSmallIcon;
|
|
item.largeIcon=m_DefaultLargeIcon;
|
|
item.extraLargeIcon=m_DefaultExtraLargeIcon;
|
|
|
|
for (int i=0;i<RWLOCK_COUNT;i++)
|
|
InitializeSRWLock(&m_RWLocks[i]);
|
|
for (int i=0;i<LOCK_COUNT;i++)
|
|
InitializeCriticalSection(&m_CriticalSections[i]);
|
|
m_bPreloadIcons=GetSettingBool(L"PreCacheIcons");
|
|
m_bPreloadFavorites=(GetSettingInt(L"Favorites")==2);
|
|
|
|
m_LoadingStage=LOAD_LOADING;
|
|
m_StartEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
|
|
m_WorkEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
|
|
m_ExitEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
|
|
m_DoneEvent=CreateEvent(NULL,TRUE,FALSE,NULL);
|
|
|
|
m_PreloadItemsThread=CreateThread(NULL,0,StaticPreloadItemsThread,this,0,&m_PreloadItemsThreadId);
|
|
m_RefreshInfoThread=CreateThread(NULL,0,StaticRefreshInfoThread,this,0,&m_RefreshInfoThreadId);
|
|
|
|
LoadOldItems();
|
|
|
|
m_bInitialized=true;
|
|
}
|
|
|
|
void CItemManager::Close( void )
|
|
{
|
|
if (!m_bInitialized) return;
|
|
|
|
m_LoadingStage=LOAD_STOPPING;
|
|
SetEvent(m_ExitEvent);
|
|
if (m_PreloadItemsThread)
|
|
{
|
|
WaitForSingleObject(m_PreloadItemsThread,INFINITE);
|
|
CloseHandle(m_PreloadItemsThread);
|
|
m_PreloadItemsThread=NULL;
|
|
}
|
|
if (m_RefreshInfoThread)
|
|
{
|
|
WaitForSingleObject(m_RefreshInfoThread,INFINITE);
|
|
CloseHandle(m_RefreshInfoThread);
|
|
m_RefreshInfoThread=NULL;
|
|
}
|
|
if (m_SaveCacheThread)
|
|
{
|
|
WaitForSingleObject(m_SaveCacheThread,INFINITE);
|
|
CloseHandle(m_SaveCacheThread);
|
|
m_SaveCacheThread=NULL;
|
|
}
|
|
m_LoadingStage=LOAD_STOPPED;
|
|
|
|
for (std::multimap<unsigned int,IconInfo>::const_iterator it=m_IconInfos.begin();it!=m_IconInfos.end();++it)
|
|
{
|
|
if (it->second.bitmap)
|
|
DeleteObject(it->second.bitmap);
|
|
}
|
|
|
|
for (int i=0;i<LOCK_COUNT;i++)
|
|
DeleteCriticalSection(&m_CriticalSections[i]);
|
|
g_pAppResolver=NULL;
|
|
CloseHandle(m_StartEvent);
|
|
CloseHandle(m_WorkEvent);
|
|
CloseHandle(m_ExitEvent);
|
|
CloseHandle(m_DoneEvent);
|
|
m_LoadIconData[0].Close();
|
|
m_bInitialized=false;
|
|
}
|
|
|
|
void CItemManager::CreateDefaultIcons( void )
|
|
{
|
|
SHFILEINFO info;
|
|
int index;
|
|
if (SHGetFileInfo(L"file",FILE_ATTRIBUTE_NORMAL,&info,sizeof(info),SHGFI_USEFILEATTRIBUTES|SHGFI_SYSICONINDEX))
|
|
index=info.iIcon;
|
|
else
|
|
index=-1;
|
|
IconInfo icon;
|
|
icon.bTemp=false;
|
|
icon.bMetro=false;
|
|
icon.timestamp.dwLowDateTime=icon.timestamp.dwHighDateTime=0;
|
|
icon.sizeType=ICON_SIZE_TYPE_SMALL;
|
|
if (index>=0)
|
|
icon.bitmap=BitmapFromIcon(LoadShellIcon(index,SMALL_ICON_SIZE),SMALL_ICON_SIZE);
|
|
else
|
|
icon.bitmap=NULL;
|
|
m_DefaultSmallIcon=&m_IconInfos.insert(std::pair<unsigned int,IconInfo>(0,icon))->second;
|
|
|
|
icon.sizeType=ICON_SIZE_TYPE_LARGE;
|
|
if (index>=0)
|
|
icon.bitmap=BitmapFromIcon(LoadShellIcon(index,LARGE_ICON_SIZE),LARGE_ICON_SIZE);
|
|
else
|
|
icon.bitmap=NULL;
|
|
m_DefaultLargeIcon=&m_IconInfos.insert(std::pair<unsigned int,IconInfo>(0,icon))->second;
|
|
|
|
icon.sizeType=ICON_SIZE_TYPE_EXTRA_LARGE;
|
|
if (index>=0)
|
|
icon.bitmap=BitmapFromIcon(LoadShellIcon(index,EXTRA_LARGE_ICON_SIZE),EXTRA_LARGE_ICON_SIZE);
|
|
else
|
|
icon.bitmap=NULL;
|
|
m_DefaultExtraLargeIcon=&m_IconInfos.insert(std::pair<unsigned int,IconInfo>(0,icon))->second;
|
|
}
|
|
|
|
CItemManager::LoadIconData &CItemManager::GetLoadIconData( void )
|
|
{
|
|
DWORD thread=GetCurrentThreadId();
|
|
if (thread==m_RefreshInfoThreadId)
|
|
return m_LoadIconData[2];
|
|
if (thread==m_PreloadItemsThreadId)
|
|
return m_LoadIconData[1];
|
|
Assert(thread==m_MainThreadId);
|
|
return m_LoadIconData[0];
|
|
}
|
|
|
|
void CItemManager::ResetTempIcons( void )
|
|
{
|
|
Assert(GetCurrentThreadId()==m_MainThreadId);
|
|
Lock cleanupLock(this,LOCK_CLEANUP);
|
|
RWLock itemLock(this,true,RWLOCK_ITEMS);
|
|
RWLock iconLock(this,true,RWLOCK_ICONS);
|
|
|
|
COLORREF sysColor=GetSystemAccentColor();
|
|
bool bResetMetro=m_OldSysAccentColor!=sysColor;
|
|
m_OldSysAccentColor=sysColor;
|
|
|
|
g_bInvertMetroIcons=GetSettingBool(L"InvertMetroIcons");
|
|
if (m_bOldInvertIcons!=g_bInvertMetroIcons)
|
|
bResetMetro=true;
|
|
m_bOldInvertIcons=g_bInvertMetroIcons;
|
|
|
|
if (bResetMetro)
|
|
{
|
|
for (std::map<unsigned int,const ItemInfo*>::iterator it=m_MetroItemInfos10.begin();it!=m_MetroItemInfos10.end();)
|
|
{
|
|
std::map<unsigned int,const ItemInfo*>::iterator next=it; ++next;
|
|
if (it->second && !it->second->packagePath.IsEmpty())
|
|
{
|
|
const_cast<ItemInfo*>(it->second)->validFlags&=~(INFO_ICON|INFO_METRO);
|
|
m_MetroItemInfos10.erase(it);
|
|
}
|
|
it=next;
|
|
}
|
|
}
|
|
{
|
|
// remove temp items from the queue
|
|
std::list<ItemInfo*>::iterator it=m_ItemQueue.begin();
|
|
while (it!=m_ItemQueue.end())
|
|
{
|
|
std::list<ItemInfo*>::iterator next=it; ++next;
|
|
if ((*it)->bTemp)
|
|
{
|
|
Assert(!(*it)->largeIcon && !(*it)->extraLargeIcon && ((*it)->smallIcon==m_DefaultSmallIcon || (*it)->smallIcon->bTemp));
|
|
m_ItemQueue.erase(it);
|
|
}
|
|
it=next;
|
|
}
|
|
}
|
|
|
|
int metroFlags=bResetMetro?INFO_METRO:0;
|
|
{
|
|
// remove temp items from the cache
|
|
std::multimap<unsigned int,ItemInfo>::iterator it=m_ItemInfos.begin();
|
|
while (it!=m_ItemInfos.end())
|
|
{
|
|
std::multimap<unsigned int,ItemInfo>::iterator next=it; ++next;
|
|
if (it->second.bTemp)
|
|
{
|
|
Assert(it->second.largeIcon==m_DefaultLargeIcon && it->second.extraLargeIcon==m_DefaultExtraLargeIcon && (it->second.smallIcon==m_DefaultSmallIcon || it->second.smallIcon->bTemp));
|
|
m_ItemInfos.erase(it);
|
|
}
|
|
else
|
|
{
|
|
if (it->second.smallIcon && (it->second.smallIcon->bTemp || (it->second.smallIcon->bMetro && bResetMetro)))
|
|
{
|
|
it->second.smallIcon=m_DefaultSmallIcon;
|
|
it->second.validFlags&=~(INFO_SMALL_ICON|metroFlags);
|
|
}
|
|
if (it->second.largeIcon && (it->second.largeIcon->bTemp || (it->second.largeIcon->bMetro && bResetMetro)))
|
|
{
|
|
it->second.largeIcon=m_DefaultLargeIcon;
|
|
it->second.validFlags&=~(INFO_LARGE_ICON|metroFlags);
|
|
}
|
|
if (it->second.extraLargeIcon && (it->second.extraLargeIcon->bTemp || (it->second.extraLargeIcon->bMetro && bResetMetro)))
|
|
{
|
|
it->second.extraLargeIcon=m_DefaultExtraLargeIcon;
|
|
it->second.validFlags&=~(INFO_EXTRA_LARGE_ICON|metroFlags);
|
|
}
|
|
}
|
|
it=next;
|
|
}
|
|
}
|
|
|
|
{
|
|
// remove temp icons
|
|
std::multimap<unsigned int,IconInfo>::iterator it=m_IconInfos.begin();
|
|
while (it!=m_IconInfos.end())
|
|
{
|
|
std::multimap<unsigned int,IconInfo>::iterator next=it; ++next;
|
|
if (it->second.bTemp || (it->second.bMetro && bResetMetro))
|
|
{
|
|
if (it->second.bitmap)
|
|
DeleteObject(it->second.bitmap);
|
|
m_IconInfos.erase(it);
|
|
}
|
|
it=next;
|
|
}
|
|
}
|
|
|
|
{
|
|
// delete old bitmaps
|
|
for (std::vector<HBITMAP>::iterator it=m_OldBitmaps.begin();it!=m_OldBitmaps.end();++it)
|
|
DeleteObject(*it);
|
|
m_OldBitmaps.clear();
|
|
}
|
|
m_TransientHash=1;
|
|
}
|
|
|
|
static bool ComparePidls( PIDLIST_ABSOLUTE pidl1, PIDLIST_ABSOLUTE pidl2 )
|
|
{
|
|
if (!pidl1 && !pidl2) return true;
|
|
if (!pidl1 || !pidl2) return false;
|
|
int size1=ILGetSize(pidl1);
|
|
int size2=ILGetSize(pidl2);
|
|
if (size1!=size2) return false;
|
|
return memcmp(pidl1,pidl2,size1)==0;
|
|
}
|
|
|
|
const CItemManager::ItemInfo *CItemManager::GetItemInfo( IShellItem *pItem, PIDLIST_ABSOLUTE pidl, int refreshFlags, TLocation location )
|
|
{
|
|
Assert(!RWLock::ThreadHasReadLock(RWLOCK_ITEMS));
|
|
if ((refreshFlags&INFO_METRO) && GetWinVersion()<WIN_VER_WIN8)
|
|
refreshFlags&=~INFO_METRO;
|
|
CComString pName;
|
|
if (refreshFlags&INFO_NO_PATH)
|
|
refreshFlags&=~INFO_NO_PATH;
|
|
else
|
|
pItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING,&pName);
|
|
|
|
CString path, PATH;
|
|
unsigned int hash;
|
|
if (pName)
|
|
{
|
|
path=pName;
|
|
PATH=path;
|
|
StringUpper(PATH);
|
|
hash=CalcFNVHash(PATH);
|
|
}
|
|
else
|
|
{
|
|
hash=CalcFNVHash(pidl,ILGetSize(pidl));
|
|
}
|
|
|
|
FILETIME writeTime={0}, createTime={0};
|
|
if (!path.IsEmpty())
|
|
{
|
|
if (!MenuGetFileTimestamp(path,&writeTime,&createTime))
|
|
{
|
|
if (refreshFlags&INFO_VALIDATE_FILE)
|
|
return NULL;
|
|
path.Empty();
|
|
}
|
|
}
|
|
|
|
bool bDelay=GetSettingBool(L"DelayIcons");
|
|
ItemInfo *pInfo=NULL;
|
|
{
|
|
RWLock lock(this,true,RWLOCK_ITEMS);
|
|
std::multimap<unsigned int,ItemInfo>::iterator it=m_ItemInfos.find(hash);
|
|
for (;it!=m_ItemInfos.end() && it->first==hash;++it)
|
|
{
|
|
if ((!PATH.IsEmpty() && wcscmp(PATH,it->second.PATH)==0) || (PATH.IsEmpty() && ILIsEqual(pidl,it->second.GetPidl())))
|
|
{
|
|
pInfo=&it->second;
|
|
break;
|
|
}
|
|
}
|
|
if (!pInfo)
|
|
{
|
|
pInfo=&m_ItemInfos.insert(std::pair<unsigned int,ItemInfo>(hash,ItemInfo()))->second;
|
|
pInfo->pidl.Clone(pidl);
|
|
pInfo->path=path;
|
|
pInfo->PATH=PATH;
|
|
pInfo->createstamp=createTime;
|
|
pInfo->writestamp=writeTime;
|
|
pInfo->smallIcon=m_DefaultSmallIcon;
|
|
pInfo->largeIcon=m_DefaultLargeIcon;
|
|
pInfo->extraLargeIcon=m_DefaultExtraLargeIcon;
|
|
pInfo->validFlags=0;
|
|
}
|
|
else
|
|
{
|
|
if (!pInfo->packagePath.IsEmpty())
|
|
MenuGetFileTimestamp(pInfo->packagePath,&writeTime,&createTime);
|
|
if (CompareFileTime(&pInfo->writestamp,&writeTime)!=0)
|
|
{
|
|
if (!PATH.IsEmpty() && !ComparePidls(pInfo->pidl,pidl) && !ComparePidls(pInfo->newPidl,pidl))
|
|
pInfo->newPidl.Clone(pidl);
|
|
pInfo->writestamp=writeTime;
|
|
pInfo->validFlags=0;
|
|
}
|
|
}
|
|
if (pInfo->location==LOCATION_UNKNOWN && !pInfo->PATH.IsEmpty())
|
|
{
|
|
if (location!=LOCATION_UNKNOWN)
|
|
pInfo->location=location;
|
|
else
|
|
pInfo->location=DetermineLocation(pInfo->PATH);
|
|
}
|
|
refreshFlags&=~pInfo->validFlags;
|
|
}
|
|
|
|
if (refreshFlags)
|
|
{
|
|
int refreshMask=bDelay?INFO_DATA:(INFO_DATA|INFO_ICON);
|
|
if (refreshFlags&refreshMask)
|
|
RefreshItemInfo(pInfo,refreshFlags&refreshMask,pItem,false);
|
|
refreshFlags&=~refreshMask;
|
|
if (refreshFlags)
|
|
{
|
|
RWLock lock(this,true,RWLOCK_ITEMS);
|
|
QueueItemInfo(pInfo,refreshFlags);
|
|
}
|
|
}
|
|
|
|
return pInfo;
|
|
}
|
|
|
|
const CItemManager::ItemInfo *CItemManager::GetItemInfo( CString path, int refreshFlags, TLocation location )
|
|
{
|
|
Assert(!RWLock::ThreadHasReadLock(RWLOCK_ITEMS));
|
|
Assert(!path.IsEmpty());
|
|
if ((refreshFlags&INFO_METRO) && GetWinVersion()<WIN_VER_WIN8)
|
|
refreshFlags&=~INFO_METRO;
|
|
CString PATH=path;
|
|
StringUpper(PATH);
|
|
unsigned int hash=CalcFNVHash(PATH);
|
|
|
|
FILETIME writeTime={0}, createTime={0};
|
|
if (!MenuGetFileTimestamp(path,&writeTime,&createTime))
|
|
{
|
|
if (refreshFlags&INFO_VALIDATE_FILE)
|
|
return NULL;
|
|
path.Empty();
|
|
}
|
|
|
|
bool bDelay=GetSettingBool(L"DelayIcons");
|
|
ItemInfo *pInfo=NULL;
|
|
{
|
|
RWLock lock(this,true,RWLOCK_ITEMS);
|
|
std::multimap<unsigned int,ItemInfo>::iterator it=m_ItemInfos.find(hash);
|
|
for (;it!=m_ItemInfos.end() && it->first==hash;++it)
|
|
{
|
|
if (wcscmp(PATH,it->second.PATH)==0)
|
|
{
|
|
pInfo=&it->second;
|
|
break;
|
|
}
|
|
}
|
|
if (!pInfo)
|
|
{
|
|
pInfo=&m_ItemInfos.insert(std::pair<unsigned int,ItemInfo>(hash,ItemInfo()))->second;
|
|
if (!PATH.IsEmpty())
|
|
MenuParseDisplayName(path,&pInfo->pidl,NULL,NULL);
|
|
if (pInfo->pidl)
|
|
pInfo->path=path;
|
|
pInfo->PATH=PATH;
|
|
pInfo->createstamp=createTime;
|
|
pInfo->writestamp=writeTime;
|
|
pInfo->smallIcon=m_DefaultSmallIcon;
|
|
pInfo->largeIcon=m_DefaultLargeIcon;
|
|
pInfo->extraLargeIcon=m_DefaultExtraLargeIcon;
|
|
pInfo->validFlags=0;
|
|
}
|
|
else
|
|
{
|
|
if (!pInfo->packagePath.IsEmpty())
|
|
MenuGetFileTimestamp(pInfo->packagePath,&writeTime,&createTime);
|
|
if (CompareFileTime(&pInfo->writestamp,&writeTime)!=0)
|
|
{
|
|
CAbsolutePidl pidl;
|
|
if (!PATH.IsEmpty())
|
|
MenuParseDisplayName(PATH,&pidl,NULL,NULL);
|
|
if (!ComparePidls(pInfo->pidl,pidl) && !ComparePidls(pInfo->newPidl,pidl))
|
|
pInfo->newPidl.Swap(pidl);
|
|
pInfo->writestamp=writeTime;
|
|
pInfo->validFlags=0;
|
|
}
|
|
}
|
|
if (pInfo->location==LOCATION_UNKNOWN && !pInfo->PATH.IsEmpty())
|
|
{
|
|
if (location!=LOCATION_UNKNOWN)
|
|
pInfo->location=location;
|
|
else
|
|
pInfo->location=DetermineLocation(pInfo->PATH);
|
|
}
|
|
refreshFlags&=~pInfo->validFlags;
|
|
}
|
|
|
|
if (refreshFlags)
|
|
{
|
|
int refreshMask=bDelay?INFO_DATA:(INFO_DATA|INFO_ICON);
|
|
if (refreshFlags&refreshMask)
|
|
RefreshItemInfo(pInfo,refreshFlags&refreshMask,NULL,false);
|
|
refreshFlags&=~refreshMask;
|
|
if (refreshFlags)
|
|
{
|
|
RWLock lock(this,true,RWLOCK_ITEMS);
|
|
QueueItemInfo(pInfo,refreshFlags);
|
|
}
|
|
}
|
|
|
|
return pInfo;
|
|
}
|
|
|
|
void CItemManager::UpdateItemInfo( const ItemInfo *pInfo, int refreshFlags, bool bHasWriteLock )
|
|
{
|
|
if (bHasWriteLock)
|
|
{
|
|
Assert(RWLock::ThreadHasWriteLock(RWLOCK_ITEMS));
|
|
}
|
|
else
|
|
{
|
|
Assert(!RWLock::ThreadHasReadLock(RWLOCK_ITEMS));
|
|
}
|
|
refreshFlags&=~pInfo->validFlags; // potentially out of lock, assuming validFlags is atomic
|
|
if (refreshFlags)
|
|
{
|
|
RWLock lock(this,true,bHasWriteLock?RWLOCK_COUNT:RWLOCK_ITEMS);
|
|
if ((refreshFlags&INFO_ICON) && !(refreshFlags&INFO_REFRESH_NOW) && GetSettingBool(L"DelayIcons"))
|
|
{
|
|
QueueItemInfo(const_cast<ItemInfo*>(pInfo),refreshFlags&INFO_ICON);
|
|
refreshFlags&=~INFO_ICON;
|
|
}
|
|
if (refreshFlags)
|
|
RefreshItemInfo(const_cast<ItemInfo*>(pInfo),refreshFlags,NULL,true);
|
|
}
|
|
}
|
|
|
|
const CItemManager::ItemInfo *CItemManager::GetCustomIcon( const wchar_t *location, int index, TIconSizeType iconSizeType, bool bTemp )
|
|
{
|
|
Assert(!RWLock::ThreadHasReadLock(RWLOCK_ITEMS));
|
|
unsigned int hash=CalcFNVHash(location,CalcFNVHash(&index,4));
|
|
ItemInfo *pInfo=NULL;
|
|
bool bDelay=GetSettingBool(L"DelayIcons");
|
|
int refreshFlags=0;
|
|
{
|
|
RWLock lock(this,true,RWLOCK_ITEMS);
|
|
std::multimap<unsigned int,ItemInfo>::iterator it=m_ItemInfos.find(hash);
|
|
for (;it!=m_ItemInfos.end() && it->first==hash;++it)
|
|
{
|
|
if (it->second.bIconOnly && it->second.bTemp==bTemp)
|
|
{
|
|
pInfo=&it->second;
|
|
break;
|
|
}
|
|
}
|
|
if (!pInfo)
|
|
{
|
|
pInfo=&m_ItemInfos.insert(std::pair<unsigned int,ItemInfo>(hash,ItemInfo()))->second;
|
|
pInfo->bIconOnly=true;
|
|
pInfo->bTemp=bTemp;
|
|
pInfo->iconPath=location;
|
|
pInfo->iconIndex=index;
|
|
pInfo->smallIcon=m_DefaultSmallIcon;
|
|
pInfo->largeIcon=m_DefaultLargeIcon;
|
|
pInfo->extraLargeIcon=m_DefaultExtraLargeIcon;
|
|
}
|
|
|
|
if (iconSizeType==ICON_SIZE_TYPE_SMALL) refreshFlags|=INFO_SMALL_ICON;
|
|
if (iconSizeType==ICON_SIZE_TYPE_LARGE) refreshFlags|=INFO_LARGE_ICON;
|
|
if (iconSizeType==ICON_SIZE_TYPE_EXTRA_LARGE) refreshFlags|=INFO_EXTRA_LARGE_ICON;
|
|
refreshFlags&=~pInfo->validFlags;
|
|
|
|
if (refreshFlags && bDelay)
|
|
{
|
|
QueueItemInfo(pInfo,refreshFlags);
|
|
refreshFlags=0;
|
|
}
|
|
}
|
|
if (!bDelay && refreshFlags)
|
|
{
|
|
RefreshItemInfo(pInfo,refreshFlags,NULL,false);
|
|
}
|
|
return pInfo;
|
|
}
|
|
|
|
const CItemManager::ItemInfo *CItemManager::GetCustomIcon( const wchar_t *path, TIconSizeType iconSizeType )
|
|
{
|
|
Assert(!RWLock::ThreadHasReadLock(RWLOCK_ITEMS));
|
|
if (!path)
|
|
{
|
|
RWLock lock(this,false,RWLOCK_ITEMS);
|
|
return &m_ItemInfos.find(0)->second;
|
|
}
|
|
wchar_t text[1024];
|
|
Strcpy(text,_countof(text),path);
|
|
DoEnvironmentSubst(text,_countof(text));
|
|
wchar_t *c=wcsrchr(text,',');
|
|
int index=0;
|
|
if (c)
|
|
{
|
|
*c=0;
|
|
index=-_wtol(c+1);
|
|
}
|
|
return GetCustomIcon(text,index,iconSizeType,false);
|
|
}
|
|
|
|
const CItemManager::ItemInfo *CItemManager::GetMetroAppInfo10( const wchar_t *appid )
|
|
{
|
|
wchar_t APPID[256];
|
|
Strcpy(APPID,_countof(APPID),appid);
|
|
CharUpper(APPID);
|
|
if (APPID[0]=='\\' || APPID[0]=='{' || APPID[1]==':' || wcsstr(APPID,L".AUTOGENERATED."))
|
|
return NULL; // attempt to recognize appids that are for sure not Metro
|
|
unsigned int hash=CalcFNVHash(APPID);
|
|
const ItemInfo *pInfo=NULL;
|
|
{
|
|
RWLock lock(this,false,RWLOCK_ITEMS);
|
|
// the key is a hash of the uppercase appid
|
|
std::map<unsigned int,const ItemInfo*>::const_iterator it=m_MetroItemInfos10.find(hash);
|
|
if (it!=m_MetroItemInfos10.end())
|
|
pInfo=it->second;
|
|
else if (m_BlackListInfos10.find(hash)!=m_BlackListInfos10.end())
|
|
return NULL;
|
|
}
|
|
if (pInfo)
|
|
{
|
|
UpdateItemInfo(pInfo,INFO_LINK|INFO_METRO);
|
|
return pInfo;
|
|
}
|
|
|
|
CComPtr<IShellItem> pItem;
|
|
if (SUCCEEDED(SHCreateItemInKnownFolder(FOLDERID_AppsFolder2,0,appid,IID_IShellItem,(void**)&pItem)))
|
|
{
|
|
CAbsolutePidl pidl;
|
|
if (SUCCEEDED(SHGetIDListFromObject(pItem,&pidl)))
|
|
pInfo=GetItemInfo(pItem,pidl,INFO_LINK|INFO_METRO,LOCATION_METRO);
|
|
}
|
|
|
|
{
|
|
RWLock lock(this,true,RWLOCK_ITEMS);
|
|
std::map<unsigned int,const ItemInfo*>::const_iterator it=m_MetroItemInfos10.find(hash);
|
|
if (it!=m_MetroItemInfos10.end() && it->second)
|
|
return it->second;
|
|
else
|
|
{
|
|
if (!pInfo)
|
|
m_BlackListInfos10.insert(hash);
|
|
if (pInfo && !pInfo->IsMetroApp())
|
|
pInfo=NULL;
|
|
m_MetroItemInfos10[hash]=pInfo;
|
|
return pInfo;
|
|
}
|
|
}
|
|
}
|
|
|
|
// requires LOCK_ITEMS to be held
|
|
void CItemManager::QueueItemInfo( ItemInfo *pInfo, int refreshFlags )
|
|
{
|
|
Assert(RWLock::ThreadHasWriteLock(RWLOCK_ITEMS));
|
|
DWORD thread=GetCurrentThreadId();
|
|
Assert(thread!=m_RefreshInfoThreadId);
|
|
pInfo->refreshFlags|=refreshFlags&~pInfo->validFlags&(INFO_DATA|INFO_ICON);
|
|
if (pInfo->refreshFlags)
|
|
{
|
|
std::list<ItemInfo*> &queue=(thread==m_PreloadItemsThreadId)?m_ItemQueueLow:m_ItemQueue;
|
|
for (std::list<ItemInfo*>::const_iterator it=queue.begin();it!=queue.end();++it)
|
|
{
|
|
if (*it==pInfo)
|
|
return;
|
|
}
|
|
queue.push_back(pInfo);
|
|
SetEvent(m_WorkEvent);
|
|
if (thread!=m_PreloadItemsThreadId)
|
|
SetEvent(m_StartEvent);
|
|
}
|
|
}
|
|
|
|
void CItemManager::WaitForShortcuts( const POINT &balloonPos )
|
|
{
|
|
if (m_PreloadItemsThreadId)
|
|
{
|
|
if (WaitForSingleObject(m_DoneEvent,1000)!=WAIT_TIMEOUT)
|
|
return;
|
|
CWindow tooltip=CreateWindowEx(WS_EX_TOPMOST|WS_EX_TOOLWINDOW|(IsLanguageRTL()?WS_EX_LAYOUTRTL:0),TOOLTIPS_CLASS,NULL,WS_POPUP|TTS_BALLOON|TTS_NOPREFIX,0,0,0,0,NULL,NULL,g_Instance,NULL);
|
|
tooltip.SendMessage(TTM_SETMAXTIPWIDTH,0,500);
|
|
TOOLINFO tool={sizeof(tool),TTF_TRANSPARENT|TTF_TRACK|(IsLanguageRTL()?TTF_RTLREADING:0U)};
|
|
tool.uId=1;
|
|
CString message=LoadStringEx(IDS_MENU_BUSY);
|
|
tool.lpszText=(LPWSTR)(const wchar_t*)message;
|
|
tooltip.SendMessage(TTM_ADDTOOL,0,(LPARAM)&tool);
|
|
tooltip.SendMessage(TTM_SETTITLE,TTI_INFO,(LPARAM)(const wchar_t*)LoadStringEx(IDS_APP_TITLE));
|
|
tooltip.SendMessage(TTM_TRACKPOSITION,0,MAKELONG(balloonPos.x,balloonPos.y));
|
|
tooltip.SendMessage(TTM_TRACKACTIVATE,TRUE,(LPARAM)&tool);
|
|
tooltip.SendMessage(TTM_UPDATE);
|
|
WaitForSingleObject(m_DoneEvent,INFINITE);
|
|
tooltip.DestroyWindow();
|
|
}
|
|
}
|
|
|
|
bool CItemManager::IsTaskbarPinned( const wchar_t *appid )
|
|
{
|
|
Assert(GetCurrentThreadId()==m_MainThreadId);
|
|
RWLock lock(this,true,RWLOCK_ITEMS);
|
|
const ItemInfo *pInfo=NULL;
|
|
for (std::multimap<unsigned int,ItemInfo>::const_iterator it=m_ItemInfos.begin();it!=m_ItemInfos.end();++it)
|
|
{
|
|
if (it->second.bLink && it->second.location==LOCATION_TASKBAR)
|
|
{
|
|
UpdateItemInfo(&it->second,INFO_LINK_APPID,true);
|
|
if (wcscmp(it->second.appid,appid)==0)
|
|
{
|
|
if (GetFileAttributes(it->second.path)!=INVALID_FILE_ATTRIBUTES)
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void EncodeRot13( wchar_t *text )
|
|
{
|
|
for (;*text;text++)
|
|
{
|
|
if (*text>='a' && *text<='z')
|
|
*text=(*text-'a'+13)%26+'a';
|
|
else if (*text>='A' && *text<='Z')
|
|
*text=(*text-'A'+13)%26+'A';
|
|
}
|
|
}
|
|
|
|
static KNOWNFOLDERID g_KnownPrefixes[]=
|
|
{
|
|
FOLDERID_SystemX86,
|
|
FOLDERID_System,
|
|
FOLDERID_Windows,
|
|
FOLDERID_ProgramFilesX86,
|
|
FOLDERID_ProgramFilesX64,
|
|
FOLDERID_Programs,
|
|
FOLDERID_CommonPrograms,
|
|
FOLDERID_StartMenu,
|
|
FOLDERID_CommonStartMenu,
|
|
};
|
|
|
|
void EncodeUserAssistPath( wchar_t *path )
|
|
{
|
|
for (int i=0;i<_countof(g_KnownPrefixes);i++)
|
|
{
|
|
CComString knownPath;
|
|
if (FAILED(SHGetKnownFolderPath(g_KnownPrefixes[i],0,NULL,&knownPath)))
|
|
continue;
|
|
int len=Strlen(knownPath);
|
|
if (_wcsnicmp(path,knownPath,len)==0)
|
|
{
|
|
CComString guid;
|
|
StringFromCLSID(g_KnownPrefixes[i],&guid);
|
|
wchar_t name[_MAX_PATH];
|
|
Sprintf(name,_countof(name),L"%s%s",(const wchar_t*)guid,path+len);
|
|
Strcpy(path,_MAX_PATH,name);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CItemManager::IsPathUsed( CRegKey ®Key, const wchar_t *path, const FILETIME &createstamp, const KnownPathGuid *knownPaths, int knownPathsCount, bool bMetroApp )
|
|
{
|
|
{
|
|
unsigned int hash=CalcFNVHash(path);
|
|
OldItemInfo key={hash};
|
|
std::vector<OldItemInfo>::const_iterator it=std::lower_bound(m_OldItemInfos.begin(),m_OldItemInfos.end(),key);
|
|
if (it!=m_OldItemInfos.end() && it->hash==hash)
|
|
{
|
|
if (CompareFileTime(&createstamp,&it->timestamp)<0)
|
|
return true;
|
|
}
|
|
}
|
|
|
|
wchar_t name[_MAX_PATH];
|
|
if (knownPaths)
|
|
{
|
|
for (int i=0;i<knownPathsCount;i++)
|
|
{
|
|
if (i==knownPathsCount-1)
|
|
Strcpy(name,_countof(name),path);
|
|
else if (wcsncmp(path,knownPaths[i].path,knownPaths[i].pathLen)==0)
|
|
{
|
|
Sprintf(name,_countof(name),L"%s%s", (const wchar_t*)knownPaths[i].guid,path+knownPaths[i].pathLen);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Strcpy(name,_countof(name),path);
|
|
}
|
|
EncodeRot13(name);
|
|
UserAssistData data;
|
|
DWORD size=sizeof(data);
|
|
if (regKey.QueryBinaryValue(name,&data,&size)==ERROR_SUCCESS)
|
|
{
|
|
if (bMetroApp)
|
|
{
|
|
// count is unreliable, the timestamp can be 0
|
|
return ((data.timestamp.dwLowDateTime|data.timestamp.dwHighDateTime)==0 || CompareFileTime(&createstamp,&data.timestamp)<0);
|
|
}
|
|
else
|
|
{
|
|
return (data.count>0 && CompareFileTime(&createstamp,&data.timestamp)<0);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CItemManager::UpdateNewPrograms( const POINT &balloonPos )
|
|
{
|
|
Assert(GetCurrentThreadId()==m_MainThreadId);
|
|
m_NewPrograms.clear();
|
|
m_NewProgramRoots.clear();
|
|
m_bHasNewPrograms[0]=m_bHasNewPrograms[1]=m_bHasNewApps[0]=m_bHasNewApps[1]=false;
|
|
#ifdef FORCE_ALL_NEW
|
|
m_bHasNewPrograms[0]=m_bHasNewPrograms[1]=m_bHasNewApps[0]=m_bHasNewApps[1]=true;
|
|
#endif
|
|
if (!GetSettingBool(L"HighlightNew"))
|
|
return;
|
|
bool bNewApps=GetSettingBool(L"HighlightNewApps");
|
|
/* {
|
|
// the new programs need the MFU system to be enabled
|
|
CRegKey regKey;
|
|
if (regKey.Open(HKEY_CURRENT_USER,L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Advanced",KEY_READ)!=ERROR_SUCCESS)
|
|
return;
|
|
DWORD val;
|
|
if (regKey.QueryDWORDValue(L"Start_TrackProgs",val)==ERROR_SUCCESS && !val)
|
|
return;
|
|
}
|
|
*/
|
|
LONGLONG installTime=0;
|
|
{
|
|
CRegKey regKey;
|
|
if (regKey.Open(HKEY_LOCAL_MACHINE,L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",KEY_READ)==ERROR_SUCCESS)
|
|
{
|
|
DWORD time=0;
|
|
if (regKey.QueryDWORDValue(L"InstallDate",time)==ERROR_SUCCESS)
|
|
{
|
|
installTime=Int32x32To64(time,10000000)+116444736000000000;
|
|
}
|
|
}
|
|
FILETIME ft={(DWORD)installTime,(DWORD)(installTime>>32)};
|
|
SYSTEMTIME st;
|
|
GetSystemTime(&st);
|
|
LOG_MENU(LOG_NEW,L"Current time: %02d.%02d.%04d:%02d:%02d",st.wDay,st.wMonth,st.wYear,st.wHour,st.wMinute);
|
|
FileTimeToSystemTime(&ft,&st);
|
|
LOG_MENU(LOG_NEW,L"Install time: %02d.%02d.%04d:%02d:%02d",st.wDay,st.wMonth,st.wYear,st.wHour,st.wMinute);
|
|
}
|
|
|
|
CRegKey regKeyExe;
|
|
regKeyExe.Open(HKEY_CURRENT_USER,USERASSIST_APPIDS_KEY,KEY_READ);
|
|
CRegKey regKeyLink;
|
|
regKeyLink.Open(HKEY_CURRENT_USER,USERASSIST_LINKS_KEY,KEY_READ);
|
|
|
|
KnownPathGuid knownPaths[_countof(g_KnownPrefixes)+1];
|
|
|
|
int OLD_PROGRAMS_AGE=GetSettingInt(L"OldProgramsAge");
|
|
if (OLD_PROGRAMS_AGE<0) OLD_PROGRAMS_AGE=0;
|
|
if (OLD_PROGRAMS_AGE>48) OLD_PROGRAMS_AGE=48;
|
|
const int INSTALL_GRACE_PERIOD=12; // ignore programs installed within 12 hours of system install
|
|
int knownPathsCount=0;
|
|
for (int i=0;i<_countof(g_KnownPrefixes);i++)
|
|
{
|
|
if (SUCCEEDED(SHGetKnownFolderPath(g_KnownPrefixes[i],0,NULL,&knownPaths[knownPathsCount].path)))
|
|
{
|
|
StringFromCLSID(g_KnownPrefixes[i],&knownPaths[knownPathsCount].guid);
|
|
knownPaths[knownPathsCount].path.MakeUpper();
|
|
knownPaths[knownPathsCount].pathLen=Strlen(knownPaths[knownPathsCount].path);
|
|
knownPathsCount++;
|
|
}
|
|
}
|
|
knownPaths[knownPathsCount++].pathLen=0;
|
|
|
|
LONGLONG curTime;
|
|
GetSystemTimeAsFileTime((FILETIME*)&curTime);
|
|
WaitForShortcuts(balloonPos);
|
|
LARGE_INTEGER newestProgram={0}, newestApp={0};
|
|
{
|
|
RWLock lock(this,true,RWLOCK_ITEMS);
|
|
for (std::multimap<unsigned int,ItemInfo>::const_iterator it=m_ItemInfos.begin();it!=m_ItemInfos.end();++it)
|
|
{
|
|
if (it->second.location!=LOCATION_START_MENU && it->second.location!=LOCATION_METRO)
|
|
continue;
|
|
if ((it->second.bMetroLink || it->second.bMetroApp) && !bNewApps)
|
|
continue;
|
|
if (!it->second.bLink && !it->second.bMetroApp)
|
|
{
|
|
LOG_MENU(LOG_NEW,L"Ignoring new: %s not a link",it->second.path);
|
|
continue;
|
|
}
|
|
if (it->second.bNoNew)
|
|
{
|
|
LOG_MENU(LOG_NEW,L"Ignoring new: %s suppressed",it->second.path);
|
|
continue;
|
|
}
|
|
#ifdef FORCE_ALL_NEW
|
|
m_NewPrograms.push_back(&it->second);
|
|
continue;
|
|
#endif
|
|
LONGLONG timestamp=it->second.createstamp.dwLowDateTime|(((LONGLONG)it->second.createstamp.dwHighDateTime)<<32);
|
|
int hours1=(int)((curTime-timestamp)/36000000000);
|
|
if (hours1<0)
|
|
{
|
|
LOG_MENU(LOG_NEW,L"Ignoring new: %s creation time too new - %d hours",it->second.path,hours1);
|
|
continue;
|
|
}
|
|
if (hours1>OLD_PROGRAMS_AGE)
|
|
{
|
|
LOG_MENU(LOG_NEW,L"Ignoring new: %s creation time too old - %d hours",it->second.path,hours1);
|
|
continue;
|
|
}
|
|
if (wcswcs(PathFindFileName(it->second.PATH),L"UNINSTALL"))
|
|
{
|
|
LOG_MENU(LOG_NEW,L"Ignoring new: %s contains UNINSTALL",it->second.path);
|
|
continue;
|
|
}
|
|
if (it->second.location==LOCATION_START_MENU)
|
|
{
|
|
if (wcscmp(PathFindExtension(it->second.targetPATH),L".EXE")!=0)
|
|
{
|
|
LOG_MENU(LOG_NEW,L"Ignoring new: %s target not exe",it->second.path);
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (it->second.bLink && GetFileAttributes(it->second.path)==INVALID_FILE_ATTRIBUTES)
|
|
{
|
|
LOG_MENU(LOG_NEW,L"Ignoring new: %s missing file",it->second.path);
|
|
continue;
|
|
}
|
|
// existing link to exe that is newer than 48 hours
|
|
int hours2=0, hours3=0;
|
|
if (it->second.location==LOCATION_START_MENU)
|
|
{
|
|
HANDLE h=CreateFile(it->second.targetPATH,FILE_READ_ATTRIBUTES,FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,NULL,OPEN_EXISTING,0,NULL);
|
|
if (h==INVALID_HANDLE_VALUE)
|
|
{
|
|
LOG_MENU(LOG_NEW,L"Ignoring new: %s failed to read attributes",it->second.path);
|
|
continue;
|
|
}
|
|
|
|
FILE_BASIC_INFO info;
|
|
hours2=0;
|
|
if (GetFileInformationByHandleEx(h,FileBasicInfo,&info,sizeof(info)))
|
|
hours2=(int)((curTime-info.ChangeTime.QuadPart)/36000000000);
|
|
CloseHandle(h);
|
|
if (hours2<0)
|
|
{
|
|
LOG_MENU(LOG_NEW,L"Ignoring new: %s target change time too new - %d hours",it->second.path,hours2);
|
|
continue; // the exe is too old
|
|
}
|
|
if (hours2>OLD_PROGRAMS_AGE)
|
|
{
|
|
LOG_MENU(LOG_NEW,L"Ignoring new: %s target change time too old - %d hours",it->second.path,hours2);
|
|
continue; // the exe is too old
|
|
}
|
|
hours3=(int)((info.ChangeTime.QuadPart-installTime)/36000000000);
|
|
if (hours3<INSTALL_GRACE_PERIOD)
|
|
{
|
|
LOG_MENU(LOG_NEW,L"Ignoring new: %s too soon after install - %d hours",it->second.path,hours3);
|
|
continue; // too soon after install
|
|
}
|
|
|
|
if (regKeyLink.m_hKey && IsPathUsed(regKeyLink,it->second.PATH,it->second.createstamp,knownPaths,knownPathsCount,it->second.bMetroApp))
|
|
{
|
|
LOG_MENU(LOG_NEW,L"Ignoring new: %s shortcut used after it was created",it->second.path);
|
|
continue; // the shortcut was used after it was created
|
|
}
|
|
if (regKeyExe.m_hKey)
|
|
{
|
|
UpdateItemInfo(&it->second,INFO_LINK_APPID,true);
|
|
CString appid=it->second.appid;
|
|
appid.MakeUpper();
|
|
if (IsPathUsed(regKeyExe,appid,it->second.createstamp,NULL,0,it->second.bMetroApp))
|
|
{
|
|
LOG_MENU(LOG_NEW,L"Ignoring new: %s exe used after the shortcut was created",it->second.path);
|
|
continue; // the exe was used after the shortcut was created
|
|
}
|
|
}
|
|
if (newestProgram.QuadPart<info.ChangeTime.QuadPart)
|
|
newestProgram.QuadPart=info.ChangeTime.QuadPart;
|
|
}
|
|
|
|
if (it->second.location==LOCATION_METRO)
|
|
{
|
|
hours3=(int)((timestamp-installTime)/36000000000);
|
|
if (hours3<INSTALL_GRACE_PERIOD)
|
|
{
|
|
LOG_MENU(LOG_NEW,L"Ignoring new: %s too soon after install - %d hours",it->second.path,hours3);
|
|
continue; // too soon after install
|
|
}
|
|
CString appid=it->second.appid;
|
|
appid.MakeUpper();
|
|
if (regKeyExe.m_hKey && IsPathUsed(regKeyExe,appid,it->second.createstamp,NULL,0,it->second.bMetroApp))
|
|
{
|
|
LOG_MENU(LOG_NEW,L"Ignoring new: %s app id used after app was created",it->second.path);
|
|
continue; // the exe was used after the shortcut was created
|
|
}
|
|
if (it->second.bLink && regKeyLink.m_hKey && IsPathUsed(regKeyLink,it->second.PATH,it->second.createstamp,knownPaths,knownPathsCount,it->second.bMetroApp))
|
|
{
|
|
LOG_MENU(LOG_NEW,L"Ignoring new: %s shortcut used after it was created",it->second.path);
|
|
continue; // the shortcut was used after it was created
|
|
}
|
|
if (newestApp.QuadPart<timestamp)
|
|
newestApp.QuadPart=timestamp;
|
|
}
|
|
|
|
m_NewPrograms.push_back(&it->second);
|
|
LOG_MENU(LOG_NEW,L"Accepting new: highlighting %s, created %d hours, target changed %d hours, since install %d hours, %I64X",it->second.path,hours1,hours2,hours3,timestamp);
|
|
}
|
|
}
|
|
|
|
if (newestProgram.QuadPart || newestApp.QuadPart)
|
|
{
|
|
CRegKey regKey;
|
|
ULONGLONG val1, val2;
|
|
if (regKey.Open(HKEY_CURRENT_USER,L"Software\\OpenShell\\StartMenu",KEY_READ)==ERROR_SUCCESS)
|
|
{
|
|
if (regKey.QueryQWORDValue(L"LastProgramsTime",val1)!=ERROR_SUCCESS)
|
|
val1=0;
|
|
if (regKey.QueryQWORDValue(L"LastAppsTime",val2)!=ERROR_SUCCESS)
|
|
val2=0;
|
|
}
|
|
if (newestProgram.QuadPart)
|
|
{
|
|
LOG_MENU(LOG_NEW,L"LastProgramsTime: %I64X",val1);
|
|
m_bHasNewPrograms[0]=true;
|
|
m_bHasNewPrograms[1]=(val1<(ULONGLONG)newestProgram.QuadPart);
|
|
}
|
|
if (newestApp.QuadPart)
|
|
{
|
|
LOG_MENU(LOG_NEW,L"LastAppsTime: %I64X",val2);
|
|
m_bHasNewApps[0]=true;
|
|
m_bHasNewApps[1]=(val2<(ULONGLONG)newestApp.QuadPart);
|
|
}
|
|
}
|
|
|
|
static const KNOWNFOLDERID *newProgramRoots[]={
|
|
&FOLDERID_StartMenu,
|
|
&FOLDERID_CommonStartMenu,
|
|
&FOLDERID_Programs,
|
|
&FOLDERID_CommonPrograms,
|
|
};
|
|
for (int i=0;i<_countof(newProgramRoots);i++)
|
|
{
|
|
CComPtr<IShellItem> pFolder;
|
|
if (SUCCEEDED(ShGetKnownFolderItem(*newProgramRoots[i],&pFolder)))
|
|
{
|
|
CAbsolutePidl pidl;
|
|
if (SUCCEEDED(SHGetIDListFromObject(pFolder,&pidl)))
|
|
m_NewProgramRoots.push_back(GetItemInfo(pFolder,pidl,0));
|
|
}
|
|
}
|
|
if (GetWinVersion()>=WIN_VER_WIN8)
|
|
{
|
|
wchar_t path[_MAX_PATH]=METRO_APP_ROOT;
|
|
DoEnvironmentSubst(path,_countof(path));
|
|
CAbsolutePidl pidl;
|
|
pidl.Attach(ILCreateFromPath(path));
|
|
CComPtr<IShellItem> pFolder;
|
|
if (SUCCEEDED(SHCreateItemFromIDList(pidl,IID_IShellItem,(void**)&pFolder)))
|
|
m_NewProgramRoots.push_back(GetItemInfo(pFolder,pidl,0));
|
|
}
|
|
}
|
|
|
|
bool CItemManager::IsNewProgram( PIDLIST_ABSOLUTE pidl, bool bFolder, bool bMetroApp )
|
|
{
|
|
Assert(GetCurrentThreadId()==m_MainThreadId);
|
|
if (m_NewPrograms.empty()) return false;
|
|
if (!bMetroApp)
|
|
{
|
|
// check if the item is under the approved roots
|
|
bool bValid=false;
|
|
for (std::vector<const ItemInfo*>::const_iterator it=m_NewProgramRoots.begin();it!=m_NewProgramRoots.end();++it)
|
|
{
|
|
if (ILIsParent((*it)->GetPidl(),pidl,FALSE))
|
|
{
|
|
bValid=true;
|
|
break;
|
|
}
|
|
}
|
|
if (!bValid) return false;
|
|
}
|
|
for (std::vector<const ItemInfo*>::const_iterator it=m_NewPrograms.begin();it!=m_NewPrograms.end();++it)
|
|
{
|
|
if (bFolder && ILIsParent(pidl,(*it)->GetPidl(),FALSE))
|
|
return true;
|
|
if (!bFolder && ILIsEqual(pidl,(*it)->GetPidl()))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void CItemManager::LoadOldItems( void )
|
|
{
|
|
m_OldItemInfos.clear();
|
|
CRegKey regItems;
|
|
if (regItems.Open(HKEY_CURRENT_USER,L"Software\\OpenShell\\StartMenu",KEY_READ)==ERROR_SUCCESS)
|
|
{
|
|
ULONG size=0;
|
|
regItems.QueryBinaryValue(L"OldItems",NULL,&size);
|
|
if (size>0 && (size%sizeof(OldItemInfo))==0)
|
|
{
|
|
m_OldItemInfos.resize(size/sizeof(OldItemInfo));
|
|
regItems.QueryBinaryValue(L"OldItems",&m_OldItemInfos[0],&size);
|
|
}
|
|
}
|
|
|
|
std::sort(m_OldItemInfos.begin(),m_OldItemInfos.end());
|
|
}
|
|
|
|
void CItemManager::RemoveNewItem( PIDLIST_ABSOLUTE pItem1, PIDLIST_ABSOLUTE pItem2, bool bFolder )
|
|
{
|
|
std::vector<unsigned> hashes;
|
|
{
|
|
RWLock lock(this,false,RWLOCK_ITEMS);
|
|
for (size_t i=0;i<m_NewPrograms.size();i++)
|
|
{
|
|
const ItemInfo *pInfo=m_NewPrograms[i];
|
|
bool bFound=false;
|
|
if (bFolder && (ILIsParent(pItem1,pInfo->GetPidl(),FALSE) || (pItem2 && ILIsParent(pItem2,pInfo->GetPidl(),FALSE))))
|
|
bFound=true;
|
|
if (!bFound && !bFolder && ILIsEqual(pItem1,pInfo->GetPidl()))
|
|
bFound=true;
|
|
if (bFound)
|
|
{
|
|
unsigned hash;
|
|
if (pInfo->bMetroApp)
|
|
{
|
|
CString appid=pInfo->appid;
|
|
appid.MakeUpper();
|
|
hash=CalcFNVHash(appid);
|
|
}
|
|
else if (pInfo->bLink)
|
|
{
|
|
hash=CalcFNVHash(pInfo->PATH);
|
|
}
|
|
else continue;
|
|
m_NewPrograms.erase(m_NewPrograms.begin()+i);
|
|
hashes.push_back(hash);
|
|
i--;
|
|
}
|
|
}
|
|
}
|
|
AddOldItems(hashes);
|
|
}
|
|
|
|
void CItemManager::RemoveNewItems( bool bPrograms, bool bMetro )
|
|
{
|
|
std::vector<unsigned> hashes;
|
|
{
|
|
RWLock lock(this,false,RWLOCK_ITEMS);
|
|
for (size_t i=0;i<m_NewPrograms.size();i++)
|
|
{
|
|
const ItemInfo *pInfo=m_NewPrograms[i];
|
|
if (pInfo->location==LOCATION_METRO && !bMetro)
|
|
continue;
|
|
if (pInfo->location!=LOCATION_METRO && !bPrograms)
|
|
continue;
|
|
unsigned hash;
|
|
if (pInfo->bMetroApp)
|
|
{
|
|
CString appid=pInfo->appid;
|
|
appid.MakeUpper();
|
|
hash=CalcFNVHash(appid);
|
|
}
|
|
else if (pInfo->bLink)
|
|
{
|
|
hash=CalcFNVHash(pInfo->PATH);
|
|
}
|
|
else continue;
|
|
m_NewPrograms.erase(m_NewPrograms.begin()+i);
|
|
hashes.push_back(hash);
|
|
i--;
|
|
}
|
|
}
|
|
AddOldItems(hashes);
|
|
if (bMetro)
|
|
m_bHasNewApps[0]=m_bHasNewApps[1]=false;
|
|
if (bPrograms)
|
|
m_bHasNewPrograms[0]=m_bHasNewPrograms[1]=false;
|
|
}
|
|
|
|
void CItemManager::AddOldItems( const std::vector<unsigned> &hashes )
|
|
{
|
|
// purge items older than 48 hours
|
|
LONGLONG timestamp;
|
|
GetSystemTimeAsFileTime((FILETIME*)×tamp);
|
|
timestamp-=36000000000ll*48;
|
|
for (size_t i=0;i<m_OldItemInfos.size();i++)
|
|
{
|
|
if (CompareFileTime((FILETIME*)×tamp,&m_OldItemInfos[i].timestamp)>0)
|
|
{
|
|
m_OldItemInfos.erase(m_OldItemInfos.begin()+i);
|
|
i--;
|
|
}
|
|
}
|
|
|
|
for (std::vector<unsigned>::const_iterator it=hashes.begin();it!=hashes.end();++it)
|
|
{
|
|
OldItemInfo key={*it};
|
|
GetSystemTimeAsFileTime(&key.timestamp);
|
|
std::vector<OldItemInfo>::iterator it2=std::lower_bound(m_OldItemInfos.begin(),m_OldItemInfos.end(),key);
|
|
if (it2!=m_OldItemInfos.end() && it2->hash==*it)
|
|
it2->timestamp=key.timestamp;
|
|
else
|
|
m_OldItemInfos.insert(it2,key);
|
|
}
|
|
|
|
CRegKey regItems;
|
|
if (regItems.Open(HKEY_CURRENT_USER,L"Software\\OpenShell\\StartMenu")!=ERROR_SUCCESS)
|
|
regItems.Create(HKEY_CURRENT_USER,L"Software\\OpenShell\\StartMenu");
|
|
|
|
if (m_OldItemInfos.empty())
|
|
regItems.SetBinaryValue(L"OldItems",NULL,0);
|
|
else
|
|
regItems.SetBinaryValue(L"OldItems",&m_OldItemInfos[0],ULONG((size_t)m_OldItemInfos.size()*sizeof(OldItemInfo)));
|
|
}
|
|
|
|
CString GetPropertyStoreString( IPropertyStore *pStore, REFPROPERTYKEY key )
|
|
{
|
|
PROPVARIANT val;
|
|
PropVariantInit(&val);
|
|
CString res;
|
|
if (SUCCEEDED(pStore->GetValue(key,&val)))
|
|
{
|
|
if (val.vt==VT_LPWSTR || val.vt==VT_BSTR)
|
|
res=val.pwszVal;
|
|
else if (val.vt==VT_LPSTR)
|
|
res=val.pszVal;
|
|
}
|
|
PropVariantClear(&val);
|
|
return res;
|
|
}
|
|
|
|
// doesn't require the lock to be held
|
|
void CItemManager::RefreshItemInfo( ItemInfo *pInfo, int refreshFlags, IShellItem *pItem0, bool bHasWriteLock )
|
|
{
|
|
ItemInfo newInfo;
|
|
|
|
{
|
|
// get info from pInfo
|
|
RWLock lock(this,false,bHasWriteLock?RWLOCK_COUNT:RWLOCK_ITEMS);
|
|
newInfo=*pInfo;
|
|
}
|
|
|
|
CComPtr<IShellItem> pItem;
|
|
CComPtr<IShellItem> pAppItem;
|
|
bool bStartScreen=
|
|
#ifndef STARTSCREEN_WIN7
|
|
GetWinVersion()>=WIN_VER_WIN8 &&
|
|
#endif
|
|
(_wcsicmp(PathFindFileName(newInfo.PATH),STARTSCREEN_COMMAND)==0);
|
|
bool bValidateIcons=!bStartScreen; // hack - don't mark the icon as valid, so we have to load it next time
|
|
{
|
|
// do the real work
|
|
|
|
int pInfo=0; // shadow the pInfo parameter while in this block
|
|
if ((refreshFlags&INFO_DATA) || !(newInfo.bIconOnly || newInfo.bMetroLink))
|
|
{
|
|
if (pItem0)
|
|
pItem=pItem0;
|
|
else
|
|
{
|
|
const CAbsolutePidl &pidl=newInfo.newPidl?newInfo.newPidl:newInfo.pidl;
|
|
if (pidl)
|
|
{
|
|
if (FAILED(SHCreateItemFromIDList(pidl,IID_IShellItem,(void**)&pItem)))
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (refreshFlags&INFO_DATA)
|
|
{
|
|
Assert(!newInfo.bIconOnly);
|
|
|
|
// these two are always updated even if INFO_LINK is not requested
|
|
newInfo.bLink=false;
|
|
newInfo.bNoPin=false;
|
|
newInfo.bNoNew=false;
|
|
newInfo.bExplicitAppId=false;
|
|
newInfo.targetPATH.Empty();
|
|
newInfo.targetPidl.Clear();
|
|
if (refreshFlags&INFO_LINK_APPID)
|
|
{
|
|
newInfo.appid.Empty();
|
|
}
|
|
|
|
if (refreshFlags&INFO_METRO)
|
|
{
|
|
newInfo.bMetroLink=false;
|
|
newInfo.bMetroApp=false;
|
|
newInfo.bProtectedLink=false;
|
|
newInfo.metroName.Empty();
|
|
newInfo.iconPath.Empty();
|
|
newInfo.iconColor=0;
|
|
}
|
|
|
|
// refresh link and metro
|
|
SFGAOF flags=0;
|
|
if (pItem && SUCCEEDED(pItem->GetAttributes(SFGAO_LINK|SFGAO_FILESYSTEM,&flags)))
|
|
{
|
|
CComQIPtr<IPropertyStore> pStore;
|
|
if (flags&SFGAO_LINK)
|
|
{
|
|
// get link properties
|
|
CComPtr<IShellLink> pLink;
|
|
if (SUCCEEDED(pItem->BindToHandler(NULL,BHID_SFUIObject,IID_IShellLink,(void**)&pLink)))
|
|
{
|
|
newInfo.bLink=true;
|
|
pStore=pLink;
|
|
|
|
if (SUCCEEDED(pLink->GetIDList(&newInfo.targetPidl)))
|
|
{
|
|
wchar_t path[_MAX_PATH];
|
|
if (SUCCEEDED(SHGetPathFromIDList(newInfo.targetPidl,path)))
|
|
{
|
|
CharUpper(path);
|
|
newInfo.targetPATH=path;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (!(flags&SFGAO_FILESYSTEM))
|
|
{
|
|
newInfo.bLink=false;
|
|
pItem->BindToHandler(NULL,BHID_PropertyStore,IID_IPropertyStore,(void**)&pStore);
|
|
}
|
|
if (pStore)
|
|
{
|
|
if (GetWinVersion()>=WIN_VER_WIN7)
|
|
{
|
|
newInfo.appid=GetPropertyStoreString(pStore,PKEY_AppUserModel_ID);
|
|
newInfo.bExplicitAppId=!newInfo.appid.IsEmpty();
|
|
}
|
|
if (!newInfo.appid.IsEmpty() && (refreshFlags&INFO_METRO) && (GetWinVersion()<=WIN_VER_WIN8 || _wcsicmp(newInfo.appid,SEARCH_APP_ID)!=0))
|
|
{
|
|
PROPVARIANT val;
|
|
PropVariantInit(&val);
|
|
if (SUCCEEDED(pStore->GetValue(PKEY_MetroAppLauncher,&val)) && (val.vt==VT_I4 || val.vt==VT_UI4) && val.intVal)
|
|
{
|
|
// get Metro properties
|
|
PropVariantClear(&val);
|
|
newInfo.bMetroLink=newInfo.bLink;
|
|
newInfo.bMetroApp=!newInfo.bLink;
|
|
newInfo.bProtectedLink=false;
|
|
if (newInfo.bMetroApp)
|
|
{
|
|
pAppItem=pItem;
|
|
newInfo.packagePath=GetPropertyStoreString(pStore,PKEY_MetroPackagePath);
|
|
if (!newInfo.packagePath.IsEmpty())
|
|
{
|
|
FILETIME writeTime={0}, createTime={0};
|
|
if (MenuGetFileTimestamp(newInfo.packagePath,&writeTime,&createTime))
|
|
{
|
|
newInfo.writestamp=writeTime;
|
|
newInfo.createstamp=createTime;
|
|
}
|
|
}
|
|
}
|
|
if (newInfo.bMetroLink && wcsncmp(newInfo.PATH,m_RootCommonPrograms,m_RootCommonPrograms.GetLength())==0)
|
|
{
|
|
const wchar_t *str=newInfo.PATH;
|
|
newInfo.bProtectedLink=m_RootCommonPrograms.GetLength()==(PathFindFileName(str)-str);
|
|
}
|
|
if (SUCCEEDED(pStore->GetValue(PKEY_MetroIconColor,&val)) && (val.vt==VT_I4 || val.vt==VT_UI4))
|
|
newInfo.iconColor=val.intVal;
|
|
if (pAppItem || SUCCEEDED(SHCreateItemInKnownFolder(FOLDERID_AppsFolder2,0,newInfo.appid,IID_IShellItem,(void**)&pAppItem)))
|
|
{
|
|
CComString pName;
|
|
if (SUCCEEDED(pAppItem->GetDisplayName(SIGDN_NORMALDISPLAY,&pName)))
|
|
{
|
|
newInfo.metroName=pName;
|
|
}
|
|
}
|
|
}
|
|
PropVariantClear(&val);
|
|
}
|
|
if (!newInfo.bMetroLink && !newInfo.bMetroApp)
|
|
{
|
|
PROPVARIANT val;
|
|
PropVariantInit(&val);
|
|
if (SUCCEEDED(pStore->GetValue(PKEY_AppUserModel_PreventPinning,&val)) && val.vt==VT_BOOL && val.boolVal)
|
|
newInfo.bNoPin=true;
|
|
PropVariantClear(&val);
|
|
if (SUCCEEDED(pStore->GetValue(PKEY_AppUserModel_ExcludeFromShowInNewInstall,&val)) && val.vt==VT_BOOL && val.boolVal)
|
|
newInfo.bNoNew=true;
|
|
PropVariantClear(&val);
|
|
}
|
|
}
|
|
|
|
if (newInfo.bLink && newInfo.appid.IsEmpty() && (refreshFlags&INFO_LINK_APPID))
|
|
{
|
|
Assert(GetCurrentThreadId()==m_MainThreadId);
|
|
if (!g_pAppResolver)
|
|
CreateAppResolver();
|
|
if (g_pAppResolver)
|
|
{
|
|
CComString pAppId;
|
|
if (SUCCEEDED(g_pAppResolver->GetAppIDForShortcut(pItem,&pAppId)))
|
|
newInfo.appid=pAppId;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (!newInfo.bIconOnly)
|
|
{
|
|
if (!newInfo.bLink)
|
|
{
|
|
newInfo.targetPidl.Clear();
|
|
newInfo.targetPATH.Empty();
|
|
newInfo.metroName.Empty();
|
|
newInfo.iconPath.Empty();
|
|
newInfo.bNoPin=newInfo.bNoNew=false;
|
|
if (!newInfo.bMetroApp)
|
|
{
|
|
newInfo.bExplicitAppId=false;
|
|
newInfo.appid.Empty();
|
|
newInfo.packagePath.Empty();
|
|
}
|
|
}
|
|
else if (newInfo.bMetroLink)
|
|
{
|
|
newInfo.targetPidl.Clear();
|
|
newInfo.targetPATH.Empty();
|
|
}
|
|
}
|
|
|
|
if (refreshFlags&INFO_ICON)
|
|
{
|
|
// load icons
|
|
if (newInfo.bIconOnly)
|
|
{
|
|
LoadCustomIcon(newInfo.iconPath,newInfo.iconIndex,refreshFlags&INFO_ICON,newInfo.smallIcon,newInfo.largeIcon,newInfo.extraLargeIcon,newInfo.bTemp);
|
|
}
|
|
else if (newInfo.bMetroLink || newInfo.bMetroApp)
|
|
{
|
|
if (pAppItem || SUCCEEDED(SHCreateItemInKnownFolder(FOLDERID_AppsFolder2,0,newInfo.appid,IID_IShellItem,(void**)&pAppItem)))
|
|
{
|
|
int iconFlags=refreshFlags&INFO_ICON;
|
|
LoadMetroIcon(pAppItem,iconFlags,newInfo.smallIcon,newInfo.largeIcon,newInfo.extraLargeIcon,&newInfo.iconColor);
|
|
if (iconFlags)
|
|
LoadShellIcon(pItem?pItem:pAppItem,iconFlags,newInfo.smallIcon,newInfo.largeIcon,newInfo.extraLargeIcon,&newInfo.iconColor);
|
|
}
|
|
}
|
|
else if (_wcsicmp(PathFindExtension(newInfo.path),L".settingcontent-ms")==0)
|
|
{
|
|
wchar_t iconPath[_MAX_PATH]=L"%windir%\\ImmersiveControlPanel\\systemsettings.exe";
|
|
DoEnvironmentSubst(iconPath,_countof(iconPath));
|
|
newInfo.iconPath=iconPath;
|
|
newInfo.iconIndex=-10;
|
|
LoadCustomIcon(newInfo.iconPath,newInfo.iconIndex,refreshFlags&INFO_ICON,newInfo.smallIcon,newInfo.largeIcon,newInfo.extraLargeIcon,false);
|
|
}
|
|
else
|
|
{
|
|
LoadShellIcon(pItem,(refreshFlags&INFO_ICON)|(bStartScreen?INFO_STARTSCREEN_ICON:0),newInfo.smallIcon,newInfo.largeIcon,newInfo.extraLargeIcon,NULL);
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
// store info in pInfo
|
|
RWLock lock(this,true,bHasWriteLock?RWLOCK_COUNT:RWLOCK_ITEMS);
|
|
|
|
if (refreshFlags&INFO_DATA)
|
|
{
|
|
pInfo->bLink=newInfo.bLink;
|
|
pInfo->bMetroLink=newInfo.bMetroLink;
|
|
pInfo->bMetroApp=newInfo.bMetroApp;
|
|
pInfo->bProtectedLink=newInfo.bProtectedLink;
|
|
pInfo->bNoPin=newInfo.bNoPin;
|
|
pInfo->bNoNew=newInfo.bNoNew;
|
|
pInfo->bExplicitAppId=newInfo.bExplicitAppId;
|
|
pInfo->targetPidl.Swap(newInfo.targetPidl);
|
|
pInfo->targetPATH=newInfo.targetPATH;
|
|
pInfo->packagePath=newInfo.packagePath;
|
|
if ((refreshFlags&INFO_LINK_APPID) || !newInfo.appid.IsEmpty())
|
|
pInfo->appid=newInfo.appid;
|
|
if (!pInfo->appid.IsEmpty())
|
|
refreshFlags|=INFO_LINK_APPID; // appid is valid, no need to resolve
|
|
pInfo->metroName=newInfo.metroName;
|
|
pInfo->iconPath=newInfo.iconPath;
|
|
pInfo->iconColor=newInfo.iconColor;
|
|
if (pInfo->bMetroApp)
|
|
{
|
|
pInfo->writestamp=newInfo.writestamp;
|
|
pInfo->createstamp=newInfo.createstamp;
|
|
}
|
|
}
|
|
|
|
if (refreshFlags&INFO_SMALL_ICON)
|
|
pInfo->smallIcon=newInfo.smallIcon;
|
|
if (refreshFlags&INFO_LARGE_ICON)
|
|
pInfo->largeIcon=newInfo.largeIcon;
|
|
if (refreshFlags&INFO_EXTRA_LARGE_ICON)
|
|
pInfo->extraLargeIcon=newInfo.extraLargeIcon;
|
|
if (bValidateIcons)
|
|
pInfo->validFlags|=refreshFlags&(INFO_DATA|INFO_ICON);
|
|
else
|
|
pInfo->validFlags|=refreshFlags&INFO_DATA;
|
|
pInfo->refreshFlags&=~refreshFlags;
|
|
}
|
|
}
|
|
|
|
void CItemManager::RefreshInfos( void )
|
|
{
|
|
Assert(GetCurrentThreadId()==m_MainThreadId);
|
|
RWLock lock(this,true,RWLOCK_ITEMS);
|
|
for (std::multimap<unsigned int,ItemInfo>::iterator it=m_ItemInfos.begin();it!=m_ItemInfos.end();++it)
|
|
{
|
|
if (it->second.newPidl)
|
|
{
|
|
it->second.pidl.Swap(it->second.newPidl);
|
|
it->second.newPidl.Clear();
|
|
it->second.validFlags=0;
|
|
}
|
|
}
|
|
}
|
|
|
|
HBITMAP CItemManager::BitmapFromIcon( HICON hIcon, int iconSize, bool bDestroyIcon )
|
|
{
|
|
if (!hIcon) return NULL;
|
|
LoadIconData &data=GetLoadIconData();
|
|
if (!data.m_pFactory)
|
|
{
|
|
if (bDestroyIcon)
|
|
DestroyIcon(hIcon);
|
|
return NULL;
|
|
}
|
|
|
|
CComPtr<IWICBitmap> pBitmap;
|
|
HRESULT hr=data.m_pFactory->CreateBitmapFromHICON(hIcon,&pBitmap);
|
|
if (bDestroyIcon)
|
|
DestroyIcon(hIcon);
|
|
if (FAILED(hr))
|
|
return NULL;
|
|
|
|
CComPtr<IWICFormatConverter> pConverter;
|
|
if (FAILED(data.m_pFactory->CreateFormatConverter(&pConverter)))
|
|
return NULL;
|
|
|
|
UINT width=0, height=0;
|
|
if (SUCCEEDED(pBitmap->GetSize(&width,&height)) && width==iconSize && height==iconSize)
|
|
{
|
|
pConverter->Initialize(pBitmap,GUID_WICPixelFormat32bppPBGRA,WICBitmapDitherTypeNone,NULL,0,WICBitmapPaletteTypeMedianCut);
|
|
}
|
|
else
|
|
{
|
|
CComPtr<IWICBitmapScaler> pScaler;
|
|
if (FAILED(data.m_pFactory->CreateBitmapScaler(&pScaler)))
|
|
return NULL;
|
|
pScaler->Initialize(pBitmap,iconSize,iconSize,WICBitmapInterpolationModeFant);
|
|
pConverter->Initialize(pScaler,GUID_WICPixelFormat32bppPBGRA,WICBitmapDitherTypeNone,NULL,0,WICBitmapPaletteTypeMedianCut);
|
|
}
|
|
|
|
BITMAPINFO bi={0};
|
|
bi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
|
|
bi.bmiHeader.biWidth=iconSize;
|
|
bi.bmiHeader.biHeight=-iconSize;
|
|
bi.bmiHeader.biPlanes=1;
|
|
bi.bmiHeader.biBitCount=32;
|
|
HDC hdc=CreateCompatibleDC(NULL);
|
|
BYTE *bits;
|
|
HBITMAP bmp=CreateDIBSection(hdc,&bi,DIB_RGB_COLORS,(void**)&bits,NULL,0);
|
|
DeleteDC(hdc);
|
|
if (!bmp) return NULL;
|
|
hr=pConverter->CopyPixels(NULL,iconSize*4,iconSize*iconSize*4,bits);
|
|
if (FAILED(hr))
|
|
{
|
|
DeleteObject(bmp);
|
|
return NULL;
|
|
}
|
|
return bmp;
|
|
}
|
|
|
|
HICON CItemManager::LoadShellIcon( int index, int iconSize )
|
|
{
|
|
LoadIconData &data=GetLoadIconData();
|
|
int sizeIndex=-1;
|
|
for (int i=0;i<_countof(data.m_IconSizes);i++)
|
|
{
|
|
if (data.m_IconSizes[i]==iconSize)
|
|
{
|
|
sizeIndex=i;
|
|
break;
|
|
}
|
|
}
|
|
if (sizeIndex==-1) return NULL;
|
|
int listType=0;
|
|
for (std::vector<std::pair<int,int>>::const_iterator it=m_ListSizes.begin();it!=m_ListSizes.end();++it)
|
|
{
|
|
listType=it->second;
|
|
if (it->first>=iconSize)
|
|
break;
|
|
}
|
|
CComPtr<IImageList> pImageList;
|
|
if (FAILED(SHGetImageList(listType,IID_IImageList,(void**)&pImageList)))
|
|
return NULL;
|
|
HICON hIcon;
|
|
|
|
if (data.m_pTempLists[sizeIndex])
|
|
{
|
|
if (SUCCEEDED(data.m_pTempLists[sizeIndex]->ReplaceFromImageList(0,pImageList,index,NULL,0)) && SUCCEEDED(data.m_pTempLists[sizeIndex]->GetIcon(0,ILD_TRANSPARENT,&hIcon)))
|
|
{
|
|
AddTrackedIcon(hIcon);
|
|
return hIcon;
|
|
}
|
|
}
|
|
if (FAILED(pImageList->GetIcon(index,ILD_TRANSPARENT,&hIcon)))
|
|
return NULL;
|
|
AddTrackedIcon(hIcon);
|
|
return hIcon;
|
|
}
|
|
|
|
HICON CItemManager::LoadShellIcon( int iconSize, IExtractIcon *pExtractW, const wchar_t *location, IExtractIconA *pExtractA, const char *locationA, int index )
|
|
{
|
|
HICON hIcon=NULL, hIcon2=NULL;
|
|
HRESULT hr;
|
|
if (iconSize<=GetSystemMetrics(SM_CXSMICON))
|
|
{
|
|
// small icon is closer
|
|
if (pExtractW)
|
|
hr=pExtractW->Extract(location,index,&hIcon2,&hIcon,MAKELONG(iconSize,iconSize));
|
|
else
|
|
hr=pExtractA->Extract(locationA,index,&hIcon2,&hIcon,MAKELONG(iconSize,iconSize));
|
|
}
|
|
else
|
|
{
|
|
// large icon is closer
|
|
if (pExtractW)
|
|
hr=pExtractW->Extract(location,index,&hIcon,&hIcon2,MAKELONG(iconSize,iconSize));
|
|
else
|
|
hr=pExtractA->Extract(locationA,index,&hIcon,&hIcon2,MAKELONG(iconSize,iconSize));
|
|
}
|
|
if (FAILED(hr)) return NULL;
|
|
|
|
if (hIcon2) DestroyIcon(hIcon2);
|
|
return hIcon;
|
|
}
|
|
|
|
void CItemManager::LoadShellIcon( IShellItem *pItem, int refreshFlags, const IconInfo *&smallIcon, const IconInfo *&largeIcon, const IconInfo *&extraLargeIcon, const DWORD *pMetroColor )
|
|
{
|
|
if (!pItem)
|
|
return;
|
|
|
|
wchar_t location[_MAX_PATH];
|
|
int index=0;
|
|
if (refreshFlags&INFO_STARTSCREEN_ICON)
|
|
{
|
|
CComPtr<IShellLink> pLink;
|
|
if (SUCCEEDED(pItem->BindToHandler(NULL,BHID_SFUIObject,IID_IShellLink,(void**)&pLink)))
|
|
{
|
|
if (SUCCEEDED(pLink->GetIconLocation(location,_countof(location),&index)) && (!location[0] || _wcsicmp(PathFindFileName(location),L"StartScreen.exe")==0))
|
|
{
|
|
unsigned int hash=CalcFNVHash(STARTSCREEN_COMMAND);
|
|
HBITMAP hSmallBitmap=NULL, hLargeBitmap=NULL, hExtraLargeBitmap=NULL;
|
|
if (refreshFlags&INFO_SMALL_ICON)
|
|
{
|
|
hSmallBitmap=GetStartScreenIcon(SMALL_ICON_SIZE);
|
|
}
|
|
|
|
if (refreshFlags&INFO_LARGE_ICON)
|
|
{
|
|
hLargeBitmap=GetStartScreenIcon(LARGE_ICON_SIZE);
|
|
}
|
|
|
|
if (refreshFlags&INFO_EXTRA_LARGE_ICON)
|
|
{
|
|
hExtraLargeBitmap=GetStartScreenIcon(EXTRA_LARGE_ICON_SIZE);
|
|
}
|
|
|
|
StoreInCache(hash,NULL,hSmallBitmap,hLargeBitmap,hExtraLargeBitmap,refreshFlags,smallIcon,largeIcon,extraLargeIcon,false,false);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// get the IExtractIcon object
|
|
CComPtr<IExtractIcon> pExtractW;
|
|
CComPtr<IExtractIconA> pExtractA;
|
|
|
|
bool bNotFileName;
|
|
bool bTransient=false;
|
|
char locationA[_MAX_PATH];
|
|
|
|
// get the icon location
|
|
if (SUCCEEDED(pItem->BindToHandler(NULL,BHID_SFUIObject,IID_IExtractIconW,(void**)&pExtractW)))
|
|
{
|
|
UINT iconFlags=0;
|
|
if (pExtractW->GetIconLocation(0,location,_countof(location),&index,&iconFlags)!=S_OK)
|
|
return;
|
|
|
|
bNotFileName=(iconFlags&GIL_NOTFILENAME)!=0;
|
|
bTransient=(iconFlags&GIL_DONTCACHE)!=0;
|
|
}
|
|
else if (SUCCEEDED(pItem->BindToHandler(NULL,BHID_SFUIObject,IID_IExtractIconA,(void**)&pExtractA)))
|
|
{
|
|
UINT iconFlags=0;
|
|
if (pExtractA->GetIconLocation(0,locationA,_countof(locationA),&index,&iconFlags)!=S_OK)
|
|
return;
|
|
|
|
MbsToWcs(location,_countof(location),locationA);
|
|
bNotFileName=(iconFlags&GIL_NOTFILENAME)!=0;
|
|
bTransient=(iconFlags&GIL_DONTCACHE)!=0;
|
|
}
|
|
else
|
|
return;
|
|
|
|
unsigned int hash;
|
|
if (bTransient)
|
|
hash=m_TransientHash++;
|
|
else if (location[0])
|
|
hash=CalcFNVHash(location,CalcFNVHash(&index,4));
|
|
else
|
|
{
|
|
// if the location is blank, use the parsing name for the hash
|
|
CComString pName;
|
|
pItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING,&pName);
|
|
hash=CalcFNVHash(pName,CalcFNVHash(&index,4));
|
|
}
|
|
|
|
if (pMetroColor)
|
|
hash=CalcFNVHash(pMetroColor,4,hash);
|
|
|
|
if (!bTransient)
|
|
FindInCache(hash,refreshFlags,smallIcon,largeIcon,extraLargeIcon);
|
|
if (!refreshFlags) return;
|
|
|
|
// extract icon
|
|
HBITMAP hSmallBitmap=NULL, hLargeBitmap=NULL, hExtraLargeBitmap=NULL;
|
|
|
|
wchar_t metroLocation[_MAX_PATH];
|
|
Strcpy(metroLocation,_countof(metroLocation),location);
|
|
if (pMetroColor && ParseMetroBitmapLocation(metroLocation))
|
|
{
|
|
if (refreshFlags&INFO_SMALL_ICON)
|
|
{
|
|
hSmallBitmap=LoadMetroBitmap(metroLocation,SMALL_ICON_SIZE,*pMetroColor);
|
|
}
|
|
if (refreshFlags&INFO_LARGE_ICON)
|
|
{
|
|
hLargeBitmap=LoadMetroBitmap(metroLocation,LARGE_ICON_SIZE,*pMetroColor);
|
|
}
|
|
if (refreshFlags&INFO_EXTRA_LARGE_ICON)
|
|
{
|
|
hExtraLargeBitmap=LoadMetroBitmap(metroLocation,EXTRA_LARGE_ICON_SIZE,*pMetroColor);
|
|
}
|
|
if (hSmallBitmap || hLargeBitmap || hExtraLargeBitmap)
|
|
{
|
|
metroLocation[1]='#';
|
|
StoreInCache(hash,metroLocation,hSmallBitmap,hLargeBitmap,hExtraLargeBitmap,refreshFlags,smallIcon,largeIcon,extraLargeIcon,bTransient,true);
|
|
return;
|
|
}
|
|
}
|
|
|
|
Strcpy(metroLocation,_countof(metroLocation),location);
|
|
if (pMetroColor && GetWinVersion()>=WIN_VER_WIN10 && ParseMetroBitmapLocation2(metroLocation))
|
|
{
|
|
if (refreshFlags&INFO_SMALL_ICON)
|
|
{
|
|
hSmallBitmap=LoadMetroBitmap2(metroLocation,SMALL_ICON_SIZE,*pMetroColor);
|
|
}
|
|
if (refreshFlags&INFO_LARGE_ICON)
|
|
{
|
|
hLargeBitmap=LoadMetroBitmap2(metroLocation,LARGE_ICON_SIZE,*pMetroColor);
|
|
}
|
|
if (refreshFlags&INFO_EXTRA_LARGE_ICON)
|
|
{
|
|
hExtraLargeBitmap=LoadMetroBitmap2(metroLocation,EXTRA_LARGE_ICON_SIZE,*pMetroColor);
|
|
}
|
|
if (hSmallBitmap || hLargeBitmap || hExtraLargeBitmap)
|
|
{
|
|
metroLocation[1]='#';
|
|
StoreInCache(hash,metroLocation,hSmallBitmap,hLargeBitmap,hExtraLargeBitmap,refreshFlags,smallIcon,largeIcon,extraLargeIcon,bTransient,true);
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (GetWinVersion()>=WIN_VER_WIN10)
|
|
pMetroColor=NULL;
|
|
|
|
int smallIconSize=SMALL_ICON_SIZE;
|
|
int largeIconSize=LARGE_ICON_SIZE;
|
|
int extraLargeIconSize=EXTRA_LARGE_ICON_SIZE;
|
|
if (pMetroColor)
|
|
{
|
|
smallIconSize-=2;
|
|
largeIconSize-=2;
|
|
extraLargeIconSize-=2;
|
|
}
|
|
HICON hSmallIcon=NULL, hLargeIcon=NULL, hExtraLargeIcon=NULL;
|
|
if (bNotFileName)
|
|
{
|
|
CAbsolutePidl pidl;
|
|
SHFILEINFO info;
|
|
if (SUCCEEDED(SHGetIDListFromObject(pItem,&pidl)) && SHGetFileInfo((const wchar_t*)(PIDLIST_ABSOLUTE)pidl,0,&info,sizeof(info),SHGFI_PIDL|SHGFI_SYSICONINDEX))
|
|
{
|
|
if (refreshFlags&INFO_SMALL_ICON)
|
|
hSmallIcon=LoadShellIcon(info.iIcon,smallIconSize);
|
|
if (refreshFlags&INFO_LARGE_ICON)
|
|
hLargeIcon=LoadShellIcon(info.iIcon,largeIconSize);
|
|
if (refreshFlags&INFO_EXTRA_LARGE_ICON)
|
|
hExtraLargeIcon=LoadShellIcon(info.iIcon,extraLargeIconSize);
|
|
}
|
|
else
|
|
{
|
|
// fall back to the extractor
|
|
if (refreshFlags&INFO_SMALL_ICON)
|
|
hSmallIcon=LoadShellIcon(smallIconSize,pExtractW,location,pExtractA,locationA,index);
|
|
if (refreshFlags&INFO_LARGE_ICON)
|
|
hLargeIcon=LoadShellIcon(largeIconSize,pExtractW,location,pExtractA,locationA,index);
|
|
if (refreshFlags&INFO_EXTRA_LARGE_ICON)
|
|
hExtraLargeIcon=LoadShellIcon(extraLargeIconSize,pExtractW,location,pExtractA,locationA,index);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DoEnvironmentSubst(location,_countof(location));
|
|
if (refreshFlags&INFO_SMALL_ICON)
|
|
hSmallIcon=ShExtractIcon(location,index==-1?0:index,smallIconSize);
|
|
if (refreshFlags&INFO_LARGE_ICON)
|
|
hLargeIcon=ShExtractIcon(location,index==-1?0:index,largeIconSize);
|
|
if (refreshFlags&INFO_EXTRA_LARGE_ICON)
|
|
hExtraLargeIcon=ShExtractIcon(location,index==-1?0:index,extraLargeIconSize);
|
|
}
|
|
if (hSmallIcon)
|
|
{
|
|
if (pMetroColor)
|
|
hSmallBitmap=BitmapFromMetroIcon(hSmallIcon,SMALL_ICON_SIZE,smallIconSize,*pMetroColor);
|
|
else
|
|
hSmallBitmap=BitmapFromIcon(hSmallIcon,smallIconSize);
|
|
}
|
|
if (hLargeIcon)
|
|
{
|
|
if (pMetroColor)
|
|
hLargeBitmap=BitmapFromMetroIcon(hLargeIcon,LARGE_ICON_SIZE,largeIconSize,*pMetroColor);
|
|
else
|
|
hLargeBitmap=BitmapFromIcon(hLargeIcon,largeIconSize);
|
|
}
|
|
if (hExtraLargeIcon)
|
|
{
|
|
if (pMetroColor)
|
|
hExtraLargeBitmap=BitmapFromMetroIcon(hExtraLargeIcon,EXTRA_LARGE_ICON_SIZE,extraLargeIconSize,*pMetroColor);
|
|
else
|
|
hExtraLargeBitmap=BitmapFromIcon(hExtraLargeIcon,extraLargeIconSize);
|
|
}
|
|
if (pMetroColor)
|
|
location[1]='#';
|
|
StoreInCache(hash,bNotFileName?NULL:location,hSmallBitmap,hLargeBitmap,hExtraLargeBitmap,refreshFlags,smallIcon,largeIcon,extraLargeIcon,bTransient,false);
|
|
}
|
|
|
|
static bool SetResContextTargetSize( IResourceContext *pResContext, int size )
|
|
{
|
|
if (GetWinVersion()>=WIN_VER_WIN10)
|
|
{
|
|
if (SUCCEEDED(pResContext->SetTargetSize(size)))
|
|
return true;
|
|
}
|
|
RESOURCE_SCALE scale;
|
|
if (size<=24)
|
|
scale=RES_SCALE_80;
|
|
else if (size<=30)
|
|
scale=RES_SCALE_100;
|
|
else if (size<=42)
|
|
scale=RES_SCALE_140;
|
|
else
|
|
scale=RES_SCALE_180;
|
|
return SUCCEEDED(pResContext->SetScale(scale));
|
|
}
|
|
|
|
void CItemManager::LoadMetroIcon( IShellItem *pItem, int &refreshFlags, const IconInfo *&smallIcon, const IconInfo *&largeIcon, const IconInfo *&extraLargeIcon, const DWORD *pMetroColor )
|
|
{
|
|
unsigned int hash;
|
|
{
|
|
CComString pName;
|
|
pItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING,&pName);
|
|
hash=CalcFNVHash(pName);
|
|
}
|
|
|
|
hash=CalcFNVHash(pMetroColor,4,hash);
|
|
|
|
FindInCache(hash,refreshFlags,smallIcon,largeIcon,extraLargeIcon);
|
|
if (!refreshFlags) return;
|
|
|
|
// extract icon
|
|
HBITMAP hSmallBitmap=NULL, hLargeBitmap=NULL, hExtraLargeBitmap=NULL;
|
|
|
|
CComPtr<IPropertyStore> pStore;
|
|
pItem->BindToHandler(NULL,BHID_PropertyStore,IID_IPropertyStore,(void**)&pStore);
|
|
if (!pStore) return;
|
|
|
|
CString packageName=GetPropertyStoreString(pStore,PKEY_MetroPackageName);
|
|
if (packageName.IsEmpty()) return;
|
|
CString iconName=GetPropertyStoreString(pStore,PKEY_MetroIcon);
|
|
if (iconName.IsEmpty()) return;
|
|
|
|
CComPtr<IResourceManager> pResManager;
|
|
if (FAILED(pResManager.CoCreateInstance(CLSID_ResourceManager)))
|
|
return;
|
|
if (FAILED(pResManager->InitializeForPackage(packageName)))
|
|
return;
|
|
CComPtr<IResourceMap> pResMap;
|
|
if (FAILED(pResManager->GetMainResourceMap(IID_IResourceMap,(void**)&pResMap)))
|
|
return;
|
|
CComPtr<IResourceContext> pResContext;
|
|
if (FAILED(pResManager->GetDefaultContext(IID_ResourceContext,(void**)&pResContext)))
|
|
return;
|
|
int iconFlags=0;
|
|
int delta=g_bInvertMetroIcons?0:2;
|
|
if ((refreshFlags&INFO_SMALL_ICON) && SetResContextTargetSize(pResContext,SMALL_ICON_SIZE-delta))
|
|
{
|
|
CComString pLocation;
|
|
if (SUCCEEDED(pResMap->GetFilePath(iconName,&pLocation)))
|
|
{
|
|
hSmallBitmap=LoadMetroBitmap0(pLocation,SMALL_ICON_SIZE,*pMetroColor);
|
|
refreshFlags&=~INFO_SMALL_ICON;
|
|
StoreInCache(hash,L"",hSmallBitmap,NULL,NULL,INFO_SMALL_ICON,smallIcon,largeIcon,extraLargeIcon,false,true);
|
|
}
|
|
}
|
|
if ((refreshFlags&INFO_LARGE_ICON) && SetResContextTargetSize(pResContext,LARGE_ICON_SIZE-delta))
|
|
{
|
|
CComString pLocation;
|
|
if (SUCCEEDED(pResMap->GetFilePath(iconName,&pLocation)))
|
|
{
|
|
hLargeBitmap=LoadMetroBitmap0(pLocation,LARGE_ICON_SIZE,*pMetroColor);
|
|
refreshFlags&=~INFO_LARGE_ICON;
|
|
StoreInCache(hash,L"",NULL,hLargeBitmap,NULL,INFO_LARGE_ICON,smallIcon,largeIcon,extraLargeIcon,false,true);
|
|
}
|
|
}
|
|
if ((refreshFlags&INFO_SMALL_ICON) && SetResContextTargetSize(pResContext,EXTRA_LARGE_ICON_SIZE-delta))
|
|
{
|
|
CComString pLocation;
|
|
if (SUCCEEDED(pResMap->GetFilePath(iconName,&pLocation)))
|
|
{
|
|
hExtraLargeBitmap=LoadMetroBitmap0(pLocation,EXTRA_LARGE_ICON_SIZE,*pMetroColor);
|
|
refreshFlags&=~INFO_EXTRA_LARGE_ICON;
|
|
StoreInCache(hash,L"",NULL,NULL,hExtraLargeBitmap,INFO_EXTRA_LARGE_ICON,smallIcon,largeIcon,extraLargeIcon,false,true);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CItemManager::FindInCache( unsigned int hash, int &refreshFlags, const IconInfo *&smallIcon, const IconInfo *&largeIcon, const IconInfo *&extraLargeIcon )
|
|
{
|
|
// look in the cache
|
|
RWLock lock(this,false,RWLOCK_ICONS);
|
|
std::multimap<unsigned int,IconInfo>::iterator it=m_IconInfos.find(hash);
|
|
for (;it!=m_IconInfos.end() && it->first==hash;++it)
|
|
{
|
|
if ((refreshFlags&INFO_SMALL_ICON) && it->second.sizeType==ICON_SIZE_TYPE_SMALL)
|
|
{
|
|
smallIcon=&it->second;
|
|
refreshFlags&=~INFO_SMALL_ICON;
|
|
}
|
|
if ((refreshFlags&INFO_LARGE_ICON) && it->second.sizeType==ICON_SIZE_TYPE_LARGE)
|
|
{
|
|
largeIcon=&it->second;
|
|
refreshFlags&=~INFO_LARGE_ICON;
|
|
}
|
|
if ((refreshFlags&INFO_EXTRA_LARGE_ICON) && it->second.sizeType==ICON_SIZE_TYPE_EXTRA_LARGE)
|
|
{
|
|
extraLargeIcon=&it->second;
|
|
refreshFlags&=~INFO_EXTRA_LARGE_ICON;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CItemManager::StoreInCache( unsigned int hash, const wchar_t *path, HBITMAP hSmallBitmap, HBITMAP hLargeBitmap, HBITMAP hExtraLargeBitmap, int refreshFlags, const IconInfo *&smallIcon, const IconInfo *&largeIcon, const IconInfo *&extraLargeIcon, bool bTemp, bool bMetro )
|
|
{
|
|
RWLock lock(this,true,RWLOCK_ICONS);
|
|
std::multimap<unsigned int,IconInfo>::iterator it=m_IconInfos.find(hash);
|
|
for (;it!=m_IconInfos.end() && it->first==hash;++it)
|
|
{
|
|
if ((refreshFlags&INFO_SMALL_ICON) && it->second.sizeType==ICON_SIZE_TYPE_SMALL)
|
|
{
|
|
if (hSmallBitmap)
|
|
{
|
|
HBITMAP old=it->second.bitmap;
|
|
it->second.bitmap=hSmallBitmap;
|
|
if (old) m_OldBitmaps.push_back(old);
|
|
hSmallBitmap=NULL;
|
|
}
|
|
smallIcon=&it->second;
|
|
refreshFlags&=~INFO_SMALL_ICON;
|
|
}
|
|
if ((refreshFlags&INFO_LARGE_ICON) && it->second.sizeType==ICON_SIZE_TYPE_LARGE)
|
|
{
|
|
if (hLargeBitmap)
|
|
{
|
|
HBITMAP old=it->second.bitmap;
|
|
it->second.bitmap=hLargeBitmap;
|
|
if (old) m_OldBitmaps.push_back(old);
|
|
hLargeBitmap=NULL;
|
|
}
|
|
largeIcon=&it->second;
|
|
refreshFlags&=~INFO_LARGE_ICON;
|
|
}
|
|
if ((refreshFlags&INFO_EXTRA_LARGE_ICON) && it->second.sizeType==ICON_SIZE_TYPE_EXTRA_LARGE)
|
|
{
|
|
if (hExtraLargeBitmap)
|
|
{
|
|
HBITMAP old=it->second.bitmap;
|
|
it->second.bitmap=hExtraLargeBitmap;
|
|
if (old) m_OldBitmaps.push_back(old);
|
|
hExtraLargeBitmap=NULL;
|
|
}
|
|
extraLargeIcon=&it->second;
|
|
refreshFlags&=~INFO_EXTRA_LARGE_ICON;
|
|
}
|
|
}
|
|
|
|
if ((refreshFlags&INFO_SMALL_ICON) && hSmallBitmap)
|
|
{
|
|
IconInfo *pInfo=&m_IconInfos.insert(std::pair<unsigned int,IconInfo>(hash,IconInfo()))->second;
|
|
pInfo->sizeType=ICON_SIZE_TYPE_SMALL;
|
|
pInfo->bTemp=bTemp;
|
|
pInfo->bMetro=bMetro;
|
|
pInfo->SetPath(path);
|
|
pInfo->bitmap=hSmallBitmap;
|
|
smallIcon=pInfo;
|
|
}
|
|
if ((refreshFlags&INFO_LARGE_ICON) && hLargeBitmap)
|
|
{
|
|
IconInfo *pInfo=&m_IconInfos.insert(std::pair<unsigned int,IconInfo>(hash,IconInfo()))->second;
|
|
pInfo->sizeType=ICON_SIZE_TYPE_LARGE;
|
|
pInfo->bTemp=bTemp;
|
|
pInfo->bMetro=bMetro;
|
|
pInfo->SetPath(path);
|
|
pInfo->bitmap=hLargeBitmap;
|
|
largeIcon=pInfo;
|
|
}
|
|
if ((refreshFlags&INFO_EXTRA_LARGE_ICON) && hExtraLargeBitmap)
|
|
{
|
|
IconInfo *pInfo=&m_IconInfos.insert(std::pair<unsigned int,IconInfo>(hash,IconInfo()))->second;
|
|
pInfo->sizeType=ICON_SIZE_TYPE_EXTRA_LARGE;
|
|
pInfo->bTemp=bTemp;
|
|
pInfo->bMetro=bMetro;
|
|
pInfo->SetPath(path);
|
|
pInfo->bitmap=hExtraLargeBitmap;
|
|
extraLargeIcon=pInfo;
|
|
}
|
|
}
|
|
|
|
void CItemManager::IconInfo::SetPath( const wchar_t *path )
|
|
{
|
|
Assert(RWLock::ThreadHasWriteLock(RWLOCK_ICONS));
|
|
if (path && (_wcsicmp(path,L"shell32.dll")==0 || _wcsicmp(path,L"imageres.dll")==0))
|
|
{
|
|
PATH=path;
|
|
StringUpper(PATH);
|
|
timestamp.dwHighDateTime=timestamp.dwLowDateTime=0;
|
|
return;
|
|
}
|
|
if (path && path[0] && path[1]=='#')
|
|
{
|
|
PATH=path;
|
|
StringUpper(PATH);
|
|
timestamp.dwHighDateTime=timestamp.dwLowDateTime=0;
|
|
return;
|
|
}
|
|
if (path && path[0] && path[1]==':' && !PathIsNetworkPath(path))
|
|
{
|
|
WIN32_FILE_ATTRIBUTE_DATA attributes={0};
|
|
if (GetFileAttributesEx(path,GetFileExInfoStandard,&attributes))
|
|
{
|
|
PATH=path;
|
|
StringUpper(PATH);
|
|
timestamp=attributes.ftLastWriteTime;
|
|
return;
|
|
}
|
|
}
|
|
PATH.Empty();
|
|
timestamp.dwHighDateTime=timestamp.dwLowDateTime=0;
|
|
}
|
|
|
|
void CItemManager::LoadCustomIcon( const wchar_t *iconPath, int iconIndex, int refreshFlags, const IconInfo *&smallIcon, const IconInfo *&largeIcon, const IconInfo *&extraLargeIcon, bool bTemp )
|
|
{
|
|
unsigned int hash=CalcFNVHash(iconPath,CalcFNVHash(&iconIndex,4));
|
|
|
|
FindInCache(hash,refreshFlags,smallIcon,largeIcon,extraLargeIcon);
|
|
if (!refreshFlags) return;
|
|
|
|
// extract icon
|
|
HBITMAP hSmallBitmap=NULL, hLargeBitmap=NULL, hExtraLargeBitmap=NULL;
|
|
if (refreshFlags&INFO_SMALL_ICON)
|
|
{
|
|
HICON hIcon;
|
|
if (!*iconPath)
|
|
hIcon=(HICON)LoadImage(g_Instance,MAKEINTRESOURCE(-iconIndex),IMAGE_ICON,SMALL_ICON_SIZE,SMALL_ICON_SIZE,LR_DEFAULTCOLOR);
|
|
else
|
|
hIcon=ShExtractIcon(iconPath,iconIndex==-1?0:iconIndex,SMALL_ICON_SIZE);
|
|
if (hIcon)
|
|
hSmallBitmap=BitmapFromIcon(hIcon,SMALL_ICON_SIZE);
|
|
}
|
|
|
|
if (refreshFlags&INFO_LARGE_ICON)
|
|
{
|
|
HICON hIcon;
|
|
if (!*iconPath)
|
|
hIcon=(HICON)LoadImage(g_Instance,MAKEINTRESOURCE(-iconIndex),IMAGE_ICON,LARGE_ICON_SIZE,LARGE_ICON_SIZE,LR_DEFAULTCOLOR);
|
|
else
|
|
hIcon=ShExtractIcon(iconPath,iconIndex==-1?0:iconIndex,LARGE_ICON_SIZE);
|
|
if (hIcon)
|
|
hLargeBitmap=BitmapFromIcon(hIcon,LARGE_ICON_SIZE);
|
|
}
|
|
|
|
if (refreshFlags&INFO_EXTRA_LARGE_ICON)
|
|
{
|
|
HICON hIcon;
|
|
if (!*iconPath)
|
|
hIcon=(HICON)LoadImage(g_Instance,MAKEINTRESOURCE(-iconIndex),IMAGE_ICON,EXTRA_LARGE_ICON_SIZE,EXTRA_LARGE_ICON_SIZE,LR_DEFAULTCOLOR);
|
|
else
|
|
hIcon=ShExtractIcon(iconPath,iconIndex==-1?0:iconIndex,EXTRA_LARGE_ICON_SIZE);
|
|
if (hIcon)
|
|
hExtraLargeBitmap=BitmapFromIcon(hIcon,EXTRA_LARGE_ICON_SIZE);
|
|
}
|
|
|
|
StoreInCache(hash,bTemp?NULL:iconPath,hSmallBitmap,hLargeBitmap,hExtraLargeBitmap,refreshFlags,smallIcon,largeIcon,extraLargeIcon,bTemp,false);
|
|
}
|
|
|
|
// Recursive function to preload the items for a folder
|
|
void CItemManager::LoadFolderItems( IShellItem *pFolder, int refreshFlags, int levels, TLocation location )
|
|
{
|
|
CShellItemEnumerator enumerator(pFolder);
|
|
if (!enumerator.IsValid()) return;
|
|
|
|
int queueFlags=refreshFlags&INFO_ICON;
|
|
refreshFlags&=~INFO_ICON;
|
|
CComPtr<IShellItem> pChild;
|
|
CAbsolutePidl childPidl;
|
|
while (enumerator.GetNext(pChild,childPidl))
|
|
{
|
|
if (location==CItemManager::LOCATION_DESKTOP)
|
|
{
|
|
// collect only links from the desktop. this is an attempt to skip the SkyDrive item, which is causing problems.
|
|
SFGAOF attr=0;
|
|
if (FAILED(pChild->GetAttributes(SFGAO_LINK,&attr)) || !(attr&SFGAO_LINK))
|
|
continue;
|
|
}
|
|
if (m_LoadingStage!=LOAD_LOADING) break;
|
|
|
|
ItemInfo *pItemInfo=const_cast<ItemInfo*>(GetItemInfo(pChild,childPidl,refreshFlags,location));
|
|
if (queueFlags)
|
|
{
|
|
RWLock lock(this,true,RWLOCK_ITEMS);
|
|
QueueItemInfo(pItemInfo,queueFlags);
|
|
}
|
|
|
|
if (levels>1)
|
|
{
|
|
SFGAOF flags=0;
|
|
if (SUCCEEDED(pChild->GetAttributes(SFGAO_FOLDER|SFGAO_STREAM|SFGAO_LINK,&flags)) && (flags&(SFGAO_FOLDER|SFGAO_STREAM|SFGAO_LINK))==SFGAO_FOLDER)
|
|
{
|
|
// go into subfolders but not archives or links to folders
|
|
LoadFolderItems(pChild,refreshFlags|queueFlags,levels-1,location);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CItemManager::TLocation CItemManager::DetermineLocation( const wchar_t *PATH )
|
|
{
|
|
if (wcsncmp(PATH,m_RootStartMenu1,m_RootStartMenu1.GetLength())==0)
|
|
return LOCATION_START_MENU;
|
|
if (wcsncmp(PATH,m_RootStartMenu2,m_RootStartMenu2.GetLength())==0)
|
|
return LOCATION_START_MENU;
|
|
if (wcsncmp(PATH,m_RootStartMenu3,m_RootStartMenu3.GetLength())==0)
|
|
return LOCATION_START_MENU;
|
|
if (wcsncmp(PATH,m_RootGames,m_RootGames.GetLength())==0)
|
|
return LOCATION_GAMES;
|
|
if (wcsncmp(PATH,m_RootDesktop,m_RootDesktop.GetLength())==0)
|
|
return LOCATION_DESKTOP;
|
|
if (wcsncmp(PATH,m_RootTaskbar,m_RootTaskbar.GetLength())==0)
|
|
return LOCATION_TASKBAR;
|
|
if (wcsncmp(PATH,m_RootMetro,m_RootMetro.GetLength())==0)
|
|
return LOCATION_METRO;
|
|
return LOCATION_UNKNOWN;
|
|
}
|
|
|
|
// Recursive function to preload the metro apps
|
|
void CItemManager::LoadMetroItems( int refreshFlags )
|
|
{
|
|
std::vector<MetroLink> links;
|
|
std::vector<CString> nonApps10;
|
|
GetMetroLinks(links,false,&nonApps10);
|
|
int queueFlags=refreshFlags&INFO_ICON;
|
|
refreshFlags&=~queueFlags;
|
|
for (std::vector<MetroLink>::const_iterator it=links.begin();it!=links.end();++it)
|
|
{
|
|
ItemInfo *pItemInfo=const_cast<ItemInfo*>(GetItemInfo(it->pItem,it->pidl,refreshFlags,LOCATION_METRO));
|
|
if (queueFlags)
|
|
{
|
|
RWLock lock(this,true,RWLOCK_ITEMS);
|
|
QueueItemInfo(pItemInfo,queueFlags);
|
|
}
|
|
if (m_LoadingStage!=LOAD_LOADING) break;
|
|
}
|
|
if (GetWinVersion()>=WIN_VER_WIN10)
|
|
{
|
|
wchar_t APPID[256];
|
|
for (std::vector<MetroLink>::const_iterator it=links.begin();it!=links.end();++it)
|
|
{
|
|
const ItemInfo *pInfo=GetItemInfo(it->pItem,it->pidl,INFO_LINK|INFO_LINK_APPID|INFO_METRO);
|
|
Strcpy(APPID,_countof(APPID),it->appid);
|
|
CharUpper(APPID);
|
|
unsigned int hash=CalcFNVHash(APPID);
|
|
{
|
|
RWLock lock(this,true,RWLOCK_ITEMS);
|
|
std::map<unsigned int,const ItemInfo*>::const_iterator it2=m_MetroItemInfos10.find(hash);
|
|
if (it2==m_MetroItemInfos10.end())
|
|
m_MetroItemInfos10[hash]=pInfo;
|
|
std::set<unsigned int>::iterator it3=m_BlackListInfos10.find(hash);
|
|
if (it3!=m_BlackListInfos10.end())
|
|
m_BlackListInfos10.erase(it3);
|
|
}
|
|
}
|
|
|
|
{
|
|
RWLock lock(this,true,RWLOCK_ITEMS);
|
|
for (std::vector<CString>::const_iterator it=nonApps10.begin();it!=nonApps10.end();++it)
|
|
{
|
|
Strcpy(APPID,_countof(APPID),*it);
|
|
CharUpper(APPID);
|
|
unsigned int hash=CalcFNVHash(APPID);
|
|
std::map<unsigned int,const ItemInfo*>::const_iterator it2=m_MetroItemInfos10.find(hash);
|
|
if (it2==m_MetroItemInfos10.end())
|
|
m_MetroItemInfos10[hash]=NULL;
|
|
std::set<unsigned int>::iterator it3=m_BlackListInfos10.find(hash);
|
|
if (it3!=m_BlackListInfos10.end())
|
|
m_BlackListInfos10.erase(it3);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static KNOWNFOLDERID FOLDERID_TaskbarPinned={'TASK', 'BA', 'R', {'P', 'I', 'N', 'N', 'E', 'D'}};
|
|
static KNOWNFOLDERID FOLDERID_ClassicPinned={'CLAS', 'SI', 'C', {'P', 'I', 'N', 'N', 'E', 'D'}};
|
|
static KNOWNFOLDERID FOLDERID_MetroApps={'MET', 'R', 'O', {'A', 'P', 'P', 'S'}};
|
|
|
|
static struct
|
|
{
|
|
KNOWNFOLDERID folder;
|
|
int refreshFlags;
|
|
int levels;
|
|
CItemManager::TLocation location;
|
|
} g_CacheFolders[]=
|
|
{
|
|
{FOLDERID_StartMenu,CItemManager::INFO_LINK|CItemManager::INFO_METRO|CItemManager::INFO_SMALL_ICON,MAX_FOLDER_LEVELS,CItemManager::LOCATION_START_MENU},
|
|
{FOLDERID_CommonStartMenu,CItemManager::INFO_LINK|CItemManager::INFO_METRO|CItemManager::INFO_SMALL_ICON,MAX_FOLDER_LEVELS,CItemManager::LOCATION_START_MENU},
|
|
{FOLDERID_Desktop,CItemManager::INFO_LINK,1,CItemManager::LOCATION_DESKTOP},
|
|
{FOLDERID_PublicDesktop,CItemManager::INFO_LINK,1,CItemManager::LOCATION_DESKTOP},
|
|
{FOLDERID_TaskbarPinned,CItemManager::INFO_LINK,1,CItemManager::LOCATION_TASKBAR},
|
|
{FOLDERID_ClassicPinned,CItemManager::INFO_LINK|CItemManager::INFO_METRO|CItemManager::INFO_SMALL_ICON,1,CItemManager::LOCATION_START_MENU},
|
|
{FOLDERID_MetroApps,CItemManager::INFO_LINK|CItemManager::INFO_METRO,1,CItemManager::LOCATION_METRO},
|
|
{FOLDERID_ControlPanelFolder,CItemManager::INFO_SMALL_ICON,1,CItemManager::LOCATION_UNKNOWN},
|
|
{FOLDERID_Favorites,CItemManager::INFO_SMALL_ICON,MAX_FOLDER_LEVELS,CItemManager::LOCATION_UNKNOWN},
|
|
};
|
|
|
|
const int NUM_WATCHED_DIRS=7;
|
|
|
|
void CItemManager::PreloadItemsThread( void )
|
|
{
|
|
int dirCount=0;
|
|
HANDLE handles[NUM_WATCHED_DIRS+1];
|
|
DWORD dirMasks[NUM_WATCHED_DIRS]={0};
|
|
DWORD dirMask=0xFFFFFFFF;
|
|
while (1)
|
|
{
|
|
for (int i=0;i<_countof(g_CacheFolders);i++)
|
|
{
|
|
if (m_LoadingStage!=LOAD_LOADING) break;
|
|
if (i==NUM_WATCHED_DIRS && dirMask==0xFFFFFFFF)
|
|
SetEvent(m_DoneEvent);
|
|
if (!(dirMask&(1<<i))) continue;
|
|
if (g_CacheFolders[i].folder==FOLDERID_Favorites && !m_bPreloadFavorites) continue;
|
|
CAbsolutePidl pidl;
|
|
CComPtr<IShellItem> pFolder;
|
|
if (g_CacheFolders[i].folder==FOLDERID_TaskbarPinned)
|
|
{
|
|
wchar_t path[_MAX_PATH]=TASKBAR_PINNED_ROOT;
|
|
DoEnvironmentSubst(path,_countof(path));
|
|
if (FAILED(SHParseDisplayName(path,NULL,&pidl,0,NULL)) || !pidl) continue;
|
|
if (FAILED(SHCreateItemFromIDList(pidl,IID_IShellItem,(void**)&pFolder)) || !pFolder) continue;
|
|
}
|
|
else if (g_CacheFolders[i].folder==FOLDERID_ClassicPinned)
|
|
{
|
|
if (GetSettingInt(L"PinnedPrograms")!=PINNED_PROGRAMS_PINNED) continue;
|
|
wchar_t path[_MAX_PATH]=START_MENU_PINNED_ROOT;
|
|
DoEnvironmentSubst(path,_countof(path));
|
|
if (FAILED(SHParseDisplayName(path,NULL,&pidl,0,NULL)) || !pidl) continue;
|
|
if (FAILED(SHCreateItemFromIDList(pidl,IID_IShellItem,(void**)&pFolder)) || !pFolder) continue;
|
|
}
|
|
else if (g_CacheFolders[i].folder==FOLDERID_MetroApps)
|
|
{
|
|
if (GetWinVersion()<WIN_VER_WIN8) continue;
|
|
wchar_t path[_MAX_PATH]=METRO_APP_ROOT;
|
|
DoEnvironmentSubst(path,_countof(path));
|
|
pidl.Attach(ILCreateFromPath(path));
|
|
}
|
|
else
|
|
{
|
|
if (FAILED(ShGetKnownFolderIDList(g_CacheFolders[i].folder,&pidl)) || !pidl) continue;
|
|
if (FAILED(SHCreateItemFromIDList(pidl,IID_IShellItem,(void**)&pFolder)) || !pFolder) continue;
|
|
}
|
|
|
|
wchar_t path[_MAX_PATH];
|
|
if (i<NUM_WATCHED_DIRS && dirMask==0xFFFFFFFF && SUCCEEDED(SHGetPathFromIDList(pidl,path)))
|
|
{
|
|
handles[dirCount]=FindFirstChangeNotification(path,TRUE,FILE_NOTIFY_CHANGE_FILE_NAME|FILE_NOTIFY_CHANGE_DIR_NAME|FILE_NOTIFY_CHANGE_LAST_WRITE);
|
|
if (handles[dirCount]!=INVALID_HANDLE_VALUE)
|
|
{
|
|
dirMasks[dirCount]=1<<i;
|
|
dirCount++;
|
|
}
|
|
}
|
|
int refreshFlags=g_CacheFolders[i].refreshFlags;
|
|
if (!m_bPreloadIcons)
|
|
refreshFlags&=CItemManager::INFO_DATA;
|
|
if (refreshFlags)
|
|
{
|
|
if (g_CacheFolders[i].folder!=FOLDERID_MetroApps)
|
|
{
|
|
if (g_CacheFolders[i].location==CItemManager::LOCATION_DESKTOP)
|
|
{
|
|
int compat=GetSettingInt(L"CompatibilityFixes");
|
|
if (compat&COMPATIBILITY_SKIP_DESKTOP)
|
|
continue;
|
|
}
|
|
LoadFolderItems(pFolder,refreshFlags,g_CacheFolders[i].levels,g_CacheFolders[i].location);
|
|
}
|
|
else
|
|
LoadMetroItems(refreshFlags);
|
|
}
|
|
}
|
|
if (dirMask==0xFFFFFFFF)
|
|
SetEvent(m_DoneEvent);
|
|
if (dirCount==0)
|
|
break;
|
|
handles[dirCount]=m_ExitEvent;
|
|
DWORD wait=WaitForMultipleObjects(dirCount+1,handles,FALSE,INFINITE);
|
|
if (wait<WAIT_OBJECT_0 || wait>WAIT_OBJECT_0+dirCount-1)
|
|
break;
|
|
|
|
int dir=wait-WAIT_OBJECT_0;
|
|
dirMask=dirMasks[dir];
|
|
|
|
if (!FindNextChangeNotification(handles[dir]))
|
|
ResetEvent(handles[dir]); // so we don't wake on this event again
|
|
Sleep(1000); // hopefully whatever file operation is in progress will finish in a second
|
|
}
|
|
for (int i=0;i<dirCount;i++)
|
|
FindCloseChangeNotification(handles[i]);
|
|
}
|
|
|
|
DWORD CALLBACK CItemManager::StaticPreloadItemsThread( void *param )
|
|
{
|
|
CItemManager *pThis=(CItemManager*)param;
|
|
SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_IDLE);
|
|
CoInitialize(NULL);
|
|
pThis->m_LoadIconData[1].Init();
|
|
wchar_t path[_MAX_PATH];
|
|
GetModuleFileName(g_Instance,path,_countof(path));
|
|
LoadLibrary(path); // stop the DLL from unloading
|
|
pThis->PreloadItemsThread();
|
|
pThis->m_LoadIconData[1].Close();
|
|
CoUninitialize();
|
|
FreeLibraryAndExitThread(g_Instance,0); // release the DLL
|
|
return 0;
|
|
}
|
|
|
|
void CItemManager::RefreshInfoThread( void )
|
|
{
|
|
WaitForSingleObject(m_StartEvent,REFRESH_DELAY);
|
|
bool bRefresh=false;
|
|
while (1)
|
|
{
|
|
HANDLE handles[2]={m_WorkEvent,m_ExitEvent};
|
|
WaitForMultipleObjects(2,handles,FALSE,INFINITE);
|
|
bRefresh=false;
|
|
int t0=GetTickCount();
|
|
while (1)
|
|
{
|
|
if (m_LoadingStage!=LOAD_LOADING)
|
|
return;
|
|
|
|
ItemInfo *pItemInfo=NULL;
|
|
int refreshFlags=0;
|
|
{
|
|
Lock cleanupLock(this,LOCK_CLEANUP);
|
|
{
|
|
RWLock lock(this,true,RWLOCK_ITEMS);
|
|
if (!m_ItemQueue.empty())
|
|
{
|
|
pItemInfo=*m_ItemQueue.begin();
|
|
m_ItemQueue.pop_front();
|
|
}
|
|
else if (!m_ItemQueueLow.empty())
|
|
{
|
|
pItemInfo=*m_ItemQueueLow.begin();
|
|
m_ItemQueueLow.pop_front();
|
|
}
|
|
else
|
|
break;
|
|
refreshFlags=pItemInfo->refreshFlags;
|
|
}
|
|
if (refreshFlags && pItemInfo->bTemp)
|
|
{
|
|
// temp items must be refreshed inside LOCK_CLEANUP because the cleanup process will delete all such items
|
|
RefreshItemInfo(pItemInfo,refreshFlags,NULL,false);
|
|
refreshFlags=0;
|
|
}
|
|
}
|
|
if (refreshFlags)
|
|
{
|
|
// non-temp items should be refreshed outside LOCK_CLEANUP
|
|
RefreshItemInfo(pItemInfo,refreshFlags,NULL,false);
|
|
}
|
|
|
|
bRefresh=true;
|
|
int t=GetTickCount();
|
|
if (t-t0>100)
|
|
{
|
|
CMenuContainer::RefreshIcons();
|
|
t0=t;
|
|
bRefresh=false;
|
|
}
|
|
}
|
|
if (bRefresh)
|
|
CMenuContainer::RefreshIcons();
|
|
}
|
|
}
|
|
|
|
DWORD CALLBACK CItemManager::StaticRefreshInfoThread( void *param )
|
|
{
|
|
CItemManager *pThis=(CItemManager*)param;
|
|
volatile DWORD MAIN_THREAD=pThis->m_MainThreadId;
|
|
SetThreadPriority(GetCurrentThread(),THREAD_PRIORITY_IDLE);
|
|
CoInitialize(NULL);
|
|
pThis->m_LoadIconData[2].Init();
|
|
pThis->RefreshInfoThread();
|
|
pThis->m_LoadIconData[2].Close();
|
|
CoUninitialize();
|
|
return MAIN_THREAD-MAIN_THREAD;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
struct IconData
|
|
{
|
|
unsigned int key;
|
|
CItemManager::TIconSizeType sizeType;
|
|
int PATHLen;
|
|
FILETIME timestamp;
|
|
int bitmapW, bitmapH;
|
|
};
|
|
|
|
struct ItemData
|
|
{
|
|
unsigned int key;
|
|
FILETIME writestamp;
|
|
FILETIME createstamp;
|
|
bool bIconOnly;
|
|
bool bLink;
|
|
bool bMetroLink;
|
|
bool bProtectedLink;
|
|
bool bNoPin;
|
|
bool bNoNew;
|
|
bool bExplicitAppId;
|
|
int pidlSize;
|
|
int pathLen;
|
|
int PATHLen;
|
|
int smallIcon;
|
|
int largeIcon;
|
|
int extraLargeIcon;
|
|
int validFlags;
|
|
int targetPidlSize;
|
|
int targetPATHLen;
|
|
int appidLen;
|
|
int metroNameLen;
|
|
int iconPathLen;
|
|
DWORD iconColor;
|
|
int iconIndex;
|
|
};
|
|
}
|
|
|
|
static DWORD ReadCacheFile( HANDLE file )
|
|
{
|
|
DWORD data;
|
|
DWORD q;
|
|
if (!ReadFile(file,&data,4,&q,NULL) || q!=4)
|
|
return 0;
|
|
return data;
|
|
}
|
|
|
|
static bool ReadCacheFile( HANDLE file, IconData &data )
|
|
{
|
|
DWORD q;
|
|
return ReadFile(file,&data,sizeof(data),&q,NULL)!=0 && q==sizeof(data);
|
|
}
|
|
|
|
static bool ReadCacheFile( HANDLE file, ItemData &data )
|
|
{
|
|
DWORD q;
|
|
return ReadFile(file,&data,sizeof(data),&q,NULL)!=0 && q==sizeof(data);
|
|
}
|
|
|
|
static bool ReadCacheFile( HANDLE file, CAbsolutePidl &data, int size )
|
|
{
|
|
if (size>0)
|
|
{
|
|
PIDLIST_ABSOLUTE pidl=(PIDLIST_ABSOLUTE)CoTaskMemAlloc(size);
|
|
if (!pidl) return false;
|
|
DWORD q;
|
|
if (!ReadFile(file,pidl,size,&q,NULL) || q!=size)
|
|
{
|
|
ILFree(pidl);
|
|
return false;
|
|
}
|
|
data.Attach(pidl);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool ReadCacheFile( HANDLE file, CString &data, int len )
|
|
{
|
|
if (len>0)
|
|
{
|
|
DWORD q;
|
|
if (!ReadFile(file,data.GetBuffer(len),len*2,&q,NULL) || q!=len*2)
|
|
{
|
|
data.Empty();
|
|
return false;
|
|
}
|
|
data.ReleaseBuffer(len);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static bool ReadCacheFile( HANDLE file, HDC hdc, HBITMAP &data, int width, int height )
|
|
{
|
|
std::vector<DWORD> bits(width*height);
|
|
if (bits.empty()) return false;
|
|
BITMAPINFO bi={0};
|
|
bi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
|
|
bi.bmiHeader.biWidth=width;
|
|
bi.bmiHeader.biHeight=height;
|
|
bi.bmiHeader.biPlanes=1;
|
|
bi.bmiHeader.biBitCount=32;
|
|
DWORD q;
|
|
int size=width*height*4;
|
|
if (!ReadFile(file,&bits[0],size,&q,NULL) || q!=size)
|
|
return false;
|
|
unsigned int *pBits;
|
|
data=CreateDIBSection(hdc,&bi,DIB_RGB_COLORS,(void**)&pBits,NULL,0);
|
|
if (!data)
|
|
return false;
|
|
memcpy(pBits,&bits[0],size);
|
|
return true;
|
|
}
|
|
|
|
static void WriteCacheFile( HANDLE file, DWORD data )
|
|
{
|
|
DWORD q;
|
|
WriteFile(file,&data,sizeof(data),&q,NULL);
|
|
}
|
|
|
|
static void WriteCacheFile( HANDLE file, const IconData &data )
|
|
{
|
|
DWORD q;
|
|
WriteFile(file,&data,sizeof(data),&q,NULL);
|
|
}
|
|
|
|
static void WriteCacheFile( HANDLE file, const ItemData &data )
|
|
{
|
|
DWORD q;
|
|
WriteFile(file,&data,sizeof(data),&q,NULL);
|
|
}
|
|
|
|
static void WriteCacheFile( HANDLE file, PIDLIST_ABSOLUTE data, int size )
|
|
{
|
|
if (size>0)
|
|
{
|
|
DWORD q;
|
|
WriteFile(file,data,size,&q,NULL);
|
|
}
|
|
}
|
|
|
|
static void WriteCacheFile( HANDLE file, const CString &data )
|
|
{
|
|
if (!data.IsEmpty())
|
|
{
|
|
DWORD q;
|
|
WriteFile(file,(const wchar_t*)data,data.GetLength()*2,&q,NULL);
|
|
}
|
|
}
|
|
|
|
static void WriteCacheFile( HANDLE file, HDC hdc, HBITMAP data, int width, int height )
|
|
{
|
|
std::vector<DWORD> bits(width*height);
|
|
if (bits.empty()) return;
|
|
BITMAPINFO bi={0};
|
|
bi.bmiHeader.biSize=sizeof(BITMAPINFOHEADER);
|
|
bi.bmiHeader.biWidth=width;
|
|
bi.bmiHeader.biHeight=height;
|
|
bi.bmiHeader.biPlanes=1;
|
|
bi.bmiHeader.biBitCount=32;
|
|
GetDIBits(hdc,data,0,height,&bits[0],&bi,DIB_RGB_COLORS);
|
|
DWORD q;
|
|
WriteFile(file,&bits[0],width*height*4,&q,NULL);
|
|
}
|
|
|
|
bool CItemManager::CompareModuleTimeStamp( const CString &PATH, const FILETIME ×tamp, std::vector<ModuleInfo> &modules )
|
|
{
|
|
for (std::vector<ModuleInfo>::const_iterator it=modules.begin();it!=modules.end();++it)
|
|
{
|
|
if (it->PATH==PATH)
|
|
return (CompareFileTime(×tamp,&it->timestamp)==0);
|
|
}
|
|
WIN32_FILE_ATTRIBUTE_DATA attributes={0};
|
|
if (GetFileAttributesEx(PATH,GetFileExInfoStandard,&attributes))
|
|
{
|
|
ModuleInfo info={PATH,attributes.ftLastWriteTime};
|
|
modules.push_back(info);
|
|
return (CompareFileTime(×tamp,&info.timestamp)==0);
|
|
}
|
|
else
|
|
{
|
|
ModuleInfo info={PATH};
|
|
modules.push_back(info);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void CItemManager::LoadCacheFile( void )
|
|
{
|
|
#ifdef DISABLE_CACHE
|
|
return;
|
|
#endif
|
|
// load cached icons and info
|
|
wchar_t path[_MAX_PATH]=L"%LOCALAPPDATA%\\OpenShell\\DataCache.db";
|
|
DoEnvironmentSubst(path,_MAX_PATH);
|
|
|
|
HANDLE file=CreateFile(path,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
|
|
if (file==INVALID_HANDLE_VALUE) return;
|
|
|
|
bool bError=true;
|
|
DWORD tag=ReadCacheFile(file);
|
|
std::vector<ModuleInfo> modules;
|
|
ModuleInfo stdInfo={L"SHELL32.DLL"};
|
|
modules.push_back(stdInfo);
|
|
stdInfo.PATH=L"IMAGERES.DLL";
|
|
modules.push_back(stdInfo);
|
|
m_BlackListInfos10.clear();
|
|
if (tag=='CLSH')
|
|
{
|
|
tag=ReadCacheFile(file);
|
|
std::vector<const IconInfo*> remapIcons(1);
|
|
remapIcons[0]=NULL;
|
|
DWORD build;
|
|
if (tag==GetVersionEx(g_Instance,&build) && ReadCacheFile(file)==CACHE_FILE_VERSION)
|
|
{
|
|
int size1=ReadCacheFile(file);
|
|
int size2=ReadCacheFile(file);
|
|
int size3=ReadCacheFile(file);
|
|
int langHash=ReadCacheFile(file);
|
|
bError=false;
|
|
tag=ReadCacheFile(file);
|
|
HDC hdc=CreateCompatibleDC(NULL);
|
|
while (tag=='ICON')
|
|
{
|
|
IconData data;
|
|
if (!ReadCacheFile(file,data))
|
|
{
|
|
bError=true;
|
|
break;
|
|
}
|
|
IconInfo info;
|
|
info.sizeType=data.sizeType;
|
|
info.timestamp=data.timestamp;
|
|
info.bTemp=false;
|
|
info.bMetro=false;
|
|
if (!ReadCacheFile(file,info.PATH,data.PATHLen))
|
|
{
|
|
bError=true;
|
|
break;
|
|
}
|
|
if (size1==SMALL_ICON_SIZE && size2==LARGE_ICON_SIZE && size3==EXTRA_LARGE_ICON_SIZE && CompareModuleTimeStamp(info.PATH,info.timestamp,modules))
|
|
{
|
|
if (!ReadCacheFile(file,hdc,info.bitmap,data.bitmapW,data.bitmapH))
|
|
{
|
|
bError=true;
|
|
break;
|
|
}
|
|
remapIcons.push_back(&m_IconInfos.insert(std::pair<unsigned int,IconInfo>(data.key,info))->second);
|
|
}
|
|
else
|
|
{
|
|
int size=data.bitmapW*data.bitmapH*4;
|
|
SetFilePointer(file,size,NULL,FILE_CURRENT);
|
|
remapIcons.push_back(NULL);
|
|
}
|
|
tag=ReadCacheFile(file);
|
|
}
|
|
DeleteDC(hdc);
|
|
if (tag!='ITEM')
|
|
{
|
|
bError=true;
|
|
}
|
|
else
|
|
{
|
|
wchar_t languages[100];
|
|
DWORD size=0;
|
|
DWORD len=_countof(languages);
|
|
GetUserPreferredUILanguages(MUI_LANGUAGE_ID,&size,languages,&len);
|
|
bError=(CalcFNVHash(languages,len*2,FNV_HASH0)!=langHash);
|
|
}
|
|
while (!bError && tag=='ITEM')
|
|
{
|
|
ItemData data;
|
|
if (!ReadCacheFile(file,data))
|
|
{
|
|
bError=true;
|
|
break;
|
|
}
|
|
ItemInfo &info=m_ItemInfos.insert(std::pair<unsigned int,ItemInfo>(data.key,ItemInfo()))->second;
|
|
|
|
info.writestamp=data.writestamp;
|
|
info.createstamp=data.createstamp;
|
|
info.bIconOnly=data.bIconOnly;
|
|
info.bTemp=false;
|
|
info.bLink=data.bLink;
|
|
info.bMetroLink=data.bMetroLink;
|
|
info.bProtectedLink=data.bProtectedLink;
|
|
info.bNoPin=data.bNoPin;
|
|
info.bNoNew=data.bNoNew;
|
|
info.bExplicitAppId=data.bExplicitAppId;
|
|
info.validFlags=data.validFlags;
|
|
info.refreshFlags=0;
|
|
info.iconColor=data.iconColor;
|
|
info.iconIndex=data.iconIndex;
|
|
|
|
info.smallIcon=data.smallIcon<(int)remapIcons.size()?remapIcons[data.smallIcon]:NULL;
|
|
if (!info.smallIcon)
|
|
{
|
|
info.validFlags&=~INFO_SMALL_ICON;
|
|
info.smallIcon=m_DefaultSmallIcon;
|
|
}
|
|
info.largeIcon=data.largeIcon<(int)remapIcons.size()?remapIcons[data.largeIcon]:NULL;
|
|
if (!info.largeIcon)
|
|
{
|
|
info.validFlags&=~INFO_LARGE_ICON;
|
|
info.largeIcon=m_DefaultLargeIcon;
|
|
}
|
|
info.extraLargeIcon=data.extraLargeIcon<(int)remapIcons.size()?remapIcons[data.extraLargeIcon]:NULL;
|
|
if (!info.extraLargeIcon)
|
|
{
|
|
info.validFlags&=~INFO_EXTRA_LARGE_ICON;
|
|
info.extraLargeIcon=m_DefaultExtraLargeIcon;
|
|
}
|
|
|
|
bError=bError || !ReadCacheFile(file,info.pidl,data.pidlSize);
|
|
bError=bError || !ReadCacheFile(file,info.path,data.pathLen);
|
|
bError=bError || !ReadCacheFile(file,info.PATH,data.PATHLen);
|
|
bError=bError || !ReadCacheFile(file,info.targetPidl,data.targetPidlSize);
|
|
bError=bError || !ReadCacheFile(file,info.targetPATH,data.targetPATHLen);
|
|
bError=bError || !ReadCacheFile(file,info.appid,data.appidLen);
|
|
bError=bError || !ReadCacheFile(file,info.metroName,data.metroNameLen);
|
|
bError=bError || !ReadCacheFile(file,info.iconPath,data.iconPathLen);
|
|
|
|
tag=ReadCacheFile(file);
|
|
}
|
|
if (tag=='BLAK')
|
|
{
|
|
int count=ReadCacheFile(file);
|
|
for (int i=0;i<count;i++)
|
|
{
|
|
unsigned int hash=ReadCacheFile(file);
|
|
m_BlackListInfos10.insert(hash);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
CloseHandle(file);
|
|
if (bError)
|
|
{
|
|
m_ItemInfos.clear();
|
|
for (std::multimap<unsigned int,IconInfo>::const_iterator it=m_IconInfos.begin();it!=m_IconInfos.end();++it)
|
|
{
|
|
if (it->second.bitmap)
|
|
DeleteObject(it->second.bitmap);
|
|
}
|
|
m_IconInfos.clear();
|
|
CreateDefaultIcons();
|
|
}
|
|
}
|
|
|
|
DWORD CALLBACK CItemManager::SaveCacheFileThread( void *param )
|
|
{
|
|
CItemManager *pThis=(CItemManager*)param;
|
|
wchar_t path[_MAX_PATH]=L"%LOCALAPPDATA%\\OpenShell";
|
|
DoEnvironmentSubst(path,_MAX_PATH);
|
|
SHCreateDirectory(NULL,path);
|
|
Strcat(path,_countof(path),L"\\DataCache.tmp");
|
|
HANDLE file=CreateFile(path,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
|
|
if (file==INVALID_HANDLE_VALUE) return 0;
|
|
|
|
WriteCacheFile(file,'CLSH');
|
|
WriteCacheFile(file,GetVersionEx(g_Instance));
|
|
WriteCacheFile(file,CACHE_FILE_VERSION);
|
|
WriteCacheFile(file,SMALL_ICON_SIZE);
|
|
WriteCacheFile(file,LARGE_ICON_SIZE);
|
|
WriteCacheFile(file,EXTRA_LARGE_ICON_SIZE);
|
|
{
|
|
wchar_t languages[100];
|
|
DWORD size=0;
|
|
DWORD len=_countof(languages);
|
|
GetUserPreferredUILanguages(MUI_LANGUAGE_ID,&size,languages,&len);
|
|
WriteCacheFile(file,CalcFNVHash(languages,len*2,FNV_HASH0));
|
|
}
|
|
|
|
std::vector<const std::pair<const unsigned int,IconInfo>*> iconInfos;
|
|
{
|
|
RWLock lock(pThis,false,RWLOCK_ICONS);
|
|
for (std::multimap<unsigned int,IconInfo>::const_iterator it=pThis->m_IconInfos.begin();it!=pThis->m_IconInfos.end();++it)
|
|
{
|
|
if (!it->second.PATH.IsEmpty() && it->second.PATH[1]!='#' && it->first!=0)
|
|
iconInfos.push_back(&*it);
|
|
}
|
|
}
|
|
|
|
std::vector<const std::pair<const unsigned int,ItemInfo>*> itemInfos;
|
|
std::vector<unsigned int> blackList;
|
|
{
|
|
RWLock lock(pThis,false,RWLOCK_ITEMS);
|
|
for (std::multimap<unsigned int,ItemInfo>::const_iterator it=pThis->m_ItemInfos.begin();it!=pThis->m_ItemInfos.end();++it)
|
|
{
|
|
if (it->first!=0)
|
|
itemInfos.push_back(&*it);
|
|
}
|
|
for (std::set<unsigned int>::const_iterator it=pThis->m_BlackListInfos10.begin();it!=pThis->m_BlackListInfos10.end();++it)
|
|
blackList.push_back(*it);
|
|
}
|
|
|
|
HDC hdc=CreateCompatibleDC(NULL);
|
|
std::map<const IconInfo*,int> remapIcons;
|
|
int iconIndex=1;
|
|
// save cached icons and info
|
|
for (std::vector<const std::pair<const unsigned int,IconInfo>*>::const_iterator it=iconInfos.begin();it!=iconInfos.end();++it)
|
|
{
|
|
RWLock lock(pThis,false,RWLOCK_ICONS);
|
|
if ((*it)->second.bTemp || (*it)->second.bMetro) continue;
|
|
remapIcons[&(*it)->second]=iconIndex++;
|
|
IconData data;
|
|
data.key=(*it)->first;
|
|
data.sizeType=(*it)->second.sizeType;
|
|
data.timestamp=(*it)->second.timestamp;
|
|
data.PATHLen=(*it)->second.PATH.GetLength();
|
|
BITMAP bmp;
|
|
GetObject((*it)->second.bitmap,sizeof(bmp),&bmp);
|
|
data.bitmapW=bmp.bmWidth;
|
|
data.bitmapH=bmp.bmHeight;
|
|
|
|
WriteCacheFile(file,'ICON');
|
|
WriteCacheFile(file,data);
|
|
WriteCacheFile(file,(*it)->second.PATH);
|
|
WriteCacheFile(file,hdc,(*it)->second.bitmap,data.bitmapW,data.bitmapH);
|
|
}
|
|
DeleteDC(hdc);
|
|
|
|
FILE *log=NULL;
|
|
if (g_LogCategories&LOG_CACHE)
|
|
{
|
|
wchar_t path3[_MAX_PATH]=L"%LOCALAPPDATA%\\OpenShell\\DataCache.txt";
|
|
DoEnvironmentSubst(path3,_countof(path3));
|
|
if (_wfopen_s(&log,path3,L"wb")==0)
|
|
{
|
|
wchar_t bom=0xFEFF;
|
|
fwrite(&bom,2,1,log);
|
|
}
|
|
}
|
|
for (std::vector<const std::pair<const unsigned int,ItemInfo>*>::const_iterator it=itemInfos.begin();it!=itemInfos.end();++it)
|
|
{
|
|
RWLock lock(pThis,false,RWLOCK_ITEMS);
|
|
if ((*it)->second.bTemp || (*it)->second.path.IsEmpty()) continue;
|
|
|
|
ItemData data;
|
|
data.key=(*it)->first;
|
|
data.writestamp=(*it)->second.writestamp;
|
|
data.createstamp=(*it)->second.createstamp;
|
|
data.bIconOnly=(*it)->second.bIconOnly;
|
|
data.bLink=(*it)->second.bLink;
|
|
data.bMetroLink=(*it)->second.bMetroLink;
|
|
data.bProtectedLink=(*it)->second.bProtectedLink;
|
|
data.bNoPin=(*it)->second.bNoPin;
|
|
data.bNoNew=(*it)->second.bNoNew;
|
|
data.bExplicitAppId=(*it)->second.bExplicitAppId;
|
|
data.pidlSize=(*it)->second.GetLatestPidl()?ILGetSize((*it)->second.GetLatestPidl()):0;
|
|
data.pathLen=(*it)->second.path.GetLength();
|
|
data.PATHLen=(*it)->second.PATH.GetLength();
|
|
|
|
std::map<const IconInfo*,int>::const_iterator remapIt=remapIcons.find((*it)->second.smallIcon);
|
|
data.smallIcon=(remapIt==remapIcons.end()?0:remapIt->second);
|
|
remapIt=remapIcons.find((*it)->second.largeIcon);
|
|
data.largeIcon=(remapIt==remapIcons.end()?0:remapIt->second);
|
|
remapIt=remapIcons.find((*it)->second.extraLargeIcon);
|
|
data.extraLargeIcon=(remapIt==remapIcons.end()?0:remapIt->second);
|
|
|
|
data.validFlags=(*it)->second.validFlags;
|
|
data.targetPidlSize=(*it)->second.targetPidl?ILGetSize((*it)->second.targetPidl):0;
|
|
data.targetPATHLen=(*it)->second.targetPATH.GetLength();
|
|
data.appidLen=(*it)->second.appid.GetLength();
|
|
data.metroNameLen=(*it)->second.metroName.GetLength();
|
|
data.iconPathLen=(*it)->second.iconPath.GetLength();
|
|
data.iconColor=(*it)->second.iconColor;
|
|
data.iconIndex=(*it)->second.iconIndex;
|
|
|
|
WriteCacheFile(file,'ITEM');
|
|
WriteCacheFile(file,data);
|
|
WriteCacheFile(file,(*it)->second.GetLatestPidl(),data.pidlSize);
|
|
WriteCacheFile(file,(*it)->second.path);
|
|
WriteCacheFile(file,(*it)->second.PATH);
|
|
WriteCacheFile(file,(*it)->second.targetPidl,data.targetPidlSize);
|
|
WriteCacheFile(file,(*it)->second.targetPATH);
|
|
WriteCacheFile(file,(*it)->second.appid);
|
|
WriteCacheFile(file,(*it)->second.metroName);
|
|
WriteCacheFile(file,(*it)->second.iconPath);
|
|
if (log) fwprintf(log,L"0x%08X - %s\r\n",(*it)->first,(const wchar_t*)(*it)->second.PATH);
|
|
}
|
|
{
|
|
WriteCacheFile(file,'BLAK');
|
|
WriteCacheFile(file,(DWORD)blackList.size());
|
|
for (std::vector<unsigned int>::const_iterator it=blackList.begin();it!=blackList.end();++it)
|
|
WriteCacheFile(file,*it);
|
|
}
|
|
if (log) fclose(log);
|
|
|
|
CloseHandle(file);
|
|
wchar_t path2[_MAX_PATH]=L"%LOCALAPPDATA%\\OpenShell\\DataCache.db";
|
|
DoEnvironmentSubst(path2,_MAX_PATH);
|
|
MoveFileEx(path,path2,MOVEFILE_REPLACE_EXISTING);
|
|
return 0;
|
|
}
|
|
|
|
void CItemManager::SaveCacheFile( void )
|
|
{
|
|
#ifdef DISABLE_CACHE
|
|
return;
|
|
#endif
|
|
if (g_LogCategories&LOG_CACHE)
|
|
{
|
|
SaveCacheFileThread(this);
|
|
return;
|
|
}
|
|
int time=GetTickCount();
|
|
if (m_LastCacheSave && (time-m_LastCacheSave)<300000) // at least 5 minutes since the last save
|
|
return;
|
|
m_LastCacheSave=time;
|
|
if (m_SaveCacheThread)
|
|
{
|
|
if (WaitForSingleObject(m_SaveCacheThread,0)==WAIT_TIMEOUT)
|
|
return;
|
|
CloseHandle(m_SaveCacheThread);
|
|
m_SaveCacheThread=NULL;
|
|
}
|
|
m_SaveCacheThread=CreateThread(NULL,0,SaveCacheFileThread,this,0,NULL);
|
|
}
|
|
|
|
void CItemManager::ClearCache( void )
|
|
{
|
|
Lock cleanupLock(this,LOCK_CLEANUP);
|
|
RWLock itemLock(this,true,RWLOCK_ITEMS);
|
|
RWLock iconLock(this,true,RWLOCK_ICONS);
|
|
|
|
wchar_t path[_MAX_PATH]=L"%LOCALAPPDATA%\\OpenShell\\DataCache.db";
|
|
DoEnvironmentSubst(path,_MAX_PATH);
|
|
DeleteFile(path);
|
|
|
|
m_BlackListInfos10.clear();
|
|
m_ItemInfos.clear();
|
|
for (std::multimap<unsigned int,IconInfo>::const_iterator it=m_IconInfos.begin();it!=m_IconInfos.end();++it)
|
|
{
|
|
if (it->second.bitmap)
|
|
DeleteObject(it->second.bitmap);
|
|
}
|
|
m_IconInfos.clear();
|
|
m_MetroItemInfos10.clear();
|
|
CreateDefaultIcons();
|
|
ItemInfo &item=m_ItemInfos.insert(std::pair<unsigned int,ItemInfo>(0,ItemInfo()))->second;
|
|
item.bIconOnly=true;
|
|
item.smallIcon=m_DefaultSmallIcon;
|
|
item.largeIcon=m_DefaultLargeIcon;
|
|
item.extraLargeIcon=m_DefaultExtraLargeIcon;
|
|
}
|
|
|
|
// retrieves the pidl and the SFGAO_FOLDER, SFGAO_STREAM, SFGAO_LINK flags for the path
|
|
// for paths starting with \\ tries to guess if it is a folder or a link based on the extension
|
|
HRESULT MenuParseDisplayName( const wchar_t *path, PIDLIST_ABSOLUTE *pPidl, SFGAOF *pFlags, TNetworkType *pNetworkType )
|
|
{
|
|
if (pFlags)
|
|
*pFlags=0;
|
|
*pPidl=NULL;
|
|
if (pNetworkType)
|
|
*pNetworkType=NETWORK_NONE;
|
|
if (PathIsNetworkPath(path))
|
|
{
|
|
*pPidl=SHSimpleIDListFromPath(path);
|
|
if (!*pPidl)
|
|
return E_FAIL;
|
|
if (pFlags || pNetworkType)
|
|
{
|
|
const wchar_t *ext=PathFindExtension(path);
|
|
if (!*ext)
|
|
{
|
|
if (pFlags)
|
|
*pFlags=SFGAO_FOLDER;
|
|
if (pNetworkType)
|
|
{
|
|
if (path[1]==':')
|
|
{
|
|
// drive:
|
|
if (path[2]=='\\' && path[3]) // drive:\folder
|
|
*pNetworkType=NETWORK_FOLDER;
|
|
else
|
|
*pNetworkType=NETWORK_DRIVE;
|
|
}
|
|
else if (path[0]=='\\' && path[1]=='\\')
|
|
{
|
|
// \\server
|
|
const wchar_t *c=wcschr(path+2,'\\');
|
|
if (c)
|
|
{
|
|
// \\server\share[\folder]
|
|
*pNetworkType=wcschr(c+2,'\\')?NETWORK_FOLDER:NETWORK_SHARE;
|
|
}
|
|
else
|
|
*pNetworkType=NETWORK_SERVER;
|
|
}
|
|
else
|
|
{
|
|
// something else
|
|
*pNetworkType=NETWORK_FOLDER;
|
|
}
|
|
}
|
|
}
|
|
else if (_wcsicmp(ext,L".lnk")==0)
|
|
{
|
|
if (pFlags)
|
|
*pFlags=SFGAO_LINK;
|
|
if (pNetworkType)
|
|
*pNetworkType=NETWORK_FILE;
|
|
}
|
|
else
|
|
{
|
|
if (pFlags)
|
|
*pFlags=0;
|
|
if (pNetworkType)
|
|
*pNetworkType=NETWORK_FILE;
|
|
}
|
|
}
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
return ShParseDisplayName(path,pPidl,SFGAO_FOLDER|SFGAO_STREAM|SFGAO_LINK,pFlags);
|
|
}
|
|
}
|
|
|
|
const wchar_t *GetDefaultNetworkIcon( TNetworkType networkType )
|
|
{
|
|
switch (networkType)
|
|
{
|
|
case NETWORK_SERVER:
|
|
return L"imageres.dll,109";
|
|
case NETWORK_SHARE:
|
|
return L"imageres.dll,143";
|
|
case NETWORK_DRIVE:
|
|
return L"imageres.dll,33";
|
|
case NETWORK_FOLDER:
|
|
return L"imageres.dll,3";
|
|
default:
|
|
return L"imageres.dll,2";
|
|
}
|
|
}
|
|
|
|
bool MenuGetFileTimestamp( const wchar_t *path, FILETIME *pWriteTime, FILETIME *pCreateTime )
|
|
{
|
|
if (PathIsNetworkPath(path))
|
|
{
|
|
memset(pWriteTime,0,sizeof(FILETIME));
|
|
memset(pCreateTime,0,sizeof(FILETIME));
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
WIN32_FILE_ATTRIBUTE_DATA attributes={0};
|
|
if (!GetFileAttributesEx(path,GetFileExInfoStandard,&attributes))
|
|
{
|
|
memset(pWriteTime,0,sizeof(FILETIME));
|
|
memset(pCreateTime,0,sizeof(FILETIME));
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
*pWriteTime=attributes.ftLastWriteTime;
|
|
*pCreateTime=attributes.ftCreationTime;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
static CString GetKnownFolderSetting( REFKNOWNFOLDERID rfid )
|
|
{
|
|
if (rfid==FOLDERID_CommonAdminTools)
|
|
return L"shell:::{D20EA4E1-3957-11d2-A40B-0C5020524153}";
|
|
|
|
wchar_t path[_MAX_PATH];
|
|
path[0]=0;
|
|
if (rfid==FOLDERID_StartMenu)
|
|
Strcpy(path,_countof(path),GetSettingString(L"FolderStartMenu"));
|
|
|
|
if (rfid==FOLDERID_CommonStartMenu)
|
|
{
|
|
Strcpy(path,_countof(path),GetSettingString(L"FolderCommonStartMenu"));
|
|
}
|
|
else if (rfid==FOLDERID_Programs)
|
|
{
|
|
CString str=GetSettingString(L"FolderPrograms");
|
|
if (!str.IsEmpty())
|
|
Strcpy(path,_countof(path),str);
|
|
else
|
|
{
|
|
str=GetSettingString(L"FolderStartMenu");
|
|
if (!str.IsEmpty())
|
|
Sprintf(path,_countof(path),L"%s\\Programs",str);
|
|
}
|
|
}
|
|
else if (rfid==FOLDERID_CommonPrograms)
|
|
{
|
|
CString str=GetSettingString(L"FolderCommonPrograms");
|
|
if (!str.IsEmpty())
|
|
Strcpy(path,_countof(path),str);
|
|
else
|
|
{
|
|
str=GetSettingString(L"FolderCommonStartMenu");
|
|
if (!str.IsEmpty())
|
|
Sprintf(path,_countof(path),L"%s\\Programs",str);
|
|
}
|
|
}
|
|
else if (rfid==FOLDERID_Favorites)
|
|
{
|
|
if (IsEdgeDefaultBrowser())
|
|
Strcpy(path,_countof(path),L"%LOCALAPPDATA%\\Packages\\Microsoft.MicrosoftEdge_8wekyb3d8bbwe\\AC\\MicrosoftEdge\\User\\Default\\Favorites");
|
|
}
|
|
if (!path[0])
|
|
return CString();
|
|
DoEnvironmentSubst(path,_countof(path));
|
|
return path;
|
|
}
|
|
|
|
STDAPI ShGetKnownFolderPath( REFKNOWNFOLDERID rfid, PWSTR *pPath )
|
|
{
|
|
CString path=GetKnownFolderSetting(rfid);
|
|
if (!path.IsEmpty())
|
|
{
|
|
int size=path.GetLength()+1;
|
|
*pPath=(PWSTR)CoTaskMemAlloc(size*2);
|
|
if (!*pPath)
|
|
return E_FAIL;
|
|
Strcpy(*pPath,size,path);
|
|
return S_OK;
|
|
}
|
|
return SHGetKnownFolderPath(rfid,0,NULL,pPath);
|
|
}
|
|
|
|
STDAPI ShGetKnownFolderIDList(REFKNOWNFOLDERID rfid, PIDLIST_ABSOLUTE *pPidl )
|
|
{
|
|
CString path=GetKnownFolderSetting(rfid);
|
|
if (!path.IsEmpty())
|
|
{
|
|
return ShParseDisplayName(path,pPidl,0,NULL);
|
|
}
|
|
return SHGetKnownFolderIDList(rfid,0,NULL,pPidl);
|
|
}
|
|
|
|
STDAPI ShGetKnownFolderItem(REFKNOWNFOLDERID rfid, IShellItem **ppItem )
|
|
{
|
|
#ifndef BUILD_SETUP
|
|
if (rfid==FOLDERID_Games && (GetTickCount()&16))
|
|
return E_FAIL;
|
|
#endif
|
|
// Skip getting the Games menu on RS4
|
|
if(IsWin10RS4() && rfid==FOLDERID_Games)
|
|
{
|
|
return E_FAIL;
|
|
}
|
|
CString path=GetKnownFolderSetting(rfid);
|
|
if (!path.IsEmpty())
|
|
{
|
|
return SHCreateItemFromParsingName(path,NULL,IID_IShellItem,(void**)ppItem);
|
|
}
|
|
return SHGetKnownFolderItem(rfid,KF_FLAG_DEFAULT,NULL,IID_IShellItem,(void**)ppItem);
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
CShellItemEnumerator::CShellItemEnumerator( IShellItem *pFolder )
|
|
{
|
|
int compat=GetSettingInt(L"CompatibilityFixes");
|
|
if (!(compat&COMPATIBILITY_ENUM_SHELLITEM))
|
|
{
|
|
CComPtr<IShellFolder> pFolder0;
|
|
HRESULT hr=pFolder->BindToHandler(NULL,BHID_SFObject,IID_IShellFolder,(void**)&pFolder0);
|
|
if (FAILED(hr))
|
|
{
|
|
CComQIPtr<IShellItem2> pFolder2=pFolder;
|
|
if (pFolder2)
|
|
{
|
|
// The operation may fail if pFolder has a simple pidl (like from a network path). Update the shell item and try again
|
|
pFolder2->Update(NULL);
|
|
hr=pFolder->BindToHandler(NULL,BHID_SFObject,IID_IShellFolder,(void**)&pFolder0);
|
|
if (FAILED(hr))
|
|
return;
|
|
}
|
|
}
|
|
if (FAILED(SHGetIDListFromObject(pFolder,&m_Root)))
|
|
return;
|
|
hr=pFolder0->EnumObjects(NULL,SHCONTF_FOLDERS|SHCONTF_NONFOLDERS,(IEnumIDList**)&m_pEnumPidls);
|
|
}
|
|
else
|
|
{
|
|
HRESULT hr=pFolder->BindToHandler(NULL,BHID_EnumItems,IID_IEnumShellItems,(void**)&m_pEnumItems);
|
|
if (FAILED(hr))
|
|
{
|
|
m_pEnumItems=NULL;
|
|
// The operation may fail if pFolder has a simple pidl (like from a network path). Update the shell item and try again
|
|
CComQIPtr<IShellItem2> pFolder2=pFolder;
|
|
if (pFolder2)
|
|
{
|
|
pFolder2->Update(NULL);
|
|
hr=pFolder->BindToHandler(NULL,BHID_EnumItems,IID_IEnumShellItems,(void**)&m_pEnumItems);
|
|
if (FAILED(hr))
|
|
{
|
|
m_pEnumItems=NULL;
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
if (compat&COMPATIBILITY_ENUM_FIX_PIDLS)
|
|
SHGetIDListFromObject(pFolder,&m_Root);
|
|
}
|
|
}
|
|
|
|
bool CShellItemEnumerator::IsValid( void ) const
|
|
{
|
|
return m_pEnumPidls || m_pEnumItems;
|
|
}
|
|
|
|
bool CShellItemEnumerator::GetNext( CComPtr<IShellItem> &pChild, CAbsolutePidl &childPidl )
|
|
{
|
|
pChild=NULL;
|
|
childPidl.Clear();
|
|
if (m_pEnumPidls)
|
|
{
|
|
CAbsolutePidl child;
|
|
if (m_pEnumPidls->Next(1,(PITEMID_CHILD*)&child,NULL)!=S_OK)
|
|
return false;
|
|
|
|
CAbsolutePidl pidl;
|
|
pidl.Attach(ILCombine(m_Root,child));
|
|
if (FAILED(SHCreateItemFromIDList(pidl,IID_IShellItem,(void**)&pChild)))
|
|
return false;
|
|
childPidl.Swap(pidl);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
CComPtr<IShellItem> pChild0;
|
|
if (m_pEnumItems->Next(1,&pChild0,NULL)!=S_OK)
|
|
return false;
|
|
|
|
CAbsolutePidl pidl0;
|
|
if (FAILED(SHGetIDListFromObject(pChild0,&pidl0)))
|
|
return false;
|
|
|
|
if (m_Root)
|
|
{
|
|
CAbsolutePidl pidl;
|
|
pidl.Attach(ILCombine(m_Root,ILFindLastID(pidl0)));
|
|
if (SUCCEEDED(SHCreateItemFromIDList(pidl,IID_IShellItem,(void**)&pChild)))
|
|
{
|
|
childPidl.Swap(pidl);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
childPidl.Swap(pidl0);
|
|
pChild=pChild0;
|
|
return true;
|
|
}
|
|
}
|