Leaked source code of windows server 2003
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

  1. //
  2. // Plug-in for the "Program Files" pane. In principle you could do other
  3. // trees with this plug-in, but it's really tailored to the special way
  4. // we do Program Files.
  5. //
  6. // Only shortcuts count, and the shortcuts are sorted by frequency of use.
  7. //
  8. #include "sfthost.h"
  9. //****************************************************************************
  10. //
  11. // Helper classes
  12. class ByUsageItem; // "item", "pitem"
  13. class ByUsageShortcut; // "scut", "pscut"
  14. class ByUsageDir; // "dir", "pdir"
  15. class ByUsageAppInfo; // "app", "papp"
  16. class ByUsageHiddenData; // "hd", "phd"
  17. // fwd declares
  18. class ByUsageUI;
  19. class ByUsageDUI;
  20. typedef CDPA<ByUsageShortcut> ByUsageShortcutList; // "sl", "psl"
  21. typedef CDPA<UNALIGNED ITEMIDLIST> CDPAPidl;// save typing
  22. typedef CDPA<ByUsageAppInfo> ByUsageAppInfoList;
  23. // Helper routines
  24. BOOL LocalFreeCallback(LPTSTR psz, LPVOID);
  25. BOOL ILFreeCallback(LPITEMIDLIST pidl, LPVOID);
  26. void AppendString(CDPA<TCHAR> dpa, LPCTSTR psz);
  27. class ByUsageRoot { // "rt", "prt"
  28. public:
  29. ByUsageShortcutList _sl; // The list of shortcuts
  30. ByUsageShortcutList _slOld; // The previous list (used when merging)
  31. LPITEMIDLIST _pidl; // Where we started enumerating
  32. BOOL _fNeedRefresh; // Does the list need to be refreshed?
  33. BOOL _fRegistered; // Has this directory been registered for ShellChangeNotifies?
  34. // these next fields are used during re-enumeration
  35. int _iOld; // First unprocessed item in _slOld
  36. int _cOld; // Number of elements in _slOld
  37. // NOTE! Cannot use destructor here because we need to destroy them
  38. // in a specific order. See ~ByUsage().
  39. void Reset();
  40. void SetNeedRefresh() { _fNeedRefresh = TRUE; }
  41. void ClearNeedRefresh() { _fNeedRefresh = FALSE; }
  42. BOOL NeedsRefresh() const { return _fNeedRefresh; }
  43. void SetRegistered() { _fRegistered = TRUE; }
  44. void ClearRegistered() { _fRegistered = FALSE; }
  45. BOOL NeedsRegister() const { return !_fRegistered; }
  46. };
  47. class CMenuItemsCache {
  48. public:
  49. CMenuItemsCache();
  50. LONG AddRef();
  51. LONG Release();
  52. HRESULT Initialize(ByUsageUI *pbuUI, FILETIME *ftOSInstall);
  53. HRESULT AttachUI(ByUsageUI *pbuUI);
  54. BOOL InitCache();
  55. HRESULT UpdateCache();
  56. BOOL IsCacheUpToDate() { return _fIsCacheUpToDate; }
  57. HRESULT GetFileCreationTimes();
  58. void DelayGetFileCreationTimes() { _fCheckNew = FALSE; }
  59. void DelayGetDarwinInfo() { _fCheckDarwin = FALSE; }
  60. void AllowGetDarwinInfo() { _fCheckDarwin = TRUE; }
  61. void Lock()
  62. {
  63. EnterCriticalSection(&_csInUse);
  64. }
  65. void Unlock()
  66. {
  67. LeaveCriticalSection(&_csInUse);
  68. }
  69. BOOL IsLocked()
  70. {
  71. return _csInUse.OwningThread == UlongToHandle(GetCurrentThreadId());
  72. }
  73. // Use a separate (heavyweight) sync object for deferral.
  74. // Keep Lock/Unlock light since we use it a lot. Deferral is
  75. // comparatively rare. Note that we process incoming SendMessage
  76. // while waiting for the popup lock. This prevents deadlocks.
  77. void LockPopup()
  78. {
  79. ASSERT(!IsLocked()); // enforce mutex hierarchy;
  80. SHWaitForSendMessageThread(_hPopupReady, INFINITE);
  81. }
  82. void UnlockPopup()
  83. {
  84. ReleaseMutex(_hPopupReady);
  85. }
  86. void OnChangeNotify(UINT id, LONG lEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
  87. void UnregisterNotifyAll();
  88. ByUsageAppInfoList *GetAppList() { return &_dpaAppInfo ; }
  89. ByUsageAppInfo *GetAppInfo(LPTSTR pszAppPath, bool fIgnoreTimestamp);
  90. ByUsageAppInfo *GetAppInfoFromHiddenData(ByUsageHiddenData *phd);
  91. ByUsageAppInfo *GetAppInfoFromSpecialPidl(LPCITEMIDLIST pidl);
  92. void StartEnum();
  93. void EndEnum();
  94. ByUsageShortcut *GetNextShortcut();
  95. static DWORD WINAPI ReInitCacheThreadProc(void *pv);
  96. static HRESULT ReCreateMenuItemsCache(ByUsageUI *pbuUI, FILETIME *ftOSInstall, CMenuItemsCache **ppMenuCache);
  97. void RefreshDarwinShortcuts(ByUsageRoot *prt);
  98. void RefreshCachedDarwinShortcuts();
  99. ByUsageShortcut *CreateShortcutFromHiddenData(ByUsageDir *pdir, LPCITEMIDLIST pidl, ByUsageHiddenData *phd, BOOL fForce = FALSE);
  100. //
  101. // Called from helper objects.
  102. //
  103. //
  104. // An app is newly created if...
  105. //
  106. // It was created less than a week ago (_ftOldApps), and
  107. // It was created after the OS was installed.
  108. //
  109. bool IsNewlyCreated(const FILETIME *pftCreated) const
  110. {
  111. return CompareFileTime(pftCreated, &_ftOldApps) >= 0;
  112. }
  113. enum { MAXNOTIFY = 6 }; // Number of ChangeNotify slots we use in the cache
  114. protected:
  115. ~CMenuItemsCache();
  116. LONG _cref;
  117. ByUsageUI * _pByUsageUI; // BEWARE: DO NOT use this member outside of a LockPopup/UnlockPopup pair.
  118. mutable CRITICAL_SECTION _csInUse;
  119. FILETIME _ftOldApps; // apps older than this are not new
  120. // Flags that control enumeration
  121. enum ENUMFL {
  122. ENUMFL_RECURSE = 0,
  123. ENUMFL_NORECURSE = 1,
  124. ENUMFL_CHECKNEW = 0,
  125. ENUMFL_NOCHECKNEW = 2,
  126. ENUMFL_DONTCHECKIFCHILD = 0,
  127. ENUMFL_CHECKISCHILDOFPREVIOUS = 4,
  128. ENUMFL_ISNOTSTARTMENU = 0,
  129. ENUMFL_ISSTARTMENU = 8,
  130. };
  131. UINT _enumfl;
  132. struct ROOTFOLDERINFO {
  133. int _csidl;
  134. UINT _enumfl;
  135. };
  136. enum { NUM_PROGLIST_ROOTS = 6 };
  137. typedef struct ENUMFOLDERINFO
  138. {
  139. CMenuItemsCache *self;
  140. ByUsageDir *pdir;
  141. ByUsageRoot *prt;
  142. } ENUMFOLDERINFO;
  143. void _SaveCache();
  144. BOOL _ShouldProcessRoot(int iRoot);
  145. void _FillFolderCache(ByUsageDir *pdir, ByUsageRoot *prt);
  146. void _MergeIntoFolderCache(ByUsageRoot *prt, ByUsageDir *pdir, CDPAPidl dpaFiles);
  147. ByUsageShortcut *_NextFromCacheInDir(ByUsageRoot *prt, ByUsageDir *pdir);
  148. ByUsageShortcut *_CreateFromCachedPidl(ByUsageRoot *prt, ByUsageDir *pdir, LPITEMIDLIST pidl);
  149. void _AddShortcutToCache(ByUsageDir *pdir, LPITEMIDLIST pidl, ByUsageShortcutList slFiles);
  150. void _TransferShortcutToCache(ByUsageRoot *prt, ByUsageShortcut *pscut);
  151. BOOL _GetExcludedDirectories();
  152. BOOL _IsExcludedDirectory(IShellFolder *psf, LPCITEMIDLIST pidl, DWORD dwAttributes);
  153. BOOL _IsInterestingDirectory(ByUsageDir *pdir);
  154. static void _InitStringList(HKEY hk, LPCTSTR pszValue, CDPA<TCHAR> dpa);
  155. void _InitKillList();
  156. bool _SetInterestingLink(ByUsageShortcut *pscut);
  157. BOOL _PathIsInterestingExe(LPCTSTR pszPath);
  158. BOOL _IsExcludedExe(LPCTSTR pszPath);
  159. HRESULT _UpdateMSIPath(ByUsageShortcut *pscut);
  160. inline static BOOL IsRestrictedCsidl(int csidl)
  161. {
  162. return (csidl == CSIDL_COMMON_PROGRAMS || csidl == CSIDL_COMMON_DESKTOPDIRECTORY || csidl == CSIDL_COMMON_STARTMENU) &&
  163. SHRestricted(REST_NOCOMMONGROUPS);
  164. }
  165. static FolderEnumCallback(LPITEMIDLIST pidlChild, ENUMFOLDERINFO *pinfo);
  166. ByUsageDir * _pdirDesktop; // ByUsageDir for the desktop
  167. int _iCurrentRoot; // For Enumeration
  168. int _iCurrentIndex;
  169. // The directories we care about.
  170. ByUsageRoot _rgrt[NUM_PROGLIST_ROOTS];
  171. ByUsageAppInfoList _dpaAppInfo; // apps we've seen so far
  172. IQueryAssociations * _pqa;
  173. CDPA<TCHAR> _dpaNotInteresting; // directories that yield shortcuts that we want to ignore
  174. CDPA<TCHAR> _dpaKill; // program names to ignore
  175. CDPA<TCHAR> _dpaKillLink;// link names (substrings) to ignore
  176. BOOL _fIsCacheUpToDate; // Do we need to walk the start menu dirs?
  177. BOOL _fIsInited;
  178. BOOL _fCheckNew; // Do we want to extract creation time for apps?
  179. BOOL _fCheckDarwin; // Do we want to fetch Darwin info?
  180. BOOL _fCSInited; // Did we successfully initialize the critsec?
  181. HANDLE _hPopupReady; // mutex handle - controls access to cache (re)initialization
  182. static const struct ROOTFOLDERINFO c_rgrfi[NUM_PROGLIST_ROOTS];
  183. };
  184. //****************************************************************************
  185. class ByUsage
  186. {
  187. friend class ByUsageUI;
  188. friend class ByUsageDUI;
  189. public: // Methods required by SFTBarHost
  190. ByUsage(ByUsageUI *pByUsageUI, ByUsageDUI *pByUsageDUI);
  191. virtual ~ByUsage();
  192. virtual HRESULT Initialize();
  193. virtual void EnumItems();
  194. virtual LPITEMIDLIST GetFullPidl(PaneItem *p);
  195. static int CompareUEMInfo(UEMINFO *puei1, UEMINFO *puei2);
  196. virtual int CompareItems(PaneItem *p1, PaneItem *p2);
  197. HRESULT GetFolderAndPidl(PaneItem *pitem, IShellFolder **ppsfOut, LPCITEMIDLIST *ppidlOut);
  198. int ReadIconSize();
  199. LRESULT OnWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  200. HRESULT ContextMenuDeleteItem(PaneItem *pitem, IContextMenu *pcm, CMINVOKECOMMANDINFOEX *pici);
  201. HRESULT ContextMenuInvokeItem(PaneItem *pitem, IContextMenu *pcm, CMINVOKECOMMANDINFOEX *pici, LPCTSTR pszVerb);
  202. HRESULT ContextMenuRenameItem(PaneItem *pitem, LPCTSTR ptszNewName);
  203. LPTSTR DisplayNameOfItem(PaneItem *pitem, IShellFolder *psf, LPCITEMIDLIST pidlItem, SHGNO shgno);
  204. LPTSTR SubtitleOfItem(PaneItem *pitem, IShellFolder *psf, LPCITEMIDLIST pidlItem);
  205. HRESULT MovePinnedItem(PaneItem *pitem, int iInsert);
  206. void PrePopulate();
  207. CMenuItemsCache *GetMenuCache() { return _pMenuCache; }
  208. BOOL IsInsertable(IDataObject *pdto);
  209. HRESULT InsertPinnedItem(IDataObject *pdto, int iInsert);
  210. void OnChangeNotify(UINT id, LONG lEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
  211. void OnPinListChange();
  212. private:
  213. // Private messages start at WM_APP
  214. enum {
  215. BUM_SETNEWITEMS = WM_APP,
  216. };
  217. enum {
  218. // We use the first slot not used by the menu items cache.
  219. NOTIFY_PINCHANGE = CMenuItemsCache::MAXNOTIFY,
  220. };
  221. inline BOOL _IsPinned(ByUsageItem *pitem);
  222. BOOL _IsPinnedExe(ByUsageItem *pitem, IShellFolder *psf, LPCITEMIDLIST pidlItem);
  223. HRESULT _GetShortcutExeTarget(IShellFolder *psf, LPCITEMIDLIST pidl, LPTSTR pszPath, UINT cchPath);
  224. void _FillPinnedItemsCache();
  225. void _EnumPinnedItemsFromCache();
  226. void _NotifyDesiredSize();
  227. void EnumFolderFromCache();
  228. void AfterEnumItems();
  229. typedef struct AFTERENUMINFO {
  230. ByUsage *self;
  231. CDPAPidl dpaNew;
  232. } AFTERENUMINFO;
  233. static BOOL CALLBACK _AfterEnumCB(ByUsageAppInfo *papp, AFTERENUMINFO *paei);
  234. static int UEMNotifyCB(void *param, const GUID *pguidGrp, int eCmd);
  235. BOOL _GetExcludedDirectories();
  236. bool _IsShortcutNew(ByUsageShortcut *pscut, ByUsageAppInfo *papp, const UEMINFO *puei);
  237. void _DestroyExcludedDirectories();
  238. LRESULT _ModifySMInfo(PSMNMMODIFYSMINFO pmsi);
  239. LRESULT _OnNotify(LPNMHDR pnm);
  240. LRESULT _OnSetNewItems(HDPA dpaNew);
  241. BOOL IsSpecialPinnedItem(ByUsageItem *pitem);
  242. BOOL IsSpecialPinnedPidl(LPCITEMIDLIST pidl);
  243. public:
  244. //
  245. // Executions within the grace period of app install are not counted
  246. // against "new"ness.
  247. //
  248. static inline __int64 FT_NEWAPPGRACEPERIOD() { return FT_ONEHOUR; }
  249. private:
  250. CDPAPidl _dpaNew; // the new guys
  251. IStartMenuPin * _psmpin; // to access the pin list
  252. LPITEMIDLIST _pidlBrowser; // Special pinned items with special names
  253. LPITEMIDLIST _pidlEmail; // ditto
  254. FILETIME _ftStartTime; /* The time when StartMenu was first invoked */
  255. FILETIME _ftNewestApp; // The time of the newest app
  256. ByUsageRoot _rtPinned;
  257. ULONG _ulPinChange; // detect if the pinlinst changed
  258. ByUsageDir * _pdirDesktop; // ByUsageDir for the desktop
  259. ByUsageUI * _pByUsageUI;
  260. HWND _hwnd;
  261. ByUsageDUI * _pByUsageDUI;
  262. CMenuItemsCache * _pMenuCache;
  263. BOOL _fUEMRegistered;
  264. int _cMFUDesired;
  265. };
  266. class ByUsageUI : public SFTBarHost
  267. {
  268. friend class ByUsage;
  269. friend class CMenuItemsCache;
  270. public:
  271. friend SFTBarHost *ByUsage_CreateInstance();
  272. private: // Methods required by SFTBarHost
  273. HRESULT Initialize() { return _byUsage.Initialize(); }
  274. void EnumItems() { _byUsage.EnumItems(); }
  275. int CompareItems(PaneItem *p1, PaneItem *p2) { return _byUsage.CompareItems(p1, p2); }
  276. HRESULT GetFolderAndPidl(PaneItem *pitem, IShellFolder **ppsfOut, LPCITEMIDLIST *ppidlOut)
  277. {
  278. return _byUsage.GetFolderAndPidl(pitem, ppsfOut, ppidlOut);
  279. }
  280. void OnChangeNotify(UINT id, LONG lEvent, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  281. {
  282. _byUsage.OnChangeNotify(id, lEvent, pidl1, pidl2);
  283. }
  284. int ReadIconSize() { return _byUsage.ReadIconSize(); }
  285. LRESULT OnWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { return _byUsage.OnWndProc(hwnd, uMsg, wParam, lParam); }
  286. HRESULT ContextMenuInvokeItem(PaneItem *pitem, IContextMenu *pcm, CMINVOKECOMMANDINFOEX *pici, LPCTSTR pszVerb) { return _byUsage.ContextMenuInvokeItem(pitem, pcm, pici, pszVerb); }
  287. HRESULT ContextMenuRenameItem(PaneItem *pitem, LPCTSTR ptszNewName) { return _byUsage.ContextMenuRenameItem(pitem, ptszNewName); }
  288. LPTSTR DisplayNameOfItem(PaneItem *pitem, IShellFolder *psf, LPCITEMIDLIST pidlItem, SHGNO shgno) { return _byUsage.DisplayNameOfItem(pitem, psf, pidlItem, shgno); }
  289. LPTSTR SubtitleOfItem(PaneItem *pitem, IShellFolder *psf, LPCITEMIDLIST pidlItem) { return _byUsage.SubtitleOfItem(pitem, psf, pidlItem); }
  290. HRESULT MovePinnedItem(PaneItem *pitem, int iInsert) { return _byUsage.MovePinnedItem(pitem, iInsert); }
  291. void PrePopulate() { _byUsage.PrePopulate(); }
  292. BOOL IsInsertable(IDataObject *pdto) { return _byUsage.IsInsertable(pdto); }
  293. HRESULT InsertPinnedItem(IDataObject *pdto, int iInsert) { return _byUsage.InsertPinnedItem(pdto, iInsert); }
  294. UINT AdjustDeleteMenuItem(PaneItem *pitem, UINT *puiFlags) { return IDS_SFTHOST_REMOVEFROMLIST; }
  295. BOOL NeedBackgroundEnum() { return TRUE; }
  296. BOOL HasDynamicContent() { return TRUE; }
  297. void RefreshNow() { PostMessage(_hwnd, SFTBM_REFRESH, FALSE, 0); }
  298. private:
  299. ByUsageUI();
  300. private:
  301. ByUsage _byUsage;
  302. };
  303. class ByUsageDUI
  304. {
  305. public:
  306. /*
  307. * Add a PaneItem to the list - if add fails, item will be delete'd.
  308. *
  309. * CLEANUP psf must be NULL; pidl must be the absolute pidl to the item
  310. * being added. Leftover from dead HOSTF_PINITEMSBYFOLDER feature.
  311. * Needs to be cleaned up.
  312. *
  313. * Passing psf and pidlChild are for perf.
  314. */
  315. virtual BOOL AddItem(PaneItem *pitem, IShellFolder *psf, LPCITEMIDLIST pidlChild) PURE;
  316. /*
  317. * Hooking into change notifications
  318. */
  319. virtual BOOL RegisterNotify(UINT id, LONG lEvents, LPITEMIDLIST pidl, BOOL fRecursive) PURE;
  320. virtual BOOL UnregisterNotify(UINT id) PURE;
  321. };