//   Implements Folder Shortcut.

#include "shellprv.h"
#include "clsobj.h"

// implemented in filefldr.cpp
extern LPTSTR PathFindCLSIDExtension(LPCTSTR pszFile, CLSID *pclsid);

BOOL CreateFolderDesktopIni(LPCTSTR pszName)
{
    SHFOLDERCUSTOMSETTINGS fcs = {0};
    fcs.dwSize = sizeof(fcs);
    fcs.dwMask = FCSM_CLSID | FCSM_FLAGS;
    fcs.pclsid = (GUID*)&CLSID_FolderShortcut;
    fcs.dwFlags = FCS_FLAG_DRAGDROP;
    return SUCCEEDED(SHGetSetFolderCustomSettings(&fcs, pszName, FCS_FORCEWRITE));
}


EXTERN_C BOOL IsFolderShortcut(LPCTSTR pszName)
{
    SHFOLDERCUSTOMSETTINGS fcs = {0};
    CLSID clsid = {0};
    fcs.dwSize = sizeof(fcs);
    fcs.dwMask = FCSM_CLSID;
    fcs.pclsid = &clsid;

    if (SUCCEEDED(SHGetSetFolderCustomSettings(&fcs, pszName, FCS_READ)))
    {
        return IsEqualGUID(clsid, CLSID_FolderShortcut);
    }
    return FALSE;
}


// exported from fsnotify.c
STDAPI_(void) SHChangeNotifyRegisterAlias(LPCITEMIDLIST pidlReal, LPCITEMIDLIST pidlAlias);


class CFolderShortcut : public IShellFolder2, 
                        public IPersistFolder3,
                        public IShellLinkA,
                        public IShellLinkW,
                        public IPersistFile,
                        public IExtractIcon,
                        public IQueryInfo,
                        public IFolderShortcutConvert,
                        public IPersistStreamInit,
                        public IPersistPropertyBag,
                        public IBrowserFrameOptions
{
public:
    // IUnknown
    STDMETHODIMP QueryInterface(REFIID, void **);
    STDMETHODIMP_(ULONG) AddRef(void);
    STDMETHODIMP_(ULONG) Release(void);
    
    // IShellFolder
    STDMETHODIMP ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pszDisplayName,
                                  ULONG *pchEaten, LPITEMIDLIST *ppidl, ULONG *pdwAttributes);
    STDMETHODIMP EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList ** ppenumIDList);
    STDMETHODIMP BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv);
    STDMETHODIMP BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv);
    STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
    STDMETHODIMP CreateViewObject (HWND hwnd, REFIID riid, void **ppv);
    STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST * apidl, ULONG *rgfInOut);
    STDMETHODIMP GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST * apidl,
                               REFIID riid, UINT * prgfInOut, void **ppv);
    STDMETHODIMP GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, LPSTRRET lpName);
    STDMETHODIMP SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, DWORD uFlags,
                           LPITEMIDLIST *ppidlOut);

    // IShellFolder2
    STDMETHODIMP GetDefaultSearchGUID(GUID *pGuid);
    STDMETHODIMP EnumSearches(IEnumExtraSearch **ppenum);
    STDMETHODIMP GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay);
    STDMETHODIMP GetDefaultColumnState(UINT iColumn, DWORD *pbState);
    STDMETHODIMP GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv);
    STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails);
    STDMETHODIMP MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid);

    // IPersist
    STDMETHODIMP GetClassID(CLSID *pClassID);

    // IPersistFolder
    STDMETHODIMP Initialize(LPCITEMIDLIST pidl);

    // IPersistFolder2
    STDMETHODIMP GetCurFolder(LPITEMIDLIST *ppidl);

    // IPersistFolder3
    STDMETHODIMP InitializeEx(IBindCtx *pbc, LPCITEMIDLIST pidlRoot, const PERSIST_FOLDER_TARGET_INFO *pfti);
    STDMETHODIMP GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO *pfti);

    // IPersistStream
    STDMETHODIMP Load(IStream *pStm);
    STDMETHODIMP Save(IStream *pStm,int fClearDirty);
    STDMETHODIMP GetSizeMax(ULARGE_INTEGER * pcbSize);

    // IPersistPropertyBag
    STDMETHODIMP Save(IPropertyBag* pPropBag, BOOL fClearDirty, BOOL fSaveAllProperties);
    STDMETHODIMP Load(IPropertyBag* pPropBag, IErrorLog* pErrorLog);

    // IPersistPropertyBag/IPersistStreamInit
    STDMETHODIMP InitNew(void);

    // IPersistFile
    STDMETHODIMP Load(LPCOLESTR pszFileName, DWORD dwMode);
    STDMETHODIMP Save(LPCOLESTR pszFileName, BOOL fRemember);
    STDMETHODIMP IsDirty() { return E_NOTIMPL; };
    STDMETHODIMP SaveCompleted(LPCOLESTR pszFileName) { return E_NOTIMPL; };
    STDMETHODIMP GetCurFile(LPOLESTR *ppszFileName);

    // IShellLinkW
    STDMETHODIMP GetPath(LPWSTR pszFile, int cchMaxPath, WIN32_FIND_DATAW *pfd, DWORD flags);
    STDMETHODIMP SetPath(LPCWSTR pszFile);
    STDMETHODIMP GetIDList(LPITEMIDLIST *ppidl);
    STDMETHODIMP SetIDList(LPCITEMIDLIST pidl);
    STDMETHODIMP GetDescription(LPWSTR pszName, int cchMaxName);
    STDMETHODIMP SetDescription(LPCWSTR pszName);
    STDMETHODIMP GetWorkingDirectory(LPWSTR pszDir, int cchMaxPath);
    STDMETHODIMP SetWorkingDirectory(LPCWSTR pszDir);
    STDMETHODIMP GetArguments(LPWSTR pszArgs, int cchMaxPath);
    STDMETHODIMP SetArguments(LPCWSTR pszArgs);
    STDMETHODIMP GetHotkey(WORD *pwHotkey);
    STDMETHODIMP SetHotkey(WORD wHotkey);
    STDMETHODIMP GetShowCmd(int *piShowCmd);
    STDMETHODIMP SetShowCmd(int iShowCmd);
    STDMETHODIMP GetIconLocation(LPWSTR pszIconPath, int cchIconPath, int *piIcon);
    STDMETHODIMP SetIconLocation(LPCWSTR pszIconPath, int iIcon);
    STDMETHODIMP Resolve(HWND hwnd, DWORD fFlags);
    STDMETHODIMP SetRelativePath(LPCWSTR pszPathRel, DWORD dwReserved);

    // IShellLinkA
    STDMETHODIMP GetPath(LPSTR pszFile, int cchMaxPath, WIN32_FIND_DATAA *pfd, DWORD flags);
    STDMETHODIMP SetPath(LPCSTR pszFile);
    STDMETHODIMP GetDescription(LPSTR pszName, int cchMaxName);
    STDMETHODIMP SetDescription(LPCSTR pszName);
    STDMETHODIMP GetWorkingDirectory(LPSTR pszDir, int cchMaxPath);
    STDMETHODIMP SetWorkingDirectory(LPCSTR pszDir);
    STDMETHODIMP GetArguments(LPSTR pszArgs, int cchMaxPath);
    STDMETHODIMP SetArguments(LPCSTR pszArgs);
    STDMETHODIMP GetIconLocation(LPSTR pszIconPath, int cchIconPath, int *piIcon);
    STDMETHODIMP SetIconLocation(LPCSTR pszIconPath, int iIcon);
    STDMETHODIMP SetRelativePath(LPCSTR pszPathRel, DWORD dwReserved);

    // IFolderShortcutConvert
    STDMETHODIMP ConvertToLink(LPCOLESTR pszPathLNK, DWORD fFlags);
    STDMETHODIMP ConvertToFolderShortcut(LPCOLESTR pszPathLNK, DWORD fFlags);

    // IExtractIcon
    STDMETHODIMP GetIconLocation(UINT uFlags, LPTSTR pszIconFile, UINT ucchMax, INT *pniIcon, UINT *puFlags);
    STDMETHODIMP Extract(LPCTSTR pcszFile, UINT uIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT ucIconSize);

    // IQueryInfo
    STDMETHODIMP GetInfoTip(DWORD dwFlags, WCHAR** ppwszTip);
    STDMETHODIMP GetInfoFlags(DWORD *pdwFlags);

    // IBrowserFrameOptions
    STDMETHODIMP GetFrameOptions(IN BROWSERFRAMEOPTIONS dwMask, IN BROWSERFRAMEOPTIONS * pdwOptions);

    CFolderShortcut();

