You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
417 lines
15 KiB
417 lines
15 KiB
//
|
|
// Plug-in for the "Program Files" pane. In principle you could do other
|
|
// trees with this plug-in, but it's really tailored to the special way
|
|
// we do Program Files.
|
|
//
|
|
// Only shortcuts count, and the shortcuts are sorted by frequency of use.
|
|
//
|
|
|
|
#include "sfthost.h"
|
|
|
|
//****************************************************************************
|
|
//
|
|
// Helper classes
|
|
|
|
class ByUsageItem; // "item", "pitem"
|
|
class ByUsageShortcut; // "scut", "pscut"
|
|
class ByUsageDir; // "dir", "pdir"
|
|
class ByUsageAppInfo; // "app", "papp"
|
|
class ByUsageHiddenData; // "hd", "phd"
|
|
|
|
// fwd declares
|
|
class ByUsageUI;
|
|
class ByUsageDUI;
|
|
|
|
typedef CDPA<ByUsageShortcut> ByUsageShortcutList; // "sl", "psl"
|
|
typedef CDPA<UNALIGNED ITEMIDLIST> CDPAPidl;// save typing
|
|
typedef CDPA<ByUsageAppInfo> ByUsageAppInfoList;
|
|
|
|
// Helper routines
|
|
BOOL LocalFreeCallback(LPTSTR psz, LPVOID);
|
|
BOOL ILFreeCallback(LPITEMIDLIST pidl, LPVOID);
|
|
void AppendString(CDPA<TCHAR> dpa, LPCTSTR psz);
|
|
|
|
class ByUsageRoot { // "rt", "prt"
|
|
public:
|
|
ByUsageShortcutList _sl; // The list of shortcuts
|
|
ByUsageShortcutList _slOld; // The previous list (used when merging)
|
|
LPITEMIDLIST _pidl; // Where we started enumerating
|
|
BOOL _fNeedRefresh; // Does the list need to be refreshed?
|
|
BOOL _fRegistered; // Has this directory been registered for ShellChangeNotifies?
|
|
|
|
// these next fields are used during re-enumeration
|
|
int _iOld; // First unprocessed item in _slOld
|
|
int _cOld; // Number of elements in _slOld
|
|
|
|
// NOTE! Cannot use destructor here because we need to destroy them
|
|
// in a specific order. See ~ByUsage().
|
|
void Reset();
|
|
|
|
void SetNeedRefresh() { _fNeedRefresh = TRUE; }
|
|
void ClearNeedRefresh() { _fNeedRefresh = FALSE; }
|
|
BOOL NeedsRefresh() const { return _fNeedRefresh; }
|
|
|
|
void SetRegistered() { _fRegistered = TRUE; }
|
|
void ClearRegistered() { _fRegistered = FALSE; }
|
|
BOOL NeedsRegister() const { return !_fRegistered; }
|
|
};
|
|
|
|
|
|
|
|
class CMenuItemsCache {
|
|
public:
|
|
CMenuItemsCache();
|
|
LONG AddRef();
|
|
LONG Release();
|
|
|
|
HRESULT Initialize(ByUsageUI *pbuUI, FILETIME *ftOSInstall);
|
|
HRESULT AttachUI(ByUsageUI *pbuUI);
|
|
BOOL InitCache();
|
|
HRESULT UpdateCache();
|
|
|
|
BOOL IsCacheUpToDate() { return _fIsCacheUpToDate; }
|
|
|
|
HRESULT GetFileCreationTimes();
|
|
void DelayGetFileCreationTimes() { _fCheckNew = FALSE; }
|
|
void DelayGetDarwinInfo() { _fCheckDarwin = FALSE; }
|
|
void AllowGetDarwinInfo() { _fCheckDarwin = TRUE; }
|
|
|
|
void Lock()
|
|
{
|
|
EnterCriticalSection(&_csInUse);
|
|
}
|
|
void Unlock()
|
|
{
|
|
LeaveCriticalSection(&_csInUse);
|
|
}
|
|
BOOL IsLocked()
|
|
{
|
|
return _csInUse.OwningThread == UlongToHandle(GetCurrentThreadId());
|
|
}
|
|
|
|
// Use a separate (heavyweight) sync object for deferral.
|
|
// Keep Lock/Unlock light since we use it a lot. Deferral is
|
|
// comparatively rare. Note that we process incoming SendMessage
|
|
// while waiting for the popup lock. This prevents deadlocks.
|
|
void LockPopup()
|
|
{
|
|
ASSERT(!IsLocked()); // enforce mutex hierarchy;
|
|
SHWaitForSendMessageThread(_hPopupReady, INFINITE);
|
|
}
|
|
void UnlockPopup()
|
|
{
|
|
ReleaseMutex(_hPopupReady);
|
|
}
|
|
|
|
void OnChangeNotify(UINT id, LONG lEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
|
|
void UnregisterNotifyAll();
|
|
|
|
ByUsageAppInfoList *GetAppList() { return &_dpaAppInfo ; }
|
|
ByUsageAppInfo *GetAppInfo(LPTSTR pszAppPath, bool fIgnoreTimestamp);
|
|
ByUsageAppInfo *GetAppInfoFromHiddenData(ByUsageHiddenData *phd);
|
|
ByUsageAppInfo *GetAppInfoFromSpecialPidl(LPCITEMIDLIST pidl);
|
|
|
|
|
|
void StartEnum();
|
|
void EndEnum();
|
|
ByUsageShortcut *GetNextShortcut();
|
|
|
|
static DWORD WINAPI ReInitCacheThreadProc(void *pv);
|
|
static HRESULT ReCreateMenuItemsCache(ByUsageUI *pbuUI, FILETIME *ftOSInstall, CMenuItemsCache **ppMenuCache);
|
|
void RefreshDarwinShortcuts(ByUsageRoot *prt);
|
|
void RefreshCachedDarwinShortcuts();
|
|
|
|
ByUsageShortcut *CreateShortcutFromHiddenData(ByUsageDir *pdir, LPCITEMIDLIST pidl, ByUsageHiddenData *phd, BOOL fForce = FALSE);
|
|
|
|
//
|
|
// Called from helper objects.
|
|
//
|
|
|
|
//
|
|
// An app is newly created if...
|
|
//
|
|
// It was created less than a week ago (_ftOldApps), and
|
|
// It was created after the OS was installed.
|
|
//
|
|
bool IsNewlyCreated(const FILETIME *pftCreated) const
|
|
{
|
|
return CompareFileTime(pftCreated, &_ftOldApps) >= 0;
|
|
}
|
|
|
|
enum { MAXNOTIFY = 6 }; // Number of ChangeNotify slots we use in the cache
|
|
|
|
protected:
|
|
~CMenuItemsCache();
|
|
|
|
LONG _cref;
|
|
ByUsageUI * _pByUsageUI; // BEWARE: DO NOT use this member outside of a LockPopup/UnlockPopup pair.
|
|
|
|
mutable CRITICAL_SECTION _csInUse;
|
|
|
|
FILETIME _ftOldApps; // apps older than this are not new
|
|
|
|
// Flags that control enumeration
|
|
enum ENUMFL {
|
|
ENUMFL_RECURSE = 0,
|
|
ENUMFL_NORECURSE = 1,
|
|
|
|
ENUMFL_CHECKNEW = 0,
|
|
ENUMFL_NOCHECKNEW = 2,
|
|
|
|
ENUMFL_DONTCHECKIFCHILD = 0,
|
|
ENUMFL_CHECKISCHILDOFPREVIOUS = 4,
|
|
|
|
ENUMFL_ISNOTSTARTMENU = 0,
|
|
ENUMFL_ISSTARTMENU = 8,
|
|
};
|
|
|
|
UINT _enumfl;
|
|
|
|
struct ROOTFOLDERINFO {
|
|
int _csidl;
|
|
UINT _enumfl;
|
|
};
|
|
|
|
enum { NUM_PROGLIST_ROOTS = 6 };
|
|
|
|
typedef struct ENUMFOLDERINFO
|
|
{
|
|
CMenuItemsCache *self;
|
|
ByUsageDir *pdir;
|
|
ByUsageRoot *prt;
|
|
} ENUMFOLDERINFO;
|
|
|
|
void _SaveCache();
|
|
|
|
BOOL _ShouldProcessRoot(int iRoot);
|
|
|
|
void _FillFolderCache(ByUsageDir *pdir, ByUsageRoot *prt);
|
|
void _MergeIntoFolderCache(ByUsageRoot *prt, ByUsageDir *pdir, CDPAPidl dpaFiles);
|
|
ByUsageShortcut *_NextFromCacheInDir(ByUsageRoot *prt, ByUsageDir *pdir);
|
|
ByUsageShortcut *_CreateFromCachedPidl(ByUsageRoot *prt, ByUsageDir *pdir, LPITEMIDLIST pidl);
|
|
|
|
void _AddShortcutToCache(ByUsageDir *pdir, LPITEMIDLIST pidl, ByUsageShortcutList slFiles);
|
|
void _TransferShortcutToCache(ByUsageRoot *prt, ByUsageShortcut *pscut);
|
|
|
|
BOOL _GetExcludedDirectories();
|
|
BOOL _IsExcludedDirectory(IShellFolder *psf, LPCITEMIDLIST pidl, DWORD dwAttributes);
|
|
BOOL _IsInterestingDirectory(ByUsageDir *pdir);
|
|
|
|
static void _InitStringList(HKEY hk, LPCTSTR pszValue, CDPA<TCHAR> dpa);
|
|
void _InitKillList();
|
|
bool _SetInterestingLink(ByUsageShortcut *pscut);
|
|
BOOL _PathIsInterestingExe(LPCTSTR pszPath);
|
|
BOOL _IsExcludedExe(LPCTSTR pszPath);
|
|
|
|
HRESULT _UpdateMSIPath(ByUsageShortcut *pscut);
|
|
|
|
inline static BOOL IsRestrictedCsidl(int csidl)
|
|
{
|
|
return (csidl == CSIDL_COMMON_PROGRAMS || csidl == CSIDL_COMMON_DESKTOPDIRECTORY || csidl == CSIDL_COMMON_STARTMENU) &&
|
|
SHRestricted(REST_NOCOMMONGROUPS);
|
|
}
|
|
|
|
static FolderEnumCallback(LPITEMIDLIST pidlChild, ENUMFOLDERINFO *pinfo);
|
|
|
|
ByUsageDir * _pdirDesktop; // ByUsageDir for the desktop
|
|
|
|
int _iCurrentRoot; // For Enumeration
|
|
int _iCurrentIndex;
|
|
|
|
// The directories we care about.
|
|
ByUsageRoot _rgrt[NUM_PROGLIST_ROOTS];
|
|
|
|
ByUsageAppInfoList _dpaAppInfo; // apps we've seen so far
|
|
|
|
IQueryAssociations * _pqa;
|
|
|
|
CDPA<TCHAR> _dpaNotInteresting; // directories that yield shortcuts that we want to ignore
|
|
CDPA<TCHAR> _dpaKill; // program names to ignore
|
|
CDPA<TCHAR> _dpaKillLink;// link names (substrings) to ignore
|
|
|
|
BOOL _fIsCacheUpToDate; // Do we need to walk the start menu dirs?
|
|
BOOL _fIsInited;
|
|
BOOL _fCheckNew; // Do we want to extract creation time for apps?
|
|
BOOL _fCheckDarwin; // Do we want to fetch Darwin info?
|
|
BOOL _fCSInited; // Did we successfully initialize the critsec?
|
|
|
|
HANDLE _hPopupReady; // mutex handle - controls access to cache (re)initialization
|
|
|
|
static const struct ROOTFOLDERINFO c_rgrfi[NUM_PROGLIST_ROOTS];
|
|
};
|
|
|
|
|
|
//****************************************************************************
|
|
|
|
class ByUsage
|
|
{
|
|
friend class ByUsageUI;
|
|
friend class ByUsageDUI;
|
|
|
|
public: // Methods required by SFTBarHost
|
|
ByUsage(ByUsageUI *pByUsageUI, ByUsageDUI *pByUsageDUI);
|
|
virtual ~ByUsage();
|
|
|
|
virtual HRESULT Initialize();
|
|
virtual void EnumItems();
|
|
virtual LPITEMIDLIST GetFullPidl(PaneItem *p);
|
|
|
|
static int CompareUEMInfo(UEMINFO *puei1, UEMINFO *puei2);
|
|
|
|
virtual int CompareItems(PaneItem *p1, PaneItem *p2);
|
|
|
|
HRESULT GetFolderAndPidl(PaneItem *pitem, IShellFolder **ppsfOut, LPCITEMIDLIST *ppidlOut);
|
|
int ReadIconSize();
|
|
LRESULT OnWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
|
HRESULT ContextMenuDeleteItem(PaneItem *pitem, IContextMenu *pcm, CMINVOKECOMMANDINFOEX *pici);
|
|
HRESULT ContextMenuInvokeItem(PaneItem *pitem, IContextMenu *pcm, CMINVOKECOMMANDINFOEX *pici, LPCTSTR pszVerb);
|
|
HRESULT ContextMenuRenameItem(PaneItem *pitem, LPCTSTR ptszNewName);
|
|
LPTSTR DisplayNameOfItem(PaneItem *pitem, IShellFolder *psf, LPCITEMIDLIST pidlItem, SHGNO shgno);
|
|
LPTSTR SubtitleOfItem(PaneItem *pitem, IShellFolder *psf, LPCITEMIDLIST pidlItem);
|
|
HRESULT MovePinnedItem(PaneItem *pitem, int iInsert);
|
|
void PrePopulate();
|
|
|
|
CMenuItemsCache *GetMenuCache() { return _pMenuCache; }
|
|
BOOL IsInsertable(IDataObject *pdto);
|
|
HRESULT InsertPinnedItem(IDataObject *pdto, int iInsert);
|
|
void OnChangeNotify(UINT id, LONG lEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
|
|
void OnPinListChange();
|
|
|
|
private:
|
|
// Private messages start at WM_APP
|
|
enum {
|
|
BUM_SETNEWITEMS = WM_APP,
|
|
};
|
|
|
|
enum {
|
|
// We use the first slot not used by the menu items cache.
|
|
NOTIFY_PINCHANGE = CMenuItemsCache::MAXNOTIFY,
|
|
};
|
|
|
|
|
|
inline BOOL _IsPinned(ByUsageItem *pitem);
|
|
BOOL _IsPinnedExe(ByUsageItem *pitem, IShellFolder *psf, LPCITEMIDLIST pidlItem);
|
|
HRESULT _GetShortcutExeTarget(IShellFolder *psf, LPCITEMIDLIST pidl, LPTSTR pszPath, UINT cchPath);
|
|
|
|
void _FillPinnedItemsCache();
|
|
void _EnumPinnedItemsFromCache();
|
|
void _NotifyDesiredSize();
|
|
|
|
void EnumFolderFromCache();
|
|
void AfterEnumItems();
|
|
|
|
typedef struct AFTERENUMINFO {
|
|
ByUsage *self;
|
|
CDPAPidl dpaNew;
|
|
} AFTERENUMINFO;
|
|
static BOOL CALLBACK _AfterEnumCB(ByUsageAppInfo *papp, AFTERENUMINFO *paei);
|
|
|
|
|
|
static int UEMNotifyCB(void *param, const GUID *pguidGrp, int eCmd);
|
|
|
|
BOOL _GetExcludedDirectories();
|
|
bool _IsShortcutNew(ByUsageShortcut *pscut, ByUsageAppInfo *papp, const UEMINFO *puei);
|
|
void _DestroyExcludedDirectories();
|
|
LRESULT _ModifySMInfo(PSMNMMODIFYSMINFO pmsi);
|
|
|
|
LRESULT _OnNotify(LPNMHDR pnm);
|
|
LRESULT _OnSetNewItems(HDPA dpaNew);
|
|
|
|
BOOL IsSpecialPinnedItem(ByUsageItem *pitem);
|
|
BOOL IsSpecialPinnedPidl(LPCITEMIDLIST pidl);
|
|
public:
|
|
//
|
|
// Executions within the grace period of app install are not counted
|
|
// against "new"ness.
|
|
//
|
|
static inline __int64 FT_NEWAPPGRACEPERIOD() { return FT_ONEHOUR; }
|
|
|
|
private:
|
|
CDPAPidl _dpaNew; // the new guys
|
|
|
|
IStartMenuPin * _psmpin; // to access the pin list
|
|
LPITEMIDLIST _pidlBrowser; // Special pinned items with special names
|
|
LPITEMIDLIST _pidlEmail; // ditto
|
|
|
|
FILETIME _ftStartTime; /* The time when StartMenu was first invoked */
|
|
FILETIME _ftNewestApp; // The time of the newest app
|
|
|
|
ByUsageRoot _rtPinned;
|
|
|
|
ULONG _ulPinChange; // detect if the pinlinst changed
|
|
|
|
ByUsageDir * _pdirDesktop; // ByUsageDir for the desktop
|
|
|
|
ByUsageUI * _pByUsageUI;
|
|
HWND _hwnd;
|
|
|
|
ByUsageDUI * _pByUsageDUI;
|
|
|
|
CMenuItemsCache * _pMenuCache;
|
|
|
|
BOOL _fUEMRegistered;
|
|
int _cMFUDesired;
|
|
};
|
|
|
|
class ByUsageUI : public SFTBarHost
|
|
{
|
|
friend class ByUsage;
|
|
friend class CMenuItemsCache;
|
|
public:
|
|
friend SFTBarHost *ByUsage_CreateInstance();
|
|
|
|
private: // Methods required by SFTBarHost
|
|
HRESULT Initialize() { return _byUsage.Initialize(); }
|
|
void EnumItems() { _byUsage.EnumItems(); }
|
|
int CompareItems(PaneItem *p1, PaneItem *p2) { return _byUsage.CompareItems(p1, p2); }
|
|
HRESULT GetFolderAndPidl(PaneItem *pitem, IShellFolder **ppsfOut, LPCITEMIDLIST *ppidlOut)
|
|
{
|
|
return _byUsage.GetFolderAndPidl(pitem, ppsfOut, ppidlOut);
|
|
}
|
|
void OnChangeNotify(UINT id, LONG lEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
|
|
{
|
|
_byUsage.OnChangeNotify(id, lEvent, pidl1, pidl2);
|
|
}
|
|
int ReadIconSize() { return _byUsage.ReadIconSize(); }
|
|
LRESULT OnWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { return _byUsage.OnWndProc(hwnd, uMsg, wParam, lParam); }
|
|
HRESULT ContextMenuInvokeItem(PaneItem *pitem, IContextMenu *pcm, CMINVOKECOMMANDINFOEX *pici, LPCTSTR pszVerb) { return _byUsage.ContextMenuInvokeItem(pitem, pcm, pici, pszVerb); }
|
|
HRESULT ContextMenuRenameItem(PaneItem *pitem, LPCTSTR ptszNewName) { return _byUsage.ContextMenuRenameItem(pitem, ptszNewName); }
|
|
LPTSTR DisplayNameOfItem(PaneItem *pitem, IShellFolder *psf, LPCITEMIDLIST pidlItem, SHGNO shgno) { return _byUsage.DisplayNameOfItem(pitem, psf, pidlItem, shgno); }
|
|
LPTSTR SubtitleOfItem(PaneItem *pitem, IShellFolder *psf, LPCITEMIDLIST pidlItem) { return _byUsage.SubtitleOfItem(pitem, psf, pidlItem); }
|
|
HRESULT MovePinnedItem(PaneItem *pitem, int iInsert) { return _byUsage.MovePinnedItem(pitem, iInsert); }
|
|
void PrePopulate() { _byUsage.PrePopulate(); }
|
|
BOOL IsInsertable(IDataObject *pdto) { return _byUsage.IsInsertable(pdto); }
|
|
HRESULT InsertPinnedItem(IDataObject *pdto, int iInsert) { return _byUsage.InsertPinnedItem(pdto, iInsert); }
|
|
UINT AdjustDeleteMenuItem(PaneItem *pitem, UINT *puiFlags) { return IDS_SFTHOST_REMOVEFROMLIST; }
|
|
|
|
BOOL NeedBackgroundEnum() { return TRUE; }
|
|
BOOL HasDynamicContent() { return TRUE; }
|
|
|
|
void RefreshNow() { PostMessage(_hwnd, SFTBM_REFRESH, FALSE, 0); }
|
|
|
|
private:
|
|
ByUsageUI();
|
|
private:
|
|
ByUsage _byUsage;
|
|
|
|
};
|
|
|
|
class ByUsageDUI
|
|
{
|
|
public:
|
|
/*
|
|
* Add a PaneItem to the list - if add fails, item will be delete'd.
|
|
*
|
|
* CLEANUP psf must be NULL; pidl must be the absolute pidl to the item
|
|
* being added. Leftover from dead HOSTF_PINITEMSBYFOLDER feature.
|
|
* Needs to be cleaned up.
|
|
*
|
|
* Passing psf and pidlChild are for perf.
|
|
*/
|
|
virtual BOOL AddItem(PaneItem *pitem, IShellFolder *psf, LPCITEMIDLIST pidlChild) PURE;
|
|
/*
|
|
* Hooking into change notifications
|
|
*/
|
|
virtual BOOL RegisterNotify(UINT id, LONG lEvents, LPITEMIDLIST pidl, BOOL fRecursive) PURE;
|
|
virtual BOOL UnregisterNotify(UINT id) PURE;
|
|
};
|