Source code of Windows XP (NT5)
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.

944 lines
28 KiB

  1. //---------------------------------------------------------------------------
  2. //
  3. // Copyright (c) Microsoft Corporation 1991-1996
  4. //
  5. // File: recdocs.cpp
  6. //
  7. // History - created from recent.c in explorer - ZekeL - 5-MAR-98
  8. // combining functionality in to one place
  9. // now that the desktop lives here.
  10. //---------------------------------------------------------------------------
  11. #include "shellprv.h"
  12. #include "recdocs.h"
  13. #include "fstreex.h"
  14. #include "shcombox.h"
  15. #include "ids.h"
  16. #include <urlhist.h>
  17. #include <runtask.h>
  18. #define DM_RECENTDOCS 0x00000000
  19. #define GETRECNAME(p) ((LPCTSTR)(p))
  20. #define GETRECPIDL(p) ((LPCITEMIDLIST) (((LPBYTE) (p)) + CbFromCch(lstrlen(GETRECNAME(p)) +1)))
  21. #define REGSTR_KEY_RECENTDOCS TEXT("RecentDocs")
  22. #define MAX_RECMRU_BUF (CbFromCch(3 * MAX_PATH)) // Max MRUBuf size
  23. // Used to blow off adding the same file multiple times
  24. TCHAR g_szLastFile[MAX_URL_STRING] = {0};
  25. FILETIME g_ftLastFileCacheUpdate = {0};
  26. STDAPI_(BOOL) SetFolderString(BOOL fCreate, LPCTSTR pszFolder, LPCTSTR pszProvider, LPCTSTR pszSection, LPCTSTR pszKey, LPCTSTR pszData);
  27. STDAPI_(void) OpenWithListSoftRegisterProcess(DWORD dwFlags, LPCTSTR pszExt, LPCTSTR pszProcess);
  28. class CTaskAddDoc : public CRunnableTask
  29. {
  30. public:
  31. CTaskAddDoc();
  32. HRESULT Init(HANDLE hMem, DWORD dwProcId);
  33. // *** pure virtuals ***
  34. virtual STDMETHODIMP RunInitRT(void);
  35. private:
  36. virtual ~CTaskAddDoc();
  37. void _AddToRecentDocs(LPCITEMIDLIST pidlItem, LPCTSTR pszPath);
  38. void _TryDeleteMRUItem(IMruDataList *pmru, DWORD cMax, LPCTSTR pszFileName, LPCITEMIDLIST pidlItem, IMruDataList *pmruOther, BOOL fOverwrite);
  39. LPBYTE _CreateMRUItem(LPCITEMIDLIST pidlItem, LPCTSTR pszItem, DWORD *pcbItem, UINT uFlags);
  40. BOOL _AddDocToRecentAndExtRecent(LPCITEMIDLIST pidlItem, LPCTSTR pszFileName, LPCTSTR pszExt);
  41. void _TryUpdateNetHood(LPCITEMIDLIST pidlFolder, LPCTSTR pszFolder);
  42. void _UpdateNetHood(LPCITEMIDLIST pidlFolder, LPCTSTR pszShare);
  43. // private members
  44. HANDLE _hMem;
  45. DWORD _dwProcId;
  46. IMruDataList *_pmruRecent;
  47. DWORD _cMaxRecent;
  48. LPITEMIDLIST _pidlTarget;
  49. };
  50. BOOL ShouldAddToRecentDocs(LPCITEMIDLIST pidl)
  51. {
  52. BOOL fRet = TRUE; // default to true
  53. IQueryAssociations *pqa;
  54. if (SUCCEEDED(SHGetAssociations(pidl, (void **)&pqa)))
  55. {
  56. DWORD dwAttributes, dwSize = sizeof(dwAttributes);
  57. if (SUCCEEDED(pqa->GetData(NULL, ASSOCDATA_EDITFLAGS, NULL, &dwAttributes, &dwSize)))
  58. {
  59. fRet = !(dwAttributes & FTA_NoRecentDocs);
  60. }
  61. pqa->Release();
  62. }
  63. return fRet;
  64. }
  65. int RecentDocsComparePidl(const BYTE * p1, const BYTE *p2, int cb)
  66. {
  67. int iRet;
  68. LPCIDFOLDER pidf1 = CFSFolder_IsValidID((LPCITEMIDLIST)p1);
  69. LPCIDFOLDER pidf2 = CFSFolder_IsValidID(GETRECPIDL(p2));
  70. if (pidf1 && pidf2)
  71. {
  72. iRet = CFSFolder_CompareNames(pidf1, pidf2);
  73. }
  74. else
  75. {
  76. ASSERTMSG(0, "Caller shouldn't be passing in bogus data");
  77. // return 0 (equal) if they're both NULL.
  78. iRet = (pidf1 != pidf2);
  79. }
  80. return iRet;
  81. }
  82. CTaskAddDoc::~CTaskAddDoc(void)
  83. {
  84. TraceMsg(DM_RECENTDOCS, "[%X] CTaskAddDoc destroyed", this);
  85. }
  86. CTaskAddDoc::CTaskAddDoc(void) : CRunnableTask(RTF_DEFAULT)
  87. {
  88. TraceMsg(DM_RECENTDOCS, "[%X] CTaskAddDoc created", this);
  89. }
  90. HRESULT CTaskAddDoc::Init( HANDLE hMem, DWORD dwProcId)
  91. {
  92. if (hMem)
  93. {
  94. _hMem = hMem;
  95. _dwProcId = dwProcId;
  96. return S_OK;
  97. }
  98. return E_FAIL;
  99. }
  100. typedef struct {
  101. DWORD dwOffsetPath;
  102. DWORD dwOffsetPidl;
  103. DWORD dwOffsetProcess;
  104. } XMITARD;
  105. LPCTSTR _OffsetToStrValidate(void *px, DWORD dw)
  106. {
  107. LPCTSTR psz = dw ? (LPTSTR)((LPBYTE)px + dw) : NULL;
  108. if (psz && IsBadStringPtr(psz, MAX_PATH))
  109. psz = NULL;
  110. return psz;
  111. }
  112. HRESULT CTaskAddDoc::RunInitRT(void)
  113. {
  114. TraceMsg(DM_RECENTDOCS, "[%X] CTaskAddDoc::RunInitRT() running", this);
  115. XMITARD *px = (XMITARD *)SHLockShared(_hMem, _dwProcId);
  116. if (px)
  117. {
  118. LPITEMIDLIST pidl = px->dwOffsetPidl ? (LPITEMIDLIST)((LPBYTE)px+px->dwOffsetPidl) : NULL;
  119. LPCTSTR pszPath = _OffsetToStrValidate(px, px->dwOffsetPath);
  120. LPCTSTR pszProcess = _OffsetToStrValidate(px, px->dwOffsetProcess);
  121. ASSERT(pszPath);
  122. if (pszPath && pszProcess)
  123. OpenWithListSoftRegisterProcess(0, PathFindExtension(pszPath), pszProcess);
  124. _AddToRecentDocs(pidl, pszPath);
  125. SHUnlockShared(px);
  126. SHFreeShared(_hMem, _dwProcId);
  127. }
  128. return S_OK;
  129. }
  130. BOOL GetExtensionClassDescription(LPCTSTR pszFile)
  131. {
  132. LPTSTR pszExt = PathFindExtension(pszFile);
  133. HKEY hk;
  134. if (*pszExt && SUCCEEDED(AssocQueryKey(0, ASSOCKEY_SHELLEXECCLASS, pszExt, NULL, &hk)))
  135. {
  136. RegCloseKey(hk);
  137. return TRUE;
  138. }
  139. return FALSE;
  140. }
  141. STDAPI_(void) FlushRunDlgMRU(void);
  142. #define MAXRECENT_DEFAULTDOC 10
  143. #define MAXRECENT_MAJORDOC 20
  144. // SRMLF_* flags to pass into CreateSharedRecentMRUList()
  145. #define SRMLF_COMPNAME 0x00000000 // default: compare using the name of the recent file
  146. #define SRMLF_COMPPIDL 0x00000001 // use the pidl in the recent folder
  147. IMruDataList *CreateSharedRecentMRUList(LPCTSTR pszClass, DWORD *pcMax, DWORD dwFlags)
  148. {
  149. IMruDataList *pmru = NULL;
  150. if (SHRestricted(REST_NORECENTDOCSHISTORY))
  151. return NULL;
  152. HKEY hk = SHGetShellKey(SHELLKEY_HKCU_EXPLORER, REGSTR_KEY_RECENTDOCS, TRUE);
  153. if (hk)
  154. {
  155. DWORD cMax;
  156. if (pszClass)
  157. {
  158. // we need to find out how many
  159. if (NOERROR == SHGetValue(HKEY_CLASSES_ROOT, pszClass, TEXT("MajorDoc"), NULL, NULL, NULL))
  160. cMax = MAXRECENT_MAJORDOC;
  161. else
  162. cMax = MAXRECENT_DEFAULTDOC;
  163. }
  164. else
  165. {
  166. // this the root MRU
  167. cMax = SHRestricted(REST_MaxRecentDocs);
  168. // default max docs...
  169. if (cMax < 1)
  170. cMax = MAXRECENTDOCS * MAXRECENT_DEFAULTDOC;
  171. }
  172. if (pcMax)
  173. *pcMax = cMax;
  174. if (SUCCEEDED(SHCoCreateInstance(NULL, &CLSID_MruLongList, NULL, IID_PPV_ARG(IMruDataList, &pmru))))
  175. {
  176. if (FAILED(pmru->InitData(cMax, MRULISTF_USE_STRCMPIW, hk, pszClass, dwFlags & SRMLF_COMPPIDL ? RecentDocsComparePidl : NULL)))
  177. {
  178. pmru->Release();
  179. pmru = NULL;
  180. }
  181. }
  182. RegCloseKey(hk);
  183. }
  184. return pmru;
  185. }
  186. HRESULT CreateRecentMRUList(IMruDataList **ppmru)
  187. {
  188. *ppmru = CreateSharedRecentMRUList(NULL, NULL, SRMLF_COMPPIDL);
  189. return *ppmru ? S_OK : E_OUTOFMEMORY;
  190. }
  191. //
  192. // _CleanRecentDocs()
  193. // cleans out the recent docs folder and the associate registry keys.
  194. //
  195. void _CleanRecentDocs(void)
  196. {
  197. LPITEMIDLIST pidlTargetLocal = SHCloneSpecialIDList(NULL, CSIDL_RECENT, TRUE);
  198. if (pidlTargetLocal)
  199. {
  200. TCHAR szDir[MAX_PATH];
  201. // first, delete all the files
  202. SHFILEOPSTRUCT sFileOp =
  203. {
  204. NULL,
  205. FO_DELETE,
  206. szDir,
  207. NULL,
  208. FOF_NOCONFIRMATION | FOF_SILENT,
  209. };
  210. SHGetPathFromIDList(pidlTargetLocal, szDir);
  211. szDir[lstrlen(szDir) +1] = 0; // double null terminate
  212. SHFileOperation(&sFileOp);
  213. ILFree(pidlTargetLocal);
  214. pidlTargetLocal = SHCloneSpecialIDList(NULL, CSIDL_NETHOOD, TRUE);
  215. if (pidlTargetLocal)
  216. {
  217. // now we take care of cleaning out the nethood
  218. // we have to more careful, cuz we let other people
  219. // add their own stuff in here.
  220. IMruDataList *pmru = CreateSharedRecentMRUList(TEXT("NetHood"), NULL, SRMLF_COMPPIDL);
  221. if (pmru)
  222. {
  223. IShellFolder* psf;
  224. if (SUCCEEDED(SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, pidlTargetLocal, &psf))))
  225. {
  226. BOOL fUpdate = FALSE;
  227. int iItem = 0;
  228. LPITEMIDLIST pidlItem;
  229. ASSERT(psf);
  230. while (SUCCEEDED(RecentDocs_Enum(pmru, iItem++, &pidlItem)))
  231. {
  232. ASSERT(pidlItem);
  233. STRRET str;
  234. if (SUCCEEDED(psf->GetDisplayNameOf(pidlItem, SHGDN_FORPARSING, &str))
  235. && SUCCEEDED(StrRetToBuf(&str, pidlItem, szDir, ARRAYSIZE(szDir))))
  236. {
  237. szDir[lstrlen(szDir) +1] = 0; // double null terminate
  238. SHFileOperation(&sFileOp);
  239. }
  240. ILFree(pidlItem);
  241. }
  242. if (fUpdate)
  243. SHChangeNotify(SHCNE_UPDATEDIR, 0, (void *)pidlTargetLocal, NULL);
  244. psf->Release();
  245. }
  246. pmru->Release();
  247. }
  248. ILFree(pidlTargetLocal);
  249. }
  250. // force the recreation of the recent folder.
  251. SHGetFolderPath(NULL, CSIDL_RECENT | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, szDir);
  252. // now delete the registry stuff
  253. HKEY hk = SHGetShellKey(SHELLKEY_HKCU_EXPLORER, NULL, FALSE);
  254. if (hk)
  255. {
  256. SHDeleteKey(hk, REGSTR_KEY_RECENTDOCS);
  257. RegCloseKey(hk);
  258. }
  259. SHChangeNotifyHandleEvents();
  260. }
  261. FlushRunDlgMRU();
  262. ENTERCRITICAL;
  263. g_szLastFile[0] = 0;
  264. g_ftLastFileCacheUpdate.dwLowDateTime = 0;
  265. g_ftLastFileCacheUpdate.dwHighDateTime = 0;
  266. LEAVECRITICAL;
  267. return;
  268. }
  269. //
  270. // WARNING - _TryDeleteMRUItem() returns an allocated string that must be freed
  271. //
  272. void CTaskAddDoc::_TryDeleteMRUItem(IMruDataList *pmru, DWORD cMax, LPCTSTR pszFileName, LPCITEMIDLIST pidlItem, IMruDataList *pmruOther, BOOL fOverwrite)
  273. {
  274. BYTE buf[MAX_RECMRU_BUF] = {0};
  275. DWORD cbItem = CbFromCch(lstrlen(pszFileName) + 1);
  276. int iItem;
  277. if (!fOverwrite || FAILED(pmru->FindData((BYTE *)pszFileName, cbItem, &iItem)))
  278. {
  279. //
  280. // if iItem is not -1 then it is already existing item that we will replace.
  281. // if it is -1 then we need to point iItem to the last in the list.
  282. // torch the last one if we have the max number of items in the list.
  283. // default to success, cuz if we dont find it we dont need to delete it
  284. iItem = cMax - 1;
  285. }
  286. // if we cannot get it in order to delete it,
  287. // then we will not overwrite the item.
  288. if (SUCCEEDED(pmru->GetData(iItem, buf, sizeof(buf))))
  289. {
  290. // convert the buf into the last segment of the pidl
  291. LPITEMIDLIST pidlFullLink = ILCombine(_pidlTarget, GETRECPIDL(buf));
  292. if (pidlFullLink)
  293. {
  294. // This is semi-gross, but some link types like calling cards are the
  295. // actual data. If we delete and recreate they lose their info for the
  296. // run. We will detect this by knowing that their pidl will be the
  297. // same as the one we are deleting...
  298. if (!ILIsEqual(pidlFullLink, pidlItem))
  299. {
  300. TCHAR sz[MAX_PATH];
  301. // now remove out link to it
  302. SHGetPathFromIDList(pidlFullLink, sz);
  303. Win32DeleteFile(sz);
  304. TraceMsg(DM_RECENTDOCS, "[%X] CTaskAddDoc::_TryDeleteMRUItem() deleting '%s'", this, sz);
  305. if (pmruOther)
  306. {
  307. // deleted a shortcut,
  308. // need to try and remove it from the pmruOther...
  309. if (SUCCEEDED(pmruOther->FindData((BYTE *)GETRECNAME(buf), CbFromCch(lstrlen(GETRECNAME(buf)) +1), &iItem)))
  310. pmruOther->Delete(iItem);
  311. }
  312. }
  313. ILFree(pidlFullLink);
  314. }
  315. }
  316. }
  317. // in:
  318. // pidlItem - full IDList for the item being added
  319. // pszItem - name (file spec) of the item (used in the display to the user)
  320. // uFlags - SHCL_ flags
  321. LPBYTE CTaskAddDoc::_CreateMRUItem(LPCITEMIDLIST pidlItem, LPCTSTR pszItem,
  322. DWORD *pcbOut, UINT uFlags)
  323. {
  324. TCHAR sz[MAX_PATH];
  325. LPBYTE pitem = NULL;
  326. // create the new one
  327. if (SHGetPathFromIDList(_pidlTarget, sz))
  328. {
  329. LPITEMIDLIST pidlFullLink;
  330. if (SUCCEEDED(CreateLinkToPidl(pidlItem, sz, &pidlFullLink, uFlags)) &&
  331. pidlFullLink)
  332. {
  333. LPCITEMIDLIST pidlLinkLast = ILFindLastID(pidlFullLink);
  334. int cbLinkLast = ILGetSize(pidlLinkLast);
  335. DWORD cbItem = CbFromCch(lstrlen(pszItem) + 1);
  336. pitem = (LPBYTE) LocalAlloc(NONZEROLPTR, cbItem + cbLinkLast);
  337. if (pitem)
  338. {
  339. memcpy( pitem, pszItem, cbItem );
  340. memcpy( pitem + cbItem, pidlLinkLast, cbLinkLast);
  341. *pcbOut = cbItem + cbLinkLast;
  342. }
  343. ILFree(pidlFullLink);
  344. }
  345. }
  346. return pitem;
  347. }
  348. HRESULT RecentDocs_Enum(IMruDataList *pmru, int iItem, LPITEMIDLIST *ppidl)
  349. {
  350. BYTE buf[MAX_RECMRU_BUF] = {0};
  351. *ppidl = NULL;
  352. if (SUCCEEDED(pmru->GetData(iItem, buf, sizeof(buf))))
  353. {
  354. *ppidl = ILClone(GETRECPIDL(buf));
  355. }
  356. return *ppidl ? S_OK : E_FAIL;
  357. }
  358. BOOL CTaskAddDoc::_AddDocToRecentAndExtRecent(LPCITEMIDLIST pidlItem, LPCTSTR pszFileName,
  359. LPCTSTR pszExt)
  360. {
  361. DWORD cbItem = CbFromCch(lstrlen(pszFileName) + 1);
  362. DWORD cMax;
  363. IMruDataList *pmru = CreateSharedRecentMRUList(pszExt, &cMax, SRMLF_COMPNAME);
  364. _TryDeleteMRUItem(_pmruRecent, _cMaxRecent, pszFileName, pidlItem, pmru, TRUE);
  365. LPBYTE pitem = _CreateMRUItem(pidlItem, pszFileName, &cbItem, 0);
  366. if (pitem)
  367. {
  368. _pmruRecent->AddData(pitem, cbItem, NULL);
  369. if (pmru)
  370. {
  371. // we dont want to delete the file if it already existed, because
  372. // the TryDelete on the RecentMRU would have already done that
  373. // we only want to delete if we have some overflow from the ExtMRU
  374. _TryDeleteMRUItem(pmru, cMax, pszFileName, pidlItem, _pmruRecent, FALSE);
  375. // can reuse the already created item to this mru
  376. pmru->AddData(pitem, cbItem, NULL);
  377. pmru->Release();
  378. }
  379. LocalFree(pitem);
  380. }
  381. // its been freed but not nulled out...
  382. return (pitem != NULL);
  383. }
  384. //
  385. // WARNING: UpdateNetHood() changes _pidlTarget to the NetHood then frees it!
  386. //
  387. void CTaskAddDoc::_UpdateNetHood(LPCITEMIDLIST pidlFolder, LPCTSTR pszShare)
  388. {
  389. if (SHRestricted(REST_NORECENTDOCSNETHOOD))
  390. return;
  391. // need to add this boy to the Network Places
  392. LPITEMIDLIST pidl = ILCreateFromPath(pszShare);
  393. if (pidl)
  394. {
  395. //
  396. // NOTE - must verify parentage here - ZekeL - 27-MAY-99
  397. // http servers exist in both the webfolders namespace
  398. // and the Internet namespace. thus we must make sure
  399. // that what ever parent the folder had, the share has
  400. // the same one.
  401. //
  402. if (ILIsParent(pidl, pidlFolder, FALSE))
  403. {
  404. ASSERT(_pidlTarget);
  405. ILFree(_pidlTarget);
  406. _pidlTarget = SHCloneSpecialIDList(NULL, CSIDL_NETHOOD, TRUE);
  407. if (_pidlTarget)
  408. {
  409. DWORD cMax;
  410. IMruDataList *pmru = CreateSharedRecentMRUList(TEXT("NetHood"), &cMax, SRMLF_COMPNAME);
  411. if (pmru)
  412. {
  413. _TryDeleteMRUItem(pmru, cMax, pszShare, pidl, NULL, TRUE);
  414. DWORD cbItem = CbFromCch(lstrlen(pszShare) + 1);
  415. // SHCL_NOUNIQUE - if there is already a shortcut with the same name,
  416. // just overwrite it; this avoids pointless duplicates in nethood
  417. LPBYTE pitem = _CreateMRUItem(pidl, pszShare, &cbItem, SHCL_MAKEFOLDERSHORTCUT | SHCL_NOUNIQUE);
  418. if (pitem)
  419. {
  420. pmru->AddData(pitem, cbItem, NULL);
  421. LocalFree(pitem);
  422. }
  423. pmru->Release();
  424. }
  425. ILFree(_pidlTarget);
  426. _pidlTarget = NULL;
  427. }
  428. }
  429. ILFree(pidl);
  430. }
  431. }
  432. BOOL _IsPlacesFolder(LPCTSTR pszFolder)
  433. {
  434. static const UINT places[] = {
  435. CSIDL_PERSONAL,
  436. CSIDL_DESKTOPDIRECTORY,
  437. CSIDL_COMMON_DESKTOPDIRECTORY,
  438. CSIDL_NETHOOD,
  439. CSIDL_FAVORITES,
  440. };
  441. return PathIsOneOf(pszFolder, places, ARRAYSIZE(places));
  442. }
  443. void _AddToUrlHistory(LPCTSTR pszPath)
  444. {
  445. ASSERT(pszPath);
  446. WCHAR szUrl[MAX_URL_STRING];
  447. DWORD cchUrl = ARRAYSIZE(szUrl);
  448. // the URL parsing APIs tolerate same in/out buffer
  449. if (SUCCEEDED(UrlCreateFromPathW(pszPath, szUrl, &cchUrl, 0)))
  450. {
  451. IUrlHistoryStg *puhs;
  452. if (SUCCEEDED(CoCreateInstance(CLSID_CUrlHistory, NULL, CLSCTX_INPROC_SERVER,
  453. IID_PPV_ARG(IUrlHistoryStg, &puhs))))
  454. {
  455. ASSERT(puhs);
  456. puhs->AddUrl(szUrl, NULL, 0);
  457. puhs->Release();
  458. }
  459. }
  460. }
  461. void CTaskAddDoc::_TryUpdateNetHood(LPCITEMIDLIST pidlFolder, LPCTSTR pszFolder)
  462. {
  463. TCHAR sz[MAX_URL_STRING];
  464. DWORD cch = SIZECHARS(sz);
  465. BOOL fUpdate = FALSE;
  466. // changing szFolder, and changing _pidlTarget here...
  467. // if this is an URL or a UNC share add it to the nethood
  468. if (UrlIs(pszFolder, URLIS_URL)
  469. && !UrlIs(pszFolder, URLIS_OPAQUE)
  470. && SUCCEEDED(UrlCombine(pszFolder, TEXT("/"), sz, &cch, 0)))
  471. fUpdate = TRUE;
  472. else if (PathIsUNC(pszFolder)
  473. && StrCpyN(sz, pszFolder, cch)
  474. && PathStripToRoot(sz))
  475. fUpdate = TRUE;
  476. if (fUpdate)
  477. _UpdateNetHood(pidlFolder, sz);
  478. }
  479. //-----------------------------------------------------------------
  480. //
  481. // Add the named file to the Recently opened MRU list, that is used
  482. // by the shell to display the recent menu of the tray.
  483. // this registry will hold two pidls: the target pointing to followed by
  484. // the pidl of the link created pointing it. In both cases,
  485. // only the last item id is stored. (we may want to change this... but
  486. // then again, we may not)
  487. void CTaskAddDoc::_AddToRecentDocs(LPCITEMIDLIST pidlItem, LPCTSTR pszItem)
  488. {
  489. TCHAR szUnescaped[MAX_PATH];
  490. LPTSTR pszFileName;
  491. // if these are NULL the caller meant to call _CleanRecentDocs()
  492. ASSERT(pszItem && *pszItem);
  493. TraceMsg(DM_RECENTDOCS, "[%X] CTaskAddDoc::_AddToRecentDocs() called for '%s'", this, pszItem);
  494. // allow only classes with default commands
  495. //
  496. // dont add if:
  497. // it is RESTRICTED
  498. // it is in the temporary directory
  499. // it actually has a file name
  500. // it can be shell exec'd with "open" verb
  501. //
  502. if ( (SHRestricted(REST_NORECENTDOCSHISTORY)) ||
  503. (PathIsTemporary(pszItem)) ||
  504. (!(pszFileName = PathFindFileName(pszItem))) ||
  505. (!*pszFileName) ||
  506. (!GetExtensionClassDescription(pszFileName))
  507. )
  508. return;
  509. // pretty up the URL file names.
  510. if (UrlIs(pszItem, URLIS_URL))
  511. {
  512. StrCpyN(szUnescaped, pszFileName, SIZECHARS(szUnescaped));
  513. UrlUnescapeInPlace(szUnescaped, 0);
  514. pszFileName = szUnescaped;
  515. }
  516. // otherwise we try our best.
  517. ASSERT(!_pidlTarget);
  518. _pidlTarget = SHCloneSpecialIDList(NULL, CSIDL_RECENT, TRUE);
  519. if (_pidlTarget)
  520. {
  521. _pmruRecent = CreateSharedRecentMRUList(NULL, &_cMaxRecent, SRMLF_COMPNAME);
  522. if (_pmruRecent)
  523. {
  524. if (_AddDocToRecentAndExtRecent(pidlItem, pszFileName, PathFindExtension(pszFileName)))
  525. {
  526. _AddToUrlHistory(pszItem);
  527. // get the folder and do it to the folder
  528. LPITEMIDLIST pidlFolder = ILClone(pidlItem);
  529. if (pidlFolder)
  530. {
  531. ILRemoveLastID(pidlFolder);
  532. // if it is a folder we already have quick
  533. // access to from the shell, dont put it in here
  534. TCHAR szFolder[MAX_URL_STRING];
  535. if (SUCCEEDED(SHGetNameAndFlags(pidlFolder, SHGDN_FORPARSING, szFolder, SIZECHARS(szFolder), NULL))
  536. && !_IsPlacesFolder(szFolder))
  537. {
  538. // get the friendly name for the folder
  539. TCHAR szTitle[MAX_PATH];
  540. if (FAILED(SHGetNameAndFlags(pidlFolder, SHGDN_NORMAL, szTitle, SIZECHARS(szTitle), NULL)))
  541. StrCpyN(szTitle, PathFindFileName(szFolder), ARRAYSIZE(szTitle));
  542. _AddDocToRecentAndExtRecent(pidlFolder, szTitle, TEXT("Folder"));
  543. _TryUpdateNetHood(pidlFolder, szFolder);
  544. }
  545. ILFree(pidlFolder);
  546. }
  547. }
  548. _pmruRecent->Release();
  549. _pmruRecent = NULL;
  550. }
  551. //cleanup
  552. if (_pidlTarget)
  553. {
  554. ILFree(_pidlTarget);
  555. _pidlTarget = NULL;
  556. }
  557. }
  558. SHChangeNotifyHandleEvents();
  559. }
  560. // This cache helps winstone!
  561. // The 1 minute timeout is incase another process cleared recent docs, or filled it
  562. // to capacity & scrolled out our cached item.
  563. #define FT_ONEMINUTE (10000000*60)
  564. BOOL CheckIfFileIsCached(LPCTSTR pszItem)
  565. {
  566. BOOL bRet = FALSE;
  567. ENTERCRITICAL;
  568. if (StrCmp(pszItem, g_szLastFile) == 0)
  569. {
  570. FILETIME ftNow;
  571. GetSystemTimeAsFileTime(&ftNow);
  572. // Pull one minute off the current time, then compare to cache time
  573. DecrementFILETIME(&ftNow, FT_ONEMINUTE);
  574. // if the cache'd time is greater than 1 minute ago, use cache
  575. if (CompareFileTime(&g_ftLastFileCacheUpdate, &ftNow) >= 0)
  576. bRet = TRUE;
  577. }
  578. LEAVECRITICAL;
  579. return bRet;
  580. }
  581. void AddToRecentDocs(LPCITEMIDLIST pidl, LPCTSTR pszItem)
  582. {
  583. HWND hwnd = GetShellWindow();
  584. // Check to see if we just added the same file to recent docs.
  585. // or this is an executeable
  586. // or something else that shouldnt be added
  587. if (!CheckIfFileIsCached(pszItem)
  588. && (!PathIsExe(pszItem))
  589. && (ShouldAddToRecentDocs(pidl))
  590. && (hwnd))
  591. {
  592. DWORD cbSizePidl = ILGetSize(pidl);
  593. DWORD cbSizePath = CbFromCch(lstrlen(pszItem) + 1);
  594. XMITARD *px;
  595. DWORD dwProcId, dwOffset;
  596. HANDLE hARD;
  597. TCHAR szApp[MAX_PATH]; // name of the app which is calling us
  598. DWORD cbSizeApp;
  599. DWORD cbSizePidlRound, cbSizePathRound, cbSizeAppRound;
  600. GetWindowThreadProcessId(hwnd, &dwProcId);
  601. if (GetModuleFileName(NULL, szApp, ARRAYSIZE(szApp)) && szApp[0])
  602. cbSizeApp = CbFromCch(1 + lstrlen(szApp));
  603. else
  604. cbSizeApp = 0;
  605. cbSizePidlRound = ROUNDUP(cbSizePidl,4);
  606. cbSizePathRound = ROUNDUP(cbSizePath,4);
  607. cbSizeAppRound = ROUNDUP(cbSizeApp,4);
  608. hARD = SHAllocShared(NULL, sizeof(XMITARD)+cbSizePathRound+cbSizePidlRound+cbSizeAppRound, dwProcId);
  609. if (!hARD)
  610. return; // Well, we are going to miss one, sorry.
  611. px = (XMITARD *)SHLockShared(hARD,dwProcId);
  612. if (!px)
  613. {
  614. SHFreeShared(hARD,dwProcId);
  615. return; // Well, we are going to miss one, sorry.
  616. }
  617. px->dwOffsetPidl = 0;
  618. px->dwOffsetPath = 0;
  619. px->dwOffsetProcess = 0;
  620. dwOffset = sizeof(XMITARD);
  621. {
  622. px->dwOffsetPath = dwOffset;
  623. memcpy((LPBYTE)px + dwOffset, pszItem, cbSizePath);
  624. dwOffset += cbSizePathRound;
  625. }
  626. {
  627. px->dwOffsetPidl = dwOffset;
  628. memcpy((LPBYTE)px + dwOffset, pidl, cbSizePidl);
  629. dwOffset += cbSizePidlRound;
  630. }
  631. if (cbSizeApp)
  632. {
  633. px->dwOffsetProcess = dwOffset;
  634. memcpy((LPBYTE)px + dwOffset, szApp, cbSizeApp);
  635. }
  636. SHUnlockShared(px);
  637. PostMessage(hwnd, CWM_ADDTORECENT, (WPARAM)hARD, (LPARAM)dwProcId);
  638. ENTERCRITICAL;
  639. StrCpyN(g_szLastFile, pszItem, ARRAYSIZE(g_szLastFile));
  640. GetSystemTimeAsFileTime(&g_ftLastFileCacheUpdate);
  641. LEAVECRITICAL;
  642. }
  643. }
  644. #if 0
  645. void DisplayRecentDebugMessage(LPTSTR psz, BOOL bPidl)
  646. {
  647. TCHAR szTmp[1024];
  648. TCHAR szFN[MAX_PATH];
  649. GetModuleFileName(NULL, szFN, ARRAYSIZE(szFN));
  650. wsprintf(szTmp, TEXT("[%d], process %s: AddToRecentDocs(%s) by %s\n"),
  651. GetTickCount(), szFN, psz,
  652. (bPidl)?TEXT("pidl"):TEXT("path"));
  653. OutputDebugString(szTmp);
  654. }
  655. #else
  656. #define DisplayRecentDebugMessage(x,y)
  657. #endif
  658. HRESULT _ParseRecentDoc(LPCWSTR psz, LPITEMIDLIST *ppidl)
  659. {
  660. BINDCTX_PARAM rgParams[] =
  661. {
  662. { STR_PARSE_TRANSLATE_ALIASES, NULL},
  663. { STR_PARSE_PREFER_FOLDER_BROWSING, NULL},
  664. };
  665. IBindCtx *pbc;
  666. HRESULT hr = BindCtx_RegisterObjectParams(NULL, rgParams, ARRAYSIZE(rgParams), &pbc);
  667. if (SUCCEEDED(hr))
  668. {
  669. hr = SHParseDisplayName(psz, pbc, ppidl, 0, 0);
  670. pbc->Release();
  671. if (FAILED(hr))
  672. {
  673. // we need to fallback to a simple parsing
  674. IBindCtx *pbcSimple;
  675. hr = SHCreateFileSysBindCtx(NULL, &pbcSimple);
  676. if (SUCCEEDED(hr))
  677. {
  678. hr = BindCtx_RegisterObjectParams(pbcSimple, rgParams, ARRAYSIZE(rgParams), &pbc);
  679. if (SUCCEEDED(hr))
  680. {
  681. hr = SHParseDisplayName(psz, pbc, ppidl, 0, 0);
  682. pbc->Release();
  683. }
  684. pbcSimple->Release();
  685. }
  686. }
  687. }
  688. return hr;
  689. }
  690. //
  691. // put things in the shells recent docs list for the start menu
  692. //
  693. // in:
  694. // uFlags SHARD_ (shell add recent docs) flags
  695. // pv LPCSTR or LPCITEMIDLIST (path or pidl indicated by uFlags)
  696. // may be NULL, meaning clear the recent list
  697. //
  698. STDAPI_(void) SHAddToRecentDocs(UINT uFlags, LPCVOID pv)
  699. {
  700. TCHAR szTemp[MAX_URL_STRING]; // for double null
  701. TraceMsg(DM_RECENTDOCS, "SHAddToRecentDocs() called with %d, [%X]", uFlags, pv);
  702. if (pv == NULL) // we should nuke all recent docs.
  703. {
  704. // we do this synchronously
  705. _CleanRecentDocs();
  706. return;
  707. }
  708. if (SHRestricted(REST_NORECENTDOCSHISTORY))
  709. // Don't bother tracking recent documents if restriction is set
  710. // for privacy.
  711. return;
  712. switch (uFlags)
  713. {
  714. case SHARD_PIDL:
  715. // pv is a LPCITEMIDLIST (pidl)
  716. if (SUCCEEDED(SHGetNameAndFlags((LPCITEMIDLIST)pv, SHGDN_FORPARSING, szTemp, SIZECHARS(szTemp), NULL)))
  717. {
  718. DisplayRecentDebugMessage((LPTSTR)pv, TRUE);
  719. AddToRecentDocs((LPCITEMIDLIST)pv, szTemp);
  720. }
  721. break;
  722. case SHARD_PATHA:
  723. // pv is an ANSI path
  724. SHAnsiToUnicode((LPCSTR)pv, szTemp, ARRAYSIZE(szTemp));
  725. pv = szTemp;
  726. // fall through to SHARD_PATHW;
  727. case SHARD_PATHW:
  728. {
  729. // pv is a UNICODE path
  730. LPITEMIDLIST pidl;
  731. if (SUCCEEDED(_ParseRecentDoc((LPCWSTR)pv, &pidl)))
  732. {
  733. DisplayRecentDebugMessage((LPTSTR)pv, FALSE);
  734. AddToRecentDocs(pidl, (LPCTSTR)pv);
  735. ILFree(pidl);
  736. }
  737. break;
  738. }
  739. default:
  740. ASSERTMSG(FALSE, "SHAddToRecent() called with invalid params");
  741. break;
  742. }
  743. }
  744. STDAPI CTaskAddDoc_Create(HANDLE hMem, DWORD dwProcId, IRunnableTask **pptask)
  745. {
  746. HRESULT hres;
  747. CTaskAddDoc *ptad = new CTaskAddDoc();
  748. if (ptad)
  749. {
  750. hres = ptad->Init(hMem, dwProcId);
  751. if (SUCCEEDED(hres))
  752. hres = ptad->QueryInterface(IID_PPV_ARG(IRunnableTask, pptask));
  753. ptad->Release();
  754. }
  755. else
  756. hres = E_OUTOFMEMORY;
  757. return hres;
  758. }
  759. STDAPI RecentDocs_GetDisplayName(LPCITEMIDLIST pidl, LPTSTR pszName, DWORD cchName)
  760. {
  761. IMruDataList *pmru;
  762. HRESULT hr = CreateRecentMRUList(&pmru);
  763. if (SUCCEEDED(hr))
  764. {
  765. int iItem;
  766. hr = pmru->FindData((BYTE *)pidl, ILGetSize(pidl), &iItem);
  767. if (SUCCEEDED(hr))
  768. {
  769. BYTE buf[MAX_RECMRU_BUF];
  770. hr = pmru->GetData(iItem, buf, sizeof(buf));
  771. if (SUCCEEDED(hr))
  772. {
  773. StrCpyN(pszName, GETRECNAME(buf), cchName);
  774. }
  775. }
  776. pmru->Release();
  777. }
  778. return hr;
  779. }