protected:
    ~CFolderShortcut();

    void _ClearState();
    void _ClearTargetFolder();

private:
    HRESULT _LoadShortcut();
    HRESULT _GetTargetIDList(BOOL fResolve);
    HRESULT _BindFolder(BOOL fResolve);
    HRESULT _GetFolder(BOOL fForceResolve);
    HRESULT _GetFolder2();

    HRESULT _GetLink();
    HRESULT _GetLinkA();
    HRESULT _GetLinkQI(REFIID riid, void **ppv);
    HRESULT _PreBindCtxHelper(IBindCtx **ppbc);

    LONG                   _cRef;      

    LPITEMIDLIST           _pidlRoot;
    LPITEMIDLIST           _pidlTarget;
    LPITEMIDLIST           _pidlTargetFldrFromInit;
    IShellFolder*          _psfTarget;
    IShellFolder2*         _psf2Target;
    IShellLinkW*           _pslTarget;
    IShellLinkA*           _pslTargetA;
    LPTSTR                 _pszLastSave;
    BOOL                   _fHaveResolved;
    DWORD                  _dwAttributesTarget;
    TCHAR                  _szFolderPath[MAX_PATH];
};

//constructor/destructor and related functions
CFolderShortcut::CFolderShortcut() : _cRef(1), _dwAttributesTarget(FILE_ATTRIBUTE_DIRECTORY)
{
    ASSERT(_pidlRoot == NULL);
    ASSERT(_pidlTarget == NULL);
    ASSERT(_psfTarget == NULL);
    ASSERT(_psf2Target == NULL);
    ASSERT(_szFolderPath[0] == 0);
    ASSERT(_pidlTargetFldrFromInit == NULL);

    DllAddRef();
}

CFolderShortcut::~CFolderShortcut()
{
    _ClearState();
    DllRelease();
}

void CFolderShortcut::_ClearTargetFolder()
{
    ATOMICRELEASE(_psfTarget);
    ATOMICRELEASE(_psf2Target);
}

void CFolderShortcut::_ClearState()
{
    _fHaveResolved = FALSE;

    Pidl_Set(&_pidlRoot, NULL);
    Pidl_Set(&_pidlTarget, NULL);
    Pidl_Set(&_pidlTargetFldrFromInit, NULL);

    Str_SetPtr(&_pszLastSave, NULL);

    _ClearTargetFolder();

    ATOMICRELEASE(_pslTarget);
    ATOMICRELEASE(_pslTargetA);

}

STDAPI CFolderShortcut_CreateInstance(IUnknown* pUnkOuter, REFIID riid, void **ppv)
{
    HRESULT hr = E_OUTOFMEMORY;
    *ppv = NULL;
    // aggregation checking is handled in class factory
    CFolderShortcut* pfolder = new CFolderShortcut();
    if (pfolder)
    {
        hr = pfolder->QueryInterface(riid, ppv);
        pfolder->Release();
    }

    return hr;
}

// ensure that _pslTarget has been created and loaded

HRESULT CFolderShortcut::_LoadShortcut()
{
    HRESULT hr;

    if (_pslTarget)
    {
        hr = S_OK;
    }
    else if (_szFolderPath[0])
    {
        TCHAR szPath[MAX_PATH];

        // leave this shortcut visible so down level clients see it and can
        // navigate through it.
        PathCombine(szPath, _szFolderPath, TEXT("target.lnk"));
        hr = LoadFromFile(CLSID_ShellLink, szPath, IID_PPV_ARG(IShellLinkW, &_pslTarget));
        if (SUCCEEDED(hr))
        {
            LPITEMIDLIST pidlTarget;

            // Prevalidate to prevent recusion
            // If GetIDList fails, that's okay; I guess it doesn't point to us after all
            if (_pslTarget->GetIDList(&pidlTarget) == S_OK)
            {
                SHGetPathFromIDList(pidlTarget, szPath);

                // Does this point to itself?
                if (StrCmpI(szPath, _szFolderPath) == 0)
                {
                    _pslTarget->Release();
                    _pslTarget = NULL;
                    hr = E_FAIL;
                }

                ILFree(pidlTarget);
            }
        }
    }
    else
    {
        hr = E_FAIL;
    }

    return hr;
}

// ensure that _pidlTarget is inited (requres _pslTarget)

