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.

1098 lines
34 KiB

  1. #include "precomp.hxx"
  2. #pragma hdrstop
  3. #include <shguidp.h> // CLSID_ShellFSFolder
  4. #include <shellp.h> // SHCoCreateInstance
  5. #include <ccstock2.h> // DataObj_GetHIDA, HIDA_ReleaseStgMedium
  6. #include <varutil.h> // VariantToBuffer
  7. #include <stralign.h> // WSTR_ALIGNED_STACK_COPY
  8. #include "resource.h"
  9. #include "timewarp.h"
  10. #include "twfldr.h"
  11. #include "contextmenu.h"
  12. #include "util.h"
  13. // {9DB7A13C-F208-4981-8353-73CC61AE2783} CLSID_TimeWarpFolder
  14. const CLSID CLSID_TimeWarpFolder = {0x9DB7A13C, 0xF208, 0x4981, {0x83, 0x53, 0x73, 0xCC, 0x61, 0xAE, 0x27, 0x83}};
  15. const SHCOLUMNID SCID_DESCRIPTIONID = { PSGUID_SHELLDETAILS, PID_DESCRIPTIONID };
  16. PCUIDTIMEWARP _IsValidTimeWarpID(PCUIDLIST_RELATIVE pidl)
  17. {
  18. if (pidl && pidl->mkid.cb>=sizeof(IDTIMEWARP) && ((PUIDTIMEWARP)pidl)->wSignature == TIMEWARP_SIGNATURE)
  19. return (PCUIDTIMEWARP)pidl;
  20. return NULL;
  21. }
  22. HRESULT CTimeWarpRegFolder::CreateInstance(IUnknown* /*punkOuter*/, IUnknown **ppunk, LPCOBJECTINFO /*poi*/)
  23. {
  24. HRESULT hr;
  25. *ppunk = NULL;
  26. CTimeWarpRegFolder *ptwf = new CTimeWarpRegFolder();
  27. if (ptwf)
  28. {
  29. hr = ptwf->QueryInterface(IID_PPV_ARG(IUnknown, ppunk));
  30. ptwf->Release();
  31. }
  32. else
  33. {
  34. hr = E_OUTOFMEMORY;
  35. }
  36. return hr;
  37. }
  38. CTimeWarpRegFolder::CTimeWarpRegFolder() : _cRef(1), _pmalloc(NULL), _pidl(NULL)
  39. {
  40. DllAddRef();
  41. }
  42. CTimeWarpRegFolder::~CTimeWarpRegFolder()
  43. {
  44. ATOMICRELEASE(_pmalloc);
  45. SHILFree((void*)_pidl); // const
  46. DllRelease();
  47. }
  48. STDMETHODIMP CTimeWarpRegFolder::QueryInterface(REFIID riid, void **ppv)
  49. {
  50. static const QITAB qit[] = {
  51. QITABENT(CTimeWarpRegFolder, IDelegateFolder),
  52. QITABENT(CTimeWarpRegFolder, IShellFolder),
  53. QITABENTMULTI(CTimeWarpRegFolder, IPersist, IPersistFolder),
  54. QITABENT(CTimeWarpRegFolder, IPersistFolder),
  55. { 0 },
  56. };
  57. return QISearch(this, qit, riid, ppv);
  58. }
  59. STDMETHODIMP_ (ULONG) CTimeWarpRegFolder::AddRef()
  60. {
  61. return InterlockedIncrement(&_cRef);
  62. }
  63. STDMETHODIMP_ (ULONG) CTimeWarpRegFolder::Release()
  64. {
  65. ASSERT( 0 != _cRef );
  66. ULONG cRef = InterlockedDecrement(&_cRef);
  67. if ( 0 == cRef )
  68. {
  69. delete this;
  70. }
  71. return cRef;
  72. }
  73. // IPersist methods
  74. STDMETHODIMP CTimeWarpRegFolder::GetClassID(CLSID *pClassID)
  75. {
  76. *pClassID = CLSID_TimeWarpFolder;
  77. return S_OK;
  78. }
  79. // IPersistFolder
  80. HRESULT CTimeWarpRegFolder::Initialize(PCIDLIST_ABSOLUTE pidl)
  81. {
  82. if (_pidl)
  83. {
  84. SHILFree((void*)_pidl); // const
  85. _pidl = NULL;
  86. }
  87. return pidl ? SHILCloneFull(pidl, &_pidl) : S_FALSE;
  88. }
  89. // IDelegateFolder
  90. HRESULT CTimeWarpRegFolder::SetItemAlloc(IMalloc *pmalloc)
  91. {
  92. IUnknown_Set((IUnknown**)&_pmalloc, pmalloc);
  93. return S_OK;
  94. }
  95. // IShellFolder
  96. STDMETHODIMP CTimeWarpRegFolder::ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pDisplayName,
  97. ULONG *pchEaten, PIDLIST_RELATIVE *ppidl, ULONG *pdwAttributes)
  98. {
  99. HRESULT hr = E_UNEXPECTED;
  100. FILETIME ftSnapTime;
  101. TraceMsg(TF_TWREGFOLDER, "TimeWarp: parsing '%s'", pDisplayName);
  102. // We could easily support a non-delegate mode, but we are never
  103. // called that way, so there's no point. This check just prevents
  104. // an AV below in the unlikely case that someone registers us
  105. // as a non-delegate (like we used to be).
  106. if (NULL == _pmalloc)
  107. {
  108. return E_UNEXPECTED;
  109. }
  110. // Do this first to ensure we have a time warp path
  111. DWORD dwErr = GetSnapshotTimeFromPath(pDisplayName, &ftSnapTime);
  112. if (ERROR_SUCCESS == dwErr)
  113. {
  114. // We only want to parse through the @GMT segment
  115. LPWSTR pszNext = wcsstr(pDisplayName, SNAPSHOT_MARKER);
  116. if (pszNext)
  117. {
  118. pszNext += SNAPSHOT_NAME_LENGTH;
  119. ASSERT(pszNext <= pDisplayName + lstrlenW(pDisplayName));
  120. ASSERT(*pszNext == L'\0' || *pszNext == L'\\');
  121. USHORT cchParse = (USHORT)(pszNext - pDisplayName);
  122. USHORT cbID = sizeof(IDTIMEWARP) - FIELD_OFFSET(IDTIMEWARP,wSignature) + cchParse*sizeof(WCHAR);
  123. ASSERT(NULL != _pmalloc);
  124. IDTIMEWARP *pid = (IDTIMEWARP*)_pmalloc->Alloc(cbID);
  125. if (pid)
  126. {
  127. ASSERT(pid->cbInner == cbID);
  128. pid->wSignature = TIMEWARP_SIGNATURE;
  129. pid->dwFlags = 0;
  130. pid->ftSnapTime = ftSnapTime;
  131. lstrcpynW(pid->wszPath, pDisplayName, cchParse+1); // +1 to allow for NULL
  132. if (*pszNext != L'\0' && *(pszNext+1) != L'\0')
  133. {
  134. // More to parse
  135. IShellFolder *psfRight;
  136. // skip the separator
  137. ASSERT(*pszNext == L'\\');
  138. pszNext++;
  139. cchParse++;
  140. // Bind to the child folder and ask it to parse the rest
  141. hr = BindToObject((PCUIDLIST_RELATIVE)pid, pbc, IID_PPV_ARG(IShellFolder, &psfRight));
  142. if (SUCCEEDED(hr))
  143. {
  144. PIDLIST_RELATIVE pidlRight;
  145. hr = psfRight->ParseDisplayName(hwnd, pbc, pszNext, pchEaten, &pidlRight, pdwAttributes);
  146. if (SUCCEEDED(hr))
  147. {
  148. *pchEaten += cchParse;
  149. hr = SHILCombine((PCIDLIST_ABSOLUTE)pid, pidlRight, (PIDLIST_ABSOLUTE*)ppidl);
  150. SHILFree(pidlRight);
  151. }
  152. psfRight->Release();
  153. }
  154. // Don't need this one anymore
  155. SHFree(pid);
  156. }
  157. else
  158. {
  159. // We're stopping here. Just return what we've got.
  160. *pchEaten = cchParse;
  161. if (pdwAttributes)
  162. {
  163. GetAttributesOf(1, (PCUITEMID_CHILD*)&pid, pdwAttributes);
  164. }
  165. *ppidl = (PIDLIST_RELATIVE)pid;
  166. hr = S_OK;
  167. }
  168. }
  169. else
  170. {
  171. hr = E_OUTOFMEMORY;
  172. }
  173. }
  174. }
  175. return hr;
  176. }
  177. STDMETHODIMP CTimeWarpRegFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppEnumIdList)
  178. {
  179. return E_NOTIMPL;
  180. }
  181. STDMETHODIMP CTimeWarpRegFolder::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbc, REFIID riid, void **ppv)
  182. {
  183. HRESULT hr = S_OK;
  184. PITEMID_CHILD pidlAlloc = NULL;
  185. BOOL bOneLevel = FALSE;
  186. *ppv = NULL;
  187. PCUIDLIST_RELATIVE pidlNext = ILGetNext(pidl);
  188. if (ILIsEmpty(pidlNext))
  189. {
  190. bOneLevel = TRUE; // we know for sure it is one level
  191. }
  192. else
  193. {
  194. hr = SHILCloneFirst(pidl, &pidlAlloc);
  195. if (SUCCEEDED(hr))
  196. {
  197. pidl = (PCUIDLIST_RELATIVE)pidlAlloc; // a single item IDLIST
  198. }
  199. }
  200. if (SUCCEEDED(hr))
  201. {
  202. if (bOneLevel)
  203. {
  204. hr = _CreateAndInit(pidl, pbc, riid, ppv);
  205. }
  206. else
  207. {
  208. IShellFolder *psfNext;
  209. hr = _CreateAndInit(pidl, pbc, IID_PPV_ARG(IShellFolder, &psfNext));
  210. if (SUCCEEDED(hr))
  211. {
  212. hr = psfNext->BindToObject(pidlNext, pbc, riid, ppv);
  213. psfNext->Release();
  214. }
  215. }
  216. }
  217. if (pidlAlloc)
  218. SHILFree(pidlAlloc); // we allocated in this case
  219. return hr;
  220. }
  221. STDMETHODIMP CTimeWarpRegFolder::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbc, REFIID riid, void **ppv)
  222. {
  223. return E_NOTIMPL;
  224. }
  225. // Copied from ILCompareRelIDs which has moved into shell\lib in lab06 (longhorn).
  226. // This can be deleted in lab06.
  227. HRESULT _CompareRelIDs(IShellFolder *psfParent, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2, LPARAM lParam)
  228. {
  229. HRESULT hr;
  230. PCUIDLIST_RELATIVE pidlRel1 = ILGetNext(pidl1);
  231. PCUIDLIST_RELATIVE pidlRel2 = ILGetNext(pidl2);
  232. if (ILIsEmpty(pidlRel1))
  233. {
  234. if (ILIsEmpty(pidlRel2))
  235. hr = ResultFromShort(0);
  236. else
  237. hr = ResultFromShort(-1);
  238. }
  239. else
  240. {
  241. if (ILIsEmpty(pidlRel2))
  242. {
  243. hr = ResultFromShort(1);
  244. }
  245. else
  246. {
  247. //
  248. // pidlRel1 and pidlRel2 point to something
  249. // (1) Bind to the next level of the IShellFolder
  250. // (2) Call its CompareIDs to let it compare the rest of IDs.
  251. //
  252. PITEMID_CHILD pidlNext;
  253. hr = SHILCloneFirst(pidl1, &pidlNext); // pidl2 would work as well
  254. if (SUCCEEDED(hr))
  255. {
  256. IShellFolder *psfNext;
  257. hr = psfParent->BindToObject(pidlNext, NULL, IID_PPV_ARG(IShellFolder, &psfNext));
  258. if (SUCCEEDED(hr))
  259. {
  260. IShellFolder2 *psf2;
  261. if (SUCCEEDED(psfNext->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2))))
  262. {
  263. psf2->Release(); // we can use the lParam
  264. }
  265. else
  266. {
  267. lParam = 0; // cant use the lParam
  268. }
  269. // columns arent valid to pass down we just care about the flags param
  270. hr = psfNext->CompareIDs((lParam & ~SHCIDS_COLUMNMASK), pidlRel1, pidlRel2);
  271. psfNext->Release();
  272. }
  273. ILFree(pidlNext);
  274. }
  275. }
  276. }
  277. return hr;
  278. }
  279. STDMETHODIMP CTimeWarpRegFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
  280. {
  281. PCUIDTIMEWARP pidTW1 = _IsValidTimeWarpID(pidl1);
  282. PCUIDTIMEWARP pidTW2 = _IsValidTimeWarpID(pidl2);
  283. if (!pidTW1 || !pidTW2)
  284. return E_INVALIDARG;
  285. int iResult = ualstrcmpiW(pidTW1->wszPath, pidTW2->wszPath);
  286. if (0 != iResult)
  287. return ResultFromShort(iResult);
  288. return _CompareRelIDs(SAFECAST(this, IShellFolder*), pidl1, pidl2, lParam);
  289. }
  290. STDMETHODIMP CTimeWarpRegFolder::CreateViewObject(HWND hwnd, REFIID riid, void **ppv)
  291. {
  292. *ppv = NULL;
  293. return E_NOTIMPL;
  294. }
  295. STDMETHODIMP CTimeWarpRegFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, SFGAOF *rgfInOut)
  296. {
  297. // Because of the limited way in which we're invoked, we know that all
  298. // child items are folders. Furthermore, the TimeWarp space is read-only
  299. // so we always return the same set of attributes.
  300. *rgfInOut = SFGAO_FILESYSTEM | SFGAO_FILESYSANCESTOR | SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_CANCOPY | SFGAO_READONLY;
  301. return S_OK;
  302. }
  303. STDMETHODIMP CTimeWarpRegFolder::GetUIObjectOf(HWND hwnd, UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
  304. REFIID riid, UINT *pRes, void **ppv)
  305. {
  306. HRESULT hr = E_NOTIMPL;
  307. PCUIDTIMEWARP pidTW = cidl ? _IsValidTimeWarpID(apidl[0]) : NULL;
  308. ASSERT(!cidl || ILIsChild(apidl[0])); // should be single level IDs only
  309. ASSERT(!cidl || pidTW); // should always be TimeWarp PIDLs
  310. if (pidTW && (IsEqualIID(riid, IID_IExtractIconW) || IsEqualIID(riid, IID_IExtractIconA)))
  311. {
  312. hr = _CreateDefExtIcon(pidTW, riid, ppv);
  313. }
  314. else if (IsEqualIID(riid, IID_IContextMenu) && pidTW)
  315. {
  316. IQueryAssociations *pqa;
  317. HKEY aKeys[2] = {0};
  318. hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &pqa));
  319. if (SUCCEEDED(hr))
  320. {
  321. // CLSID_ShellFSFolder = {F3364BA0-65B9-11CE-A9BA-00AA004AE837}
  322. hr = pqa->Init(ASSOCF_INIT_NOREMAPCLSID | ASSOCF_INIT_DEFAULTTOFOLDER, L"{F3364BA0-65B9-11CE-A9BA-00AA004AE837}", NULL, hwnd);
  323. if (SUCCEEDED(hr))
  324. {
  325. pqa->GetKey(0, ASSOCKEY_CLASS, NULL, &aKeys[0]);
  326. pqa->GetKey(0, ASSOCKEY_BASECLASS, NULL, &aKeys[1]);
  327. }
  328. pqa->Release();
  329. }
  330. hr = THR(CDefFolderMenu_Create2(_pidl, hwnd, cidl, apidl, SAFECAST(this, IShellFolder*), ContextMenuCB, ARRAYSIZE(aKeys), aKeys, (IContextMenu**)ppv));
  331. for (int i = 0; i < ARRAYSIZE(aKeys); i++)
  332. {
  333. if (aKeys[i])
  334. RegCloseKey(aKeys[i]);
  335. }
  336. }
  337. else if (IsEqualIID(riid, IID_IDataObject) && cidl)
  338. {
  339. //hr = THR(SHCreateFileDataObject(_pidl, cidl, apidl, NULL, (IDataObject**)ppv));
  340. hr = THR(CIDLData_CreateFromIDArray(_pidl, cidl, (PCUIDLIST_RELATIVE_ARRAY)apidl, (IDataObject**)ppv));
  341. }
  342. else if (IsEqualIID(riid, IID_IDropTarget))
  343. {
  344. hr = E_ACCESSDENIED;
  345. }
  346. return hr;
  347. }
  348. STDMETHODIMP CTimeWarpRegFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD uFlags, STRRET *pName)
  349. {
  350. HRESULT hr;
  351. PCUIDTIMEWARP pidTW = _IsValidTimeWarpID(pidl);
  352. if (pidTW)
  353. {
  354. LPCWSTR pszPath;
  355. WSTR_ALIGNED_STACK_COPY(&pszPath, pidTW->wszPath);
  356. // If we aren't being asked for a friendly name, just use the path
  357. if ((uFlags & SHGDN_FORPARSING) && !(uFlags & SHGDN_FORADDRESSBAR))
  358. {
  359. pName->uType = STRRET_WSTR;
  360. hr = SHStrDup(pszPath, &pName->pOleStr);
  361. }
  362. else
  363. {
  364. PIDLIST_ABSOLUTE pidlTarget;
  365. // Ok, we're doing the friendly date thing. Start by getting the
  366. // target pidl without the GMT stamp.
  367. hr = GetFSIDListFromTimeWarpPath(&pidlTarget, pszPath);
  368. if (SUCCEEDED(hr))
  369. {
  370. WCHAR szName[MAX_PATH];
  371. // Get the name
  372. hr = SHGetNameAndFlagsW(pidlTarget, uFlags, szName, ARRAYSIZE(szName), NULL);
  373. if (SUCCEEDED(hr))
  374. {
  375. ASSERT(!(uFlags & SHGDN_FORPARSING) || (uFlags & SHGDN_FORADDRESSBAR));
  376. // Add the date string
  377. pName->uType = STRRET_WSTR;
  378. hr = FormatFriendlyDateName(&pName->pOleStr, szName, &pidTW->ftSnapTime);
  379. }
  380. SHILFree(pidlTarget);
  381. }
  382. }
  383. }
  384. else
  385. {
  386. hr = E_INVALIDARG;
  387. }
  388. return THR(hr);
  389. }
  390. STDMETHODIMP CTimeWarpRegFolder::SetNameOf(HWND hwnd, PCUITEMID_CHILD pidl, LPCOLESTR pName, SHGDNF uFlags, PITEMID_CHILD *ppidlOut)
  391. {
  392. return E_NOTIMPL;
  393. }
  394. HRESULT CTimeWarpRegFolder::_CreateAndInit(PCUIDLIST_RELATIVE pidl, LPBC pbc, REFIID riid, void **ppv)
  395. {
  396. HRESULT hr = E_FAIL;
  397. PCUIDTIMEWARP pidTW = _IsValidTimeWarpID(pidl);
  398. ASSERT(ILIsChild(pidl)); // NULL is OK
  399. *ppv = NULL;
  400. if (pidTW)
  401. {
  402. // Can't do normal parsing, since it validates the path each step
  403. // of the way. The @GMT element isn't enumerated in its parent dir,
  404. // so normal parsing fails there with ERROR_PATH_NOT_FOUND.
  405. //
  406. // Therefore, we can't let the FS folder parse the target path.
  407. // Instead, we create a simple pidl here and give it to him.
  408. PIDLIST_ABSOLUTE pidlTarget;
  409. LPCWSTR pszPath;
  410. WSTR_ALIGNED_STACK_COPY(&pszPath, pidTW->wszPath);
  411. hr = SimpleIDListFromAttributes(pszPath, FILE_ATTRIBUTE_DIRECTORY, &pidlTarget);
  412. if (SUCCEEDED(hr))
  413. {
  414. PIDLIST_ABSOLUTE pidlFull;
  415. hr = SHILCombine(_pidl, pidl, &pidlFull);
  416. if (SUCCEEDED(hr))
  417. {
  418. hr = CTimeWarpFolder::CreateInstance(CLSID_ShellFSFolder, pidlFull, pidlTarget, pszPath, &pidTW->ftSnapTime, riid, ppv);
  419. SHILFree(pidlFull);
  420. }
  421. SHILFree(pidlTarget);
  422. }
  423. }
  424. return hr;
  425. }
  426. HRESULT CTimeWarpRegFolder::_CreateDefExtIcon(PCUIDTIMEWARP pidTW, REFIID riid, void **ppv)
  427. {
  428. // Truncation here isn't really a problem. SHCreateFileExtractIcon
  429. // doesn't actually require the path to exist, so it succeeds anyway.
  430. // Worst case, you might see the wrong icon in the treeview.
  431. WCHAR szPath[MAX_PATH];
  432. ualstrcpynW(szPath, pidTW->wszPath, ARRAYSIZE(szPath));
  433. EliminateGMTPathSegment(szPath);
  434. return SHCreateFileExtractIconW(szPath, FILE_ATTRIBUTE_DIRECTORY, riid, ppv);
  435. }
  436. void _LaunchPropSheet(HWND hwnd, IDataObject *pdtobj)
  437. {
  438. STGMEDIUM medium;
  439. LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
  440. if (pida)
  441. {
  442. PCUIDTIMEWARP pidTW = _IsValidTimeWarpID(IDA_GetPIDLItem(pida, 0));
  443. if (pidTW)
  444. {
  445. PIDLIST_ABSOLUTE pidlTarget;
  446. LPCWSTR pszPath;
  447. WSTR_ALIGNED_STACK_COPY(&pszPath, pidTW->wszPath);
  448. HRESULT hr = GetFSIDListFromTimeWarpPath(&pidlTarget, pszPath);
  449. if (SUCCEEDED(hr))
  450. {
  451. SHELLEXECUTEINFOW sei =
  452. {
  453. sizeof(sei),
  454. SEE_MASK_INVOKEIDLIST, // fMask
  455. hwnd, // hwnd
  456. L"properties", // lpVerb
  457. NULL, // lpFile
  458. NULL, // lpParameters
  459. NULL, // lpDirectory
  460. SW_SHOWNORMAL, // nShow
  461. NULL, // hInstApp
  462. pidlTarget, // lpIDList
  463. NULL, // lpClass
  464. 0, // hkeyClass
  465. 0, // dwHotKey
  466. NULL // hIcon
  467. };
  468. ShellExecuteEx(&sei);
  469. SHILFree(pidlTarget);
  470. }
  471. }
  472. HIDA_ReleaseStgMedium(pida, &medium);
  473. }
  474. }
  475. STDMETHODIMP CTimeWarpRegFolder::ContextMenuCB(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
  476. {
  477. HRESULT hr = E_NOTIMPL;
  478. switch(uMsg)
  479. {
  480. case DFM_MERGECONTEXTMENU:
  481. hr = S_OK; // use default extension
  482. break;
  483. case DFM_INVOKECOMMANDEX:
  484. switch(wParam)
  485. {
  486. default:
  487. ASSERT(FALSE);
  488. hr = S_FALSE; // do default
  489. break;
  490. case DFM_CMD_PROPERTIES:
  491. // Background properties
  492. _LaunchPropSheet(hwnd, pdtobj);
  493. hr = S_OK;
  494. break;
  495. }
  496. break;
  497. default:
  498. break;
  499. }
  500. return hr;
  501. }
  502. //
  503. // Folder implementation aggregating the file system folder
  504. //
  505. STDMETHODIMP CTimeWarpFolder::CreateInstance(REFCLSID rclsid, PCIDLIST_ABSOLUTE pidlRoot, PCIDLIST_ABSOLUTE pidlTarget,
  506. LPCWSTR pszTargetPath, const FILETIME UNALIGNED *pftSnapTime,
  507. REFIID riid, void **ppv)
  508. {
  509. HRESULT hr;
  510. CTimeWarpFolder *psf = new CTimeWarpFolder(pftSnapTime);
  511. if (psf)
  512. {
  513. hr = psf->_Init(rclsid, pidlRoot, pidlTarget, pszTargetPath);
  514. if (SUCCEEDED(hr))
  515. {
  516. hr = psf->QueryInterface(riid, ppv);
  517. }
  518. psf->Release();
  519. }
  520. else
  521. {
  522. hr = E_OUTOFMEMORY;
  523. }
  524. return hr;
  525. }
  526. CTimeWarpFolder::CTimeWarpFolder(const FILETIME UNALIGNED *pftSnapTime) : _cRef(1), _ftSnapTime(*pftSnapTime),
  527. _punk(NULL), _psf(NULL), _psf2(NULL), _ppf3(NULL), _pidlRoot(NULL)
  528. {
  529. DllAddRef();
  530. }
  531. CTimeWarpFolder::~CTimeWarpFolder()
  532. {
  533. _cRef = 1000; // deal with aggregation re-enter
  534. if (_punk)
  535. {
  536. SHReleaseInnerInterface(SAFECAST(this, IShellFolder*), (IUnknown**)&_psf);
  537. SHReleaseInnerInterface(SAFECAST(this, IShellFolder*), (IUnknown**)&_psf2);
  538. SHReleaseInnerInterface(SAFECAST(this, IShellFolder*), (IUnknown**)&_ppf3);
  539. _punk->Release();
  540. }
  541. SHILFree((void*)_pidlRoot); // const
  542. DllRelease();
  543. }
  544. HRESULT CTimeWarpFolder::_Init(REFCLSID rclsid, PCIDLIST_ABSOLUTE pidlRoot, PCIDLIST_ABSOLUTE pidlTarget, LPCWSTR pszTargetPath)
  545. {
  546. HRESULT hr = Initialize(pidlRoot);
  547. if (hr == S_OK)
  548. {
  549. // Aggregate the real folder object (usually CLSID_ShellFSFolder)
  550. hr = SHCoCreateInstance(NULL, &rclsid, SAFECAST(this, IShellFolder*), IID_PPV_ARG(IUnknown, &_punk));
  551. if (SUCCEEDED(hr))
  552. {
  553. hr = SHQueryInnerInterface(SAFECAST(this, IShellFolder*), _punk, IID_PPV_ARG(IPersistFolder3, &_ppf3));
  554. if (SUCCEEDED(hr))
  555. {
  556. PERSIST_FOLDER_TARGET_INFO pfti;
  557. pfti.pidlTargetFolder = (PIDLIST_ABSOLUTE)pidlTarget;
  558. pfti.szNetworkProvider[0] = L'\0';
  559. pfti.dwAttributes = FILE_ATTRIBUTE_DIRECTORY;
  560. pfti.csidl = -1;
  561. // We check the target path length in CTimeWarpFolder::_CreateAndInit.
  562. // If it's too big, we shouldn't get this far.
  563. ASSERT(lstrlenW(pszTargetPath) < ARRAYSIZE(pfti.szTargetParsingName));
  564. lstrcpynW(pfti.szTargetParsingName, pszTargetPath, ARRAYSIZE(pfti.szTargetParsingName));
  565. hr = _ppf3->InitializeEx(NULL, pidlRoot, &pfti);
  566. }
  567. }
  568. }
  569. return hr;
  570. }
  571. STDMETHODIMP CTimeWarpFolder::QueryInterface(REFIID riid, void **ppv)
  572. {
  573. static const QITAB qit[] = {
  574. QITABENTMULTI(CTimeWarpFolder, IShellFolder, IShellFolder2),
  575. QITABENT(CTimeWarpFolder, IShellFolder2),
  576. QITABENTMULTI(CTimeWarpFolder, IPersist, IPersistFolder),
  577. QITABENT(CTimeWarpFolder, IPersistFolder),
  578. { 0 },
  579. };
  580. HRESULT hr = QISearch(this, qit, riid, ppv);
  581. if (FAILED(hr) && _punk)
  582. hr = _punk->QueryInterface(riid, ppv); // aggregated guy
  583. return hr;
  584. }
  585. STDMETHODIMP_ (ULONG) CTimeWarpFolder::AddRef()
  586. {
  587. return InterlockedIncrement(&_cRef);
  588. }
  589. STDMETHODIMP_ (ULONG) CTimeWarpFolder::Release()
  590. {
  591. ASSERT( 0 != _cRef );
  592. ULONG cRef = InterlockedDecrement(&_cRef);
  593. if ( 0 == cRef )
  594. {
  595. delete this;
  596. }
  597. return cRef;
  598. }
  599. // IPersist
  600. STDMETHODIMP CTimeWarpFolder::GetClassID(CLSID *pClassID)
  601. {
  602. *pClassID = CLSID_TimeWarpFolder; //CLSID_ShellFSFolder?
  603. return S_OK;
  604. }
  605. // IPersistFolder
  606. HRESULT CTimeWarpFolder::Initialize(PCIDLIST_ABSOLUTE pidl)
  607. {
  608. if (_pidlRoot)
  609. {
  610. SHILFree((void*)_pidlRoot); // const
  611. _pidlRoot = NULL;
  612. }
  613. return pidl ? SHILCloneFull(pidl, &_pidlRoot) : S_FALSE;
  614. }
  615. HRESULT CTimeWarpFolder::_CreateAndInit(REFCLSID rclsid, PCUIDLIST_RELATIVE pidl, LPBC pbc, REFIID riid, void **ppv)
  616. {
  617. HRESULT hr = E_FAIL;
  618. ASSERT(ILIsChild(pidl)); // NULL is OK
  619. *ppv = NULL;
  620. if (pidl && _ppf3)
  621. {
  622. PERSIST_FOLDER_TARGET_INFO targetInfo;
  623. hr = _ppf3->GetFolderTargetInfo(&targetInfo);
  624. if (SUCCEEDED(hr))
  625. {
  626. ASSERT(NULL != _pidlRoot);
  627. // Concatenate pidl onto both _pidlRoot and targetInfo.pidlTargetFolder
  628. PIDLIST_ABSOLUTE pidlFull;
  629. hr = SHILCombine(_pidlRoot, pidl, &pidlFull);
  630. if (SUCCEEDED(hr))
  631. {
  632. PIDLIST_ABSOLUTE pidlTargetFull;
  633. hr = SHILCombine(targetInfo.pidlTargetFolder, pidl, &pidlTargetFull);
  634. if (SUCCEEDED(hr))
  635. {
  636. LPWSTR pszName;
  637. // Concatenate the child name onto targetInfo.szTargetParsingName
  638. hr = DisplayNameOfAsOLESTR(this, ILMAKECHILD(pidl), SHGDN_INFOLDER | SHGDN_FORPARSING, &pszName);
  639. if (SUCCEEDED(hr))
  640. {
  641. TraceMsg(TF_TWFOLDER, "TimeWarpFolder: binding to '%s'", pszName);
  642. // IPersistFolder3 has a fixed path limit (MAX_PATH),
  643. // which happens to be the same limit as PathAppend.
  644. // Fail here if the name is too long.
  645. COMPILETIME_ASSERT(ARRAYSIZE(targetInfo.szTargetParsingName) >= MAX_PATH);
  646. if (PathAppend(targetInfo.szTargetParsingName, pszName))
  647. {
  648. hr = CTimeWarpFolder::CreateInstance(rclsid, pidlFull, pidlTargetFull, targetInfo.szTargetParsingName, &_ftSnapTime, riid, ppv);
  649. }
  650. else
  651. {
  652. hr = HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE);
  653. }
  654. LocalFree(pszName);
  655. }
  656. SHILFree(pidlTargetFull);
  657. }
  658. SHILFree(pidlFull);
  659. }
  660. SHILFree(targetInfo.pidlTargetFolder);
  661. }
  662. }
  663. return hr;
  664. }
  665. // verify that _psf (aggregated file system folder) has been inited
  666. HRESULT CTimeWarpFolder::_GetFolder()
  667. {
  668. HRESULT hr = S_OK;
  669. if (_psf == NULL)
  670. hr = SHQueryInnerInterface(SAFECAST(this, IShellFolder*), _punk, IID_PPV_ARG(IShellFolder, &_psf));
  671. return hr;
  672. }
  673. HRESULT CTimeWarpFolder::_GetFolder2()
  674. {
  675. HRESULT hr = S_OK;
  676. if (_psf2 == NULL)
  677. hr = SHQueryInnerInterface(SAFECAST(this, IShellFolder*), _punk, IID_PPV_ARG(IShellFolder2, &_psf2));
  678. return hr;
  679. }
  680. HRESULT CTimeWarpFolder::_GetClass(PCUITEMID_CHILD pidlChild, CLSID *pclsid)
  681. {
  682. HRESULT hr;
  683. VARIANT varDID;
  684. hr = GetDetailsEx(pidlChild, &SCID_DESCRIPTIONID, &varDID);
  685. if (SUCCEEDED(hr))
  686. {
  687. SHDESCRIPTIONID did;
  688. if (VariantToBuffer(&varDID, &did, sizeof(did)))
  689. {
  690. // Ordinary directories (non-junctions) return GUID_NULL.
  691. if (SHDID_FS_DIRECTORY == did.dwDescriptionId && IsEqualGUID(did.clsid, GUID_NULL))
  692. *pclsid = CLSID_ShellFSFolder;
  693. else
  694. *pclsid = did.clsid;
  695. }
  696. else
  697. {
  698. hr = E_FAIL;
  699. }
  700. VariantClear(&varDID);
  701. }
  702. return hr;
  703. }
  704. STDMETHODIMP CTimeWarpFolder::ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pDisplayName,
  705. ULONG *pchEaten, PIDLIST_RELATIVE *ppidl, ULONG *pdwAttributes)
  706. {
  707. HRESULT hr = _GetFolder();
  708. if (SUCCEEDED(hr))
  709. {
  710. hr = _psf->ParseDisplayName(hwnd, pbc, pDisplayName, pchEaten, ppidl, pdwAttributes);
  711. if (SUCCEEDED(hr) && pdwAttributes)
  712. {
  713. // Time Warp is a read-only namespace. Don't allow move, delete, etc.
  714. *pdwAttributes = (*pdwAttributes | SFGAO_READONLY) & ~(SFGAO_CANMOVE | SFGAO_CANDELETE | SFGAO_CANRENAME | SFGAO_CANLINK);
  715. }
  716. }
  717. return hr;
  718. }
  719. STDMETHODIMP CTimeWarpFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppEnumIdList)
  720. {
  721. HRESULT hr = _GetFolder();
  722. if (SUCCEEDED(hr))
  723. hr = _psf->EnumObjects(hwnd, grfFlags, ppEnumIdList);
  724. return hr;
  725. }
  726. STDMETHODIMP CTimeWarpFolder::BindToObject(PCUIDLIST_RELATIVE pidl, LPBC pbc, REFIID riid, void **ppv)
  727. {
  728. HRESULT hr = S_OK;
  729. PCUITEMID_CHILD pidlChild = pidl;
  730. PITEMID_CHILD pidlAlloc = NULL;
  731. BOOL bOneLevel = FALSE;
  732. *ppv = NULL;
  733. PCUIDLIST_RELATIVE pidlNext = ILGetNext(pidl);
  734. if (ILIsEmpty(pidlNext))
  735. {
  736. bOneLevel = TRUE; // we know for sure it is one level
  737. }
  738. else
  739. {
  740. hr = SHILCloneFirst(pidl, &pidlAlloc);
  741. if (SUCCEEDED(hr))
  742. {
  743. pidlChild = pidlAlloc; // a single item IDLIST
  744. }
  745. }
  746. if (SUCCEEDED(hr))
  747. {
  748. CLSID clsid;
  749. // We might be at a junction to something other than FSFolder, e.g.
  750. // a ZIP or CAB folder, so get the CLSID of the child.
  751. hr = _GetClass(pidlChild, &clsid);
  752. if (SUCCEEDED(hr))
  753. {
  754. if (bOneLevel)
  755. {
  756. hr = _CreateAndInit(clsid, pidlChild, pbc, riid, ppv);
  757. }
  758. else
  759. {
  760. IShellFolder *psfNext;
  761. hr = _CreateAndInit(clsid, pidlChild, pbc, IID_PPV_ARG(IShellFolder, &psfNext));
  762. if (SUCCEEDED(hr))
  763. {
  764. hr = psfNext->BindToObject(pidlNext, pbc, riid, ppv);
  765. psfNext->Release();
  766. }
  767. }
  768. }
  769. if (FAILED(hr))
  770. {
  771. // Return an un-aggregated object
  772. if (SUCCEEDED(_GetFolder()))
  773. {
  774. hr = _psf->BindToObject(pidl, pbc, riid, ppv);
  775. }
  776. }
  777. }
  778. if (pidlAlloc)
  779. SHILFree(pidlAlloc); // we allocated in this case
  780. return hr;
  781. }
  782. STDMETHODIMP CTimeWarpFolder::BindToStorage(PCUIDLIST_RELATIVE pidl, LPBC pbc, REFIID riid, void **ppv)
  783. {
  784. HRESULT hr = _GetFolder();
  785. if (SUCCEEDED(hr))
  786. hr = _psf->BindToStorage(pidl, pbc, riid, ppv);
  787. return hr;
  788. }
  789. STDMETHODIMP CTimeWarpFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
  790. {
  791. HRESULT hr = _GetFolder();
  792. if (SUCCEEDED(hr))
  793. hr = _psf->CompareIDs(lParam, pidl1, pidl2);
  794. return hr;
  795. }
  796. STDMETHODIMP CTimeWarpFolder::CreateViewObject(HWND hwnd, REFIID riid, void **ppv)
  797. {
  798. HRESULT hr;
  799. *ppv = NULL;
  800. if (IsEqualIID(riid, IID_IDropTarget))
  801. {
  802. // Drag/drop not allowed to a timewarp folder
  803. TraceMsg(TF_TWFOLDER, "TimeWarpFolder denying IDropTarget (CVO)");
  804. hr = E_ACCESSDENIED;
  805. }
  806. else
  807. {
  808. hr = _GetFolder();
  809. if (SUCCEEDED(hr))
  810. {
  811. hr = _psf->CreateViewObject(hwnd, riid, ppv);
  812. if (SUCCEEDED(hr) && IsEqualIID(riid, IID_IContextMenu))
  813. {
  814. // Wrap the background menu object so we can disable the New submenu
  815. void *pvWrap;
  816. if (SUCCEEDED(Create_ContextMenuWithoutPopups((IContextMenu*)*ppv, riid, &pvWrap)))
  817. {
  818. ((IUnknown*)*ppv)->Release();
  819. *ppv = pvWrap;
  820. }
  821. }
  822. }
  823. }
  824. return hr;
  825. }
  826. STDMETHODIMP CTimeWarpFolder::GetAttributesOf(UINT cidl, PCUITEMID_CHILD_ARRAY apidl, SFGAOF *rgfInOut)
  827. {
  828. HRESULT hr = _GetFolder();
  829. if (SUCCEEDED(hr))
  830. hr = _psf->GetAttributesOf(cidl, apidl, rgfInOut);
  831. // Time Warp is a read-only namespace. Don't allow move, delete, etc.
  832. *rgfInOut = (*rgfInOut | SFGAO_READONLY) & ~(SFGAO_CANMOVE | SFGAO_CANDELETE | SFGAO_CANRENAME | SFGAO_CANLINK);
  833. return hr;
  834. }
  835. STDMETHODIMP CTimeWarpFolder::GetUIObjectOf(HWND hwnd, UINT cidl, PCUITEMID_CHILD_ARRAY apidl,
  836. REFIID riid, UINT *pRes, void **ppv)
  837. {
  838. HRESULT hr = E_NOTIMPL;
  839. ASSERT(!cidl || ILIsChild(apidl[0])); // should be single level IDs only
  840. if (IsEqualIID(riid, IID_IDropTarget))
  841. {
  842. TraceMsg(TF_TWFOLDER, "TimeWarpFolder denying IDropTarget (GUIOO)");
  843. hr = E_ACCESSDENIED;
  844. }
  845. else
  846. {
  847. hr = _GetFolder();
  848. if (SUCCEEDED(hr))
  849. {
  850. hr = _psf->GetUIObjectOf(hwnd, cidl, apidl, riid, pRes, ppv);
  851. if (SUCCEEDED(hr) && IsEqualIID(riid, IID_IContextMenu))
  852. {
  853. // Wrap the menu object so we can eliminate some commands
  854. void *pvWrap;
  855. if (SUCCEEDED(Create_ContextMenuWithoutVerbs((IContextMenu*)*ppv, L"pin;find", riid, &pvWrap)))
  856. {
  857. ((IUnknown*)*ppv)->Release();
  858. *ppv = pvWrap;
  859. }
  860. }
  861. }
  862. }
  863. return hr;
  864. }
  865. STDMETHODIMP CTimeWarpFolder::GetDisplayNameOf(PCUITEMID_CHILD pidl, DWORD uFlags, STRRET *pName)
  866. {
  867. HRESULT hr = _GetFolder();
  868. if (SUCCEEDED(hr))
  869. {
  870. hr = _psf->GetDisplayNameOf(pidl, uFlags, pName);
  871. // If it's for the address bar, add the friendly date string
  872. if (SUCCEEDED(hr)&& (uFlags & SHGDN_FORADDRESSBAR))
  873. {
  874. WCHAR szName[MAX_PATH];
  875. // Note that this clears the STRRET
  876. hr = StrRetToBufW(pName, pidl, szName, ARRAYSIZE(szName));
  877. if (SUCCEEDED(hr))
  878. {
  879. if (uFlags & SHGDN_FORPARSING)
  880. {
  881. // Remove the GMT path segment in this case
  882. EliminateGMTPathSegment(szName);
  883. }
  884. pName->uType = STRRET_WSTR;
  885. hr = FormatFriendlyDateName(&pName->pOleStr, szName, &_ftSnapTime);
  886. }
  887. }
  888. }
  889. return hr;
  890. }
  891. STDMETHODIMP CTimeWarpFolder::SetNameOf(HWND hwnd, PCUITEMID_CHILD pidl, LPCOLESTR pName, SHGDNF uFlags, PITEMID_CHILD *ppidlOut)
  892. {
  893. HRESULT hr = _GetFolder();
  894. if (SUCCEEDED(hr))
  895. hr = _psf->SetNameOf(hwnd, pidl, pName, uFlags, ppidlOut);
  896. return hr;
  897. }
  898. STDMETHODIMP CTimeWarpFolder::GetDefaultSearchGUID(LPGUID lpGuid)
  899. {
  900. HRESULT hr = _GetFolder2();
  901. if (SUCCEEDED(hr))
  902. hr = _psf2->GetDefaultSearchGUID(lpGuid);
  903. return hr;
  904. }
  905. STDMETHODIMP CTimeWarpFolder::EnumSearches(LPENUMEXTRASEARCH *ppenum)
  906. {
  907. HRESULT hr = _GetFolder2();
  908. if (SUCCEEDED(hr))
  909. hr = _psf2->EnumSearches(ppenum);
  910. return hr;
  911. }
  912. STDMETHODIMP CTimeWarpFolder::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
  913. {
  914. HRESULT hr = _GetFolder2();
  915. if (SUCCEEDED(hr))
  916. hr = _psf2->GetDefaultColumn(dwRes, pSort, pDisplay);
  917. return hr;
  918. }
  919. STDMETHODIMP CTimeWarpFolder::GetDefaultColumnState(UINT iColumn, DWORD *pbState)
  920. {
  921. HRESULT hr = _GetFolder2();
  922. if (SUCCEEDED(hr))
  923. hr = _psf2->GetDefaultColumnState(iColumn, pbState);
  924. return hr;
  925. }
  926. STDMETHODIMP CTimeWarpFolder::GetDetailsEx(PCUITEMID_CHILD pidl, const SHCOLUMNID *pscid, VARIANT *pv)
  927. {
  928. HRESULT hr = _GetFolder2();
  929. if (SUCCEEDED(hr))
  930. hr = _psf2->GetDetailsEx(pidl, pscid, pv);
  931. return hr;
  932. }
  933. STDMETHODIMP CTimeWarpFolder::GetDetailsOf(PCUITEMID_CHILD pidl, UINT iColumn, LPSHELLDETAILS pDetail)
  934. {
  935. HRESULT hr = _GetFolder2();
  936. if (SUCCEEDED(hr))
  937. hr = _psf2->GetDetailsOf(pidl, iColumn, pDetail);
  938. return hr;
  939. }
  940. STDMETHODIMP CTimeWarpFolder::MapColumnToSCID(UINT iCol, SHCOLUMNID *pscid)
  941. {
  942. HRESULT hr = _GetFolder2();
  943. if (SUCCEEDED(hr))
  944. hr = _psf2->MapColumnToSCID(iCol, pscid);
  945. return hr;
  946. }