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.

920 lines
26 KiB

  1. #include "shellprv.h"
  2. #pragma hdrstop
  3. #include <shguidp.h> // CLSID_MyDocuments, CLSID_ShellFSFolder
  4. #include <shellp.h> // SHCoCreateInstance
  5. #include <shlguidp.h> // IID_IResolveShellLink
  6. #include "util.h"
  7. #include "ids.h"
  8. enum CALLING_APP_TYPE
  9. {
  10. APP_IS_UNKNOWN = 0,
  11. APP_IS_NORMAL,
  12. APP_IS_OFFICE
  13. };
  14. class CMyDocsFolderLinkResolver : public IResolveShellLink
  15. {
  16. private:
  17. LONG _cRef;
  18. public:
  19. CMyDocsFolderLinkResolver() : _cRef(1) { DllAddRef(); };
  20. ~CMyDocsFolderLinkResolver() { DllRelease(); };
  21. // IUnknown
  22. STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
  23. STDMETHOD_(ULONG, AddRef)();
  24. STDMETHOD_(ULONG, Release)();
  25. // IResolveShellLink
  26. STDMETHOD(ResolveShellLink)(IUnknown* punk, HWND hwnd, DWORD fFlags);
  27. };
  28. STDMETHODIMP CMyDocsFolderLinkResolver::QueryInterface(REFIID riid, void **ppv)
  29. {
  30. static const QITAB qit[] = {
  31. QITABENT(CMyDocsFolderLinkResolver, IResolveShellLink),
  32. { 0 },
  33. };
  34. return QISearch(this, qit, riid, ppv);
  35. }
  36. STDMETHODIMP_ (ULONG) CMyDocsFolderLinkResolver::AddRef()
  37. {
  38. return InterlockedIncrement(&_cRef);
  39. }
  40. STDMETHODIMP_ (ULONG) CMyDocsFolderLinkResolver::Release()
  41. {
  42. if (InterlockedDecrement(&_cRef))
  43. return _cRef;
  44. delete this;
  45. return 0;
  46. }
  47. STDMETHODIMP CMyDocsFolderLinkResolver::ResolveShellLink(IUnknown* punk, HWND hwnd, DWORD fFlags)
  48. {
  49. // No action needed to resolve a link to the mydocs folder:
  50. return S_OK;
  51. }
  52. // shell folder implementation for icon on the desktop. the purpouse of this object is
  53. // 1) to give access to MyDocs high up in the name space
  54. // this makes it easier for end users to get to MyDocs
  55. // 2) allow for end user custimization of the real MyDocs folder
  56. // through the provided property page on this icon
  57. // NOTE: this object agregates the file system folder so we get away with a minimal set of interfaces
  58. // on this object. the real file system folder does stuff like IPersistFolder2 for us
  59. class CMyDocsFolder : public IPersistFolder,
  60. public IShellFolder2,
  61. public IShellIconOverlay
  62. {
  63. public:
  64. CMyDocsFolder();
  65. HRESULT Init();
  66. // IUnknown
  67. STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
  68. STDMETHOD_(ULONG, AddRef)();
  69. STDMETHOD_(ULONG, Release)();
  70. // IShellFolder
  71. STDMETHOD(ParseDisplayName)(HWND hwnd, LPBC pbc, LPOLESTR pDisplayName,
  72. ULONG *pchEaten, LPITEMIDLIST *ppidl, ULONG *pdwAttributes);
  73. STDMETHOD(EnumObjects)(HWND hwnd, DWORD grfFlags, IEnumIDList **ppEnumIDList);
  74. STDMETHOD(BindToObject)(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv);
  75. STDMETHOD(BindToStorage)(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv);
  76. STDMETHOD(CompareIDs)(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
  77. STDMETHOD(CreateViewObject)(HWND hwnd, REFIID riid, void **ppv);
  78. STDMETHOD(GetAttributesOf)(UINT cidl, LPCITEMIDLIST * apidl, ULONG * rgfInOut);
  79. STDMETHOD(GetUIObjectOf)(HWND hwnd, UINT cidl, LPCITEMIDLIST * apidl, REFIID riid, UINT * prgfInOut, void **ppv);
  80. STDMETHOD(GetDisplayNameOf)(LPCITEMIDLIST pidl, DWORD uFlags, STRRET *pName);
  81. STDMETHOD(SetNameOf)(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, DWORD uFlags, LPITEMIDLIST* ppidlOut);
  82. // IShellFolder2
  83. STDMETHODIMP GetDefaultSearchGUID(LPGUID lpGuid);
  84. STDMETHODIMP EnumSearches(LPENUMEXTRASEARCH *ppenum);
  85. STDMETHODIMP GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay);
  86. STDMETHODIMP GetDefaultColumnState(UINT iColumn, DWORD *pbState);
  87. STDMETHODIMP GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv);
  88. STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails);
  89. STDMETHODIMP MapColumnToSCID(UINT iCol, SHCOLUMNID *pscid);
  90. // IPersist, IPersistFreeThreadedObject
  91. STDMETHOD(GetClassID)(CLSID *pClassID);
  92. // IPersistFolder
  93. STDMETHOD(Initialize)(LPCITEMIDLIST pidl);
  94. // IPersistFolder2, IPersistFolder3, etc are all implemented by
  95. // the folder we agregate
  96. // IShellIconOverlay
  97. STDMETHODIMP GetOverlayIndex(LPCITEMIDLIST pidl, int *pIndex);
  98. STDMETHODIMP GetOverlayIconIndex(LPCITEMIDLIST pidl, int *pIconIndex);
  99. private:
  100. ~CMyDocsFolder();
  101. HRESULT _GetFolder();
  102. HRESULT _GetFolder2();
  103. HRESULT _GetShellIconOverlay();
  104. void _FreeFolder();
  105. HRESULT _PathFromIDList(LPCITEMIDLIST pidl, LPTSTR pszPath);
  106. HRESULT _PathToIDList(LPCTSTR pszPath, LPITEMIDLIST *ppidl);
  107. HRESULT _GetFolderOverlayInfo(int *pIndex, BOOL fIconIndex);
  108. LONG _cRef;
  109. IUnknown * _punk; // points to IUnknown for shell folder in use...
  110. IShellFolder * _psf; // points to shell folder in use...
  111. IShellFolder2 * _psf2; // points to shell folder in use...
  112. IShellIconOverlay* _psio; // points to shell folder in use...
  113. LPITEMIDLIST _pidl; // copy of pidl passed to us in Initialize()
  114. CALLING_APP_TYPE _host;
  115. HRESULT RealInitialize(LPCITEMIDLIST pidlRoot, LPCITEMIDLIST pidlBindTo, LPTSTR pRootPath);
  116. CALLING_APP_TYPE _WhoIsCalling();
  117. };
  118. STDAPI CMyDocsFolder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv)
  119. {
  120. HRESULT hr;
  121. CMyDocsFolder *pmydocs = new CMyDocsFolder();
  122. if (pmydocs)
  123. {
  124. hr = pmydocs->Init();
  125. if (SUCCEEDED(hr))
  126. hr = pmydocs->QueryInterface(riid, ppv);
  127. pmydocs->Release();
  128. }
  129. else
  130. {
  131. *ppv = NULL;
  132. hr = E_OUTOFMEMORY;
  133. }
  134. return hr;
  135. }
  136. CMyDocsFolder::CMyDocsFolder() : _cRef(1), _host(APP_IS_UNKNOWN),
  137. _psf(NULL), _psf2(NULL), _psio(NULL), _punk(NULL), _pidl(NULL)
  138. {
  139. DllAddRef();
  140. }
  141. CMyDocsFolder::~CMyDocsFolder()
  142. {
  143. _cRef = 1000; // deal with agregation re-enter
  144. _FreeFolder();
  145. ILFree(_pidl);
  146. DllRelease();
  147. }
  148. HRESULT CMyDocsFolder::Init()
  149. {
  150. // agregate a file system folder object early so we can
  151. // delegate QI() to him that we don't implement
  152. HRESULT hr = SHCoCreateInstance(NULL, &CLSID_ShellFSFolder, SAFECAST(this, IShellFolder *), IID_PPV_ARG(IUnknown, &_punk));
  153. if (SUCCEEDED(hr))
  154. {
  155. IPersistFolder3 *ppf3;
  156. hr = SHQueryInnerInterface(SAFECAST(this, IShellFolder *), _punk, IID_PPV_ARG(IPersistFolder3, &ppf3));
  157. if (SUCCEEDED(hr))
  158. {
  159. PERSIST_FOLDER_TARGET_INFO pfti = {0};
  160. pfti.dwAttributes = FILE_ATTRIBUTE_DIRECTORY;
  161. pfti.csidl = CSIDL_PERSONAL | CSIDL_FLAG_PFTI_TRACKTARGET;
  162. hr = SHGetFolderLocation(NULL, CSIDL_PERSONAL, NULL, 0, &_pidl);
  163. if (SUCCEEDED(hr))
  164. {
  165. hr = ppf3->InitializeEx(NULL, _pidl, &pfti);
  166. }
  167. SHReleaseInnerInterface(SAFECAST(this, IShellFolder *), (IUnknown **)&ppf3);
  168. }
  169. }
  170. return hr;
  171. }
  172. STDMETHODIMP CMyDocsFolder::QueryInterface(REFIID riid, void **ppv)
  173. {
  174. static const QITAB qit[] = {
  175. QITABENTMULTI(CMyDocsFolder, IShellFolder, IShellFolder2),
  176. QITABENT(CMyDocsFolder, IShellFolder2),
  177. QITABENTMULTI(CMyDocsFolder, IPersist, IPersistFolder),
  178. QITABENT(CMyDocsFolder, IPersistFolder),
  179. QITABENT(CMyDocsFolder, IShellIconOverlay),
  180. // QITABENTMULTI2(CMyDocsFolder, IID_IPersistFreeThreadedObject, IPersist), // IID_IPersistFreeThreadedObject
  181. { 0 },
  182. };
  183. HRESULT hr = QISearch(this, qit, riid, ppv);
  184. if (FAILED(hr) && _punk)
  185. hr = _punk->QueryInterface(riid, ppv); // agregated guy
  186. return hr;
  187. }
  188. STDMETHODIMP_ (ULONG) CMyDocsFolder::AddRef()
  189. {
  190. return InterlockedIncrement(&_cRef);
  191. }
  192. STDMETHODIMP_ (ULONG) CMyDocsFolder::Release()
  193. {
  194. if (InterlockedDecrement(&_cRef))
  195. return _cRef;
  196. delete this;
  197. return 0;
  198. }
  199. // Determine who is calling us so that we can do app specific
  200. // compatibility hacks when needed
  201. CALLING_APP_TYPE CMyDocsFolder::_WhoIsCalling()
  202. {
  203. // Check to see if we have the value already...
  204. if (_host == APP_IS_UNKNOWN)
  205. {
  206. if (SHGetAppCompatFlags (ACF_APPISOFFICE) & ACF_APPISOFFICE)
  207. _host = APP_IS_OFFICE;
  208. else
  209. _host = APP_IS_NORMAL;
  210. }
  211. return _host;
  212. }
  213. // IPersist methods
  214. STDMETHODIMP CMyDocsFolder::GetClassID(CLSID *pClassID)
  215. {
  216. *pClassID = CLSID_MyDocuments;
  217. return S_OK;
  218. }
  219. HRESULT _BindToIDListParent(LPCITEMIDLIST pidl, LPBC pbc, IShellFolder **ppsf, LPITEMIDLIST *ppidlLast)
  220. {
  221. HRESULT hr;
  222. LPITEMIDLIST pidlParent = ILCloneParent(pidl);
  223. if (pidlParent)
  224. {
  225. hr = SHBindToObjectEx(NULL, pidlParent, pbc, IID_PPV_ARG(IShellFolder, ppsf));
  226. ILFree(pidlParent);
  227. }
  228. else
  229. hr = E_OUTOFMEMORY;
  230. if (ppidlLast)
  231. *ppidlLast = ILFindLastID(pidl);
  232. return hr;
  233. }
  234. HRESULT _ConfirmMyDocsPath(HWND hwnd)
  235. {
  236. TCHAR szPath[MAX_PATH];
  237. HRESULT hr = SHGetFolderPath(hwnd, CSIDL_PERSONAL | CSIDL_FLAG_CREATE, NULL, SHGFP_TYPE_CURRENT, szPath);
  238. if (S_OK != hr)
  239. {
  240. TCHAR szTitle[MAX_PATH];
  241. // above failed, get unverified path
  242. SHGetFolderPath(NULL, CSIDL_PERSONAL | CSIDL_FLAG_DONT_VERIFY, NULL, SHGFP_TYPE_CURRENT, szPath);
  243. LPCTSTR pszMsg = PathIsNetworkPath(szPath) ? MAKEINTRESOURCE(IDS_CANT_FIND_MYDOCS_NET) :
  244. MAKEINTRESOURCE(IDS_CANT_FIND_MYDOCS);
  245. PathCompactPath(NULL, szPath, 400);
  246. GetMyDocumentsDisplayName(szTitle, ARRAYSIZE(szTitle));
  247. ShellMessageBox(g_hinst, hwnd, pszMsg, szTitle,
  248. MB_OK | MB_ICONSTOP, szPath, szTitle);
  249. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); // user saw the message
  250. }
  251. else if (hr == S_FALSE)
  252. hr = E_FAIL;
  253. return hr;
  254. }
  255. // like SHGetPathFromIDList() except this uses the bind context to make sure
  256. // we don't get into loops since there can be cases where there are multiple
  257. // instances of this folder that can cause binding loops.
  258. HRESULT CMyDocsFolder::_PathFromIDList(LPCITEMIDLIST pidl, LPTSTR pszPath)
  259. {
  260. *pszPath = 0;
  261. LPBC pbc;
  262. HRESULT hr = CreateBindCtx(NULL, &pbc);
  263. if (SUCCEEDED(hr))
  264. {
  265. // this bind context skips extension taged with our CLSID
  266. hr = pbc->RegisterObjectParam(STR_SKIP_BINDING_CLSID, SAFECAST(this, IShellFolder *));
  267. if (SUCCEEDED(hr))
  268. {
  269. LPITEMIDLIST pidlLast;
  270. IShellFolder *psf;
  271. hr = _BindToIDListParent(pidl, pbc, &psf, &pidlLast);
  272. if (SUCCEEDED(hr))
  273. {
  274. hr = DisplayNameOf(psf, pidlLast, SHGDN_FORPARSING, pszPath, MAX_PATH);
  275. psf->Release();
  276. }
  277. }
  278. pbc->Release();
  279. }
  280. return hr;
  281. }
  282. HRESULT CMyDocsFolder::_PathToIDList(LPCTSTR pszPath, LPITEMIDLIST *ppidl)
  283. {
  284. IShellFolder *psfDesktop;
  285. HRESULT hr = SHGetDesktopFolder(&psfDesktop);
  286. if (SUCCEEDED(hr))
  287. {
  288. LPBC pbc;
  289. hr = CreateBindCtx( 0, &pbc );
  290. if (SUCCEEDED(hr))
  291. {
  292. BIND_OPTS bo = {sizeof(bo), 0};
  293. bo.grfFlags = BIND_JUSTTESTEXISTENCE; // skip all junctions
  294. hr = pbc->SetBindOptions( &bo );
  295. if (SUCCEEDED(hr))
  296. {
  297. WCHAR szPath[MAX_PATH];
  298. SHTCharToUnicode(pszPath, szPath, ARRAYSIZE(szPath));
  299. hr = psfDesktop->ParseDisplayName(NULL, pbc, szPath, NULL, ppidl, NULL);
  300. }
  301. pbc->Release();
  302. }
  303. psfDesktop->Release();
  304. }
  305. return hr;
  306. }
  307. void CMyDocsFolder::_FreeFolder()
  308. {
  309. if (_punk)
  310. {
  311. SHReleaseInnerInterface(SAFECAST(this, IShellFolder *), (IUnknown **)&_psf);
  312. SHReleaseInnerInterface(SAFECAST(this, IShellFolder *), (IUnknown **)&_psf2);
  313. SHReleaseInnerInterface(SAFECAST(this, IShellFolder *), (IUnknown **)&_psio);
  314. _punk->Release();
  315. _punk = NULL;
  316. }
  317. }
  318. // verify that _psf (agregated file system folder) has been inited
  319. HRESULT CMyDocsFolder::_GetFolder()
  320. {
  321. HRESULT hr;
  322. if (_psf)
  323. {
  324. hr = S_OK;
  325. }
  326. else
  327. {
  328. hr = SHQueryInnerInterface(SAFECAST(this, IShellFolder *), _punk, IID_PPV_ARG(IShellFolder, &_psf));
  329. }
  330. return hr;
  331. }
  332. HRESULT CMyDocsFolder::_GetFolder2()
  333. {
  334. HRESULT hr;
  335. if (_psf2)
  336. hr = S_OK;
  337. else
  338. {
  339. hr = _GetFolder();
  340. if (SUCCEEDED(hr))
  341. hr = SHQueryInnerInterface(SAFECAST(this, IShellFolder *), _punk, IID_PPV_ARG(IShellFolder2, &_psf2));
  342. }
  343. return hr;
  344. }
  345. HRESULT CMyDocsFolder::_GetShellIconOverlay()
  346. {
  347. HRESULT hr;
  348. if (_psio)
  349. {
  350. hr = S_OK;
  351. }
  352. else
  353. {
  354. hr = _GetFolder();
  355. if (SUCCEEDED(hr))
  356. {
  357. hr = SHQueryInnerInterface(SAFECAST(this, IShellFolder *), _punk, IID_PPV_ARG(IShellIconOverlay, &_psio));
  358. }
  359. }
  360. return hr;
  361. }
  362. // returns:
  363. // S_OK -- goodness
  364. // S_FALSE freed the pidl, set to empty
  365. // E_OUTOFMEMORY
  366. HRESULT _SetIDList(LPITEMIDLIST* ppidl, LPCITEMIDLIST pidl)
  367. {
  368. if (*ppidl)
  369. {
  370. ILFree(*ppidl);
  371. *ppidl = NULL;
  372. }
  373. return pidl ? SHILClone(pidl, ppidl) : S_FALSE;
  374. }
  375. BOOL IsMyDocsIDList(LPCITEMIDLIST pidl)
  376. {
  377. BOOL bIsMyDocs = FALSE;
  378. if (pidl && !ILIsEmpty(pidl) && ILIsEmpty(_ILNext(pidl)))
  379. {
  380. LPITEMIDLIST pidlMyDocs;
  381. if (SUCCEEDED(SHGetFolderLocation(NULL, CSIDL_PERSONAL, NULL, 0, &pidlMyDocs)))
  382. {
  383. bIsMyDocs = ILIsEqual(pidl, pidlMyDocs);
  384. ILFree(pidlMyDocs);
  385. }
  386. }
  387. return bIsMyDocs;
  388. }
  389. // Scans a desktop.ini file for sections to see if all of them are empty...
  390. BOOL IsDesktopIniEmpty(LPCTSTR pIniFile)
  391. {
  392. TCHAR szSections[1024]; // for section names
  393. if (GetPrivateProfileSectionNames(szSections, ARRAYSIZE(szSections), pIniFile))
  394. {
  395. for (LPTSTR pTmp = szSections; *pTmp; pTmp += lstrlen(pTmp) + 1)
  396. {
  397. TCHAR szSection[1024]; // for section key names and values
  398. GetPrivateProfileSection(pTmp, szSection, ARRAYSIZE(szSection), pIniFile);
  399. if (szSection[0])
  400. {
  401. return FALSE;
  402. }
  403. }
  404. }
  405. return TRUE;
  406. }
  407. // Remove our entries from the desktop.ini file in this directory, and
  408. // then test the desktop.ini to see if it's empty. If it is, delete it
  409. // and remove the system/readonly bit from the directory...
  410. void MyDocsUnmakeSystemFolder(LPCTSTR pPath)
  411. {
  412. TCHAR szIniFile[MAX_PATH];
  413. PathCombine(szIniFile, pPath, c_szDesktopIni);
  414. // Remove CLSID2
  415. WritePrivateProfileString(TEXT(".ShellClassInfo"), TEXT("CLSID2"), NULL, szIniFile);
  416. // Remove InfoTip
  417. WritePrivateProfileString(TEXT(".ShellClassInfo"), TEXT("InfoTip"), NULL, szIniFile);
  418. // Remove Icon
  419. WritePrivateProfileString(TEXT(".ShellClassInfo"), TEXT("IconFile"), NULL, szIniFile);
  420. DWORD dwAttrb = GetFileAttributes(szIniFile);
  421. if (dwAttrb != 0xFFFFFFFF)
  422. {
  423. if (IsDesktopIniEmpty(szIniFile))
  424. {
  425. dwAttrb &= ~(FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN);
  426. SetFileAttributes(szIniFile, dwAttrb);
  427. DeleteFile(szIniFile);
  428. }
  429. PathUnmakeSystemFolder(pPath);
  430. }
  431. }
  432. // IPersistFolder
  433. HRESULT CMyDocsFolder::Initialize(LPCITEMIDLIST pidl)
  434. {
  435. HRESULT hr;
  436. if (IsMyDocsIDList(pidl))
  437. {
  438. hr = _SetIDList(&_pidl, pidl);
  439. }
  440. else
  441. {
  442. TCHAR szPathInit[MAX_PATH], szMyDocs[MAX_PATH];
  443. // we are being inited by some folder other than the one on the
  444. // desktop (from the old mydocs desktop.ini). if this the current users
  445. // MyDocs we will untag it now so we don't get called on this anymore
  446. SHGetFolderPath(NULL, CSIDL_PERSONAL | CSIDL_FLAG_DONT_VERIFY, NULL, SHGFP_TYPE_CURRENT, szMyDocs);
  447. if (SUCCEEDED(_PathFromIDList(pidl, szPathInit)) &&
  448. lstrcmpi(szPathInit, szMyDocs) == 0)
  449. {
  450. MyDocsUnmakeSystemFolder(szMyDocs);
  451. }
  452. hr = E_FAIL; // don't init on the file system folder anymore
  453. }
  454. return hr;
  455. }
  456. STDMETHODIMP CMyDocsFolder::ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pDisplayName,
  457. ULONG* pchEaten, LPITEMIDLIST* ppidl, ULONG *pdwAttributes)
  458. {
  459. HRESULT hr = _GetFolder();
  460. if (SUCCEEDED(hr))
  461. hr = _psf->ParseDisplayName(hwnd, pbc, pDisplayName, pchEaten, ppidl, pdwAttributes);
  462. return hr;
  463. }
  464. STDMETHODIMP CMyDocsFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppEnumIdList)
  465. {
  466. HRESULT hr = _GetFolder();
  467. if (SUCCEEDED(hr))
  468. hr = _psf->EnumObjects(hwnd, grfFlags, ppEnumIdList);
  469. return hr;
  470. }
  471. STDMETHODIMP CMyDocsFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
  472. {
  473. HRESULT hr = _GetFolder();
  474. if (SUCCEEDED(hr))
  475. hr = _psf->BindToObject(pidl, pbc, riid, ppv);
  476. return hr;
  477. }
  478. STDMETHODIMP CMyDocsFolder::BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
  479. {
  480. HRESULT hr = _GetFolder();
  481. if (SUCCEEDED(hr))
  482. hr = _psf->BindToStorage(pidl, pbc, riid, ppv);
  483. return hr;
  484. }
  485. STDMETHODIMP CMyDocsFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  486. {
  487. HRESULT hr = _GetFolder();
  488. if (SUCCEEDED(hr))
  489. hr = _psf->CompareIDs(lParam, pidl1, pidl2);
  490. return hr;
  491. }
  492. /*
  493. void UpdateSendToFile()
  494. {
  495. IPersistFile *ppf;
  496. if (SUCCEEDED(SHCoCreateInstance(NULL, &CLSID_MyDocsDropTarget, NULL, IID_PPV_ARG(IPersistFile, &ppf))))
  497. {
  498. ppf->Load(NULL, 0); // hack, get this guy to update his icon
  499. ppf->Release();
  500. }
  501. }
  502. */
  503. STDMETHODIMP CMyDocsFolder::CreateViewObject(HWND hwnd, REFIID riid, void **ppv)
  504. {
  505. *ppv = NULL;
  506. HRESULT hr;
  507. if (riid == IID_IResolveShellLink)
  508. {
  509. // No work needed to resolve a link to the mydocs folder, because it is a virtual
  510. // folder whose location is always tracked by the shell, so return our implementation
  511. // of IResolveShellLink - which does nothing when Resolve() is called
  512. CMyDocsFolderLinkResolver* pslr = new CMyDocsFolderLinkResolver;
  513. if (pslr)
  514. {
  515. hr = pslr->QueryInterface(riid, ppv);
  516. pslr->Release();
  517. }
  518. else
  519. {
  520. hr = E_OUTOFMEMORY;
  521. }
  522. }
  523. else if (riid == IID_IShellLinkA ||
  524. riid == IID_IShellLinkW)
  525. {
  526. LPITEMIDLIST pidl;
  527. hr = SHGetFolderLocation(NULL, CSIDL_PERSONAL | CSIDL_FLAG_NO_ALIAS, NULL, 0, &pidl);
  528. if (SUCCEEDED(hr))
  529. {
  530. IShellLink *psl;
  531. hr = SHCoCreateInstance(NULL, &CLSID_ShellLink, NULL, IID_PPV_ARG(IShellLink, &psl));
  532. if (SUCCEEDED(hr))
  533. {
  534. hr = psl->SetIDList(pidl);
  535. if (SUCCEEDED(hr))
  536. {
  537. hr = psl->QueryInterface(riid, ppv);
  538. }
  539. psl->Release();
  540. }
  541. ILFree(pidl);
  542. }
  543. }
  544. else
  545. {
  546. hr = _GetFolder();
  547. if (SUCCEEDED(hr))
  548. {
  549. if (hwnd && (IID_IShellView == riid))
  550. hr = _ConfirmMyDocsPath(hwnd);
  551. if (SUCCEEDED(hr))
  552. hr = _psf->CreateViewObject(hwnd, riid, ppv);
  553. }
  554. }
  555. return hr;
  556. }
  557. DWORD _GetRealMyDocsAttributes(DWORD dwAttributes)
  558. {
  559. DWORD dwRet = SFGAO_HASPROPSHEET; // default to this in the falure case
  560. // so you can redirect mydocs via the property page
  561. LPITEMIDLIST pidl;
  562. HRESULT hr = SHGetFolderLocation(NULL, CSIDL_PERSONAL | CSIDL_FLAG_NO_ALIAS, NULL, 0, &pidl);
  563. if (SUCCEEDED(hr))
  564. {
  565. IShellFolder *psf;
  566. LPITEMIDLIST pidlLast;
  567. hr = _BindToIDListParent(pidl, NULL, &psf, &pidlLast);
  568. if (SUCCEEDED(hr))
  569. {
  570. dwRet = SHGetAttributes(psf, pidlLast, dwAttributes);
  571. psf->Release();
  572. }
  573. ILFree(pidl);
  574. }
  575. return dwRet;
  576. }
  577. #define MYDOCS_CLSID TEXT("{450d8fba-ad25-11d0-98a8-0800361b1103}") // CLSID_MyDocuments
  578. DWORD MyDocsGetAttributes()
  579. {
  580. DWORD dwAttributes = SFGAO_CANLINK | // 00000004
  581. SFGAO_CANRENAME | // 00000010
  582. SFGAO_CANDELETE | // 00000020
  583. SFGAO_HASPROPSHEET | // 00000040
  584. SFGAO_DROPTARGET | // 00000100
  585. SFGAO_FILESYSANCESTOR | // 10000000
  586. SFGAO_FOLDER | // 20000000
  587. SFGAO_FILESYSTEM | // 40000000
  588. SFGAO_HASSUBFOLDER | // 80000000
  589. SFGAO_STORAGEANCESTOR |
  590. SFGAO_STORAGE;
  591. // SFGAO_NONENUMERATED // 00100000
  592. // // F0400174
  593. HKEY hkey;
  594. if (ERROR_SUCCESS == RegOpenKey(HKEY_CLASSES_ROOT, TEXT("CLSID\\") MYDOCS_CLSID TEXT("\\ShellFolder"), &hkey))
  595. {
  596. DWORD dwSize = sizeof(dwAttributes);
  597. RegQueryValueEx(hkey, TEXT("Attributes"), NULL, NULL, (BYTE *)&dwAttributes, &dwSize);
  598. RegCloseKey(hkey);
  599. }
  600. return dwAttributes;
  601. }
  602. // these are the attributes from the real mydocs folder that we want to merge
  603. // in with the desktop icons attributes
  604. #define SFGAO_ATTRIBS_MERGE (SFGAO_SHARE | SFGAO_HASPROPSHEET)
  605. STDMETHODIMP CMyDocsFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST* apidl, ULONG* rgfInOut)
  606. {
  607. HRESULT hr;
  608. if (IsSelf(cidl, apidl))
  609. {
  610. DWORD dwRequested = *rgfInOut;
  611. *rgfInOut = MyDocsGetAttributes();
  612. if (dwRequested & SFGAO_ATTRIBS_MERGE)
  613. *rgfInOut |= _GetRealMyDocsAttributes(SFGAO_ATTRIBS_MERGE);
  614. // RegItem "CallForAttributes" gets us here...
  615. switch(_WhoIsCalling())
  616. {
  617. case APP_IS_OFFICE:
  618. *rgfInOut &= ~(SFGAO_FILESYSANCESTOR | SFGAO_CANMONIKER |
  619. SFGAO_HASPROPSHEET | SFGAO_NONENUMERATED);
  620. break;
  621. }
  622. if (SHRestricted(REST_MYDOCSNOPROP))
  623. {
  624. (*rgfInOut) &= ~SFGAO_HASPROPSHEET;
  625. }
  626. hr = S_OK;
  627. }
  628. else
  629. {
  630. hr = _GetFolder();
  631. if (SUCCEEDED(hr))
  632. hr = _psf->GetAttributesOf(cidl, apidl, rgfInOut);
  633. }
  634. return hr;
  635. }
  636. STDMETHODIMP CMyDocsFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST *aidl,
  637. REFIID riid, UINT *pRes, void **ppv)
  638. {
  639. HRESULT hr = _GetFolder();
  640. if (SUCCEEDED(hr))
  641. hr = _psf->GetUIObjectOf(hwnd, cidl, aidl, riid, pRes, ppv);
  642. return hr;
  643. }
  644. STDMETHODIMP CMyDocsFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, STRRET *pName)
  645. {
  646. HRESULT hr;
  647. if (IsSelf(1, &pidl))
  648. {
  649. TCHAR szMyDocsPath[MAX_PATH];
  650. hr = SHGetFolderPath(NULL, CSIDL_PERSONAL | CSIDL_FLAG_DONT_VERIFY, NULL, SHGFP_TYPE_CURRENT, szMyDocsPath);
  651. if (SUCCEEDED(hr))
  652. {
  653. // RegItems "WantsFORPARSING" gets us here. allows us to control our parsing name
  654. LPTSTR psz = ((uFlags & SHGDN_INFOLDER) ? PathFindFileName(szMyDocsPath) : szMyDocsPath);
  655. hr = StringToStrRet(psz, pName);
  656. }
  657. }
  658. else
  659. {
  660. hr = _GetFolder();
  661. if (SUCCEEDED(hr))
  662. hr = _psf->GetDisplayNameOf(pidl, uFlags, pName);
  663. }
  664. return hr;
  665. }
  666. STDMETHODIMP CMyDocsFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pName, DWORD uFlags, LPITEMIDLIST *ppidlOut)
  667. {
  668. HRESULT hr = _GetFolder();
  669. if (SUCCEEDED(hr))
  670. hr = _psf->SetNameOf(hwnd, pidl, pName, uFlags, ppidlOut);
  671. return hr;
  672. }
  673. STDMETHODIMP CMyDocsFolder::GetDefaultSearchGUID(LPGUID lpGuid)
  674. {
  675. HRESULT hr = _GetFolder2();
  676. if (SUCCEEDED(hr))
  677. hr = _psf2->GetDefaultSearchGUID(lpGuid);
  678. return hr;
  679. }
  680. STDMETHODIMP CMyDocsFolder::EnumSearches(LPENUMEXTRASEARCH *ppenum)
  681. {
  682. HRESULT hr = _GetFolder2();
  683. if (SUCCEEDED(hr))
  684. hr = _psf2->EnumSearches(ppenum);
  685. return hr;
  686. }
  687. STDMETHODIMP CMyDocsFolder::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
  688. {
  689. HRESULT hr = _GetFolder2();
  690. if (SUCCEEDED(hr))
  691. hr = _psf2->GetDefaultColumn(dwRes, pSort, pDisplay);
  692. return hr;
  693. }
  694. STDMETHODIMP CMyDocsFolder::GetDefaultColumnState(UINT iColumn, DWORD *pbState)
  695. {
  696. HRESULT hr = _GetFolder2();
  697. if (SUCCEEDED(hr))
  698. hr = _psf2->GetDefaultColumnState(iColumn, pbState);
  699. return hr;
  700. }
  701. STDMETHODIMP CMyDocsFolder::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
  702. {
  703. HRESULT hr = _GetFolder2();
  704. if (SUCCEEDED(hr))
  705. hr = _psf2->GetDetailsEx(pidl, pscid, pv);
  706. return hr;
  707. }
  708. STDMETHODIMP CMyDocsFolder::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, LPSHELLDETAILS pDetail)
  709. {
  710. HRESULT hr = _GetFolder2();
  711. if (SUCCEEDED(hr))
  712. hr = _psf2->GetDetailsOf(pidl, iColumn, pDetail);
  713. return hr;
  714. }
  715. STDMETHODIMP CMyDocsFolder::MapColumnToSCID(UINT iCol, SHCOLUMNID *pscid)
  716. {
  717. HRESULT hr = _GetFolder2();
  718. if (SUCCEEDED(hr))
  719. hr = _psf2->MapColumnToSCID(iCol, pscid);
  720. return hr;
  721. }
  722. HRESULT CMyDocsFolder::_GetFolderOverlayInfo(int *pIndex, BOOL fIconIndex)
  723. {
  724. HRESULT hr;
  725. if (pIndex)
  726. {
  727. LPITEMIDLIST pidl;
  728. hr = SHGetFolderLocation(NULL, CSIDL_PERSONAL | CSIDL_FLAG_NO_ALIAS, NULL, 0, &pidl);
  729. if (SUCCEEDED(hr))
  730. {
  731. IShellFolder *psf;
  732. LPITEMIDLIST pidlLast;
  733. hr = _BindToIDListParent(pidl, NULL, &psf, &pidlLast);
  734. if (SUCCEEDED(hr))
  735. {
  736. IShellIconOverlay* psio;
  737. hr = psf->QueryInterface(IID_PPV_ARG(IShellIconOverlay, &psio));
  738. if (SUCCEEDED(hr))
  739. {
  740. if (fIconIndex)
  741. hr = psio->GetOverlayIconIndex(pidlLast, pIndex);
  742. else
  743. hr = psio->GetOverlayIndex(pidlLast, pIndex);
  744. psio->Release();
  745. }
  746. psf->Release();
  747. }
  748. ILFree(pidl);
  749. }
  750. }
  751. else
  752. {
  753. hr = E_INVALIDARG;
  754. }
  755. return hr;
  756. }
  757. STDMETHODIMP CMyDocsFolder::GetOverlayIndex(LPCITEMIDLIST pidl, int *pIndex)
  758. {
  759. HRESULT hr = E_FAIL;
  760. if (IsSelf(1, &pidl))
  761. {
  762. if (pIndex && *pIndex == OI_ASYNC)
  763. hr = E_PENDING;
  764. else
  765. hr = _GetFolderOverlayInfo(pIndex, FALSE);
  766. }
  767. else
  768. {
  769. // forward to aggregated dude
  770. if (SUCCEEDED(_GetShellIconOverlay()))
  771. {
  772. hr = _psio->GetOverlayIndex(pidl, pIndex);
  773. }
  774. }
  775. return hr;
  776. }
  777. STDMETHODIMP CMyDocsFolder::GetOverlayIconIndex(LPCITEMIDLIST pidl, int *pIconIndex)
  778. {
  779. HRESULT hr = E_FAIL;
  780. if (IsSelf(1, &pidl))
  781. {
  782. hr = _GetFolderOverlayInfo(pIconIndex, TRUE);
  783. }
  784. else if (SUCCEEDED(_GetShellIconOverlay()))
  785. {
  786. // forward to aggregated dude
  787. hr = _psio->GetOverlayIconIndex(pidl, pIconIndex);
  788. }
  789. return hr;
  790. }