HRESULT CFolderShortcut::_GetTargetIDList(BOOL bResolve)
{
    HRESULT hr = _LoadShortcut();
    if (SUCCEEDED(hr))
    {
        if (_pidlTarget)
        {
            hr = S_OK;
        }
        else
        {
            if (bResolve)
                _pslTarget->Resolve(NULL, SLR_UPDATE | SLR_NO_UI);

            hr = _pslTarget->GetIDList(&_pidlTarget);
            if (hr == S_FALSE)
                hr = E_FAIL;      // convert empty to failure

            if (SUCCEEDED(hr))
            {
                //  make sure we dont have another shortcut here
                IShellLink *psl;
                if (SUCCEEDED(SHBindToObject(NULL, IID_IShellLink, _pidlTarget, (void**)&psl)))
                {
                    ILFree(_pidlTarget);
                    hr = psl->GetIDList(&_pidlTarget);

                    if (SUCCEEDED(hr))
                    {
                        hr = _pslTarget->SetIDList(_pidlTarget);
                    }
                    
                    psl->Release();
                }
            }

            if (FAILED(hr) && _pidlTarget)
            {
                ILFree(_pidlTarget);
                _pidlTarget = NULL;
            }
        }
    }
    return hr;
}

// create _psfTarget (requires _pidlTarget)

HRESULT CFolderShortcut::_BindFolder(BOOL bResolve)
{
    ASSERT(_psfTarget == NULL);

    HRESULT hr = _GetTargetIDList(bResolve);
    if (SUCCEEDED(hr))
    {
        IBindCtx *pbc = NULL;   // in/out param below
        hr = _PreBindCtxHelper(&pbc);    // avoid loops in the name space
        if (SUCCEEDED(hr))
        {
            if (SUCCEEDED(hr))
            {
                IShellFolder *psfDesktop;
                hr = SHGetDesktopFolder(&psfDesktop);
                if (SUCCEEDED(hr))
                {
                    // Are we trying to bind to the desktop folder?
                    if (ILIsEmpty(_pidlTarget))
                    {
                        // Yes; Clone the desktop shell folder.
                        _psfTarget = psfDesktop;
                        _psfTarget->AddRef();
                        hr = S_OK;
                    }
                    else
                    {
                        // No. Bind to it.
                        hr = psfDesktop->BindToObject(_pidlTarget, pbc, IID_PPV_ARG(IShellFolder, &_psfTarget));
                    }

                    if (SUCCEEDED(hr))
                    {
                        // optionally re-target the folder (if he is a file system folder) 
                        // to separate the location in the name space (_pidlRoot) 
                        // and the folder being viewed (pfsfi.szFolderPath).

                        IPersistFolder3 *ppf;
                        if (SUCCEEDED(_psfTarget->QueryInterface(IID_PPV_ARG(IPersistFolder3, &ppf))))
                        {
                            PERSIST_FOLDER_TARGET_INFO pfti = { 0 };

                            pfti.pidlTargetFolder = _pidlTarget;
                            pfti.dwAttributes = _dwAttributesTarget;
                            pfti.csidl = -1;

                            hr = ppf->InitializeEx(pbc, _pidlRoot, &pfti);
                            ppf->Release();
                        }
                    }
                    psfDesktop->Release();
                }
            }
            pbc->Release();
        }
    }
    return hr;
}

// ensure that _psfTarget is inited

HRESULT CFolderShortcut::_GetFolder(BOOL fForceResolve)
{
    HRESULT hr;

    if (fForceResolve)
    {
        if (_fHaveResolved)
        {
            hr = _psfTarget ? S_OK : E_FAIL;
        }
        else
        {
            _fHaveResolved = TRUE;  // don't do this again

            _ClearTargetFolder();
            Pidl_Set(&_pidlTarget, NULL);

            hr = _BindFolder(fForceResolve);
        }
    }
    else if (_psfTarget)
    {
        hr = S_OK;
    }
    else
    {
        hr = _BindFolder(fForceResolve);
    }
    return hr;
}

// ensure that _psf2Target is inited

HRESULT CFolderShortcut::_GetFolder2()
{
    if (_psf2Target)
        return S_OK;

    HRESULT hr = _GetFolder(FALSE);
    if (SUCCEEDED(hr))
        hr = _psfTarget->QueryInterface(IID_PPV_ARG(IShellFolder2, &_psf2Target));
    return hr;
}

STDMETHODIMP CFolderShortcut::QueryInterface(REFIID riid, void **ppvObj)
{
    static const QITAB qit[] = {
        QITABENTMULTI(CFolderShortcut, IShellFolder, IShellFolder2),
        QITABENT(CFolderShortcut, IShellFolder2),
        QITABENTMULTI(CFolderShortcut, IPersist, IPersistFolder3),
        QITABENTMULTI(CFolderShortcut, IPersistFolder, IPersistFolder3),
        QITABENTMULTI(CFolderShortcut, IPersistFolder2, IPersistFolder3),
        QITABENT(CFolderShortcut, IPersistFolder3),
        QITABENT(CFolderShortcut, IPersistStreamInit),
        QITABENTMULTI(CFolderShortcut, IPersistStream, IPersistStreamInit),
        QITABENT(CFolderShortcut, IShellLinkA),
        QITABENT(CFolderShortcut, IShellLinkW),
        QITABENT(CFolderShortcut, IPersistFile),
        QITABENT(CFolderShortcut, IFolderShortcutConvert),
        QITABENT(CFolderShortcut, IExtractIcon),
        QITABENT(CFolderShortcut, IQueryInfo),
        QITABENT(CFolderShortcut, IPersistPropertyBag),
        QITABENT(CFolderShortcut, IBrowserFrameOptions),
        { 0 },
    };
    return QISearch(this, qit, riid, ppvObj);
}

STDMETHODIMP_(ULONG) CFolderShortcut::AddRef()
{
    return InterlockedIncrement(&_cRef);
}

STDMETHODIMP_(ULONG) CFolderShortcut::Release()
{
    if (InterlockedDecrement(&_cRef))
        return _cRef;
    delete this;
    return 0;
}

// either create or init the passed bind ctx with the params to avoid loops in the name space

HRESULT CFolderShortcut::_PreBindCtxHelper(IBindCtx **ppbc)
{
    HRESULT hr;
    if (*ppbc)
    {
        (*ppbc)->AddRef();
        hr = S_OK;
    }
    else
    {
         hr = BindCtx_CreateWithMode(STGM_READ | STGM_SHARE_DENY_WRITE, ppbc);
    }

    if (SUCCEEDED(hr)) 
        (*ppbc)->RegisterObjectParam(STR_SKIP_BINDING_CLSID, SAFECAST(this, IShellFolder2 *));

    return hr;
}

