fldrscut.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 42k
Category:

Windows Kernel

Development Platform:

Visual C++

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