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.

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