// IShellFolder methods

HRESULT CFolderShortcut::ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pwszDisplayName,
        ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG *pdwAttributes)
{
    if (!ppidl)
        return E_INVALIDARG;
    *ppidl = NULL;
    if (!pwszDisplayName)
        return E_INVALIDARG;

    HRESULT hr = _GetFolder(FALSE);
    if (SUCCEEDED(hr))
    {
        hr = _PreBindCtxHelper(&pbc);
        if (SUCCEEDED(hr))
        {
            hr = _psfTarget->ParseDisplayName(hwnd, pbc, pwszDisplayName, 
                                                pchEaten, ppidl, pdwAttributes);
            pbc->Release();
        }
    }
    return hr;
}

HRESULT CFolderShortcut::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList)
{
    HRESULT hr = _GetFolder(TRUE);

    if (SUCCEEDED(hr))
        hr = _psfTarget->EnumObjects(hwnd, grfFlags, ppenumIDList);
    if (SUCCEEDED(hr))
        SHChangeNotifyRegisterAlias(_pidlTarget, _pidlRoot);
    
    return hr;
}

HRESULT CFolderShortcut::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
{
    HRESULT hr = _GetFolder(TRUE);
    if (SUCCEEDED(hr))
    {
        hr = _PreBindCtxHelper(&pbc);
        if (SUCCEEDED(hr))
        {
            hr = _psfTarget->BindToObject(pidl, pbc, riid, ppv);
            pbc->Release();

            if (SUCCEEDED(hr))
                SHChangeNotifyRegisterAlias(_pidlTarget, _pidlRoot);
        }
    }
    return hr;
}

HRESULT CFolderShortcut::BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
{
    return BindToObject(pidl, pbc, riid, ppv);
}

HRESULT CFolderShortcut::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
    HRESULT hr = _GetFolder(FALSE);
    if (SUCCEEDED(hr))
        hr = _psfTarget->CompareIDs(lParam, pidl1, pidl2);
    return hr;
}

HRESULT CFolderShortcut::CreateViewObject(HWND hwnd, REFIID riid, void **ppv)
{
    HRESULT hr = _GetFolder(TRUE);

    if ( SUCCEEDED(hr) )
        hr = _psfTarget->CreateViewObject(hwnd, riid, ppv);

    if ( SUCCEEDED(hr) && (IsEqualIID(riid, IID_IShellView) || IsEqualIID(riid, IID_IShellView2)) )
        SHChangeNotifyRegisterAlias(_pidlTarget, _pidlRoot);

    return hr;
}

HRESULT CFolderShortcut::GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *rgfInOut)
{
    if (IsSelf (cidl, apidl))
    {
        // since our folder is marked "CallForAttributes" we get to report
        // our attributes at runtime instead of the normal way via the registry
        if (SHGetAppCompatFlags (ACF_STRIPFOLDERBIT) & ACF_STRIPFOLDERBIT)
        {
            *rgfInOut = SFGAO_LINK | SFGAO_CAPABILITYMASK | SFGAO_FILESYSTEM;
        }
        else
        {
            *rgfInOut = SFGAO_FOLDER | SFGAO_FILESYSTEM | SFGAO_STORAGE |
                          SFGAO_LINK | SFGAO_DROPTARGET | SFGAO_CANRENAME | SFGAO_CANDELETE |
                          SFGAO_CANLINK | SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_HASSUBFOLDER;
        }
        return S_OK;
    }

    HRESULT hr = _GetFolder(FALSE);
    if (SUCCEEDED(hr))
        hr = _psfTarget->GetAttributesOf(cidl, apidl, rgfInOut);
    return hr;
}

HRESULT CFolderShortcut::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl,
                                       REFIID riid, UINT *prgfInOut, void **ppv)
{
    HRESULT hr = _GetFolder(FALSE);
    if (SUCCEEDED(hr))
        hr = _psfTarget->GetUIObjectOf(hwnd, cidl, apidl, riid, prgfInOut, ppv);
    return hr;
}

HRESULT CFolderShortcut::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, STRRET *pName)
{
    HRESULT hr = _GetFolder(FALSE);
    if (SUCCEEDED(hr))
        hr = _psfTarget->GetDisplayNameOf(pidl, uFlags, pName);
    return hr;
}

HRESULT CFolderShortcut::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl,
                                   LPCOLESTR pszName, DWORD uFlags,
                                   LPITEMIDLIST *ppidlOut)
{
    HRESULT hr = _GetFolder(FALSE);
    if (SUCCEEDED(hr))
        hr = _psfTarget->SetNameOf(hwnd, pidl, pszName, uFlags, ppidlOut);
    return hr;
}

STDMETHODIMP CFolderShortcut::GetDefaultSearchGUID(LPGUID lpGuid)
{
    HRESULT hr = _GetFolder2();
    if (SUCCEEDED(hr))
        hr = _psf2Target->GetDefaultSearchGUID(lpGuid);
    return hr;
}

STDMETHODIMP CFolderShortcut::EnumSearches(LPENUMEXTRASEARCH *ppenum)
{
    HRESULT hr = _GetFolder2();
    if (SUCCEEDED(hr))
        hr = _psf2Target->EnumSearches(ppenum);
    return hr;
}

STDMETHODIMP CFolderShortcut::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
{
    HRESULT hr = _GetFolder2();
    if (SUCCEEDED(hr))
        hr = _psf2Target->GetDefaultColumn(dwRes, pSort, pDisplay);
    return hr;
}

STDMETHODIMP CFolderShortcut::GetDefaultColumnState(UINT iColumn, DWORD *pbState)
{    
    HRESULT hr = _GetFolder2();
    if (SUCCEEDED(hr))
        hr = _psf2Target->GetDefaultColumnState(iColumn, pbState);
    return hr;
}

STDMETHODIMP CFolderShortcut::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
{
    HRESULT hr = _GetFolder2();
    if (SUCCEEDED(hr))
        hr = _psf2Target->GetDetailsEx(pidl, pscid, pv);
    return hr;
}

STDMETHODIMP CFolderShortcut::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, LPSHELLDETAILS pDetail)
{
    HRESULT hr = _GetFolder2();
    if (SUCCEEDED(hr))
        hr = _psf2Target->GetDetailsOf(pidl, iColumn, pDetail);
    return hr;
}

STDMETHODIMP CFolderShortcut::MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid)
{
    HRESULT hr = _GetFolder2();
    if (SUCCEEDED(hr))
        hr = _psf2Target->MapColumnToSCID(iColumn, pscid);
    return hr;
}

