|
|
// 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; }
|