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.

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