// IPersist
HRESULT CFolderShortcut::GetClassID(CLSID *pCLSID)
{
    *pCLSID = CLSID_FolderShortcut;
    return S_OK;
}

// IPersistFolder
HRESULT CFolderShortcut::Initialize(LPCITEMIDLIST pidl)
{
    HRESULT hr;

    // is the link loaded (could have been loaded through IPersistStream::Load)?
    if (_pslTarget)
    {
        // Yes, it's loaded so re-initialize
        // note, _szFolderPath will be empty since we are not loaded from the file system

        hr = Pidl_Set(&_pidlRoot, pidl) ? S_OK : E_OUTOFMEMORY;
    }
    else
    {
        // we explictly require initialization through 
        // IPersistFolder3::InitializeEx, if we don't do these we can
        // not defent against loops in the name space
        hr = E_FAIL;
    }

    return hr;
}

// IPersistFolder2
STDMETHODIMP CFolderShortcut::GetCurFolder(LPITEMIDLIST *ppidl)
{
    return GetCurFolderImpl(this->_pidlRoot, ppidl);
}

// IPersistFolder3
STDMETHODIMP CFolderShortcut::InitializeEx(IBindCtx *pbc, LPCITEMIDLIST pidlRoot, const PERSIST_FOLDER_TARGET_INFO *pfti)
{
    HRESULT hr = E_INVALIDARG;  // assume failure

    if ( NULL == pbc || (pbc && !SHSkipJunction(pbc, &CLSID_FolderShortcut)) )
    {
        _ClearState();

        if (pidlRoot)
            hr = SHILClone(pidlRoot, &_pidlRoot);

        if (pfti && pfti->pidlTargetFolder)
        {
            if ( SUCCEEDED(hr) )
                hr = SHILClone(pfti->pidlTargetFolder, &_pidlTargetFldrFromInit);

            if ( SUCCEEDED(hr) && !_szFolderPath[0] )
                hr = SHGetPathFromIDList(pfti->pidlTargetFolder, _szFolderPath) ? S_OK : E_FAIL;
        }
        else
        {
            if ( SUCCEEDED(hr) && !_szFolderPath[0] )
                hr = SHGetPathFromIDList(_pidlRoot, _szFolderPath) ? S_OK : E_FAIL;
        }

        if ( SUCCEEDED(hr) )
            hr = _LoadShortcut();
    }
    return hr;
}

HRESULT CFolderShortcut::GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO *pfti)
{
    HRESULT hr = S_OK;

    ZeroMemory(pfti, sizeof(*pfti)); 

    if ( _pidlTargetFldrFromInit )
        hr = SHILClone(_pidlTargetFldrFromInit, &pfti->pidlTargetFolder);

    pfti->dwAttributes = -1;
    pfti->csidl = -1;
    return hr;
}

HRESULT CFolderShortcut::_GetLink()
{
    HRESULT hr = _LoadShortcut();
    if (FAILED(hr))
    {
        // get an empty one in case we are going to be asked to save
        hr = SHCoCreateInstance(NULL, &CLSID_ShellLink, NULL, IID_PPV_ARG(IShellLinkW, &_pslTarget));
    }
    return hr;
}

HRESULT CFolderShortcut::_GetLinkQI(REFIID riid, void **ppv)
{
    HRESULT hr = _GetLink();
    if (SUCCEEDED(hr))
        hr = _pslTarget->QueryInterface(riid, ppv);
    return hr;
}

HRESULT CFolderShortcut::_GetLinkA()
{
    return _pslTargetA ? S_OK : _GetLinkQI(IID_PPV_ARG(IShellLinkA, &_pslTargetA));
}

// IPersistFile
STDMETHODIMP CFolderShortcut::Load(LPCOLESTR pszFileName, DWORD dwMode)
{
    _ClearState();

    SHUnicodeToTChar(pszFileName, _szFolderPath, ARRAYSIZE(_szFolderPath));
    return _LoadShortcut();
}

BOOL _IsFolder(LPCITEMIDLIST pidl)
{
    ULONG rgInfo = SFGAO_FOLDER;
    HRESULT hr = SHGetNameAndFlags(pidl, SHGDN_NORMAL, NULL, 0, &rgInfo);
    return SUCCEEDED(hr) && (rgInfo & SFGAO_FOLDER);
}

void PathStripTrailingDots(LPTSTR szPath)
{
    if (szPath[0] == TEXT('\0'))
        return;

    LPTSTR psz = &szPath[lstrlen(szPath) - 1];

    while ((*psz == TEXT('.')) && 
           (psz >= szPath))
    {
        *psz-- = TEXT('\0');
    }

}


STDMETHODIMP CFolderShortcut::Save(LPCOLESTR pszFileName, BOOL fRemember)
{
    HRESULT hr = _GetTargetIDList(FALSE);

    // We need to make sure the folder shortcut can be saved keeping in mind the MAX_PATH limitation
    // cchFSReserved is the number of characters to reserve for the largest file that will be created
    // in the foldershortcut directory, in this case, it is the ARRAYSIZE of "\\desktop.ini"
    static const int cchFSReserved = ARRAYSIZE(TEXT("\\desktop.ini")); 

    LPITEMIDLIST pidlInternet;

    // Don't create a folder shortcut to the internet folder.
    if (SUCCEEDED(hr) && SUCCEEDED(SHGetSpecialFolderLocation(NULL, CSIDL_INTERNET, &pidlInternet)))
    {
        if (ILIsEqual(_pidlTarget, pidlInternet))
        {
            hr = E_INVALIDARG;
        }
        ILFree(pidlInternet);
    }

    if (SUCCEEDED(hr) && _IsFolder(_pidlTarget))
    {
        // we know the target is a folder, create a folder shortcut.
        BOOL fCreatedDir;
        TCHAR szName[MAX_PATH];

        SHUnicodeToTChar(pszFileName, szName, ARRAYSIZE(szName));

        // Remove any exisiting extension. 
        // We dont want "Shortcut To My Documents.lnk.{GUID}
        if (PathFindCLSIDExtension(szName,NULL))
        {
            PathRemoveExtension(szName);
        }

        PathStripTrailingDots(szName);

        // Can't create a fldrshcut with too long a path
        if ((MAX_PATH - cchFSReserved) < lstrlen(szName))
        {
            hr = CO_E_PATHTOOLONG;
        }
        
        if (SUCCEEDED(hr))
        {
            if (PathIsDirectory(szName))
                fCreatedDir = FALSE;
            else
                fCreatedDir = SHCreateDirectory(NULL, szName) == 0;

            CreateFolderDesktopIni(szName);

            // Now initialize the child link
            IPersistFile *ppf;
            hr = _pslTarget->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
            if (SUCCEEDED(hr))
            {
                WCHAR wszName[MAX_PATH];
                SHTCharToUnicode(szName, wszName, ARRAYSIZE(wszName));

                PathAppendW(wszName, L"target.lnk");

                hr = ppf->Save(wszName, fRemember);
                if (SUCCEEDED(hr))
                {
                    if (fRemember)
                        Str_SetPtr(&_pszLastSave, szName);
                }

                ppf->Release();
            }

            if (FAILED(hr) && fCreatedDir) 
            {
                RemoveDirectory(szName);    // cleanup after ourselves.
            }
        }
    }
    else
    {
        // ensure that if we save as a file we do so with the right extension
        WCHAR szFile[MAX_PATH];
        StrCpy(szFile, pszFileName);
        PathRenameExtension(szFile, L".lnk");

        // the target is not a folder, create a normal shortcut in this case
        IPersistFile *ppf;
        hr = _pslTarget->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
        if (SUCCEEDED(hr))
        {
            hr = ppf->Save(szFile, fRemember);
            ppf->Release();
        }
    }
    return hr;
}

