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.

1487 lines
44 KiB

  1. // Implements Folder Shortcut.
  2. #include "shellprv.h"
  3. #include "clsobj.h"
  4. // implemented in filefldr.cpp
  5. extern LPTSTR PathFindCLSIDExtension(LPCTSTR pszFile, CLSID *pclsid);
  6. BOOL CreateFolderDesktopIni(LPCTSTR pszName)
  7. {
  8. SHFOLDERCUSTOMSETTINGS fcs = {0};
  9. fcs.dwSize = sizeof(fcs);
  10. fcs.dwMask = FCSM_CLSID | FCSM_FLAGS;
  11. fcs.pclsid = (GUID*)&CLSID_FolderShortcut;
  12. fcs.dwFlags = FCS_FLAG_DRAGDROP;
  13. return SUCCEEDED(SHGetSetFolderCustomSettings(&fcs, pszName, FCS_FORCEWRITE));
  14. }
  15. EXTERN_C BOOL IsFolderShortcut(LPCTSTR pszName)
  16. {
  17. SHFOLDERCUSTOMSETTINGS fcs = {0};
  18. CLSID clsid = {0};
  19. fcs.dwSize = sizeof(fcs);
  20. fcs.dwMask = FCSM_CLSID;
  21. fcs.pclsid = &clsid;
  22. if (SUCCEEDED(SHGetSetFolderCustomSettings(&fcs, pszName, FCS_READ)))
  23. {
  24. return IsEqualGUID(clsid, CLSID_FolderShortcut);
  25. }
  26. return FALSE;
  27. }
  28. // exported from fsnotify.c
  29. STDAPI_(void) SHChangeNotifyRegisterAlias(LPCITEMIDLIST pidlReal, LPCITEMIDLIST pidlAlias);
  30. class CFolderShortcut : public IShellFolder2,
  31. public IPersistFolder3,
  32. public IShellLinkA,
  33. public IShellLinkW,
  34. public IPersistFile,
  35. public IExtractIcon,
  36. public IQueryInfo,
  37. public IFolderShortcutConvert,
  38. public IPersistStreamInit,
  39. public IPersistPropertyBag,
  40. public IBrowserFrameOptions
  41. {
  42. public:
  43. // IUnknown
  44. STDMETHODIMP QueryInterface(REFIID, void **);
  45. STDMETHODIMP_(ULONG) AddRef(void);
  46. STDMETHODIMP_(ULONG) Release(void);
  47. // IShellFolder
  48. STDMETHODIMP ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pszDisplayName,
  49. ULONG *pchEaten, LPITEMIDLIST *ppidl, ULONG *pdwAttributes);
  50. STDMETHODIMP EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList ** ppenumIDList);
  51. STDMETHODIMP BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv);
  52. STDMETHODIMP BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv);
  53. STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
  54. STDMETHODIMP CreateViewObject (HWND hwnd, REFIID riid, void **ppv);
  55. STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST * apidl, ULONG *rgfInOut);
  56. STDMETHODIMP GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST * apidl,
  57. REFIID riid, UINT * prgfInOut, void **ppv);
  58. STDMETHODIMP GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, LPSTRRET lpName);
  59. STDMETHODIMP SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, DWORD uFlags,
  60. LPITEMIDLIST *ppidlOut);
  61. // IShellFolder2
  62. STDMETHODIMP GetDefaultSearchGUID(GUID *pGuid);
  63. STDMETHODIMP EnumSearches(IEnumExtraSearch **ppenum);
  64. STDMETHODIMP GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay);
  65. STDMETHODIMP GetDefaultColumnState(UINT iColumn, DWORD *pbState);
  66. STDMETHODIMP GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv);
  67. STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails);
  68. STDMETHODIMP MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid);
  69. // IPersist
  70. STDMETHODIMP GetClassID(CLSID *pClassID);
  71. // IPersistFolder
  72. STDMETHODIMP Initialize(LPCITEMIDLIST pidl);
  73. // IPersistFolder2
  74. STDMETHODIMP GetCurFolder(LPITEMIDLIST *ppidl);
  75. // IPersistFolder3
  76. STDMETHODIMP InitializeEx(IBindCtx *pbc, LPCITEMIDLIST pidlRoot, const PERSIST_FOLDER_TARGET_INFO *pfti);
  77. STDMETHODIMP GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO *pfti);
  78. // IPersistStream
  79. STDMETHODIMP Load(IStream *pStm);
  80. STDMETHODIMP Save(IStream *pStm,int fClearDirty);
  81. STDMETHODIMP GetSizeMax(ULARGE_INTEGER * pcbSize);
  82. // IPersistPropertyBag
  83. STDMETHODIMP Save(IPropertyBag* pPropBag, BOOL fClearDirty, BOOL fSaveAllProperties);
  84. STDMETHODIMP Load(IPropertyBag* pPropBag, IErrorLog* pErrorLog);
  85. // IPersistPropertyBag/IPersistStreamInit
  86. STDMETHODIMP InitNew(void);
  87. // IPersistFile
  88. STDMETHODIMP Load(LPCOLESTR pszFileName, DWORD dwMode);
  89. STDMETHODIMP Save(LPCOLESTR pszFileName, BOOL fRemember);
  90. STDMETHODIMP IsDirty() { return E_NOTIMPL; };
  91. STDMETHODIMP SaveCompleted(LPCOLESTR pszFileName) { return E_NOTIMPL; };
  92. STDMETHODIMP GetCurFile(LPOLESTR *ppszFileName);
  93. // IShellLinkW
  94. STDMETHODIMP GetPath(LPWSTR pszFile, int cchMaxPath, WIN32_FIND_DATAW *pfd, DWORD flags);
  95. STDMETHODIMP SetPath(LPCWSTR pszFile);
  96. STDMETHODIMP GetIDList(LPITEMIDLIST *ppidl);
  97. STDMETHODIMP SetIDList(LPCITEMIDLIST pidl);
  98. STDMETHODIMP GetDescription(LPWSTR pszName, int cchMaxName);
  99. STDMETHODIMP SetDescription(LPCWSTR pszName);
  100. STDMETHODIMP GetWorkingDirectory(LPWSTR pszDir, int cchMaxPath);
  101. STDMETHODIMP SetWorkingDirectory(LPCWSTR pszDir);
  102. STDMETHODIMP GetArguments(LPWSTR pszArgs, int cchMaxPath);
  103. STDMETHODIMP SetArguments(LPCWSTR pszArgs);
  104. STDMETHODIMP GetHotkey(WORD *pwHotkey);
  105. STDMETHODIMP SetHotkey(WORD wHotkey);
  106. STDMETHODIMP GetShowCmd(int *piShowCmd);
  107. STDMETHODIMP SetShowCmd(int iShowCmd);
  108. STDMETHODIMP GetIconLocation(LPWSTR pszIconPath, int cchIconPath, int *piIcon);
  109. STDMETHODIMP SetIconLocation(LPCWSTR pszIconPath, int iIcon);
  110. STDMETHODIMP Resolve(HWND hwnd, DWORD fFlags);
  111. STDMETHODIMP SetRelativePath(LPCWSTR pszPathRel, DWORD dwReserved);
  112. // IShellLinkA
  113. STDMETHODIMP GetPath(LPSTR pszFile, int cchMaxPath, WIN32_FIND_DATAA *pfd, DWORD flags);
  114. STDMETHODIMP SetPath(LPCSTR pszFile);
  115. STDMETHODIMP GetDescription(LPSTR pszName, int cchMaxName);
  116. STDMETHODIMP SetDescription(LPCSTR pszName);
  117. STDMETHODIMP GetWorkingDirectory(LPSTR pszDir, int cchMaxPath);
  118. STDMETHODIMP SetWorkingDirectory(LPCSTR pszDir);
  119. STDMETHODIMP GetArguments(LPSTR pszArgs, int cchMaxPath);
  120. STDMETHODIMP SetArguments(LPCSTR pszArgs);
  121. STDMETHODIMP GetIconLocation(LPSTR pszIconPath, int cchIconPath, int *piIcon);
  122. STDMETHODIMP SetIconLocation(LPCSTR pszIconPath, int iIcon);
  123. STDMETHODIMP SetRelativePath(LPCSTR pszPathRel, DWORD dwReserved);
  124. // IFolderShortcutConvert
  125. STDMETHODIMP ConvertToLink(LPCOLESTR pszPathLNK, DWORD fFlags);
  126. STDMETHODIMP ConvertToFolderShortcut(LPCOLESTR pszPathLNK, DWORD fFlags);
  127. // IExtractIcon
  128. STDMETHODIMP GetIconLocation(UINT uFlags, LPTSTR pszIconFile, UINT ucchMax, INT *pniIcon, UINT *puFlags);
  129. STDMETHODIMP Extract(LPCTSTR pcszFile, UINT uIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT ucIconSize);
  130. // IQueryInfo
  131. STDMETHODIMP GetInfoTip(DWORD dwFlags, WCHAR** ppwszTip);
  132. STDMETHODIMP GetInfoFlags(DWORD *pdwFlags);
  133. // IBrowserFrameOptions
  134. STDMETHODIMP GetFrameOptions(IN BROWSERFRAMEOPTIONS dwMask, IN BROWSERFRAMEOPTIONS * pdwOptions);
  135. CFolderShortcut();
  136. protected:
  137. ~CFolderShortcut();
  138. void _ClearState();
  139. void _ClearTargetFolder();
  140. private:
  141. HRESULT _LoadShortcut();
  142. HRESULT _GetTargetIDList(BOOL fResolve);
  143. HRESULT _BindFolder(BOOL fResolve);
  144. HRESULT _GetFolder(BOOL fForceResolve);
  145. HRESULT _GetFolder2();
  146. HRESULT _GetLink();
  147. HRESULT _GetLinkA();
  148. HRESULT _GetLinkQI(REFIID riid, void **ppv);
  149. HRESULT _PreBindCtxHelper(IBindCtx **ppbc);
  150. LONG _cRef;
  151. LPITEMIDLIST _pidlRoot;
  152. LPITEMIDLIST _pidlTarget;
  153. LPITEMIDLIST _pidlTargetFldrFromInit;
  154. IShellFolder* _psfTarget;
  155. IShellFolder2* _psf2Target;
  156. IShellLinkW* _pslTarget;
  157. IShellLinkA* _pslTargetA;
  158. LPTSTR _pszLastSave;
  159. BOOL _fHaveResolved;
  160. DWORD _dwAttributesTarget;
  161. TCHAR _szFolderPath[MAX_PATH];
  162. };
  163. //constructor/destructor and related functions
  164. CFolderShortcut::CFolderShortcut() : _cRef(1), _dwAttributesTarget(FILE_ATTRIBUTE_DIRECTORY)
  165. {
  166. ASSERT(_pidlRoot == NULL);
  167. ASSERT(_pidlTarget == NULL);
  168. ASSERT(_psfTarget == NULL);
  169. ASSERT(_psf2Target == NULL);
  170. ASSERT(_szFolderPath[0] == 0);
  171. ASSERT(_pidlTargetFldrFromInit == NULL);
  172. DllAddRef();
  173. }
  174. CFolderShortcut::~CFolderShortcut()
  175. {
  176. _ClearState();
  177. DllRelease();
  178. }
  179. void CFolderShortcut::_ClearTargetFolder()
  180. {
  181. ATOMICRELEASE(_psfTarget);
  182. ATOMICRELEASE(_psf2Target);
  183. }
  184. void CFolderShortcut::_ClearState()
  185. {
  186. _fHaveResolved = FALSE;
  187. Pidl_Set(&_pidlRoot, NULL);
  188. Pidl_Set(&_pidlTarget, NULL);
  189. Pidl_Set(&_pidlTargetFldrFromInit, NULL);
  190. Str_SetPtr(&_pszLastSave, NULL);
  191. _ClearTargetFolder();
  192. ATOMICRELEASE(_pslTarget);
  193. ATOMICRELEASE(_pslTargetA);
  194. }
  195. STDAPI CFolderShortcut_CreateInstance(IUnknown* pUnkOuter, REFIID riid, void **ppv)
  196. {
  197. HRESULT hr = E_OUTOFMEMORY;
  198. *ppv = NULL;
  199. // aggregation checking is handled in class factory
  200. CFolderShortcut* pfolder = new CFolderShortcut();
  201. if (pfolder)
  202. {
  203. hr = pfolder->QueryInterface(riid, ppv);
  204. pfolder->Release();
  205. }
  206. return hr;
  207. }
  208. // ensure that _pslTarget has been created and loaded
  209. HRESULT CFolderShortcut::_LoadShortcut()
  210. {
  211. HRESULT hr;
  212. if (_pslTarget)
  213. {
  214. hr = S_OK;
  215. }
  216. else if (_szFolderPath[0])
  217. {
  218. TCHAR szPath[MAX_PATH];
  219. // leave this shortcut visible so down level clients see it and can
  220. // navigate through it.
  221. if (PathCombine(szPath, _szFolderPath, TEXT("target.lnk")))
  222. {
  223. hr = LoadFromFile(CLSID_ShellLink, szPath, IID_PPV_ARG(IShellLinkW, &_pslTarget));
  224. if (SUCCEEDED(hr))
  225. {
  226. LPITEMIDLIST pidlTarget;
  227. // Prevalidate to prevent recusion
  228. // If GetIDList fails, that's okay; I guess it doesn't point to us after all
  229. if (_pslTarget->GetIDList(&pidlTarget) == S_OK)
  230. {
  231. SHGetPathFromIDList(pidlTarget, szPath);
  232. // Does this point to itself?
  233. if (StrCmpI(szPath, _szFolderPath) == 0)
  234. {
  235. _pslTarget->Release();
  236. _pslTarget = NULL;
  237. hr = E_FAIL;
  238. }
  239. ILFree(pidlTarget);
  240. }
  241. }
  242. }
  243. else
  244. {
  245. hr = E_FAIL;
  246. }
  247. }
  248. else
  249. {
  250. hr = E_FAIL;
  251. }
  252. return hr;
  253. }
  254. // ensure that _pidlTarget is inited (requres _pslTarget)
  255. HRESULT CFolderShortcut::_GetTargetIDList(BOOL bResolve)
  256. {
  257. HRESULT hr = _LoadShortcut();
  258. if (SUCCEEDED(hr))
  259. {
  260. if (_pidlTarget)
  261. {
  262. hr = S_OK;
  263. }
  264. else
  265. {
  266. if (bResolve)
  267. _pslTarget->Resolve(NULL, SLR_UPDATE | SLR_NO_UI);
  268. hr = _pslTarget->GetIDList(&_pidlTarget);
  269. if (hr == S_FALSE)
  270. hr = E_FAIL; // convert empty to failure
  271. if (SUCCEEDED(hr))
  272. {
  273. // make sure we dont have another shortcut here
  274. IShellLink *psl;
  275. if (SUCCEEDED(SHBindToObject(NULL, IID_IShellLink, _pidlTarget, (void**)&psl)))
  276. {
  277. ILFree(_pidlTarget);
  278. hr = psl->GetIDList(&_pidlTarget);
  279. if (SUCCEEDED(hr))
  280. {
  281. hr = _pslTarget->SetIDList(_pidlTarget);
  282. }
  283. psl->Release();
  284. }
  285. }
  286. if (FAILED(hr) && _pidlTarget)
  287. {
  288. ILFree(_pidlTarget);
  289. _pidlTarget = NULL;
  290. }
  291. }
  292. }
  293. return hr;
  294. }
  295. // create _psfTarget (requires _pidlTarget)
  296. HRESULT CFolderShortcut::_BindFolder(BOOL bResolve)
  297. {
  298. ASSERT(_psfTarget == NULL);
  299. HRESULT hr = _GetTargetIDList(bResolve);
  300. if (SUCCEEDED(hr))
  301. {
  302. IBindCtx *pbc = NULL; // in/out param below
  303. hr = _PreBindCtxHelper(&pbc); // avoid loops in the name space
  304. if (SUCCEEDED(hr))
  305. {
  306. if (SUCCEEDED(hr))
  307. {
  308. IShellFolder *psfDesktop;
  309. hr = SHGetDesktopFolder(&psfDesktop);
  310. if (SUCCEEDED(hr))
  311. {
  312. // Are we trying to bind to the desktop folder?
  313. if (ILIsEmpty(_pidlTarget))
  314. {
  315. // Yes; Clone the desktop shell folder.
  316. _psfTarget = psfDesktop;
  317. _psfTarget->AddRef();
  318. hr = S_OK;
  319. }
  320. else
  321. {
  322. // No. Bind to it.
  323. hr = psfDesktop->BindToObject(_pidlTarget, pbc, IID_PPV_ARG(IShellFolder, &_psfTarget));
  324. }
  325. if (SUCCEEDED(hr))
  326. {
  327. // optionally re-target the folder (if he is a file system folder)
  328. // to separate the location in the name space (_pidlRoot)
  329. // and the folder being viewed (pfsfi.szFolderPath).
  330. IPersistFolder3 *ppf;
  331. if (SUCCEEDED(_psfTarget->QueryInterface(IID_PPV_ARG(IPersistFolder3, &ppf))))
  332. {
  333. PERSIST_FOLDER_TARGET_INFO pfti = { 0 };
  334. pfti.pidlTargetFolder = _pidlTarget;
  335. pfti.dwAttributes = _dwAttributesTarget;
  336. pfti.csidl = -1;
  337. hr = ppf->InitializeEx(pbc, _pidlRoot, &pfti);
  338. ppf->Release();
  339. }
  340. }
  341. psfDesktop->Release();
  342. }
  343. }
  344. pbc->Release();
  345. }
  346. }
  347. return hr;
  348. }
  349. // ensure that _psfTarget is inited
  350. HRESULT CFolderShortcut::_GetFolder(BOOL fForceResolve)
  351. {
  352. HRESULT hr;
  353. if (fForceResolve)
  354. {
  355. if (_fHaveResolved)
  356. {
  357. hr = _psfTarget ? S_OK : E_FAIL;
  358. }
  359. else
  360. {
  361. _fHaveResolved = TRUE; // don't do this again
  362. _ClearTargetFolder();
  363. Pidl_Set(&_pidlTarget, NULL);
  364. hr = _BindFolder(fForceResolve);
  365. }
  366. }
  367. else if (_psfTarget)
  368. {
  369. hr = S_OK;
  370. }
  371. else
  372. {
  373. hr = _BindFolder(fForceResolve);
  374. }
  375. return hr;
  376. }
  377. // ensure that _psf2Target is inited
  378. HRESULT CFolderShortcut::_GetFolder2()
  379. {
  380. if (_psf2Target)
  381. return S_OK;
  382. HRESULT hr = _GetFolder(FALSE);
  383. if (SUCCEEDED(hr))
  384. hr = _psfTarget->QueryInterface(IID_PPV_ARG(IShellFolder2, &_psf2Target));
  385. return hr;
  386. }
  387. STDMETHODIMP CFolderShortcut::QueryInterface(REFIID riid, void **ppvObj)
  388. {
  389. static const QITAB qit[] = {
  390. QITABENTMULTI(CFolderShortcut, IShellFolder, IShellFolder2),
  391. QITABENT(CFolderShortcut, IShellFolder2),
  392. QITABENTMULTI(CFolderShortcut, IPersist, IPersistFolder3),
  393. QITABENTMULTI(CFolderShortcut, IPersistFolder, IPersistFolder3),
  394. QITABENTMULTI(CFolderShortcut, IPersistFolder2, IPersistFolder3),
  395. QITABENT(CFolderShortcut, IPersistFolder3),
  396. QITABENT(CFolderShortcut, IPersistStreamInit),
  397. QITABENTMULTI(CFolderShortcut, IPersistStream, IPersistStreamInit),
  398. QITABENT(CFolderShortcut, IShellLinkA),
  399. QITABENT(CFolderShortcut, IShellLinkW),
  400. QITABENT(CFolderShortcut, IPersistFile),
  401. QITABENT(CFolderShortcut, IFolderShortcutConvert),
  402. QITABENT(CFolderShortcut, IExtractIcon),
  403. QITABENT(CFolderShortcut, IQueryInfo),
  404. QITABENT(CFolderShortcut, IPersistPropertyBag),
  405. QITABENT(CFolderShortcut, IBrowserFrameOptions),
  406. { 0 },
  407. };
  408. return QISearch(this, qit, riid, ppvObj);
  409. }
  410. STDMETHODIMP_(ULONG) CFolderShortcut::AddRef()
  411. {
  412. return InterlockedIncrement(&_cRef);
  413. }
  414. STDMETHODIMP_(ULONG) CFolderShortcut::Release()
  415. {
  416. ASSERT( 0 != _cRef );
  417. ULONG cRef = InterlockedDecrement(&_cRef);
  418. if ( 0 == cRef )
  419. {
  420. delete this;
  421. }
  422. return cRef;
  423. }
  424. // either create or init the passed bind ctx with the params to avoid loops in the name space
  425. HRESULT CFolderShortcut::_PreBindCtxHelper(IBindCtx **ppbc)
  426. {
  427. HRESULT hr;
  428. if (*ppbc)
  429. {
  430. (*ppbc)->AddRef();
  431. hr = S_OK;
  432. }
  433. else
  434. {
  435. hr = BindCtx_CreateWithMode(STGM_READ | STGM_SHARE_DENY_WRITE, ppbc);
  436. }
  437. if (SUCCEEDED(hr))
  438. (*ppbc)->RegisterObjectParam(STR_SKIP_BINDING_CLSID, SAFECAST(this, IShellFolder2 *));
  439. return hr;
  440. }
  441. // IShellFolder methods
  442. HRESULT CFolderShortcut::ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pwszDisplayName,
  443. ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG *pdwAttributes)
  444. {
  445. if (!ppidl)
  446. return E_INVALIDARG;
  447. *ppidl = NULL;
  448. if (!pwszDisplayName)
  449. return E_INVALIDARG;
  450. HRESULT hr = _GetFolder(FALSE);
  451. if (SUCCEEDED(hr))
  452. {
  453. hr = _PreBindCtxHelper(&pbc);
  454. if (SUCCEEDED(hr))
  455. {
  456. hr = _psfTarget->ParseDisplayName(hwnd, pbc, pwszDisplayName,
  457. pchEaten, ppidl, pdwAttributes);
  458. pbc->Release();
  459. }
  460. }
  461. return hr;
  462. }
  463. HRESULT CFolderShortcut::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList)
  464. {
  465. HRESULT hr = _GetFolder(TRUE);
  466. if (SUCCEEDED(hr))
  467. hr = _psfTarget->EnumObjects(hwnd, grfFlags, ppenumIDList);
  468. if (SUCCEEDED(hr))
  469. SHChangeNotifyRegisterAlias(_pidlTarget, _pidlRoot);
  470. return hr;
  471. }
  472. HRESULT CFolderShortcut::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
  473. {
  474. HRESULT hr = _GetFolder(TRUE);
  475. if (SUCCEEDED(hr))
  476. {
  477. hr = _PreBindCtxHelper(&pbc);
  478. if (SUCCEEDED(hr))
  479. {
  480. hr = _psfTarget->BindToObject(pidl, pbc, riid, ppv);
  481. pbc->Release();
  482. if (SUCCEEDED(hr))
  483. SHChangeNotifyRegisterAlias(_pidlTarget, _pidlRoot);
  484. }
  485. }
  486. return hr;
  487. }
  488. HRESULT CFolderShortcut::BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
  489. {
  490. return BindToObject(pidl, pbc, riid, ppv);
  491. }
  492. HRESULT CFolderShortcut::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  493. {
  494. HRESULT hr = _GetFolder(FALSE);
  495. if (SUCCEEDED(hr))
  496. hr = _psfTarget->CompareIDs(lParam, pidl1, pidl2);
  497. return hr;
  498. }
  499. HRESULT CFolderShortcut::CreateViewObject(HWND hwnd, REFIID riid, void **ppv)
  500. {
  501. HRESULT hr = _GetFolder(TRUE);
  502. if ( SUCCEEDED(hr) )
  503. hr = _psfTarget->CreateViewObject(hwnd, riid, ppv);
  504. if ( SUCCEEDED(hr) && (IsEqualIID(riid, IID_IShellView) || IsEqualIID(riid, IID_IShellView2)) )
  505. SHChangeNotifyRegisterAlias(_pidlTarget, _pidlRoot);
  506. return hr;
  507. }
  508. HRESULT CFolderShortcut::GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *rgfInOut)
  509. {
  510. if (IsSelf (cidl, apidl))
  511. {
  512. // since our folder is marked "CallForAttributes" we get to report
  513. // our attributes at runtime instead of the normal way via the registry
  514. if (SHGetAppCompatFlags (ACF_STRIPFOLDERBIT) & ACF_STRIPFOLDERBIT)
  515. {
  516. *rgfInOut = SFGAO_LINK | SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM;
  517. }
  518. else
  519. {
  520. *rgfInOut = SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_STORAGE |
  521. SFGAO_LINK | SFGAO_DROPTARGET | SFGAO_CANRENAME | SFGAO_CANDELETE |
  522. SFGAO_CANLINK | SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_HASSUBFOLDER;
  523. }
  524. return S_OK;
  525. }
  526. HRESULT hr = _GetFolder(FALSE);
  527. if (SUCCEEDED(hr))
  528. hr = _psfTarget->GetAttributesOf(cidl, apidl, rgfInOut);
  529. return hr;
  530. }
  531. HRESULT CFolderShortcut::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl,
  532. REFIID riid, UINT *prgfInOut, void **ppv)
  533. {
  534. HRESULT hr = _GetFolder(FALSE);
  535. if (SUCCEEDED(hr))
  536. hr = _psfTarget->GetUIObjectOf(hwnd, cidl, apidl, riid, prgfInOut, ppv);
  537. return hr;
  538. }
  539. HRESULT CFolderShortcut::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, STRRET *pName)
  540. {
  541. HRESULT hr = _GetFolder(FALSE);
  542. if (SUCCEEDED(hr))
  543. hr = _psfTarget->GetDisplayNameOf(pidl, uFlags, pName);
  544. return hr;
  545. }
  546. HRESULT CFolderShortcut::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl,
  547. LPCOLESTR pszName, DWORD uFlags,
  548. LPITEMIDLIST *ppidlOut)
  549. {
  550. HRESULT hr = _GetFolder(FALSE);
  551. if (SUCCEEDED(hr))
  552. hr = _psfTarget->SetNameOf(hwnd, pidl, pszName, uFlags, ppidlOut);
  553. return hr;
  554. }
  555. STDMETHODIMP CFolderShortcut::GetDefaultSearchGUID(LPGUID lpGuid)
  556. {
  557. HRESULT hr = _GetFolder2();
  558. if (SUCCEEDED(hr))
  559. hr = _psf2Target->GetDefaultSearchGUID(lpGuid);
  560. return hr;
  561. }
  562. STDMETHODIMP CFolderShortcut::EnumSearches(LPENUMEXTRASEARCH *ppenum)
  563. {
  564. HRESULT hr = _GetFolder2();
  565. if (SUCCEEDED(hr))
  566. hr = _psf2Target->EnumSearches(ppenum);
  567. return hr;
  568. }
  569. STDMETHODIMP CFolderShortcut::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
  570. {
  571. HRESULT hr = _GetFolder2();
  572. if (SUCCEEDED(hr))
  573. hr = _psf2Target->GetDefaultColumn(dwRes, pSort, pDisplay);
  574. return hr;
  575. }
  576. STDMETHODIMP CFolderShortcut::GetDefaultColumnState(UINT iColumn, DWORD *pbState)
  577. {
  578. HRESULT hr = _GetFolder2();
  579. if (SUCCEEDED(hr))
  580. hr = _psf2Target->GetDefaultColumnState(iColumn, pbState);
  581. return hr;
  582. }
  583. STDMETHODIMP CFolderShortcut::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
  584. {
  585. HRESULT hr = _GetFolder2();
  586. if (SUCCEEDED(hr))
  587. hr = _psf2Target->GetDetailsEx(pidl, pscid, pv);
  588. return hr;
  589. }
  590. STDMETHODIMP CFolderShortcut::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, LPSHELLDETAILS pDetail)
  591. {
  592. HRESULT hr = _GetFolder2();
  593. if (SUCCEEDED(hr))
  594. hr = _psf2Target->GetDetailsOf(pidl, iColumn, pDetail);
  595. return hr;
  596. }
  597. STDMETHODIMP CFolderShortcut::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid)
  598. {
  599. HRESULT hr = _GetFolder2();
  600. if (SUCCEEDED(hr))
  601. hr = _psf2Target->MapColumnToSCID(iColumn, pscid);
  602. return hr;
  603. }
  604. // IPersist
  605. HRESULT CFolderShortcut::GetClassID(CLSID *pCLSID)
  606. {
  607. *pCLSID = CLSID_FolderShortcut;
  608. return S_OK;
  609. }
  610. // IPersistFolder
  611. HRESULT CFolderShortcut::Initialize(LPCITEMIDLIST pidl)
  612. {
  613. HRESULT hr;
  614. // is the link loaded (could have been loaded through IPersistStream::Load)?
  615. if (_pslTarget)
  616. {
  617. // Yes, it's loaded so re-initialize
  618. // note, _szFolderPath will be empty since we are not loaded from the file system
  619. hr = Pidl_Set(&_pidlRoot, pidl) ? S_OK : E_OUTOFMEMORY;
  620. }
  621. else
  622. {
  623. // we explictly require initialization through
  624. // IPersistFolder3::InitializeEx, if we don't do these we can
  625. // not defent against loops in the name space
  626. hr = E_FAIL;
  627. }
  628. return hr;
  629. }
  630. // IPersistFolder2
  631. STDMETHODIMP CFolderShortcut::GetCurFolder(LPITEMIDLIST *ppidl)
  632. {
  633. return GetCurFolderImpl(this->_pidlRoot, ppidl);
  634. }
  635. // IPersistFolder3
  636. STDMETHODIMP CFolderShortcut::InitializeEx(IBindCtx *pbc, LPCITEMIDLIST pidlRoot, const PERSIST_FOLDER_TARGET_INFO *pfti)
  637. {
  638. HRESULT hr = E_INVALIDARG; // assume failure
  639. if ( NULL == pbc || (pbc && !SHSkipJunction(pbc, &CLSID_FolderShortcut)) )
  640. {
  641. _ClearState();
  642. if (pidlRoot)
  643. hr = SHILClone(pidlRoot, &_pidlRoot);
  644. if (pfti && pfti->pidlTargetFolder)
  645. {
  646. if ( SUCCEEDED(hr) )
  647. hr = SHILClone(pfti->pidlTargetFolder, &_pidlTargetFldrFromInit);
  648. if ( SUCCEEDED(hr) && !_szFolderPath[0] )
  649. hr = SHGetPathFromIDList(pfti->pidlTargetFolder, _szFolderPath) ? S_OK : E_FAIL;
  650. }
  651. else
  652. {
  653. if ( SUCCEEDED(hr) && !_szFolderPath[0] )
  654. hr = SHGetPathFromIDList(_pidlRoot, _szFolderPath) ? S_OK : E_FAIL;
  655. }
  656. if ( SUCCEEDED(hr) )
  657. hr = _LoadShortcut();
  658. }
  659. return hr;
  660. }
  661. HRESULT CFolderShortcut::GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO *pfti)
  662. {
  663. HRESULT hr = S_OK;
  664. ZeroMemory(pfti, sizeof(*pfti));
  665. if ( _pidlTargetFldrFromInit )
  666. hr = SHILClone(_pidlTargetFldrFromInit, &pfti->pidlTargetFolder);
  667. pfti->dwAttributes = -1;
  668. pfti->csidl = -1;
  669. return hr;
  670. }
  671. HRESULT CFolderShortcut::_GetLink()
  672. {
  673. HRESULT hr = _LoadShortcut();
  674. if (FAILED(hr))
  675. {
  676. // get an empty one in case we are going to be asked to save
  677. hr = SHCoCreateInstance(NULL, &CLSID_ShellLink, NULL, IID_PPV_ARG(IShellLinkW, &_pslTarget));
  678. }
  679. return hr;
  680. }
  681. HRESULT CFolderShortcut::_GetLinkQI(REFIID riid, void **ppv)
  682. {
  683. HRESULT hr = _GetLink();
  684. if (SUCCEEDED(hr))
  685. hr = _pslTarget->QueryInterface(riid, ppv);
  686. return hr;
  687. }
  688. HRESULT CFolderShortcut::_GetLinkA()
  689. {
  690. return _pslTargetA ? S_OK : _GetLinkQI(IID_PPV_ARG(IShellLinkA, &_pslTargetA));
  691. }
  692. // IPersistFile
  693. STDMETHODIMP CFolderShortcut::Load(LPCOLESTR pszFileName, DWORD dwMode)
  694. {
  695. _ClearState();
  696. SHUnicodeToTChar(pszFileName, _szFolderPath, ARRAYSIZE(_szFolderPath));
  697. return _LoadShortcut();
  698. }
  699. BOOL _IsFolder(LPCITEMIDLIST pidl)
  700. {
  701. ULONG rgInfo = SFGAO_FOLDER;
  702. HRESULT hr = SHGetNameAndFlags(pidl, SHGDN_NORMAL, NULL, 0, &rgInfo);
  703. return SUCCEEDED(hr) && (rgInfo & SFGAO_FOLDER);
  704. }
  705. void PathStripTrailingDots(LPTSTR szPath)
  706. {
  707. if (szPath[0] == TEXT('\0'))
  708. return;
  709. LPTSTR psz = &szPath[lstrlen(szPath) - 1];
  710. while ((*psz == TEXT('.')) &&
  711. (psz >= szPath))
  712. {
  713. *psz-- = TEXT('\0');
  714. }
  715. }
  716. STDMETHODIMP CFolderShortcut::Save(LPCOLESTR pszFileName, BOOL fRemember)
  717. {
  718. HRESULT hr = _GetTargetIDList(FALSE);
  719. // We need to make sure the folder shortcut can be saved keeping in mind the MAX_PATH limitation
  720. // cchFSReserved is the number of characters to reserve for the largest file that will be created
  721. // in the foldershortcut directory, in this case, it is the ARRAYSIZE of "\\desktop.ini"
  722. static const int cchFSReserved = ARRAYSIZE(TEXT("\\desktop.ini"));
  723. LPITEMIDLIST pidlInternet;
  724. // Don't create a folder shortcut to the internet folder.
  725. if (SUCCEEDED(hr) && SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_INTERNET, &pidlInternet)))
  726. {
  727. if (ILIsEqual(_pidlTarget, pidlInternet))
  728. {
  729. hr = E_INVALIDARG;
  730. }
  731. ILFree(pidlInternet);
  732. }
  733. if (SUCCEEDED(hr) && _IsFolder(_pidlTarget))
  734. {
  735. // we know the target is a folder, create a folder shortcut.
  736. BOOL fCreatedDir;
  737. TCHAR szName[MAX_PATH];
  738. SHUnicodeToTChar(pszFileName, szName, ARRAYSIZE(szName));
  739. // Remove any exisiting extension.
  740. // We dont want "Shortcut To My Documents.lnk.{GUID}
  741. if (PathFindCLSIDExtension(szName,NULL))
  742. {
  743. PathRemoveExtension(szName);
  744. }
  745. PathStripTrailingDots(szName);
  746. // Can't create a fldrshcut with too long a path
  747. if ((MAX_PATH - cchFSReserved) < lstrlen(szName))
  748. {
  749. hr = CO_E_PATHTOOLONG;
  750. }
  751. if (SUCCEEDED(hr))
  752. {
  753. if (PathIsDirectory(szName))
  754. fCreatedDir = FALSE;
  755. else
  756. fCreatedDir = SHCreateDirectory(NULL, szName) == 0;
  757. CreateFolderDesktopIni(szName);
  758. // Now initialize the child link
  759. IPersistFile *ppf;
  760. hr = _pslTarget->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
  761. if (SUCCEEDED(hr))
  762. {
  763. WCHAR wszName[MAX_PATH];
  764. SHTCharToUnicode(szName, wszName, ARRAYSIZE(wszName));
  765. if (PathAppendW(wszName, L"target.lnk"))
  766. {
  767. hr = ppf->Save(wszName, fRemember);
  768. if (SUCCEEDED(hr))
  769. {
  770. if (fRemember)
  771. Str_SetPtr(&_pszLastSave, szName);
  772. }
  773. }
  774. else
  775. {
  776. hr = HRESULT_FROM_WIN32(ERROR_FILENAME_EXCED_RANGE);
  777. }
  778. ppf->Release();
  779. }
  780. if (FAILED(hr) && fCreatedDir)
  781. {
  782. RemoveDirectory(szName); // cleanup after ourselves.
  783. }
  784. }
  785. }
  786. else
  787. {
  788. // ensure that if we save as a file we do so with the right extension
  789. WCHAR szFile[MAX_PATH];
  790. hr = StringCchCopy(szFile, ARRAYSIZE(szFile), pszFileName);
  791. if (SUCCEEDED(hr))
  792. {
  793. PathRenameExtension(szFile, L".lnk");
  794. // the target is not a folder, create a normal shortcut in this case
  795. IPersistFile *ppf;
  796. hr = _pslTarget->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
  797. if (SUCCEEDED(hr))
  798. {
  799. hr = ppf->Save(szFile, fRemember);
  800. ppf->Release();
  801. }
  802. }
  803. }
  804. return hr;
  805. }
  806. STDMETHODIMP CFolderShortcut::GetCurFile(LPOLESTR *ppszFileName)
  807. {
  808. HRESULT hr = E_FAIL;
  809. if (_pszLastSave)
  810. hr = SHStrDup(_pszLastSave, ppszFileName);
  811. else if (_pslTarget)
  812. {
  813. IPersistFile *ppf;
  814. hr = _pslTarget->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
  815. if (SUCCEEDED(hr))
  816. {
  817. hr = ppf->GetCurFile(ppszFileName);
  818. ppf->Release();
  819. }
  820. }
  821. return hr;
  822. }
  823. // IShellLinkW
  824. STDMETHODIMP CFolderShortcut::GetPath(LPWSTR pszFile, int cchMaxPath, WIN32_FIND_DATAW *pfd, DWORD flags)
  825. {
  826. HRESULT hr = _GetLink();
  827. if (SUCCEEDED(hr))
  828. hr = _pslTarget->GetPath(pszFile, cchMaxPath, pfd, flags);
  829. return hr;
  830. }
  831. STDMETHODIMP CFolderShortcut::SetPath(LPCWSTR pwszFile)
  832. {
  833. HRESULT hr = _GetLink();
  834. if (SUCCEEDED(hr) && PathIsDirectoryW(pwszFile))
  835. {
  836. hr = _pslTarget->SetPath(pwszFile);
  837. Pidl_Set(&_pidlTarget, NULL);
  838. }
  839. return hr;
  840. }
  841. STDMETHODIMP CFolderShortcut::GetIDList(LPITEMIDLIST *ppidl)
  842. {
  843. HRESULT hr = _GetLink();
  844. if (SUCCEEDED(hr))
  845. hr = _pslTarget->GetIDList(ppidl);
  846. else
  847. *ppidl = NULL;
  848. return hr;
  849. }
  850. STDMETHODIMP CFolderShortcut::SetIDList(LPCITEMIDLIST pidl)
  851. {
  852. HRESULT hr = _GetLink();
  853. if (SUCCEEDED(hr))
  854. {
  855. hr = _pslTarget->SetIDList(pidl);
  856. Pidl_Set(&_pidlTarget, NULL);
  857. }
  858. return hr;
  859. }
  860. STDMETHODIMP CFolderShortcut::GetDescription(LPWSTR wszName, int cchMaxName)
  861. {
  862. HRESULT hr = _GetLink();
  863. if (SUCCEEDED(hr))
  864. hr = _pslTarget->GetDescription(wszName, cchMaxName);
  865. return hr;
  866. }
  867. STDMETHODIMP CFolderShortcut::SetDescription(LPCWSTR wszName)
  868. {
  869. HRESULT hr = _GetLink();
  870. if (SUCCEEDED(hr))
  871. hr = _pslTarget->SetDescription(wszName);
  872. return hr;
  873. }
  874. STDMETHODIMP CFolderShortcut::GetWorkingDirectory(LPWSTR wszDir, int cchMaxPath)
  875. {
  876. HRESULT hr = _GetLink();
  877. if (SUCCEEDED(hr))
  878. hr = _pslTarget->GetWorkingDirectory(wszDir, cchMaxPath);
  879. return hr;
  880. }
  881. STDMETHODIMP CFolderShortcut::SetWorkingDirectory(LPCWSTR wszDir)
  882. {
  883. HRESULT hr = _GetLink();
  884. if (SUCCEEDED(hr))
  885. hr = _pslTarget->SetWorkingDirectory(wszDir);
  886. return hr;
  887. }
  888. STDMETHODIMP CFolderShortcut::GetArguments(LPWSTR wszArgs, int cchMaxPath)
  889. {
  890. HRESULT hr = _GetLink();
  891. if (SUCCEEDED(hr))
  892. hr = _pslTarget->GetArguments(wszArgs, cchMaxPath);//this is probably not at all useful.
  893. return hr;
  894. }
  895. STDMETHODIMP CFolderShortcut::SetArguments(LPCWSTR wszArgs)
  896. {
  897. HRESULT hr = _GetLink();
  898. if (SUCCEEDED(hr))
  899. hr = _pslTarget->SetArguments(wszArgs);//this is probably not at all useful.
  900. return hr;
  901. }
  902. STDMETHODIMP CFolderShortcut::GetHotkey(WORD *pwHotkey)
  903. {
  904. HRESULT hr = _GetLink();
  905. if (SUCCEEDED(hr))
  906. hr = _pslTarget->GetHotkey(pwHotkey);
  907. return hr;
  908. }
  909. STDMETHODIMP CFolderShortcut::SetHotkey(WORD wHotkey)
  910. {
  911. HRESULT hr = _GetLink();
  912. if (SUCCEEDED(hr))
  913. hr = _pslTarget->SetHotkey(wHotkey);
  914. return hr;
  915. }
  916. STDMETHODIMP CFolderShortcut::GetShowCmd(int *piShowCmd)
  917. {
  918. HRESULT hr = _GetLink();
  919. if (SUCCEEDED(hr))
  920. hr = _pslTarget->GetShowCmd(piShowCmd);
  921. return hr;
  922. }
  923. STDMETHODIMP CFolderShortcut::SetShowCmd(int iShowCmd)
  924. {
  925. HRESULT hr = _GetLink();
  926. if (SUCCEEDED(hr))
  927. hr = _pslTarget->SetShowCmd(iShowCmd);
  928. return hr;
  929. }
  930. STDMETHODIMP CFolderShortcut::GetIconLocation(LPWSTR wszIconPath, int cchIconPath, int *piIcon)
  931. {
  932. HRESULT hr = _GetLink();
  933. if (SUCCEEDED(hr))
  934. hr = _pslTarget->GetIconLocation(wszIconPath, cchIconPath, piIcon);
  935. return hr;
  936. }
  937. STDMETHODIMP CFolderShortcut::SetIconLocation(LPCWSTR wszIconPath, int iIcon)
  938. {
  939. HRESULT hr = _GetLink();
  940. if (SUCCEEDED(hr))
  941. hr = _pslTarget->SetIconLocation(wszIconPath, iIcon);
  942. return hr;
  943. }
  944. STDMETHODIMP CFolderShortcut::Resolve(HWND hwnd, DWORD fFlags)
  945. {
  946. HRESULT hr = _GetLink();
  947. if (SUCCEEDED(hr))
  948. hr = _pslTarget->Resolve(hwnd, fFlags);
  949. return hr;
  950. }
  951. STDMETHODIMP CFolderShortcut::SetRelativePath(LPCWSTR wszPathRel, DWORD dwReserved)
  952. {
  953. HRESULT hr = _GetLink();
  954. if (SUCCEEDED(hr))
  955. hr = _pslTarget->SetRelativePath(wszPathRel, dwReserved);
  956. return hr;
  957. }
  958. // IShellLinkA
  959. STDMETHODIMP CFolderShortcut::GetPath(LPSTR pszFile, int cchMaxPath, WIN32_FIND_DATAA *pfd, DWORD flags)
  960. {
  961. HRESULT hr = _GetLinkA();
  962. if (SUCCEEDED(hr))
  963. hr = _pslTargetA->GetPath(pszFile, cchMaxPath, pfd, flags);
  964. return hr;
  965. }
  966. STDMETHODIMP CFolderShortcut::GetDescription(LPSTR pszName, int cchMaxName)
  967. {
  968. HRESULT hr = _GetLinkA();
  969. if (SUCCEEDED(hr))
  970. hr = _pslTargetA->GetDescription(pszName, cchMaxName);
  971. return hr;
  972. }
  973. STDMETHODIMP CFolderShortcut::GetWorkingDirectory(LPSTR pszDir, int cchMaxPath)
  974. {
  975. HRESULT hr = _GetLinkA();
  976. if (SUCCEEDED(hr))
  977. hr = _pslTargetA->GetWorkingDirectory(pszDir, cchMaxPath);
  978. return hr;
  979. }
  980. STDMETHODIMP CFolderShortcut::GetArguments(LPSTR pszArgs, int cchMaxPath)
  981. {
  982. HRESULT hr = _GetLinkA();
  983. if (SUCCEEDED(hr))
  984. hr = _pslTargetA->GetArguments(pszArgs, cchMaxPath);//this is probably not at all useful.
  985. return hr;
  986. }
  987. STDMETHODIMP CFolderShortcut::GetIconLocation(LPSTR pszIconPath, int cchIconPath, int *piIcon)
  988. {
  989. HRESULT hr = _GetLinkA();
  990. if (SUCCEEDED(hr))
  991. hr = _pslTargetA->GetIconLocation(pszIconPath, cchIconPath, piIcon);
  992. return hr;
  993. }
  994. STDMETHODIMP CFolderShortcut::SetPath(LPCSTR pszFile)
  995. {
  996. HRESULT hr = _GetLinkA();
  997. if (SUCCEEDED(hr) && PathIsDirectoryA(pszFile))
  998. {
  999. hr = _pslTargetA->SetPath(pszFile);
  1000. Pidl_Set(&_pidlTarget, NULL);
  1001. }
  1002. return hr;
  1003. }
  1004. STDMETHODIMP CFolderShortcut::SetDescription(LPCSTR pszName)
  1005. {
  1006. HRESULT hr = _GetLinkA();
  1007. if (SUCCEEDED(hr))
  1008. hr = _pslTargetA->SetDescription(pszName);
  1009. return hr;
  1010. }
  1011. STDMETHODIMP CFolderShortcut::SetWorkingDirectory(LPCSTR pszDir)
  1012. {
  1013. HRESULT hr = _GetLinkA();
  1014. if (SUCCEEDED(hr))
  1015. hr = _pslTargetA->SetWorkingDirectory(pszDir);
  1016. return hr;
  1017. }
  1018. STDMETHODIMP CFolderShortcut::SetArguments(LPCSTR pszArgs)
  1019. {
  1020. HRESULT hr = _GetLinkA();
  1021. if (SUCCEEDED(hr))
  1022. hr = _pslTargetA->SetArguments(pszArgs);
  1023. return hr;
  1024. }
  1025. STDMETHODIMP CFolderShortcut::SetIconLocation(LPCSTR pszIconPath, int iIcon)
  1026. {
  1027. HRESULT hr = _GetLinkA();
  1028. if (SUCCEEDED(hr))
  1029. hr = _pslTargetA->SetIconLocation(pszIconPath, iIcon);
  1030. return hr;
  1031. }
  1032. STDMETHODIMP CFolderShortcut::SetRelativePath(LPCSTR pszPathRel, DWORD dwReserved)
  1033. {
  1034. HRESULT hr = _GetLinkA();
  1035. if (SUCCEEDED(hr))
  1036. hr = _pslTargetA->SetRelativePath(pszPathRel, dwReserved);
  1037. return hr;
  1038. }
  1039. STDMETHODIMP CFolderShortcut::GetIconLocation(UINT uFlags, LPTSTR pszIconFile, UINT ucchMax, PINT pniIcon, PUINT puFlags)
  1040. {
  1041. IExtractIcon *pxi;
  1042. HRESULT hr = _GetLinkQI(IID_PPV_ARG(IExtractIcon, &pxi));
  1043. if (SUCCEEDED(hr))
  1044. {
  1045. hr = pxi->GetIconLocation(uFlags, pszIconFile, ucchMax, pniIcon, puFlags);
  1046. pxi->Release();
  1047. }
  1048. return hr;
  1049. }
  1050. STDMETHODIMP CFolderShortcut::Extract(LPCTSTR pcszFile, UINT uIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT ucIconSize)
  1051. {
  1052. IExtractIcon *pxi;
  1053. HRESULT hr = _GetLinkQI(IID_PPV_ARG(IExtractIcon, &pxi));
  1054. if (SUCCEEDED(hr))
  1055. {
  1056. hr = pxi->Extract(pcszFile, uIconIndex, phiconLarge, phiconSmall, ucIconSize);
  1057. pxi->Release();
  1058. }
  1059. return hr;
  1060. }
  1061. HRESULT CFolderShortcut::GetInfoTip(DWORD dwFlags, WCHAR** ppwszText)
  1062. {
  1063. IQueryInfo *pqi;
  1064. HRESULT hr = _GetLinkQI(IID_PPV_ARG(IQueryInfo, &pqi));
  1065. if (SUCCEEDED(hr))
  1066. {
  1067. hr = pqi->GetInfoTip(dwFlags | QITIPF_LINKUSETARGET, ppwszText);
  1068. pqi->Release();
  1069. }
  1070. return hr;
  1071. }
  1072. HRESULT CFolderShortcut::GetInfoFlags(DWORD *pdwFlags)
  1073. {
  1074. IQueryInfo *pqi;
  1075. HRESULT hr = _GetLinkQI(IID_PPV_ARG(IQueryInfo, &pqi));
  1076. if (SUCCEEDED(hr))
  1077. {
  1078. hr = pqi->GetInfoFlags(pdwFlags);
  1079. pqi->Release();
  1080. }
  1081. return hr;
  1082. }
  1083. // IBrowserFrameOptions
  1084. HRESULT CFolderShortcut::GetFrameOptions(IN BROWSERFRAMEOPTIONS dwMask, IN BROWSERFRAMEOPTIONS * pdwOptions)
  1085. {
  1086. HRESULT hr = _GetFolder(FALSE);
  1087. *pdwOptions = BFO_NONE;
  1088. if (SUCCEEDED(hr))
  1089. {
  1090. IBrowserFrameOptions *pbfo;
  1091. hr = _psfTarget->QueryInterface(IID_PPV_ARG(IBrowserFrameOptions, &pbfo));
  1092. if (SUCCEEDED(hr))
  1093. {
  1094. hr = pbfo->GetFrameOptions(dwMask, pdwOptions);
  1095. pbfo->Release();
  1096. }
  1097. }
  1098. return hr;
  1099. }
  1100. // IPersistStream
  1101. STDMETHODIMP CFolderShortcut::Load(IStream *pStm)
  1102. {
  1103. _ClearState();
  1104. IPersistStream *pps;
  1105. HRESULT hr = SHCoCreateInstance(NULL, &CLSID_ShellLink, NULL, IID_PPV_ARG(IPersistStream, &pps));
  1106. if (SUCCEEDED(hr))
  1107. {
  1108. hr = pps->Load(pStm);
  1109. if (SUCCEEDED(hr))
  1110. pps->QueryInterface(IID_PPV_ARG(IShellLinkW, &_pslTarget)); // keep this guy
  1111. pps->Release();
  1112. }
  1113. return hr;
  1114. }
  1115. // IPersistStream
  1116. STDMETHODIMP CFolderShortcut::Save(IStream *pStm, int fClearDirty)
  1117. {
  1118. return E_NOTIMPL;
  1119. }
  1120. // IPersistStream
  1121. STDMETHODIMP CFolderShortcut::GetSizeMax(ULARGE_INTEGER * pcbSize)
  1122. {
  1123. return E_NOTIMPL;
  1124. }
  1125. //
  1126. // IFolderShortcut::ConvertToLink.
  1127. //
  1128. // destructively convert a Folder Shortcut into a Shell Link.
  1129. //
  1130. // pszFolderShortcut is the path to an existing folder shortcut
  1131. // c:\Folder Shortcut.{guid} - deleted
  1132. // c:\Folder Shortcut.lnk - created
  1133. //
  1134. STDMETHODIMP CFolderShortcut::ConvertToLink(LPCOLESTR pszFolderShortcut, DWORD fFlags)
  1135. {
  1136. HRESULT hr = E_FAIL;
  1137. TCHAR szName[MAX_PATH];
  1138. SHUnicodeToTChar(pszFolderShortcut, szName, ARRAYSIZE(szName));
  1139. if (PathIsDirectory(szName) && IsFolderShortcut(szName))
  1140. {
  1141. TCHAR szLinkName[MAX_PATH];
  1142. // c:\Folder Shortcut\target.lnk
  1143. hr = StringCchCopy(szLinkName, ARRAYSIZE(szLinkName), szName);
  1144. if (SUCCEEDED(hr))
  1145. {
  1146. hr = E_FAIL;
  1147. if (PathAppend(szLinkName, TEXT("target.lnk")))
  1148. {
  1149. PathRenameExtension(szName, TEXT(".lnk"));
  1150. // FS.{guid} -> FS.lnk
  1151. if (CopyFile(szLinkName, szName, FALSE))
  1152. {
  1153. PathRemoveExtension(szName);
  1154. if (DeleteFile(szLinkName)
  1155. && PathAppend(szName, TEXT("desktop.ini"))
  1156. && DeleteFile(szName)
  1157. && PathRemoveFileSpec(szName)
  1158. && RemoveDirectory(szName))
  1159. {
  1160. hr = S_OK;
  1161. }
  1162. }
  1163. }
  1164. }
  1165. }
  1166. return hr;
  1167. }
  1168. //
  1169. // IFolderShortcut::ConvertToFolderShortcut.
  1170. //
  1171. // destructively convert a Shell Link (.lnk) -> Folder Shortcut (Folder.{guid}).
  1172. // pszPathLNK is the path to an existing .lnk file
  1173. // c:\Folder Shortcut.lnk - deleted
  1174. // c:\Folder Shortcut.{guid} - created
  1175. //
  1176. STDMETHODIMP CFolderShortcut::ConvertToFolderShortcut(LPCOLESTR pszPathLNK, DWORD fFlags)
  1177. {
  1178. //must bind to the link, resolve it, and make sure it points to a folder.
  1179. IShellLink *psl;
  1180. HRESULT hr = SHCoCreateInstance(NULL, &CLSID_ShellLink, NULL, IID_PPV_ARG(IShellLink, &psl));
  1181. if (SUCCEEDED(hr))
  1182. {
  1183. IPersistFile *ppf;
  1184. hr = psl->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
  1185. if (SUCCEEDED(hr))
  1186. {
  1187. hr = ppf->Load(pszPathLNK, STGM_READ);
  1188. if (SUCCEEDED(hr))
  1189. {
  1190. hr = psl->Resolve(NULL, SLR_NO_UI); // make sure the link is real
  1191. if (SUCCEEDED(hr))
  1192. {
  1193. LPITEMIDLIST pidl;
  1194. hr = psl->GetIDList(&pidl);
  1195. if (hr == S_OK)
  1196. {
  1197. // this should maybe work on the pidl so that
  1198. // it doesn't have to worry about files.
  1199. if (_IsFolder(pidl))
  1200. {
  1201. TCHAR szPath[MAX_PATH], szName[MAX_PATH];
  1202. SHUnicodeToTChar(pszPathLNK, szName, ARRAYSIZE(szName));
  1203. hr = StringCchCopy(szPath, ARRAYSIZE(szPath), szName);
  1204. if (SUCCEEDED(hr))
  1205. {
  1206. hr = E_FAIL;
  1207. PathRemoveExtension(szName);
  1208. BOOL fCreatedDir = SHCreateDirectory(NULL, szName) == 0;
  1209. if (CreateFolderDesktopIni(szName) &&
  1210. PathAppend(szName, TEXT("target.lnk")))
  1211. {
  1212. //copy the link file into the new directory.
  1213. if (CopyFile(szPath, szName, FALSE))
  1214. {
  1215. if (DeleteFile(szPath)) //if all goes well, delete the old.
  1216. hr = S_OK;
  1217. }
  1218. else
  1219. {
  1220. PathRemoveFileSpec(szName);
  1221. if (fCreatedDir)
  1222. RemoveDirectory(szName);
  1223. }
  1224. }
  1225. }
  1226. }
  1227. else
  1228. hr = E_FAIL;
  1229. ILFree(pidl);
  1230. }
  1231. else
  1232. hr = E_FAIL;
  1233. }
  1234. }
  1235. ppf->Release();
  1236. }
  1237. psl->Release();
  1238. }
  1239. return hr;
  1240. }
  1241. // IPersistPropertyBag
  1242. STDMETHODIMP CFolderShortcut::Save(IPropertyBag* pPropBag, BOOL fClearDirty, BOOL fSaveAllProperties)
  1243. {
  1244. return E_NOTIMPL;
  1245. }
  1246. // IPersistPropertyBag
  1247. STDMETHODIMP CFolderShortcut::Load(IPropertyBag* pPropBag, IErrorLog* pErrorLog)
  1248. {
  1249. _ClearState();
  1250. IPersistPropertyBag* pppb;
  1251. HRESULT hr = SHCoCreateInstance(NULL, &CLSID_ShellLink, NULL, IID_PPV_ARG(IPersistPropertyBag, &pppb));
  1252. if (SUCCEEDED(hr))
  1253. {
  1254. hr = pppb->Load(pPropBag, pErrorLog);
  1255. if (SUCCEEDED(hr))
  1256. {
  1257. hr = pppb->QueryInterface(IID_PPV_ARG(IShellLinkW, &_pslTarget));
  1258. DWORD dwFlags;
  1259. if (SUCCEEDED(SHPropertyBag_ReadDWORD(pPropBag, L"Attributes", &dwFlags)))
  1260. _dwAttributesTarget = dwFlags;
  1261. }
  1262. pppb->Release();
  1263. }
  1264. return hr;
  1265. }
  1266. STDMETHODIMP CFolderShortcut::InitNew(void)
  1267. {
  1268. _ClearState();
  1269. return S_OK;
  1270. }