mirror of
https://github.com/Open-Shell/Open-Shell-Menu.git
synced 2026-04-11 17:37:22 +10:00
484 lines
16 KiB
C++
484 lines
16 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
|
|
|
|
#pragma once
|
|
|
|
#include "ComHelper.h"
|
|
#include <map>
|
|
#include <set>
|
|
#include <list>
|
|
#include <vector>
|
|
|
|
interface IImageList2;
|
|
interface IWICImagingFactory;
|
|
|
|
// CItemManager - global cache for item information
|
|
|
|
class CItemManager;
|
|
extern CItemManager g_ItemManager;
|
|
|
|
class CItemManager
|
|
{
|
|
public:
|
|
CItemManager( void );
|
|
~CItemManager( void );
|
|
|
|
static int SMALL_ICON_SIZE;
|
|
static int LARGE_ICON_SIZE;
|
|
static int EXTRA_LARGE_ICON_SIZE;
|
|
|
|
// Initializes the manager. Called from DllMain
|
|
void Init( void );
|
|
void Close( void );
|
|
void ResetTempIcons( void );
|
|
void LoadCacheFile( void );
|
|
void SaveCacheFile( void );
|
|
void ClearCache( void );
|
|
|
|
static int GetDPI( bool bOverride ) { return (bOverride && s_DPIOverride)?s_DPIOverride:s_DPI; }
|
|
static bool GetDPIOverride( void ) { return s_DPIOverride!=0; }
|
|
|
|
enum TIconSizeType
|
|
{
|
|
ICON_SIZE_TYPE_SMALL,
|
|
ICON_SIZE_TYPE_LARGE,
|
|
ICON_SIZE_TYPE_EXTRA_LARGE,
|
|
ICON_SIZE_TYPE_SMALL_METRO,
|
|
ICON_SIZE_TYPE_LARGE_METRO,
|
|
ICON_SIZE_TYPE_EXTRA_LARGE_METRO,
|
|
|
|
ICON_SIZE_COUNT
|
|
};
|
|
|
|
int GetIconSize(TIconSizeType iconSizeType) const;
|
|
|
|
struct IconInfo
|
|
{
|
|
TIconSizeType sizeType;
|
|
bool bTemp; // the icon will be destroyed when the menu closes
|
|
bool bMetro; // this is a Metro icon. it may depend on the system color
|
|
FILETIME timestamp;
|
|
HBITMAP bitmap; // bitmaps are guaranteed to be valid on the main thread (if the handle is read atomically)
|
|
|
|
void SetPath( const wchar_t *path );
|
|
const CString &GetPath( void ) const { Assert(RWLock::ThreadHasReadLock(RWLOCK_ICONS)); return PATH; }
|
|
|
|
private:
|
|
CString PATH; // metro icon paths start with # and are not saved to cache file
|
|
|
|
friend class CItemManager;
|
|
};
|
|
|
|
enum
|
|
{
|
|
INFO_LINK=1, // bLink, appid, bNoPin, bNoNew, targetPidl, targetPath, arguments
|
|
INFO_METRO=2, // bLink, bMetroLink, bMetroApp, appid, metroName, package, packagePath, iconPath, color
|
|
INFO_LINK_APPID=4, // the appid is validated by the app resolver (for jumplists and UserAssist items). Can only be used from the main thread
|
|
|
|
INFO_SMALL_ICON=16,
|
|
INFO_LARGE_ICON=32,
|
|
INFO_EXTRA_LARGE_ICON=64,
|
|
|
|
INFO_NO_PATH=8192, // don't trust the parsing name
|
|
INFO_VALIDATE_FILE=16384, // if the path doesn't exist returns NULL
|
|
INFO_REFRESH_NOW=32768,
|
|
INFO_STARTSCREEN_ICON=65536,
|
|
|
|
INFO_DATA=INFO_LINK|INFO_METRO|INFO_LINK_APPID,
|
|
INFO_ICON=INFO_SMALL_ICON|INFO_LARGE_ICON|INFO_EXTRA_LARGE_ICON,
|
|
};
|
|
|
|
enum TLocation
|
|
{
|
|
LOCATION_UNKNOWN,
|
|
LOCATION_START_MENU,
|
|
LOCATION_GAMES,
|
|
LOCATION_DESKTOP,
|
|
LOCATION_TASKBAR,
|
|
LOCATION_METRO,
|
|
};
|
|
|
|
struct ItemInfo
|
|
{
|
|
ItemInfo( void )
|
|
{
|
|
smallIcon=largeIcon=extraLargeIcon=NULL;
|
|
validFlags=refreshFlags=0;
|
|
bIconOnly=bTemp=bLink=bExplicitAppId=bNoPin=bNoNew=bMetroLink=bMetroApp=bProtectedLink=false;
|
|
writestamp.dwHighDateTime=writestamp.dwLowDateTime=0;
|
|
createstamp.dwHighDateTime=createstamp.dwLowDateTime=0;
|
|
location=LOCATION_UNKNOWN;
|
|
}
|
|
|
|
// PATH never changes after the item is created. it can be accessed without a lock
|
|
CString PATH;
|
|
|
|
// these are replaced atomically with pointers that are always valid
|
|
const IconInfo *smallIcon;
|
|
const IconInfo *largeIcon;
|
|
const IconInfo *extraLargeIcon;
|
|
|
|
const CAbsolutePidl &GetPidl( void ) const { Assert(GetCurrentThreadId()==g_ItemManager.m_MainThreadId || RWLock::ThreadHasReadLock(RWLOCK_ITEMS)); return pidl; }
|
|
bool IsLink( void ) const { Assert(RWLock::ThreadHasReadLock(RWLOCK_ITEMS)); return bLink; }
|
|
bool IsMetroLink( void ) const { Assert(RWLock::ThreadHasReadLock(RWLOCK_ITEMS)); return bMetroLink; }
|
|
bool IsMetroApp( void ) const { Assert(RWLock::ThreadHasReadLock(RWLOCK_ITEMS)); return bMetroApp; }
|
|
bool IsProtectedLink( void ) const { Assert(RWLock::ThreadHasReadLock(RWLOCK_ITEMS)); return bProtectedLink; }
|
|
bool IsNoPin( void ) const { Assert(RWLock::ThreadHasReadLock(RWLOCK_ITEMS)); return bNoPin; }
|
|
bool IsNoNew( void ) const { Assert(RWLock::ThreadHasReadLock(RWLOCK_ITEMS)); return bNoNew; }
|
|
bool IsExplicitAppId( void ) const { Assert(RWLock::ThreadHasReadLock(RWLOCK_ITEMS)); return bExplicitAppId; }
|
|
const CString &GetPath( void ) const { Assert(RWLock::ThreadHasReadLock(RWLOCK_ITEMS)); return path; }
|
|
const CString &GetAppid( void ) const { Assert(RWLock::ThreadHasReadLock(RWLOCK_ITEMS)); return appid; }
|
|
const CString &GetTargetPATH( void ) const { Assert(RWLock::ThreadHasReadLock(RWLOCK_ITEMS)); return targetPATH; }
|
|
const CAbsolutePidl &GetTargetPidl( void ) const { Assert(RWLock::ThreadHasReadLock(RWLOCK_ITEMS)); return targetPidl; }
|
|
const CString &GetMetroName( void ) const { Assert(RWLock::ThreadHasReadLock(RWLOCK_ITEMS)); return metroName; }
|
|
const CString &GetIconPath( void ) const { Assert(RWLock::ThreadHasReadLock(RWLOCK_ITEMS)); return iconPath; }
|
|
const CString &GetPackagePath( void ) const { Assert(RWLock::ThreadHasReadLock(RWLOCK_ITEMS)); return packagePath; }
|
|
TLocation GetLocation( void ) const { Assert(RWLock::ThreadHasReadLock(RWLOCK_ITEMS)); return location; }
|
|
|
|
private:
|
|
CAbsolutePidl pidl;
|
|
CAbsolutePidl newPidl;
|
|
FILETIME writestamp; // valid only for items with paths. the rest are assumed to never change
|
|
FILETIME createstamp; // valid only for items with paths. the rest are assumed to never change
|
|
bool bIconOnly;
|
|
bool bTemp; // the item and its icon will be destroyed when the menu closes (only allowed for small-icon items)
|
|
bool bLink;
|
|
bool bMetroLink;
|
|
bool bMetroApp;
|
|
bool bProtectedLink; // Metro link in the common Programs folder
|
|
bool bNoPin; // the link shouldn't be pinned
|
|
bool bNoNew; // the link shouldn't be new
|
|
bool bExplicitAppId;
|
|
CString path; // only for a file
|
|
CString packagePath; // only for a metro app
|
|
TLocation location;
|
|
|
|
int validFlags;
|
|
int refreshFlags; // 0 if not in the queue, the item can't be deleted if this is !=0
|
|
|
|
CAbsolutePidl targetPidl;
|
|
CString targetPATH;
|
|
CString appid;
|
|
CString metroName;
|
|
CString iconPath;
|
|
DWORD iconColor;
|
|
|
|
int iconIndex; // used only if bIconOnly
|
|
|
|
const CAbsolutePidl &GetLatestPidl( void ) const { Assert(RWLock::ThreadHasReadLock(RWLOCK_ITEMS)); return newPidl?newPidl:pidl; }
|
|
|
|
friend class CItemManager;
|
|
};
|
|
|
|
const ItemInfo *GetItemInfo( IShellItem *pItem, PIDLIST_ABSOLUTE pidl, int refreshFlags, TLocation location=LOCATION_UNKNOWN );
|
|
const ItemInfo *GetItemInfo( CString path, int refreshFlags, TLocation location=LOCATION_UNKNOWN );
|
|
const ItemInfo *GetCustomIcon( const wchar_t *location, int index, TIconSizeType iconSizeType, bool bTemp );
|
|
const ItemInfo *GetCustomIcon( const wchar_t *path, TIconSizeType iconSizeType );
|
|
const ItemInfo* GetLinkIcon(IShellLink* link, TIconSizeType iconSizeType);
|
|
const ItemInfo *GetMetroAppInfo10( const wchar_t *appid );
|
|
void UpdateItemInfo( const ItemInfo *pInfo, int refreshFlags, bool bHasWriteLock=false );
|
|
void WaitForShortcuts( const POINT &balloonPos );
|
|
bool IsTaskbarPinned( const wchar_t *appid );
|
|
void UpdateNewPrograms( const POINT &balloonPos );
|
|
bool IsNewProgram( PIDLIST_ABSOLUTE pidl, bool bFolder, bool bMetroApp );
|
|
bool HasNewPrograms( bool bReal ) { return m_bHasNewPrograms[bReal?0:1]; }
|
|
bool HasNewApps( bool bReal ) { return m_bHasNewApps[bReal?0:1]; }
|
|
void RefreshInfos( void );
|
|
|
|
void RemoveNewItem( PIDLIST_ABSOLUTE pItem1, PIDLIST_ABSOLUTE pItem2, bool bFolder );
|
|
void RemoveNewItems( bool bPrograms, bool bMetro );
|
|
void SaveOldItems( void );
|
|
|
|
enum TRWLock
|
|
{
|
|
RWLOCK_ITEMS,
|
|
RWLOCK_ICONS,
|
|
RWLOCK_COUNT,
|
|
};
|
|
|
|
class RWLock
|
|
{
|
|
public:
|
|
RWLock( CItemManager *pThis, bool bWrite, TRWLock index )
|
|
{
|
|
if (index==RWLOCK_COUNT)
|
|
{
|
|
m_pLock=NULL;
|
|
return;
|
|
}
|
|
#ifdef _DEBUG
|
|
m_pState=&g_LockState[index];
|
|
Assert(*m_pState==0);
|
|
#endif
|
|
m_pLock=&pThis->m_RWLocks[index];
|
|
m_bWrite=bWrite;
|
|
if (bWrite)
|
|
AcquireSRWLockExclusive(m_pLock);
|
|
else
|
|
AcquireSRWLockShared(m_pLock);
|
|
#ifdef _DEBUG
|
|
*m_pState=bWrite?2:1;
|
|
#endif
|
|
}
|
|
|
|
~RWLock( void )
|
|
{
|
|
if (!m_pLock) return;
|
|
#ifdef _DEBUG
|
|
Assert(*m_pState==(m_bWrite?2:1));
|
|
*m_pState=0;
|
|
#endif
|
|
if (m_bWrite)
|
|
ReleaseSRWLockExclusive(m_pLock);
|
|
else
|
|
ReleaseSRWLockShared(m_pLock);
|
|
}
|
|
|
|
#ifdef _DEBUG
|
|
static bool ThreadHasReadLock( TRWLock index ) { return g_LockState[index]!=0; }
|
|
static bool ThreadHasWriteLock( TRWLock index ) { return g_LockState[index]==2; }
|
|
#endif
|
|
|
|
private:
|
|
SRWLOCK *m_pLock;
|
|
bool m_bWrite;
|
|
|
|
#ifdef _DEBUG
|
|
int *m_pState;
|
|
static _declspec(thread) int g_LockState[RWLOCK_COUNT]; // 0 - none, 1 - read, 2 - write
|
|
#endif
|
|
};
|
|
|
|
private:
|
|
static int s_DPI;
|
|
static int s_DPIOverride;
|
|
|
|
enum TLock
|
|
{
|
|
LOCK_CLEANUP,
|
|
LOCK_COUNT,
|
|
};
|
|
|
|
SRWLOCK m_RWLocks[RWLOCK_COUNT];
|
|
CRITICAL_SECTION m_CriticalSections[LOCK_COUNT];
|
|
DWORD m_CriticalSectionOwners[LOCK_COUNT];
|
|
HANDLE m_StartEvent; // start the refresh thread
|
|
HANDLE m_WorkEvent; // kicks off the refresh thread
|
|
HANDLE m_ExitEvent; // exit all threads
|
|
HANDLE m_DoneEvent; // done preloading start menu items
|
|
HANDLE m_PreloadItemsThread;
|
|
HANDLE m_RefreshInfoThread;
|
|
HANDLE m_SaveCacheThread;
|
|
DWORD m_MainThreadId, m_PreloadItemsThreadId, m_RefreshInfoThreadId;
|
|
|
|
// per-thread info used to load icons
|
|
struct LoadIconData
|
|
{
|
|
int m_IconSizes[ICON_SIZE_COUNT];
|
|
HIMAGELIST m_TempLists[ICON_SIZE_COUNT];
|
|
CComPtr<IImageList2> m_pTempLists[ICON_SIZE_COUNT];
|
|
CComPtr<IWICImagingFactory> m_pFactory;
|
|
|
|
void Init( void );
|
|
void Close( void );
|
|
};
|
|
|
|
LoadIconData m_LoadIconData[3]; // one for each thread (main, preload, refresh)
|
|
LoadIconData &GetLoadIconData( void );
|
|
|
|
class Lock
|
|
{
|
|
public:
|
|
Lock( CItemManager *pThis, TLock index )
|
|
{
|
|
m_pSection=&pThis->m_CriticalSections[index];
|
|
EnterCriticalSection(m_pSection);
|
|
m_pOwner=&pThis->m_CriticalSectionOwners[index];
|
|
if (!*m_pOwner)
|
|
*m_pOwner=GetCurrentThreadId();
|
|
else
|
|
m_pOwner=NULL;
|
|
}
|
|
|
|
~Lock( void )
|
|
{
|
|
if (m_pOwner) *m_pOwner=0;
|
|
LeaveCriticalSection(m_pSection);
|
|
}
|
|
|
|
private:
|
|
CRITICAL_SECTION *m_pSection;
|
|
DWORD *m_pOwner;
|
|
};
|
|
|
|
bool ThreadHasLock( TLock index ) { return m_CriticalSectionOwners[index]==GetCurrentThreadId(); }
|
|
|
|
// requires LOCK_ITEMS to be held
|
|
void QueueItemInfo( ItemInfo *pInfo, int refreshFlags );
|
|
// doesn't require a lock
|
|
void RefreshItemInfo( ItemInfo *pInfo, int refreshFlags, IShellItem *pItem, bool bHasWriteLock );
|
|
|
|
void FindInCache( unsigned int hash, int &refreshFlags, const IconInfo *&smallIcon, const IconInfo *&largeIcon, const IconInfo *&extraLargeIcon );
|
|
void 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 );
|
|
void LoadShellIcon( IShellItem *pItem, int refreshFlags, const IconInfo *&smallIcon, const IconInfo *&largeIcon, const IconInfo *&extraLargeIcon, const DWORD *pMetroColor );
|
|
void LoadMetroIcon( IShellItem *pItem, int &refreshFlags, const IconInfo *&smallIcon, const IconInfo *&largeIcon, const IconInfo *&extraLargeIcon, const DWORD *pMetroColor );
|
|
void LoadCustomIcon( const wchar_t *iconPath, int iconIndex, int refreshFlags, const IconInfo *&smallIcon, const IconInfo *&largeIcon, const IconInfo *&extraLargeIcon, bool bTemp );
|
|
HICON LoadShellIcon( int index, int iconSize );
|
|
HICON LoadShellIcon( int iconSize, IExtractIcon *pExtractW, const wchar_t *location, IExtractIconA *pExtractA, const char *locationA, int index );
|
|
HBITMAP BitmapFromIcon( HICON hIcon, int iconSize, bool bDestroyIcon=true );
|
|
|
|
bool m_bInitialized;
|
|
|
|
// sizes for all shell image lists <size, list>
|
|
std::vector<std::pair<int,int>> m_ListSizes;
|
|
|
|
// the key is a hash of the path or the pidl
|
|
std::multimap<unsigned int,ItemInfo> m_ItemInfos;
|
|
|
|
// the key is a hash of the uppercase appid (win10 only)
|
|
std::map<unsigned int,const ItemInfo*> m_MetroItemInfos10;
|
|
// hashes of appids that are for sure not valid
|
|
std::set<unsigned int> m_BlackListInfos10;
|
|
|
|
// the key is a hash of the location and index
|
|
std::multimap<unsigned int,IconInfo> m_IconInfos;
|
|
|
|
// bitmaps that were replaced but may still be used by the main thread
|
|
std::vector<HBITMAP> m_OldBitmaps;
|
|
|
|
const IconInfo *m_DefaultSmallIcon;
|
|
const IconInfo *m_DefaultLargeIcon;
|
|
const IconInfo *m_DefaultExtraLargeIcon;
|
|
|
|
// list of items to process in background
|
|
std::list<ItemInfo*> m_ItemQueue;
|
|
std::list<ItemInfo*> m_ItemQueueLow; // lower priority
|
|
|
|
std::vector<const ItemInfo*> m_NewPrograms;
|
|
std::vector<const ItemInfo*> m_NewProgramRoots;
|
|
unsigned int m_TransientHash;
|
|
bool m_bHasNewPrograms[2]; // 0 - for real, 1 - filtered
|
|
bool m_bHasNewApps[2]; // 0 - for real, 1 - filtered
|
|
|
|
bool m_bPreloadIcons;
|
|
bool m_bPreloadFavorites;
|
|
enum TLoadingStage
|
|
{
|
|
LOAD_STOPPED, // the loading threads are not running
|
|
LOAD_STOPPING, // the loading threads are stopping
|
|
LOAD_LOADING, // the loading threads are running
|
|
};
|
|
volatile TLoadingStage m_LoadingStage;
|
|
int m_LastCacheSave;
|
|
COLORREF m_OldSysAccentColor;
|
|
bool m_bOldInvertIcons;
|
|
|
|
void LoadFolderItems( IShellItem *pFolder, int refreshFlags, int levels, TLocation location );
|
|
void LoadMetroItems( int refreshFlags );
|
|
void PreloadItemsThread( void );
|
|
void CreateDefaultIcons( void );
|
|
static DWORD CALLBACK StaticPreloadItemsThread( void *param );
|
|
void RefreshInfoThread( void );
|
|
static DWORD CALLBACK StaticRefreshInfoThread( void *param );
|
|
static DWORD CALLBACK SaveCacheFileThread( void *param );
|
|
|
|
// all paths are in caps and end with \
|
|
CString m_RootStartMenu1;
|
|
CString m_RootStartMenu2;
|
|
CString m_RootStartMenu3;
|
|
CString m_RootCommonPrograms;
|
|
CString m_RootGames;
|
|
CString m_RootDesktop;
|
|
CString m_RootTaskbar;
|
|
CString m_RootMetro;
|
|
// can be called from any thread
|
|
TLocation DetermineLocation( const wchar_t *PATH );
|
|
|
|
struct ModuleInfo
|
|
{
|
|
CString PATH;
|
|
FILETIME timestamp;
|
|
};
|
|
static bool CompareModuleTimeStamp( const CString &PATH, const FILETIME ×tamp, std::vector<ModuleInfo> &modules );
|
|
|
|
struct KnownPathGuid
|
|
{
|
|
CComString path;
|
|
CComString guid;
|
|
int pathLen;
|
|
};
|
|
|
|
struct OldItemInfo
|
|
{
|
|
unsigned int hash;
|
|
FILETIME timestamp;
|
|
|
|
bool operator<( const OldItemInfo &x ) const { return hash<x.hash; }
|
|
};
|
|
|
|
std::vector<OldItemInfo> m_OldItemInfos;
|
|
|
|
void LoadOldItems( void );
|
|
bool IsPathUsed( CRegKey ®Key, const wchar_t *path, const FILETIME &createstamp, const KnownPathGuid *knownPaths, int knownPathsCount, bool bMetroApp );
|
|
void AddOldItems( const std::vector<unsigned> &hashes );
|
|
};
|
|
|
|
CString GetPropertyStoreString( IPropertyStore *pStore, REFPROPERTYKEY key );
|
|
|
|
class CShellItemEnumerator
|
|
{
|
|
public:
|
|
CShellItemEnumerator( IShellItem *pFolder );
|
|
bool IsValid( void ) const;
|
|
bool GetNext( CComPtr<IShellItem> &pChild, CAbsolutePidl &childPidl );
|
|
|
|
private:
|
|
CComPtr<IEnumIDList> m_pEnumPidls;
|
|
CComPtr<IEnumShellItems> m_pEnumItems;
|
|
CAbsolutePidl m_Root;
|
|
};
|
|
|
|
struct UserAssistData
|
|
{
|
|
int pad1;
|
|
int count;
|
|
int pad2[2];
|
|
float history[10];
|
|
int last;
|
|
FILETIME timestamp;
|
|
int pad3;
|
|
};
|
|
|
|
void EncodeUserAssistPath( wchar_t *path );
|
|
void EncodeRot13( wchar_t *text );
|
|
|
|
enum TNetworkType
|
|
{
|
|
NETWORK_NONE,
|
|
NETWORK_SERVER, // \\server
|
|
NETWORK_SHARE, // \\server\share
|
|
NETWORK_DRIVE, // Q:
|
|
NETWORK_FOLDER, // either \\server\share\folder or Q:\folder
|
|
NETWORK_FILE, // something with extension
|
|
};
|
|
|
|
HRESULT MenuParseDisplayName( const wchar_t *path, PIDLIST_ABSOLUTE *pPidl, SFGAOF *pFlags, TNetworkType *pNetworkType );
|
|
const wchar_t *GetDefaultNetworkIcon( TNetworkType networkType );
|
|
bool MenuGetFileTimestamp( const wchar_t *path, FILETIME *pWriteTime, FILETIME *pCreateTime );
|
|
STDAPI ShGetKnownFolderPath( REFKNOWNFOLDERID rfid, PWSTR *pPath );
|
|
STDAPI ShGetKnownFolderIDList(REFKNOWNFOLDERID rfid, PIDLIST_ABSOLUTE *pPidl );
|
|
STDAPI ShGetKnownFolderItem(REFKNOWNFOLDERID rfid, IShellItem **ppItem );
|
|
HBITMAP ColorizeMonochromeImage(HBITMAP bitmap, DWORD color);
|
|
|
|
#define TASKBAR_PINNED_ROOT L"%APPDATA%\\Microsoft\\Internet Explorer\\Quick Launch\\User Pinned\\TaskBar"
|
|
#define STARTSCREEN_COMMAND L"startscreen.lnk"
|
|
#define USERASSIST_LINKS_KEY L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{F4E57C4B-2036-45F0-A9AB-443BCFE33D9F}\\Count"
|
|
#define USERASSIST_APPIDS_KEY L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\UserAssist\\{CEBFF5CD-ACE2-4F4F-9178-9926F41749EA}\\Count"
|
|
|
|
|
|
//#define STARTSCREEN_WIN7
|
|
#ifdef BUILD_SETUP
|
|
#undef STARTSCREEN_WIN7
|
|
#endif
|