STDMETHODIMP CFolderShortcut::GetCurFile(LPOLESTR *ppszFileName)
{
    HRESULT hr = E_FAIL;
    if (_pszLastSave)
        hr = SHStrDup(_pszLastSave, ppszFileName);
    else if (_pslTarget)
    {
        IPersistFile *ppf;
        hr = _pslTarget->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
        if (SUCCEEDED(hr))
        {
            hr = ppf->GetCurFile(ppszFileName);
            ppf->Release();
        }
    }
    return hr;
}

// IShellLinkW

STDMETHODIMP CFolderShortcut::GetPath(LPWSTR pszFile, int cchMaxPath, WIN32_FIND_DATAW *pfd, DWORD flags)
{
    HRESULT hr = _GetLink();
    if (SUCCEEDED(hr))
        hr = _pslTarget->GetPath(pszFile, cchMaxPath, pfd, flags);
    return hr;
}

STDMETHODIMP CFolderShortcut::SetPath(LPCWSTR pwszFile)
{
    HRESULT hr = _GetLink();
    if (SUCCEEDED(hr) && PathIsDirectoryW(pwszFile))
    {
        hr = _pslTarget->SetPath(pwszFile);
        Pidl_Set(&_pidlTarget, NULL);
    }
    return hr;
}

STDMETHODIMP CFolderShortcut::GetIDList(LPITEMIDLIST *ppidl)
{
    HRESULT hr = _GetLink();
    if (SUCCEEDED(hr))
        hr = _pslTarget->GetIDList(ppidl);
    else
        *ppidl = NULL;
    return hr;
}

STDMETHODIMP CFolderShortcut::SetIDList(LPCITEMIDLIST pidl)
{
    HRESULT hr = _GetLink();
    if (SUCCEEDED(hr))
    {
        hr = _pslTarget->SetIDList(pidl);
        Pidl_Set(&_pidlTarget, NULL);
    }
    return hr;
}

STDMETHODIMP CFolderShortcut::GetDescription(LPWSTR wszName, int cchMaxName)
{
    HRESULT hr = _GetLink();
    if (SUCCEEDED(hr))
        hr = _pslTarget->GetDescription(wszName, cchMaxName);
    return hr;
}

STDMETHODIMP CFolderShortcut::SetDescription(LPCWSTR wszName)
{
    HRESULT hr = _GetLink();
    if (SUCCEEDED(hr))
        hr = _pslTarget->SetDescription(wszName);
    return hr;
}

STDMETHODIMP CFolderShortcut::GetWorkingDirectory(LPWSTR wszDir, int cchMaxPath)
{
    HRESULT hr = _GetLink();
    if (SUCCEEDED(hr))
        hr = _pslTarget->GetWorkingDirectory(wszDir, cchMaxPath);
    return hr;
}

STDMETHODIMP CFolderShortcut::SetWorkingDirectory(LPCWSTR wszDir)
{
    HRESULT hr = _GetLink();
    if (SUCCEEDED(hr))
        hr = _pslTarget->SetWorkingDirectory(wszDir);
    return hr;
}

STDMETHODIMP CFolderShortcut::GetArguments(LPWSTR wszArgs, int cchMaxPath)
{
    HRESULT hr = _GetLink();
    if (SUCCEEDED(hr))
        hr = _pslTarget->GetArguments(wszArgs, cchMaxPath);//this is probably not at all useful.
    return hr;
}

STDMETHODIMP CFolderShortcut::SetArguments(LPCWSTR wszArgs)
{
    HRESULT hr = _GetLink();
    if (SUCCEEDED(hr))
        hr = _pslTarget->SetArguments(wszArgs);//this is probably not at all useful.
    return hr;
}

STDMETHODIMP CFolderShortcut::GetHotkey(WORD *pwHotkey)
{
    HRESULT hr = _GetLink();
    if (SUCCEEDED(hr))
        hr = _pslTarget->GetHotkey(pwHotkey);
    return hr;
}

STDMETHODIMP CFolderShortcut::SetHotkey(WORD wHotkey)
{
    HRESULT hr = _GetLink();
   if (SUCCEEDED(hr))
        hr = _pslTarget->SetHotkey(wHotkey);
    return hr;
}

STDMETHODIMP CFolderShortcut::GetShowCmd(int *piShowCmd)
{
    HRESULT hr = _GetLink();
    if (SUCCEEDED(hr))
        hr = _pslTarget->GetShowCmd(piShowCmd);
    return hr;
}

STDMETHODIMP CFolderShortcut::SetShowCmd(int iShowCmd)
{
    HRESULT hr = _GetLink();
   if (SUCCEEDED(hr))
        hr = _pslTarget->SetShowCmd(iShowCmd);
    return hr;
}

STDMETHODIMP CFolderShortcut::GetIconLocation(LPWSTR wszIconPath, int cchIconPath, int *piIcon)
{
    HRESULT hr = _GetLink();
    if (SUCCEEDED(hr))
        hr = _pslTarget->GetIconLocation(wszIconPath, cchIconPath, piIcon);
    return hr;
}

STDMETHODIMP CFolderShortcut::SetIconLocation(LPCWSTR wszIconPath, int iIcon)
{
    HRESULT hr = _GetLink();
    if  (SUCCEEDED(hr))
        hr = _pslTarget->SetIconLocation(wszIconPath, iIcon);

    return hr;
}

STDMETHODIMP CFolderShortcut::Resolve(HWND hwnd, DWORD fFlags)
{
    HRESULT hr = _GetLink();
    if (SUCCEEDED(hr))
        hr = _pslTarget->Resolve(hwnd, fFlags);
    return hr;
}

STDMETHODIMP CFolderShortcut::SetRelativePath(LPCWSTR wszPathRel, DWORD dwReserved)
{
    HRESULT hr = _GetLink();
    if (SUCCEEDED(hr))
        hr = _pslTarget->SetRelativePath(wszPathRel, dwReserved);

    return hr;
}

// IShellLinkA
STDMETHODIMP CFolderShortcut::GetPath(LPSTR pszFile, int cchMaxPath, WIN32_FIND_DATAA *pfd, DWORD flags)
{
    HRESULT hr = _GetLinkA();
    if (SUCCEEDED(hr))
        hr = _pslTargetA->GetPath(pszFile, cchMaxPath, pfd, flags);
    return hr;
}

STDMETHODIMP CFolderShortcut::GetDescription(LPSTR pszName, int cchMaxName)
{
    HRESULT hr = _GetLinkA();
    if (SUCCEEDED(hr))
        hr = _pslTargetA->GetDescription(pszName, cchMaxName);
    return hr;
}

STDMETHODIMP CFolderShortcut::GetWorkingDirectory(LPSTR pszDir, int cchMaxPath)
{
    HRESULT hr = _GetLinkA();
    if (SUCCEEDED(hr))
        hr = _pslTargetA->GetWorkingDirectory(pszDir, cchMaxPath);
    return hr;
}

STDMETHODIMP CFolderShortcut::GetArguments(LPSTR pszArgs, int cchMaxPath)
{
    HRESULT hr = _GetLinkA();
    if (SUCCEEDED(hr))
        hr = _pslTargetA->GetArguments(pszArgs, cchMaxPath);//this is probably not at all useful.
    return hr;
}

STDMETHODIMP CFolderShortcut::GetIconLocation(LPSTR pszIconPath, int cchIconPath, int *piIcon)
{
    HRESULT hr = _GetLinkA();
    if (SUCCEEDED(hr)) 
       hr = _pslTargetA->GetIconLocation(pszIconPath, cchIconPath, piIcon);    
    return hr;
}

STDMETHODIMP CFolderShortcut::SetPath(LPCSTR pszFile)
{
    HRESULT hr = _GetLinkA();
    if (SUCCEEDED(hr) && PathIsDirectoryA(pszFile))
    {
        hr = _pslTargetA->SetPath(pszFile);
        Pidl_Set(&_pidlTarget, NULL);
    }
    return hr;
}

STDMETHODIMP CFolderShortcut::SetDescription(LPCSTR pszName)
{
    HRESULT hr = _GetLinkA();
    if (SUCCEEDED(hr))
        hr = _pslTargetA->SetDescription(pszName);
    return hr;
}

STDMETHODIMP CFolderShortcut::SetWorkingDirectory(LPCSTR pszDir)
{
    HRESULT hr = _GetLinkA();
    if (SUCCEEDED(hr))
        hr = _pslTargetA->SetWorkingDirectory(pszDir);
    return hr;
}

STDMETHODIMP CFolderShortcut::SetArguments(LPCSTR pszArgs)
{
    HRESULT hr = _GetLinkA();
    if (SUCCEEDED(hr))
        hr = _pslTargetA->SetArguments(pszArgs);
    return hr;
}

STDMETHODIMP CFolderShortcut::SetIconLocation(LPCSTR pszIconPath, int iIcon)
{
    HRESULT hr = _GetLinkA();
    if (SUCCEEDED(hr))    
        hr = _pslTargetA->SetIconLocation(pszIconPath, iIcon);
    return hr;
}

STDMETHODIMP CFolderShortcut::SetRelativePath(LPCSTR pszPathRel, DWORD dwReserved)
{
    HRESULT hr = _GetLinkA();
    if (SUCCEEDED(hr))
        hr = _pslTargetA->SetRelativePath(pszPathRel, dwReserved);
    return hr;
}

STDMETHODIMP CFolderShortcut::GetIconLocation(UINT uFlags, LPTSTR pszIconFile, UINT ucchMax, PINT pniIcon, PUINT puFlags)
{
    IExtractIcon *pxi;
    HRESULT hr = _GetLinkQI(IID_PPV_ARG(IExtractIcon, &pxi));
    if (SUCCEEDED(hr))
    {
        hr = pxi->GetIconLocation(uFlags, pszIconFile, ucchMax, pniIcon, puFlags);
        pxi->Release();
    }
    return hr;
}

STDMETHODIMP CFolderShortcut::Extract(LPCTSTR pcszFile, UINT uIconIndex, HICON *phiconLarge, HICON *phiconSmall, UINT ucIconSize)
{
    IExtractIcon *pxi;
    HRESULT hr = _GetLinkQI(IID_PPV_ARG(IExtractIcon, &pxi));
    if (SUCCEEDED(hr))
    {
        hr = pxi->Extract(pcszFile, uIconIndex, phiconLarge, phiconSmall, ucIconSize);
        pxi->Release();
    }
    return hr;
}

HRESULT CFolderShortcut::GetInfoTip(DWORD dwFlags, WCHAR** ppwszText)
{
    IQueryInfo *pqi;
    HRESULT hr = _GetLinkQI(IID_PPV_ARG(IQueryInfo, &pqi));
    if (SUCCEEDED(hr))
    {
        hr = pqi->GetInfoTip(dwFlags | QITIPF_LINKUSETARGET, ppwszText);
        pqi->Release();
    }
    return hr;
}

HRESULT CFolderShortcut::GetInfoFlags(DWORD *pdwFlags)
{
    IQueryInfo *pqi;
    HRESULT hr = _GetLinkQI(IID_PPV_ARG(IQueryInfo, &pqi));
    if (SUCCEEDED(hr))
    {
        hr = pqi->GetInfoFlags(pdwFlags);
        pqi->Release();
    }
    return hr;
}


// IBrowserFrameOptions
HRESULT CFolderShortcut::GetFrameOptions(IN BROWSERFRAMEOPTIONS dwMask, IN BROWSERFRAMEOPTIONS * pdwOptions)
{
    HRESULT hr = _GetFolder(FALSE);

    *pdwOptions = BFO_NONE;
    if (SUCCEEDED(hr))
    {
        IBrowserFrameOptions *pbfo;

        hr = _psfTarget->QueryInterface(IID_PPV_ARG(IBrowserFrameOptions, &pbfo));
        if (SUCCEEDED(hr))
        {
            hr = pbfo->GetFrameOptions(dwMask, pdwOptions);        
            pbfo->Release();
        }
    }
    
    return hr;
}


// IPersistStream
STDMETHODIMP CFolderShortcut::Load(IStream *pStm)
{
    _ClearState();

    IPersistStream *pps;
    HRESULT hr = SHCoCreateInstance(NULL, &CLSID_ShellLink, NULL, IID_PPV_ARG(IPersistStream, &pps));
    if (SUCCEEDED(hr))
    {
        hr = pps->Load(pStm);
        if (SUCCEEDED(hr))
            pps->QueryInterface(IID_PPV_ARG(IShellLinkW, &_pslTarget));  // keep this guy
        pps->Release();
    }
    return hr;
}

// IPersistStream
STDMETHODIMP CFolderShortcut::Save(IStream *pStm, int fClearDirty)
{
    return E_NOTIMPL;
}

// IPersistStream
STDMETHODIMP CFolderShortcut::GetSizeMax(ULARGE_INTEGER * pcbSize)
{
    return E_NOTIMPL;
}

//
// IFolderShortcut::ConvertToLink.
//
// destructively convert a Folder Shortcut into a Shell Link.
//
//  pszFolderShortcut is the path to an existing folder shortcut
//  c:\Folder Shortcut.{guid}   - deleted
//  c:\Folder Shortcut.lnk      - created
//
STDMETHODIMP CFolderShortcut::ConvertToLink(LPCOLESTR pszFolderShortcut, DWORD fFlags)
{
    HRESULT hr = E_FAIL;
    TCHAR szName[MAX_PATH];

    SHUnicodeToTChar(pszFolderShortcut, szName, ARRAYSIZE(szName));

    if (PathIsDirectory(szName) && IsFolderShortcut(szName))
    {
        TCHAR szLinkName[MAX_PATH];

        // c:\Folder Shortcut\target.lnk 
        StrCpyN(szLinkName, szName, ARRAYSIZE(szLinkName));
        PathAppend(szLinkName, TEXT("target.lnk"));

        PathRenameExtension(szName, TEXT(".lnk"));

        // FS.lnk -> FS.{guid}
        CopyFile(szLinkName, szName, FALSE);

        PathRemoveExtension(szName);

        if (DeleteFile(szLinkName) && 
            PathAppend(szName, TEXT("desktop.ini")) && DeleteFile(szName) &&
            PathRemoveFileSpec(szName) && RemoveDirectory(szName))
        {
            hr = S_OK;
        }
    }
    return hr;
}

//
// IFolderShortcut::ConvertToFolderShortcut.
//
// destructively convert a Shell Link (.lnk) -> Folder Shortcut (Folder.{guid}).
//  pszPathLNK is the path to an existing .lnk file
//  c:\Folder Shortcut.lnk      - deleted
//  c:\Folder Shortcut.{guid}   - created
//
STDMETHODIMP CFolderShortcut::ConvertToFolderShortcut(LPCOLESTR pszPathLNK, DWORD fFlags)
{
    //must bind to the link, resolve it, and make sure it points to a folder.
    IShellLink *psl;
    HRESULT hr = SHCoCreateInstance(NULL, &CLSID_ShellLink, NULL, IID_PPV_ARG(IShellLink, &psl));
    if (SUCCEEDED(hr))  
    {
        IPersistFile *ppf;
        hr = psl->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
        if (SUCCEEDED(hr))
        {
            hr = ppf->Load(pszPathLNK, STGM_READ);
            if (SUCCEEDED(hr))
            {
                hr = psl->Resolve(NULL, SLR_NO_UI); // make sure the link is real
                if (SUCCEEDED(hr))
                {
                    LPITEMIDLIST pidl;

                    hr = psl->GetIDList(&pidl);
                    if (hr == S_OK)
                    {
                        // this should maybe work on the pidl so that 
                        // it doesn't have to worry about files.
                        if (_IsFolder(pidl))
                        {             
                            hr = E_FAIL;

                            TCHAR szPath[MAX_PATH], szName[MAX_PATH]; 
                            SHUnicodeToTChar(pszPathLNK, szName, ARRAYSIZE(szName));
                            StrCpyN(szPath, szName, ARRAYSIZE(szPath));
                            PathRemoveExtension(szName);
                            BOOL fCreatedDir = SHCreateDirectory(NULL, szName) == 0;

                            if (CreateFolderDesktopIni(szName) &&
                                PathAppend(szName, TEXT("target.lnk")))
                            {   
                                //copy the link file into the new directory.
                                if (CopyFile(szPath, szName, FALSE))
                                {
                                    if (DeleteFile(szPath)) //if all goes well, delete the old.
                                        hr = S_OK;
                                }
                                else
                                {
                                    PathRemoveFileSpec(szName);
                                    if (fCreatedDir)
                                        RemoveDirectory(szName);
                                }
                            }
                        }
                        else
                            hr = E_FAIL;
                        ILFree(pidl);
                    }
                    else
                        hr = E_FAIL;
                }
            }
            ppf->Release();
        }
        psl->Release();
    }
    
    return hr;
}

// IPersistPropertyBag
STDMETHODIMP CFolderShortcut::Save(IPropertyBag* pPropBag, BOOL fClearDirty, BOOL fSaveAllProperties)
{
    return E_NOTIMPL;
}

// IPersistPropertyBag
STDMETHODIMP CFolderShortcut::Load(IPropertyBag* pPropBag, IErrorLog* pErrorLog)
{
    _ClearState();

    IPersistPropertyBag* pppb;
    HRESULT hr = SHCoCreateInstance(NULL, &CLSID_ShellLink, NULL, IID_PPV_ARG(IPersistPropertyBag, &pppb));
    if (SUCCEEDED(hr))
    {
        hr = pppb->Load(pPropBag, pErrorLog);
        if (SUCCEEDED(hr))
        {
            hr = pppb->QueryInterface(IID_PPV_ARG(IShellLinkW, &_pslTarget));

            DWORD dwFlags;
            if (SUCCEEDED(SHPropertyBag_ReadDWORD(pPropBag, L"Attributes", &dwFlags)))
                _dwAttributesTarget = dwFlags;
        }
        pppb->Release();
    }

    return hr;
}

STDMETHODIMP CFolderShortcut::InitNew(void)
{
    _ClearState();
    return S_OK;
}
