1876 lines
54 KiB
C++
1876 lines
54 KiB
C++
|
#include "shellprv.h"
|
||
|
#include <caggunk.h>
|
||
|
#include "ids.h"
|
||
|
#include "datautil.h"
|
||
|
#include "idlcomm.h"
|
||
|
#include "idldata.h"
|
||
|
#include "views.h"
|
||
|
#include "stgutil.h"
|
||
|
#pragma hdrstop
|
||
|
|
||
|
typedef struct
|
||
|
{
|
||
|
WORD wSize;
|
||
|
DWORD dwMagic;
|
||
|
DWORD dwType;
|
||
|
ULARGE_INTEGER cbFileSize;
|
||
|
union
|
||
|
{
|
||
|
FILETIME ftModified;
|
||
|
ULARGE_INTEGER ulModified;
|
||
|
};
|
||
|
WCHAR szName[MAX_PATH];
|
||
|
WORD wZero;
|
||
|
} STGITEM;
|
||
|
|
||
|
typedef UNALIGNED STGITEM * LPSTGITEM;
|
||
|
typedef const UNALIGNED STGITEM * LPCSTGITEM;
|
||
|
|
||
|
// this sets stgfldr pidls apart from others
|
||
|
#define STGITEM_MAGIC 0x08311978
|
||
|
|
||
|
|
||
|
static const struct
|
||
|
{
|
||
|
UINT iTitle;
|
||
|
UINT cchCol;
|
||
|
UINT iFmt;
|
||
|
}
|
||
|
g_aStgColumns[] =
|
||
|
{
|
||
|
{IDS_NAME_COL, 20, LVCFMT_LEFT},
|
||
|
{IDS_SIZE_COL, 10, LVCFMT_RIGHT},
|
||
|
{IDS_TYPE_COL, 20, LVCFMT_LEFT},
|
||
|
{IDS_MODIFIED_COL, 20, LVCFMT_LEFT},
|
||
|
};
|
||
|
|
||
|
enum
|
||
|
{
|
||
|
STG_COL_NAME,
|
||
|
STG_COL_SIZE,
|
||
|
STG_COL_TYPE,
|
||
|
STG_COL_MODIFIED,
|
||
|
};
|
||
|
|
||
|
|
||
|
// folder object
|
||
|
|
||
|
class CStgFolder;
|
||
|
class CStgEnum;
|
||
|
class CStgDropTarget;
|
||
|
|
||
|
STDAPI CStgEnum_CreateInstance(CStgFolder *pstgf, DWORD grfFlags, IEnumIDList **ppenum);
|
||
|
STDAPI CStgDropTarget_CreateInstance(CStgFolder *pstgf, HWND hwnd, IDropTarget **ppdt);
|
||
|
STDAPI CStgFolder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv);
|
||
|
|
||
|
|
||
|
class CStgFolder : public CAggregatedUnknown, IShellFolder2, IPersistFolder3, IShellFolderViewCB, IStorage, IPersistStorage
|
||
|
{
|
||
|
public:
|
||
|
CStgFolder(IUnknown *punkAgg, CStgFolder *pstgfParent);
|
||
|
~CStgFolder();
|
||
|
|
||
|
// IUnknown
|
||
|
STDMETHODIMP QueryInterface(REFIID riid, void **ppv) { return CAggregatedUnknown::QueryInterface(riid,ppv);};
|
||
|
STDMETHODIMP_(ULONG) AddRef(void) { return CAggregatedUnknown::AddRef();};
|
||
|
STDMETHODIMP_(ULONG) Release(void) { return CAggregatedUnknown::Release();};
|
||
|
|
||
|
// IPersist
|
||
|
STDMETHODIMP GetClassID(CLSID *pClassID)
|
||
|
{ *pClassID = CLSID_StgFolder; return S_OK; }
|
||
|
STDMETHODIMP Initialize(LPCITEMIDLIST pidl);
|
||
|
|
||
|
// IPersistFolder2
|
||
|
STDMETHODIMP GetCurFolder(LPITEMIDLIST *ppidl);
|
||
|
|
||
|
// IPersistFolder3
|
||
|
STDMETHODIMP GetFolderTargetInfo(PERSIST_FOLDER_TARGET_INFO *ppfti)
|
||
|
{ return E_NOTIMPL; }
|
||
|
STDMETHODIMP InitializeEx(IBindCtx *pbc, LPCITEMIDLIST pidlRoot, const PERSIST_FOLDER_TARGET_INFO *ppfti);
|
||
|
|
||
|
// IShellFolder
|
||
|
STDMETHODIMP ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pszName, ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG *pdwAttributes);
|
||
|
STDMETHODIMP EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList)
|
||
|
{ return CStgEnum_CreateInstance(this, grfFlags, ppenumIDList); };
|
||
|
STDMETHODIMP BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv);
|
||
|
STDMETHODIMP BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
|
||
|
{ return BindToObject(pidl, pbc, riid, ppv); };
|
||
|
STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
|
||
|
STDMETHODIMP CreateViewObject(HWND hwndOwner, REFIID riid, void **ppv);
|
||
|
STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST * apidl, ULONG *rgfInOut);
|
||
|
STDMETHODIMP GetUIObjectOf(HWND hwndOwner, 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)
|
||
|
{ return E_NOTIMPL; };
|
||
|
STDMETHODIMP EnumSearches(IEnumExtraSearch **ppenum)
|
||
|
{ return E_NOTIMPL; };
|
||
|
STDMETHODIMP GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
|
||
|
{ return E_NOTIMPL; };
|
||
|
STDMETHODIMP GetDefaultColumnState(UINT iColumn, DWORD *pbState)
|
||
|
{ return E_NOTIMPL; }
|
||
|
STDMETHODIMP GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
|
||
|
{ return E_NOTIMPL; };
|
||
|
STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails);
|
||
|
STDMETHODIMP MapColumnToSCID(UINT iColumn, SHCOLUMNID *pscid)
|
||
|
{ return E_NOTIMPL; };
|
||
|
|
||
|
// IShellFolderViewCB
|
||
|
STDMETHODIMP MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||
|
|
||
|
// IStorage
|
||
|
STDMETHODIMP Commit(DWORD grfCommitFlags);
|
||
|
STDMETHODIMP Revert();
|
||
|
STDMETHODIMP SetClass(REFCLSID clsid);
|
||
|
STDMETHODIMP SetStateBits(DWORD grfStateBits, DWORD grfMask);
|
||
|
STDMETHODIMP Stat(STATSTG *pstatstg, DWORD grfStatFlag);
|
||
|
STDMETHODIMP EnumElements(DWORD reserved1, void *reserved2, DWORD reserved3, IEnumSTATSTG **ppenum);
|
||
|
STDMETHODIMP OpenStream(LPCWSTR pszRel, VOID *reserved1, DWORD grfMode, DWORD reserved2, IStream **ppstm);
|
||
|
STDMETHODIMP OpenStorage(LPCWSTR pszRel, IStorage *pstgPriority, DWORD grfMode, SNB snbExclude, DWORD reserved, IStorage **ppstg);
|
||
|
STDMETHODIMP DestroyElement(LPCWSTR pszRel);
|
||
|
STDMETHODIMP RenameElement(LPCWSTR pwcsOldName, LPCWSTR pwcsNewName);
|
||
|
STDMETHODIMP SetElementTimes(LPCWSTR pszRel, const FILETIME *pctime, const FILETIME *patime, const FILETIME *pmtime);
|
||
|
STDMETHODIMP CopyTo(DWORD ciidExclude, const IID *rgiidExclude, SNB snbExclude, IStorage *pstgDest);
|
||
|
STDMETHODIMP MoveElementTo(LPCWSTR pszRel, IStorage *pstgDest, LPCWSTR pwcsNewName, DWORD grfFlags);
|
||
|
STDMETHODIMP CreateStream(LPCWSTR pwcsName, DWORD grfMode, DWORD res1, DWORD res2, IStream **ppstm);
|
||
|
STDMETHODIMP CreateStorage(LPCWSTR pwcsName, DWORD grfMode, DWORD res1, DWORD res2, IStorage **ppstg);
|
||
|
|
||
|
// IPersistStorage
|
||
|
STDMETHODIMP IsDirty(void);
|
||
|
STDMETHODIMP InitNew(IStorage *pStg);
|
||
|
STDMETHODIMP Load(IStorage *pStg);
|
||
|
STDMETHODIMP Save(IStorage *pStgSave, BOOL fSameAsLoad);
|
||
|
STDMETHODIMP SaveCompleted(IStorage *pStgNew);
|
||
|
STDMETHODIMP HandsOffStorage(void);
|
||
|
|
||
|
private:
|
||
|
LONG _cRef;
|
||
|
CStgFolder *_pstgfParent;
|
||
|
IStorage *_pstg;
|
||
|
IStorage *_pstgLoad;
|
||
|
LPITEMIDLIST _pidl;
|
||
|
DWORD _dwMode;
|
||
|
|
||
|
virtual HRESULT v_InternalQueryInterface(REFIID riid, void **ppv);
|
||
|
|
||
|
HRESULT _BindToStorageObject(LPCITEMIDLIST pidl, DWORD grfMode, IStorage **ppstg);
|
||
|
BOOL _OkayWithCurrentMode(DWORD grfMode);
|
||
|
HRESULT _EnsureStorage(DWORD grfMode);
|
||
|
void _CloseStorage();
|
||
|
HRESULT _InitNewStgFolder(CStgFolder *pstgf, DWORD grfMode, LPCITEMIDLIST pidlNew);
|
||
|
HRESULT _AllocIDList(STATSTG stat, LPITEMIDLIST *ppidl, BOOL *pfFolder);
|
||
|
HRESULT _AllocIDList(LPCTSTR pszName, LPITEMIDLIST *ppidl, BOOL *pfFolder);
|
||
|
HRESULT _SetShortcutStorage(IStorage *pstgLink);
|
||
|
HRESULT _GetTypeOf(LPCSTGITEM pistg, LPTSTR pszBuffer, INT cchBuffer);
|
||
|
ULONG _GetAttributesOf(LPCSTGITEM pistg, ULONG rgfIn);
|
||
|
BOOL _ShowExtension();
|
||
|
static HRESULT CALLBACK _ItemsMenuCB(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam);
|
||
|
|
||
|
// folder view callback handlers
|
||
|
HRESULT _OnBackgroundEnum(DWORD pv)
|
||
|
{ return S_OK; };
|
||
|
HRESULT _OnGetNotify(DWORD pv, LPITEMIDLIST *ppidl, LONG *plEvents);
|
||
|
|
||
|
LPCSTGITEM _IsStgItem(LPCITEMIDLIST pidl);
|
||
|
DWORD _IsFolder(LPCSTGITEM psitem);
|
||
|
|
||
|
friend CStgEnum;
|
||
|
friend CStgDropTarget;
|
||
|
friend HRESULT CStgFolder_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv);
|
||
|
};
|
||
|
|
||
|
|
||
|
// Construction and IUnknown for folder root
|
||
|
|
||
|
CStgFolder::CStgFolder(IUnknown *punkAgg, CStgFolder *pstgfParent) : CAggregatedUnknown(punkAgg),
|
||
|
_dwMode(STGM_READ), _cRef(1)
|
||
|
{
|
||
|
ASSERT(NULL == _pidl);
|
||
|
ASSERT(NULL == _pstg);
|
||
|
ASSERT(NULL == _pstgLoad);
|
||
|
|
||
|
_pstgfParent = pstgfParent;
|
||
|
if (_pstgfParent)
|
||
|
_pstgfParent->AddRef();
|
||
|
|
||
|
DllAddRef();
|
||
|
}
|
||
|
|
||
|
CStgFolder::~CStgFolder()
|
||
|
{
|
||
|
ILFree(_pidl);
|
||
|
|
||
|
if (_pstg)
|
||
|
_pstg->Commit(STGC_DEFAULT);
|
||
|
|
||
|
ATOMICRELEASE(_pstg);
|
||
|
ATOMICRELEASE(_pstgfParent);
|
||
|
ATOMICRELEASE(_pstgLoad);
|
||
|
|
||
|
DllRelease();
|
||
|
}
|
||
|
|
||
|
HRESULT CStgFolder::v_InternalQueryInterface(REFIID riid, void **ppv)
|
||
|
{
|
||
|
static const QITAB qit[] =
|
||
|
{
|
||
|
QITABENTMULTI(CStgFolder, IShellFolder, IShellFolder2), // IID_IShellFolder
|
||
|
QITABENT (CStgFolder, IShellFolder2), // IID_IShellFolder2
|
||
|
QITABENTMULTI(CStgFolder, IPersist, IPersistFolder3), // IID_IPersist
|
||
|
QITABENTMULTI(CStgFolder, IPersistFolder, IPersistFolder3), // IID_IPersistFolder
|
||
|
QITABENTMULTI(CStgFolder, IPersistFolder2, IPersistFolder3), // IID_IPersistFolder2
|
||
|
QITABENT (CStgFolder, IPersistStorage), // IID_IPersistStorage
|
||
|
QITABENT (CStgFolder, IPersistFolder3), // IID_IPersistFolder3
|
||
|
QITABENT (CStgFolder, IShellFolderViewCB), // IID_IShellFolderViewCB
|
||
|
QITABENT (CStgFolder, IStorage), // IID_IStorage
|
||
|
{ 0 },
|
||
|
};
|
||
|
|
||
|
if (IsEqualIID(CLSID_StgFolder, riid))
|
||
|
{
|
||
|
*ppv = this; // not ref counted
|
||
|
return S_OK;
|
||
|
}
|
||
|
return QISearch(this, qit, riid, ppv);
|
||
|
}
|
||
|
|
||
|
|
||
|
STDAPI CStgFolder_CreateInstance(IUnknown* punkOuter, REFIID riid, void** ppv)
|
||
|
{
|
||
|
CStgFolder *pstgf = new CStgFolder(punkOuter, NULL);
|
||
|
if (!pstgf)
|
||
|
return E_OUTOFMEMORY;
|
||
|
|
||
|
HRESULT hr = pstgf->_GetInner()->QueryInterface(riid, ppv);
|
||
|
pstgf->_GetInner()->Release();
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
LPCSTGITEM CStgFolder::_IsStgItem(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
if (pidl && ((LPCSTGITEM) pidl)->dwMagic == STGITEM_MAGIC)
|
||
|
return (LPCSTGITEM)pidl;
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
// BOOL, but returns FILE_ATTRIBUTE_DIRECTORY for convience
|
||
|
|
||
|
DWORD CStgFolder::_IsFolder(LPCSTGITEM pistg)
|
||
|
{
|
||
|
return pistg->dwType == STGTY_STORAGE ? FILE_ATTRIBUTE_DIRECTORY : 0;
|
||
|
}
|
||
|
|
||
|
// Creates an item identifier list for the objects in the namespace
|
||
|
|
||
|
HRESULT CStgFolder::_AllocIDList(STATSTG stat, LPITEMIDLIST *ppidl, BOOL *pfFolder)
|
||
|
{
|
||
|
// Note the terminating NULL is already in the sizeof(STGITEM)
|
||
|
STGITEM sitem = {0};
|
||
|
UINT uNameLen = lstrlen(stat.pwcsName);
|
||
|
|
||
|
if (uNameLen >= MAX_PATH)
|
||
|
{
|
||
|
return E_INVALIDARG;
|
||
|
}
|
||
|
|
||
|
sitem.wSize = (WORD)(FIELD_OFFSET(STGITEM, szName[uNameLen + 1]));
|
||
|
sitem.dwMagic = STGITEM_MAGIC;
|
||
|
sitem.dwType = stat.type;
|
||
|
sitem.cbFileSize = stat.cbSize;
|
||
|
sitem.ftModified = stat.mtime;
|
||
|
lstrcpyn(sitem.szName, stat.pwcsName, uNameLen + 1);
|
||
|
|
||
|
if (pfFolder)
|
||
|
{
|
||
|
*pfFolder = _IsFolder(&sitem);
|
||
|
}
|
||
|
|
||
|
return SHILClone((LPCITEMIDLIST)&sitem, ppidl);
|
||
|
}
|
||
|
|
||
|
HRESULT CStgFolder::_AllocIDList(LPCTSTR pszName, LPITEMIDLIST *ppidl, BOOL *pfFolder)
|
||
|
{
|
||
|
// given a name, look it up in the current storage object we have and
|
||
|
// compute the STATSTG which we can then build an IDLIST from.
|
||
|
|
||
|
DWORD grfMode = STGM_READ;
|
||
|
HRESULT hr = _EnsureStorage(grfMode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
STATSTG stat;
|
||
|
|
||
|
// is it a stream or a storage?
|
||
|
|
||
|
IStream *pstrm;
|
||
|
hr = _pstg->OpenStream(pszName, NULL, grfMode, 0, &pstrm);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = pstrm->Stat(&stat, STATFLAG_DEFAULT);
|
||
|
pstrm->Release();
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
IStorage *pstg;
|
||
|
hr = _pstg->OpenStorage(pszName, NULL, grfMode, NULL, 0, &pstg);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = pstg->Stat(&stat, STATFLAG_DEFAULT);
|
||
|
pstg->Release();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// if that worked then lets allocate the object,
|
||
|
// nb: release the name returned in the STATSTG
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = _AllocIDList(stat, ppidl, pfFolder);
|
||
|
CoTaskMemFree(stat.pwcsName);
|
||
|
}
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
void CStgFolder::_CloseStorage()
|
||
|
{
|
||
|
if (_pstg)
|
||
|
{
|
||
|
_pstg->Commit(STGC_DEFAULT);
|
||
|
ATOMICRELEASE(_pstg);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CStgFolder::_BindToStorageObject(LPCITEMIDLIST pidl, DWORD grfMode, IStorage **ppstg)
|
||
|
{
|
||
|
IBindCtx *pbc;
|
||
|
HRESULT hr = SHCreateSkipBindCtx(NULL, &pbc); // NULL to mean we skip all CLSIDs
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
BIND_OPTS bo = {sizeof(bo)};
|
||
|
hr = pbc->GetBindOptions(&bo);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
bo.grfMode = grfMode;
|
||
|
hr = pbc->SetBindOptions(&bo);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = SHBindToObjectEx(NULL, pidl, pbc, IID_PPV_ARG(IStorage, ppstg));
|
||
|
}
|
||
|
}
|
||
|
pbc->Release();
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CStgFolder::_SetShortcutStorage(IStorage *pstgLink)
|
||
|
{
|
||
|
#if 1
|
||
|
return CShortcutStorage_CreateInstance(pstgLink, IID_PPV_ARG(IStorage, &_pstg));
|
||
|
#else
|
||
|
IUnknown_Set((IUnknown **)&_pstg, (IUnknown *)pstgLink);
|
||
|
return S_OK;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL CStgFolder::_OkayWithCurrentMode(DWORD grfMode)
|
||
|
{
|
||
|
DWORD dwNewBits = grfMode & (STGM_READ | STGM_WRITE | STGM_READWRITE);
|
||
|
DWORD dwOldBits = _dwMode & (STGM_READ | STGM_WRITE | STGM_READWRITE);
|
||
|
return (dwOldBits == STGM_READWRITE) || (dwOldBits == dwNewBits);
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CStgFolder::_EnsureStorage(DWORD grfMode)
|
||
|
{
|
||
|
// if we have a storage and its mode encompasses the grfMode we need then we
|
||
|
// can skip the whole thing.
|
||
|
|
||
|
HRESULT hr = S_OK;
|
||
|
if (!_pstg || !_OkayWithCurrentMode(grfMode))
|
||
|
{
|
||
|
_dwMode = grfMode;
|
||
|
|
||
|
_CloseStorage();
|
||
|
|
||
|
if (_pstgfParent)
|
||
|
{
|
||
|
hr = _pstgfParent->_EnsureStorage(grfMode);
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
LPCWSTR pwszName;
|
||
|
LPCSTGITEM pit = _IsStgItem(ILFindLastID(_pidl));
|
||
|
WSTR_ALIGNED_STACK_COPY(&pwszName, pit->szName);
|
||
|
|
||
|
hr = _pstgfParent->_pstg->OpenStorage(pwszName, NULL, grfMode, NULL, 0, &_pstg);
|
||
|
}
|
||
|
}
|
||
|
else if (_pstgLoad)
|
||
|
{
|
||
|
hr = _pstgLoad->QueryInterface(IID_PPV_ARG(IStorage, &_pstg));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
IStorage *pstgLink;
|
||
|
hr = _BindToStorageObject(_pidl, grfMode, &pstgLink);
|
||
|
if (hr == STG_E_FILENOTFOUND)
|
||
|
hr = _BindToStorageObject(_pidl, grfMode | STGM_CREATE, &pstgLink);
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = _SetShortcutStorage(pstgLink);
|
||
|
pstgLink->Release();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL CStgFolder::_ShowExtension()
|
||
|
{
|
||
|
SHELLSTATE ss;
|
||
|
SHGetSetSettings(&ss, SSF_SHOWEXTENSIONS, FALSE);
|
||
|
return ss.fShowExtensions;
|
||
|
}
|
||
|
|
||
|
HRESULT CStgFolder::_GetTypeOf(LPCSTGITEM pistg, LPTSTR pszBuffer, INT cchBuffer)
|
||
|
{
|
||
|
*pszBuffer = TEXT('\0'); // null out the return buffer
|
||
|
|
||
|
LPCWSTR pwszName;
|
||
|
WSTR_ALIGNED_STACK_COPY(&pwszName, pistg->szName);
|
||
|
|
||
|
LPTSTR pszExt = PathFindExtension(pwszName);
|
||
|
if (pszExt)
|
||
|
{
|
||
|
StrCpyN(pszBuffer, pszExt, cchBuffer);
|
||
|
}
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
CStgFolder* _GetStgFolder(IShellFolder *psf)
|
||
|
{
|
||
|
CStgFolder *pstgf;
|
||
|
return SUCCEEDED(psf->QueryInterface(CLSID_StgFolder, (void**)&pstgf)) ? pstgf : NULL;
|
||
|
}
|
||
|
|
||
|
// IPersist / IPersistFolder etc
|
||
|
|
||
|
STDMETHODIMP CStgFolder::Initialize(LPCITEMIDLIST pidl)
|
||
|
{
|
||
|
ILFree(_pidl);
|
||
|
return SHILClone(pidl, &_pidl);
|
||
|
}
|
||
|
|
||
|
HRESULT CStgFolder::GetCurFolder(LPITEMIDLIST *ppidl)
|
||
|
{
|
||
|
if (_pidl)
|
||
|
return SHILClone(_pidl, ppidl);
|
||
|
|
||
|
*ppidl = NULL;
|
||
|
return S_FALSE;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CStgFolder::InitializeEx(IBindCtx *pbc, LPCITEMIDLIST pidlRoot, const PERSIST_FOLDER_TARGET_INFO *ppfti)
|
||
|
{
|
||
|
ASSERTMSG(_pstg == NULL, "shouldn't initialize again if we already have a storage");
|
||
|
|
||
|
HRESULT hr = Initialize(pidlRoot);
|
||
|
if (SUCCEEDED(hr) && pbc)
|
||
|
{
|
||
|
// we don't care if these don't succeed so don't propagate the hr here
|
||
|
BIND_OPTS bo = {sizeof(bo)};
|
||
|
if (SUCCEEDED(pbc->GetBindOptions(&bo)))
|
||
|
{
|
||
|
_dwMode = bo.grfMode;
|
||
|
}
|
||
|
|
||
|
IUnknown *punk;
|
||
|
if (SUCCEEDED(pbc->GetObjectParam(STGSTR_STGTOBIND, &punk)))
|
||
|
{
|
||
|
IStorage *pstgLink;
|
||
|
hr = punk->QueryInterface(IID_PPV_ARG(IStorage, &pstgLink));
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = _SetShortcutStorage(pstgLink);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
STATSTG stat;
|
||
|
hr = _pstg->Stat(&stat, STATFLAG_NONAME);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
// we want to know what mode we're opened in, so we will only
|
||
|
// have to re-open if necessary
|
||
|
_dwMode = stat.grfMode;
|
||
|
}
|
||
|
}
|
||
|
pstgLink->Release();
|
||
|
}
|
||
|
punk->Release();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
// IShellFolder(2)
|
||
|
|
||
|
HRESULT CStgFolder::ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pszName, ULONG *pchEaten, LPITEMIDLIST *ppidl, ULONG *pdwAttributes)
|
||
|
{
|
||
|
HRESULT hr;
|
||
|
|
||
|
if (!ppidl)
|
||
|
return E_INVALIDARG;
|
||
|
*ppidl = NULL;
|
||
|
if (!pszName)
|
||
|
return E_INVALIDARG;
|
||
|
|
||
|
TCHAR szName[MAX_PATH];
|
||
|
hr = _NextSegment((LPCWSTR*)&pszName, szName, ARRAYSIZE(szName), TRUE);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = _AllocIDList(szName, ppidl, NULL);
|
||
|
if (SUCCEEDED(hr) && pszName)
|
||
|
{
|
||
|
IShellFolder *psf;
|
||
|
hr = BindToObject(*ppidl, pbc, IID_PPV_ARG(IShellFolder, &psf));
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
ULONG chEaten;
|
||
|
LPITEMIDLIST pidlNext;
|
||
|
hr = psf->ParseDisplayName(hwnd, pbc, pszName, &chEaten, &pidlNext, pdwAttributes);
|
||
|
if (SUCCEEDED(hr))
|
||
|
hr = SHILAppend(pidlNext, ppidl);
|
||
|
psf->Release();
|
||
|
}
|
||
|
}
|
||
|
else if (SUCCEEDED(hr) && pdwAttributes && *pdwAttributes)
|
||
|
{
|
||
|
GetAttributesOf(1, (LPCITEMIDLIST *)ppidl, pdwAttributes);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// clean up if the parse failed.
|
||
|
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
ILFree(*ppidl);
|
||
|
*ppidl = NULL;
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CStgFolder::_InitNewStgFolder(CStgFolder *pstgf, DWORD grfMode, LPCITEMIDLIST pidlNew)
|
||
|
{
|
||
|
HRESULT hr = pstgf->Initialize(pidlNew);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
pstgf->_dwMode = grfMode;
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
HRESULT CStgFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
|
||
|
{
|
||
|
*ppv = NULL;
|
||
|
|
||
|
LPCSTGITEM pistg = _IsStgItem(pidl);
|
||
|
if (!pistg)
|
||
|
return E_FAIL;
|
||
|
|
||
|
DWORD grfMode = BindCtx_GetMode(pbc, STGM_READ);
|
||
|
HRESULT hr;
|
||
|
if (IsEqualIID(riid, IID_IStream))
|
||
|
{
|
||
|
// they are requesting a stream on the current
|
||
|
// item, therefore lets return it to them.
|
||
|
|
||
|
hr = _EnsureStorage(grfMode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
LPCWSTR pwszName;
|
||
|
WSTR_ALIGNED_STACK_COPY(&pwszName, pistg->szName);
|
||
|
|
||
|
IStream *pstrm;
|
||
|
hr = _pstg->OpenStream(pwszName, NULL, grfMode, 0, &pstrm);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = pstrm->QueryInterface(riid, ppv);
|
||
|
pstrm->Release();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// its not an IStream request, so lets bind to the shell folder
|
||
|
// and get the interface that the caller requested.
|
||
|
|
||
|
LPCITEMIDLIST pidlNext = _ILNext(pidl);
|
||
|
LPITEMIDLIST pidlSubFolder = ILCombineParentAndFirst(_pidl, pidl, pidlNext);
|
||
|
if (pidlSubFolder)
|
||
|
{
|
||
|
CStgFolder *pstgf = new CStgFolder(NULL, this);
|
||
|
if (pstgf)
|
||
|
{
|
||
|
hr = _InitNewStgFolder(pstgf, grfMode, pidlSubFolder);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
// if there's nothing left in the pidl, get the interface on this one.
|
||
|
if (ILIsEmpty(pidlNext))
|
||
|
hr = pstgf->QueryInterface(riid, ppv);
|
||
|
else
|
||
|
{
|
||
|
// otherwise, hand the rest of it off to the new shellfolder.
|
||
|
hr = pstgf->BindToObject(pidlNext, pbc, riid, ppv);
|
||
|
}
|
||
|
}
|
||
|
pstgf->Release();
|
||
|
}
|
||
|
else
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
|
||
|
ILFree(pidlSubFolder);
|
||
|
}
|
||
|
else
|
||
|
hr = E_OUTOFMEMORY;
|
||
|
}
|
||
|
|
||
|
ASSERT((SUCCEEDED(hr) && *ppv) || (FAILED(hr) && (NULL == *ppv))); // Assert hr is consistent w/out param.
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CStgFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
|
||
|
{
|
||
|
LPCSTGITEM pistg1 = _IsStgItem(pidl1);
|
||
|
LPCSTGITEM pistg2 = _IsStgItem(pidl2);
|
||
|
INT nCmp = 0;
|
||
|
|
||
|
if (!pistg1 || !pistg2)
|
||
|
return E_INVALIDARG;
|
||
|
|
||
|
// folders always sort to the top of the list, if either of these items
|
||
|
// are folders then compare on the folderness
|
||
|
|
||
|
if (_IsFolder(pistg1) || _IsFolder(pistg2))
|
||
|
{
|
||
|
if (_IsFolder(pistg1) && !_IsFolder(pistg2))
|
||
|
nCmp = -1;
|
||
|
else if (!_IsFolder(pistg1) && _IsFolder(pistg2))
|
||
|
nCmp = 1;
|
||
|
}
|
||
|
|
||
|
// if they match (or are not folders, then lets compare based on the column ID.
|
||
|
|
||
|
if (nCmp == 0)
|
||
|
{
|
||
|
switch (lParam & SHCIDS_COLUMNMASK)
|
||
|
{
|
||
|
case STG_COL_NAME: // caught later on
|
||
|
break;
|
||
|
|
||
|
case STG_COL_SIZE:
|
||
|
{
|
||
|
if (pistg1->cbFileSize.QuadPart < pistg2->cbFileSize.QuadPart)
|
||
|
nCmp = -1;
|
||
|
else if (pistg1->cbFileSize.QuadPart > pistg2->cbFileSize.QuadPart)
|
||
|
nCmp = 1;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case STG_COL_TYPE:
|
||
|
{
|
||
|
TCHAR szType1[MAX_PATH], szType2[MAX_PATH];
|
||
|
_GetTypeOf(pistg1, szType1, ARRAYSIZE(szType1));
|
||
|
_GetTypeOf(pistg2, szType2, ARRAYSIZE(szType2));
|
||
|
nCmp = StrCmpI(szType1, szType2);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case STG_COL_MODIFIED:
|
||
|
{
|
||
|
if (pistg1->ulModified.QuadPart < pistg2->ulModified.QuadPart)
|
||
|
nCmp = -1;
|
||
|
else if (pistg1->ulModified.QuadPart > pistg2->ulModified.QuadPart)
|
||
|
nCmp = 1;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (nCmp == 0)
|
||
|
{
|
||
|
nCmp = ualstrcmpi(pistg1->szName, pistg2->szName);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return ResultFromShort(nCmp);
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CStgFolder::_OnGetNotify(DWORD pv, LPITEMIDLIST *ppidl, LONG *plEvents)
|
||
|
{
|
||
|
*ppidl = _pidl;
|
||
|
*plEvents = SHCNE_RENAMEITEM | SHCNE_RENAMEFOLDER | \
|
||
|
SHCNE_CREATE | SHCNE_DELETE | SHCNE_UPDATEDIR | SHCNE_UPDATEITEM | \
|
||
|
SHCNE_MKDIR | SHCNE_RMDIR;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP CStgFolder::MessageSFVCB(UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
HRESULT hr = E_FAIL;
|
||
|
switch (uMsg)
|
||
|
{
|
||
|
HANDLE_MSG(0, SFVM_BACKGROUNDENUM, _OnBackgroundEnum);
|
||
|
HANDLE_MSG(0, SFVM_GETNOTIFY, _OnGetNotify);
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CStgFolder::CreateViewObject(HWND hwnd, REFIID riid, void **ppv)
|
||
|
{
|
||
|
HRESULT hr = E_NOINTERFACE;
|
||
|
*ppv = NULL;
|
||
|
|
||
|
if (IsEqualIID(riid, IID_IShellView))
|
||
|
{
|
||
|
SFV_CREATE sSFV = { 0 };
|
||
|
sSFV.cbSize = sizeof(sSFV);
|
||
|
sSFV.psfvcb = this;
|
||
|
sSFV.pshf = this;
|
||
|
hr = SHCreateShellFolderView(&sSFV, (IShellView**)ppv);
|
||
|
}
|
||
|
else if (IsEqualIID(riid, IID_IContextMenu))
|
||
|
{
|
||
|
HKEY hkNoFiles = NULL;
|
||
|
|
||
|
RegOpenKey(HKEY_CLASSES_ROOT, TEXT("Directory\\Background"), &hkNoFiles);
|
||
|
|
||
|
hr = CDefFolderMenu_Create2(_pidl, hwnd,
|
||
|
0, NULL,
|
||
|
this, NULL,
|
||
|
1, &hkNoFiles,
|
||
|
(IContextMenu **)ppv);
|
||
|
if (hkNoFiles)
|
||
|
RegCloseKey(hkNoFiles);
|
||
|
}
|
||
|
else if (IsEqualIID(riid, IID_IDropTarget))
|
||
|
{
|
||
|
hr = CStgDropTarget_CreateInstance(this, hwnd, (IDropTarget **)ppv);
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
ULONG CStgFolder::_GetAttributesOf(LPCSTGITEM pistg, ULONG rgfIn)
|
||
|
{
|
||
|
ULONG dwResult = rgfIn & (SFGAO_CANCOPY | SFGAO_CANDELETE |
|
||
|
SFGAO_CANLINK | SFGAO_CANMOVE |
|
||
|
SFGAO_CANRENAME | SFGAO_HASPROPSHEET |
|
||
|
SFGAO_DROPTARGET);
|
||
|
|
||
|
// if the items is a folder then lets check to see if it has sub folders etc...
|
||
|
|
||
|
if (pistg && _IsFolder(pistg) && (rgfIn & (SFGAO_FOLDER | SFGAO_HASSUBFOLDER)))
|
||
|
{
|
||
|
dwResult |= rgfIn & SFGAO_FOLDER;
|
||
|
if (rgfIn & SFGAO_HASSUBFOLDER)
|
||
|
{
|
||
|
IShellFolder *psf = NULL;
|
||
|
if (SUCCEEDED(BindToObject((LPITEMIDLIST)pistg, NULL, IID_PPV_ARG(IShellFolder, &psf))))
|
||
|
{
|
||
|
IEnumIDList * pei;
|
||
|
if (S_OK == psf->EnumObjects(NULL, SHCONTF_FOLDERS, &pei))
|
||
|
{
|
||
|
LPITEMIDLIST pidl;
|
||
|
if (S_OK == pei->Next(1, &pidl, NULL))
|
||
|
{
|
||
|
dwResult |= rgfIn & SFGAO_HASSUBFOLDER;
|
||
|
ILFree(pidl);
|
||
|
}
|
||
|
pei->Release();
|
||
|
}
|
||
|
psf->Release();
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return dwResult;
|
||
|
}
|
||
|
|
||
|
HRESULT CStgFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *prgfInOut)
|
||
|
{
|
||
|
UINT rgfOut = *prgfInOut;
|
||
|
|
||
|
// return attributes of the namespace root?
|
||
|
|
||
|
if ( !cidl || !apidl )
|
||
|
{
|
||
|
rgfOut &= SFGAO_FOLDER | SFGAO_FILESYSTEM |
|
||
|
SFGAO_LINK | SFGAO_DROPTARGET |
|
||
|
SFGAO_CANRENAME | SFGAO_CANDELETE |
|
||
|
SFGAO_CANLINK | SFGAO_CANCOPY |
|
||
|
SFGAO_CANMOVE | SFGAO_HASSUBFOLDER;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
for (UINT i = 0; i < cidl; i++)
|
||
|
rgfOut &= _GetAttributesOf(_IsStgItem(apidl[i]), *prgfInOut);
|
||
|
}
|
||
|
|
||
|
*prgfInOut = rgfOut;
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CALLBACK CStgFolder::_ItemsMenuCB(IShellFolder *psf, HWND hwnd, IDataObject *pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
switch (uMsg)
|
||
|
{
|
||
|
case DFM_MERGECONTEXTMENU:
|
||
|
break;
|
||
|
|
||
|
case DFM_INVOKECOMMANDEX:
|
||
|
{
|
||
|
DFMICS *pdfmics = (DFMICS *)lParam;
|
||
|
switch (wParam)
|
||
|
{
|
||
|
case DFM_CMD_DELETE:
|
||
|
hr = StgDeleteUsingDataObject(hwnd, pdfmics->fMask, pdtobj);
|
||
|
break;
|
||
|
|
||
|
case DFM_CMD_PROPERTIES:
|
||
|
break;
|
||
|
|
||
|
default:
|
||
|
// This is common menu items, use the default code.
|
||
|
hr = S_FALSE;
|
||
|
break;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
default:
|
||
|
hr = E_NOTIMPL;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CStgFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl,
|
||
|
REFIID riid, UINT *prgfInOut, void **ppv)
|
||
|
{
|
||
|
HRESULT hr = E_INVALIDARG;
|
||
|
LPCSTGITEM pistg = cidl ? _IsStgItem(apidl[0]) : NULL;
|
||
|
|
||
|
if (pistg &&
|
||
|
(IsEqualIID(riid, IID_IExtractIconA) || IsEqualIID(riid, IID_IExtractIconW)))
|
||
|
{
|
||
|
LPCWSTR pwszName;
|
||
|
WSTR_ALIGNED_STACK_COPY(&pwszName, pistg->szName);
|
||
|
|
||
|
hr = SHCreateFileExtractIconW(pwszName, _IsFolder(pistg), riid, ppv);
|
||
|
}
|
||
|
else if (IsEqualIID(riid, IID_IDataObject) && cidl)
|
||
|
{
|
||
|
hr = CIDLData_CreateInstance(_pidl, cidl, apidl, NULL, (IDataObject **)ppv);
|
||
|
}
|
||
|
#if 0
|
||
|
else if (IsEqualIID(riid, IID_IContextMenu) && pistg)
|
||
|
{
|
||
|
// get the association for these files and lets attempt to
|
||
|
// build the context menu for the selection.
|
||
|
|
||
|
IQueryAssociations *pqa;
|
||
|
hr = GetUIObjectOf(hwnd, 1, (LPCITEMIDLIST*)&pistg, IID_PPV_ARG_NULL(IQueryAssociations, &pqa));
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
HKEY ahk[3];
|
||
|
// this is broken for docfiles (shell\ext\stgfldr's keys work though)
|
||
|
// maybe because GetClassFile punts when it's not fs?
|
||
|
DWORD cKeys = SHGetAssocKeys(pqa, ahk, ARRAYSIZE(ahk));
|
||
|
hr = CDefFolderMenu_Create2(_pidl, hwnd, cidl, apidl,
|
||
|
this, _ItemsMenuCB, cKeys, ahk,
|
||
|
(IContextMenu **)ppv);
|
||
|
|
||
|
SHRegCloseKeys(ahk, cKeys);
|
||
|
pqa->Release();
|
||
|
}
|
||
|
}
|
||
|
else if (IsEqualIID(riid, IID_IQueryAssociations) && pistg)
|
||
|
{
|
||
|
// need to create a valid Assoc obj here
|
||
|
}
|
||
|
#endif
|
||
|
else if (IsEqualIID(riid, IID_IDropTarget) && pistg)
|
||
|
{
|
||
|
// If a directory is selected in the view, the drop is going to a folder,
|
||
|
// so we need to bind to that folder and ask it to create a drop target
|
||
|
|
||
|
if (_IsFolder(pistg))
|
||
|
{
|
||
|
IShellFolder *psf;
|
||
|
hr = BindToObject(apidl[0], NULL, IID_PPV_ARG(IShellFolder, &psf));
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = psf->CreateViewObject(hwnd, IID_IDropTarget, ppv);
|
||
|
psf->Release();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
hr = CreateViewObject(hwnd, IID_IDropTarget, ppv);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CStgFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD dwFlags, STRRET *pStrRet)
|
||
|
{
|
||
|
HRESULT hr = E_INVALIDARG;
|
||
|
LPCSTGITEM pistg = _IsStgItem(pidl);
|
||
|
if (pistg)
|
||
|
{
|
||
|
LPCWSTR pwszName;
|
||
|
WSTR_ALIGNED_STACK_COPY(&pwszName, pistg->szName);
|
||
|
|
||
|
if (dwFlags & SHGDN_FORPARSING)
|
||
|
{
|
||
|
if (dwFlags & SHGDN_INFOLDER)
|
||
|
{
|
||
|
hr = StringToStrRet(pwszName, pStrRet); // relative name
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// compute the for parsing name based on the path to the storage object
|
||
|
// and then the in folder name of the object
|
||
|
|
||
|
TCHAR szTemp[MAX_PATH];
|
||
|
SHGetNameAndFlags(_pidl, dwFlags, szTemp, ARRAYSIZE(szTemp), NULL);
|
||
|
PathAppend(szTemp, pwszName);
|
||
|
hr = StringToStrRet(szTemp, pStrRet);
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
SHFILEINFO sfi;
|
||
|
if (SHGetFileInfo(pwszName, _IsFolder(pistg), &sfi, sizeof(sfi), SHGFI_USEFILEATTRIBUTES | SHGFI_DISPLAYNAME))
|
||
|
hr = StringToStrRet(sfi.szDisplayName, pStrRet);
|
||
|
else
|
||
|
hr = E_FAIL;
|
||
|
}
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CStgFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName, DWORD dwFlags, LPITEMIDLIST *ppidlOut)
|
||
|
{
|
||
|
LPCSTGITEM pistg = _IsStgItem(pidl);
|
||
|
if (!pistg)
|
||
|
return E_INVALIDARG;
|
||
|
|
||
|
HRESULT hr = _EnsureStorage(STGM_READWRITE);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
LPCWSTR pwszName;
|
||
|
WSTR_ALIGNED_STACK_COPY(&pwszName, pistg->szName);
|
||
|
|
||
|
// format up the new name before we attempt to perform the rename
|
||
|
|
||
|
TCHAR szNewName[MAX_PATH];
|
||
|
StrCpyN(szNewName, pszName, ARRAYSIZE(szNewName));
|
||
|
if (!_ShowExtension())
|
||
|
{
|
||
|
StrCatBuff(szNewName, PathFindExtension(pwszName), ARRAYSIZE(szNewName));
|
||
|
}
|
||
|
|
||
|
hr = _pstg->RenameElement(pwszName, szNewName);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = _pstg->Commit(STGC_DEFAULT);
|
||
|
}
|
||
|
|
||
|
// if that was successful lets return the
|
||
|
// new IDLIST we generated.
|
||
|
|
||
|
if (SUCCEEDED(hr) && ppidlOut)
|
||
|
hr = _AllocIDList(szNewName, ppidlOut, NULL);
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CStgFolder::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetail)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
TCHAR szTemp[MAX_PATH];
|
||
|
|
||
|
// is this a valid column?
|
||
|
|
||
|
if (iColumn >= ARRAYSIZE(g_aStgColumns))
|
||
|
return E_NOTIMPL;
|
||
|
|
||
|
pDetail->str.uType = STRRET_CSTR;
|
||
|
pDetail->str.cStr[0] = 0;
|
||
|
|
||
|
LPCSTGITEM pistg = _IsStgItem(pidl);
|
||
|
if (!pistg)
|
||
|
{
|
||
|
// when the IDLIST is not a storage item then we return the column information
|
||
|
// back to the caller.
|
||
|
|
||
|
pDetail->fmt = g_aStgColumns[iColumn].iFmt;
|
||
|
pDetail->cxChar = g_aStgColumns[iColumn].cchCol;
|
||
|
LoadString(HINST_THISDLL, g_aStgColumns[iColumn].iTitle, szTemp, ARRAYSIZE(szTemp));
|
||
|
hr = StringToStrRet(szTemp, &(pDetail->str));
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// return the property to the caller that is being requested, this is based on the
|
||
|
// list of columns we gave out when the view was created.
|
||
|
LPCWSTR pwszName;
|
||
|
WSTR_ALIGNED_STACK_COPY(&pwszName, pistg->szName);
|
||
|
|
||
|
switch (iColumn)
|
||
|
{
|
||
|
case STG_COL_NAME:
|
||
|
hr = StringToStrRet(pwszName, &(pDetail->str));
|
||
|
break;
|
||
|
|
||
|
case STG_COL_SIZE:
|
||
|
if (!_IsFolder(pistg))
|
||
|
{
|
||
|
ULARGE_INTEGER ullSize = pistg->cbFileSize;
|
||
|
StrFormatKBSize(ullSize.QuadPart, szTemp, ARRAYSIZE(szTemp));
|
||
|
hr = StringToStrRet(szTemp, &(pDetail->str));
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case STG_COL_TYPE:
|
||
|
{
|
||
|
SHFILEINFO sfi;
|
||
|
if (SHGetFileInfo(pwszName, _IsFolder(pistg), &sfi, sizeof(sfi), SHGFI_USEFILEATTRIBUTES | SHGFI_TYPENAME))
|
||
|
hr = StringToStrRet(sfi.szTypeName, &(pDetail->str));
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
case STG_COL_MODIFIED:
|
||
|
SHFormatDateTime(&pistg->ftModified, NULL, szTemp, ARRAYSIZE(szTemp));
|
||
|
hr = StringToStrRet(szTemp, &(pDetail->str));
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
// IStorage
|
||
|
|
||
|
HRESULT CStgFolder::Commit(DWORD grfCommitFlags)
|
||
|
{
|
||
|
HRESULT hr = _EnsureStorage(_dwMode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
ASSERTMSG(_pstg != NULL, "no _pstg in Commit");
|
||
|
hr = _pstg->Commit(grfCommitFlags);
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CStgFolder::Revert()
|
||
|
{
|
||
|
HRESULT hr = _EnsureStorage(_dwMode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
ASSERTMSG(_pstg != NULL, "no _pstg in Revert");
|
||
|
hr = _pstg->Revert();
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CStgFolder::SetClass(REFCLSID clsid)
|
||
|
{
|
||
|
HRESULT hr = _EnsureStorage(_dwMode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
ASSERTMSG(_pstg != NULL, "no _pstg in SetClass");
|
||
|
hr = _pstg->SetClass(clsid);
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CStgFolder::SetStateBits(DWORD grfStateBits, DWORD grfMask)
|
||
|
{
|
||
|
HRESULT hr = _EnsureStorage(_dwMode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
ASSERTMSG(_pstg != NULL, "no _pstg in SetStateBits");
|
||
|
hr = _pstg->SetStateBits(grfStateBits, grfMask);
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CStgFolder::Stat(STATSTG *pstatstg, DWORD grfStatFlag)
|
||
|
{
|
||
|
HRESULT hr = _EnsureStorage(_dwMode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
ASSERTMSG(_pstg != NULL, "no _pstg in Stat");
|
||
|
hr = _pstg->Stat(pstatstg, grfStatFlag);
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CStgFolder::EnumElements(DWORD reserved1, void *reserved2, DWORD reserved3, IEnumSTATSTG **ppenum)
|
||
|
{
|
||
|
HRESULT hr = _EnsureStorage(_dwMode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
ASSERTMSG(_pstg != NULL, "no _pstg in EnumElements");
|
||
|
hr = _pstg->EnumElements(reserved1, reserved2, reserved3, ppenum);
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CStgFolder::OpenStream(LPCWSTR pszRel, VOID *reserved1, DWORD grfMode, DWORD reserved2, IStream **ppstm)
|
||
|
{
|
||
|
HRESULT hr = _EnsureStorage(grfMode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
ASSERTMSG(_pstg != NULL, "no _pstg in OpenStream");
|
||
|
hr = _pstg->OpenStream(pszRel, reserved1, grfMode, reserved2, ppstm);
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CStgFolder::OpenStorage(LPCWSTR pszRel, IStorage *pstgPriority, DWORD grfMode, SNB snbExclude, DWORD reserved, IStorage **ppstg)
|
||
|
{
|
||
|
HRESULT hr = _EnsureStorage(grfMode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
ASSERTMSG(_pstg != NULL, "no _pstg in OpenStorage");
|
||
|
hr = _pstg->OpenStorage(pszRel, pstgPriority, grfMode, snbExclude, reserved, ppstg);
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
HRESULT CStgFolder::DestroyElement(LPCWSTR pszRel)
|
||
|
{
|
||
|
HRESULT hr = _EnsureStorage(_dwMode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
ASSERTMSG(_pstg != NULL, "no _pstg in DestroyElement");
|
||
|
|
||
|
LPITEMIDLIST pidl;
|
||
|
BOOL fFolder;
|
||
|
hr = _AllocIDList(pszRel, &pidl, &fFolder);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
LPITEMIDLIST pidlAbs;
|
||
|
hr = SHILCombine(_pidl, pidl, &pidlAbs);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = _pstg->DestroyElement(pszRel);
|
||
|
if (SUCCEEDED(hr))
|
||
|
SHChangeNotify(fFolder ? SHCNE_RMDIR : SHCNE_DELETE, SHCNF_IDLIST, pidlAbs, NULL);
|
||
|
ILFree(pidlAbs);
|
||
|
}
|
||
|
ILFree(pidl);
|
||
|
}
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CStgFolder::RenameElement(LPCWSTR pwcsOldName, LPCWSTR pwcsNewName)
|
||
|
{
|
||
|
HRESULT hr = _EnsureStorage(_dwMode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
ASSERTMSG(_pstg != NULL, "no _pstg in RenameElement");
|
||
|
|
||
|
LPITEMIDLIST pidlOld;
|
||
|
BOOL fFolder;
|
||
|
hr = _AllocIDList(pwcsOldName, &pidlOld, &fFolder);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
LPITEMIDLIST pidlAbsOld;
|
||
|
hr = SHILCombine(_pidl, pidlOld, &pidlAbsOld);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = _pstg->RenameElement(pwcsOldName, pwcsNewName);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
LPITEMIDLIST pidlNew;
|
||
|
hr = _AllocIDList(pwcsNewName, &pidlNew, NULL);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
LPITEMIDLIST pidlAbsNew;
|
||
|
hr = SHILCombine(_pidl, pidlNew, &pidlAbsNew);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
SHChangeNotify(fFolder ? SHCNE_RENAMEFOLDER : SHCNE_RENAMEITEM, SHCNF_IDLIST, pidlAbsOld, pidlAbsNew);
|
||
|
ILFree(pidlAbsNew);
|
||
|
}
|
||
|
ILFree(pidlNew);
|
||
|
}
|
||
|
}
|
||
|
ILFree(pidlAbsOld);
|
||
|
}
|
||
|
ILFree(pidlOld);
|
||
|
}
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CStgFolder::SetElementTimes(LPCWSTR pszRel, const FILETIME *pctime, const FILETIME *patime, const FILETIME *pmtime)
|
||
|
{
|
||
|
HRESULT hr = _EnsureStorage(_dwMode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
ASSERTMSG(_pstg != NULL, "no _pstg in SetElementTimes");
|
||
|
|
||
|
hr = _pstg->SetElementTimes(pszRel, pctime, patime, pmtime);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
LPITEMIDLIST pidl;
|
||
|
BOOL fFolder;
|
||
|
hr = _AllocIDList(pszRel, &pidl, &fFolder);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
LPITEMIDLIST pidlAbs;
|
||
|
hr = SHILCombine(_pidl, pidl, &pidlAbs);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
SHChangeNotify(fFolder ? SHCNE_UPDATEDIR : SHCNE_UPDATEITEM, SHCNF_IDLIST, pidlAbs, NULL);
|
||
|
ILFree(pidlAbs);
|
||
|
}
|
||
|
ILFree(pidl);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CStgFolder::CopyTo(DWORD ciidExclude, const IID *rgiidExclude, SNB snbExclude, IStorage *pstgDest)
|
||
|
{
|
||
|
HRESULT hr = _EnsureStorage(_dwMode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
ASSERTMSG(_pstg != NULL, "no _pstg in CopyTo");
|
||
|
|
||
|
hr = _pstg->CopyTo(ciidExclude, rgiidExclude, snbExclude, pstgDest);
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CStgFolder::MoveElementTo(LPCWSTR pszRel, IStorage *pstgDest, LPCWSTR pwcsNewName, DWORD grfFlags)
|
||
|
{
|
||
|
HRESULT hr = _EnsureStorage(_dwMode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
ASSERTMSG(_pstg != NULL, "no _pstg in MoveElementTo");
|
||
|
|
||
|
hr = _pstg->MoveElementTo(pszRel, pstgDest, pwcsNewName, grfFlags);
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CStgFolder::CreateStream(LPCWSTR pwcsName, DWORD grfMode, DWORD res1, DWORD res2, IStream **ppstm)
|
||
|
{
|
||
|
HRESULT hr = _EnsureStorage(grfMode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
ASSERTMSG(_pstg != NULL, "no _pstg in CreateStream");
|
||
|
|
||
|
hr = _pstg->CreateStream(pwcsName, grfMode, res1, res2, ppstm);
|
||
|
if (SUCCEEDED(hr))
|
||
|
hr = _pstg->Commit(STGC_DEFAULT);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
LPITEMIDLIST pidl;
|
||
|
hr = _AllocIDList(pwcsName, &pidl, NULL);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
LPITEMIDLIST pidlAbs;
|
||
|
hr = SHILCombine(_pidl, pidl, &pidlAbs);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
SHChangeNotify(SHCNE_CREATE, SHCNF_IDLIST, pidlAbs, NULL);
|
||
|
ILFree(pidlAbs);
|
||
|
}
|
||
|
ILFree(pidl);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CStgFolder::CreateStorage(LPCWSTR pwcsName, DWORD grfMode, DWORD res1, DWORD res2, IStorage **ppstg)
|
||
|
{
|
||
|
HRESULT hr = _EnsureStorage(grfMode);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
ASSERTMSG(_pstg != NULL, "no _pstg in CreateStorage");
|
||
|
|
||
|
hr = _pstg->CreateStorage(pwcsName, grfMode, res1, res2, ppstg);
|
||
|
if (SUCCEEDED(hr))
|
||
|
hr = _pstg->Commit(STGC_DEFAULT);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
LPITEMIDLIST pidl;
|
||
|
hr = _AllocIDList(pwcsName, &pidl, NULL);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
LPITEMIDLIST pidlAbs;
|
||
|
hr = SHILCombine(_pidl, pidl, &pidlAbs);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
SHChangeNotify(SHCNE_MKDIR, SHCNF_IDLIST, pidlAbs, NULL);
|
||
|
ILFree(pidlAbs);
|
||
|
}
|
||
|
ILFree(pidl);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
// IPersistStorage
|
||
|
HRESULT CStgFolder::IsDirty(void)
|
||
|
{
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
HRESULT CStgFolder::InitNew(IStorage *pStg)
|
||
|
{
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
HRESULT CStgFolder::Load(IStorage *pStg)
|
||
|
{
|
||
|
IUnknown_Set((IUnknown **)&_pstgLoad, (IUnknown *)pStg);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
HRESULT CStgFolder::Save(IStorage *pStgSave, BOOL fSameAsLoad)
|
||
|
{
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
HRESULT CStgFolder::SaveCompleted(IStorage *pStgNew)
|
||
|
{
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
HRESULT CStgFolder::HandsOffStorage(void)
|
||
|
{
|
||
|
return E_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Enumerator object for storages
|
||
|
|
||
|
class CStgEnum : public IEnumIDList
|
||
|
{
|
||
|
public:
|
||
|
CStgEnum(CStgFolder* prf, DWORD grfFlags);
|
||
|
~CStgEnum();
|
||
|
|
||
|
// IUnknown
|
||
|
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
||
|
STDMETHODIMP_(ULONG) AddRef();
|
||
|
STDMETHODIMP_(ULONG) Release();
|
||
|
|
||
|
// IEnumIDList
|
||
|
STDMETHODIMP Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched);
|
||
|
STDMETHODIMP Skip(ULONG celt)
|
||
|
{ return E_NOTIMPL; };
|
||
|
STDMETHODIMP Reset()
|
||
|
{ ATOMICRELEASE(_pEnum); return S_OK; };
|
||
|
STDMETHODIMP Clone(IEnumIDList **ppenum)
|
||
|
{ return E_NOTIMPL; };
|
||
|
|
||
|
private:
|
||
|
LONG _cRef;
|
||
|
CStgFolder* _pstgf;
|
||
|
DWORD _grfFlags;
|
||
|
IEnumSTATSTG *_pEnum;
|
||
|
};
|
||
|
|
||
|
|
||
|
STDAPI CStgEnum_CreateInstance(CStgFolder *pstgf, DWORD grfFlags, IEnumIDList **ppenum)
|
||
|
{
|
||
|
CStgEnum *penum = new CStgEnum(pstgf, grfFlags);
|
||
|
if (!penum)
|
||
|
return E_OUTOFMEMORY;
|
||
|
|
||
|
HRESULT hr = penum->QueryInterface(IID_PPV_ARG(IEnumIDList, ppenum));
|
||
|
penum->Release();
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
CStgEnum::CStgEnum(CStgFolder *pstgf, DWORD grfFlags) :
|
||
|
_cRef(1), _pstgf(pstgf), _grfFlags(grfFlags)
|
||
|
{
|
||
|
_pstgf->AddRef();
|
||
|
}
|
||
|
|
||
|
CStgEnum::~CStgEnum()
|
||
|
{
|
||
|
ATOMICRELEASE(_pEnum);
|
||
|
_pstgf->Release();
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP_(ULONG) CStgEnum::AddRef()
|
||
|
{
|
||
|
return InterlockedIncrement(&_cRef);
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP_(ULONG) CStgEnum::Release()
|
||
|
{
|
||
|
if (InterlockedDecrement(&_cRef))
|
||
|
return _cRef;
|
||
|
|
||
|
delete this;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
HRESULT CStgEnum::QueryInterface(REFIID riid, void **ppv)
|
||
|
{
|
||
|
static const QITAB qit[] =
|
||
|
{
|
||
|
QITABENT(CStgEnum, IEnumIDList), // IID_IEnumIDList
|
||
|
{ 0 },
|
||
|
};
|
||
|
return QISearch(this, qit, riid, ppv);
|
||
|
}
|
||
|
|
||
|
|
||
|
HRESULT CStgEnum::Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched)
|
||
|
{
|
||
|
HRESULT hr = S_OK;
|
||
|
|
||
|
// do we have an enumerator, if not then lets get one from the
|
||
|
// storage object
|
||
|
|
||
|
if (!_pEnum)
|
||
|
{
|
||
|
// we need to reopen docfile storages to get a new enumerator
|
||
|
// because they keep giving us a stale one.
|
||
|
// if we don't close to get a new enumerator, when we delete a file
|
||
|
// the SHCNE_UPDATEDIR wont work because we'll spit out the same
|
||
|
// pidls that should have been deleted.
|
||
|
// can we get around this?
|
||
|
_pstgf->_CloseStorage();
|
||
|
|
||
|
hr = _pstgf->_EnsureStorage(STGM_READ);
|
||
|
if (SUCCEEDED(hr))
|
||
|
hr = _pstgf->_pstg->EnumElements(0, NULL, 0, &_pEnum);
|
||
|
}
|
||
|
|
||
|
// now return items, if all is good and we have stuff to pass back.
|
||
|
|
||
|
for (UINT cItems = 0; (cItems != celt) && SUCCEEDED(hr) && (hr != S_FALSE) ; )
|
||
|
{
|
||
|
STATSTG statstg = { 0 };
|
||
|
hr = _pEnum->Next(1, &statstg, NULL);
|
||
|
if (SUCCEEDED(hr) && (hr != S_FALSE))
|
||
|
{
|
||
|
BOOL fFolder;
|
||
|
LPITEMIDLIST pidl;
|
||
|
|
||
|
hr = _pstgf->_AllocIDList(statstg, &pidl, &fFolder);
|
||
|
CoTaskMemFree(statstg.pwcsName);
|
||
|
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
if (fFolder)
|
||
|
{
|
||
|
if (!(_grfFlags & SHCONTF_FOLDERS))
|
||
|
{
|
||
|
ILFree(pidl);
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
else if (!(_grfFlags & SHCONTF_NONFOLDERS))
|
||
|
{
|
||
|
ILFree(pidl);
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
rgelt[cItems++] = pidl; // return the idlist
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (hr == S_FALSE)
|
||
|
ATOMICRELEASE(_pEnum);
|
||
|
|
||
|
if (pceltFetched)
|
||
|
*pceltFetched = cItems;
|
||
|
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Drop target object
|
||
|
|
||
|
|
||
|
class CStgDropTarget : public IDropTarget
|
||
|
{
|
||
|
public:
|
||
|
CStgDropTarget(CStgFolder *pstgf, HWND hwnd);
|
||
|
~CStgDropTarget();
|
||
|
|
||
|
// IUnknown
|
||
|
STDMETHODIMP QueryInterface(REFIID riid, void **ppv);
|
||
|
STDMETHODIMP_(ULONG) AddRef(void);
|
||
|
STDMETHODIMP_(ULONG) Release(void);
|
||
|
|
||
|
// IDropTarget
|
||
|
STDMETHODIMP DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
|
||
|
STDMETHODIMP DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
|
||
|
STDMETHODIMP DragLeave();
|
||
|
STDMETHODIMP Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect);
|
||
|
|
||
|
private:
|
||
|
LONG _cRef;
|
||
|
|
||
|
CStgFolder *_pstgf;
|
||
|
HWND _hwnd; // EVIL: used as a site and UI host
|
||
|
IDataObject *_pdtobj; // used durring DragOver() and DoDrop(), don't use on background thread
|
||
|
|
||
|
UINT _idCmd;
|
||
|
DWORD _grfKeyStateLast; // for previous DragOver/Enter
|
||
|
DWORD _dwEffectLastReturned; // stashed effect that's returned by base class's dragover
|
||
|
DWORD _dwEffectPreferred; // if dwData & DTID_PREFERREDEFFECT
|
||
|
|
||
|
DWORD _GetDropEffect(DWORD *pdwEffect, DWORD grfKeyState);
|
||
|
HRESULT _Transfer(IDataObject *pdtobj, UINT uiCmd);
|
||
|
};
|
||
|
|
||
|
CStgDropTarget::CStgDropTarget(CStgFolder *pstgf, HWND hwnd) :
|
||
|
_cRef(1),
|
||
|
_pstgf(pstgf),
|
||
|
_hwnd(hwnd),
|
||
|
_grfKeyStateLast(-1)
|
||
|
{
|
||
|
_pstgf->AddRef();
|
||
|
}
|
||
|
|
||
|
CStgDropTarget::~CStgDropTarget()
|
||
|
{
|
||
|
DragLeave();
|
||
|
ATOMICRELEASE(_pstgf);
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP_(ULONG) CStgDropTarget::AddRef()
|
||
|
{
|
||
|
return InterlockedIncrement(&_cRef);
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP_(ULONG) CStgDropTarget::Release()
|
||
|
{
|
||
|
if (InterlockedDecrement(&_cRef))
|
||
|
return _cRef;
|
||
|
|
||
|
delete this;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
HRESULT CStgDropTarget::QueryInterface(REFIID riid, void**ppv)
|
||
|
{
|
||
|
static const QITAB qit[] = {
|
||
|
QITABENT(CStgDropTarget, IDropTarget),
|
||
|
{ 0 },
|
||
|
};
|
||
|
return QISearch(this, qit, riid,ppv);
|
||
|
}
|
||
|
|
||
|
STDAPI CStgDropTarget_CreateInstance(CStgFolder *pstgf, HWND hwnd, IDropTarget **ppdt)
|
||
|
{
|
||
|
CStgDropTarget *psdt = new CStgDropTarget(pstgf, hwnd);
|
||
|
if (!psdt)
|
||
|
return E_OUTOFMEMORY;
|
||
|
|
||
|
HRESULT hr = psdt->QueryInterface(IID_PPV_ARG(IDropTarget, ppdt));
|
||
|
psdt->Release();
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
#if 0
|
||
|
HRESULT StorageFromDataObj(IDataObject *pdtobj, IShellFolder **ppsf, IStorage **ppstg)
|
||
|
{
|
||
|
*ppsf = NULL;
|
||
|
*ppstg = NULL;
|
||
|
|
||
|
HRESULT hr = E_FAIL;
|
||
|
STGMEDIUM medium = {0};
|
||
|
LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
|
||
|
if (pida)
|
||
|
{
|
||
|
if (SUCCEEDED(SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, IDA_GetIDListPtr(pida, (UINT)-1), ppsf))))
|
||
|
{
|
||
|
// for (UINT i = 0; i < pida->cidl; i++)
|
||
|
for (UINT i = 0; i < 1; i++)
|
||
|
{
|
||
|
hr = (*ppsf)->BindToObject(IDA_GetIDListPtr(pida, i), NULL, IID_PPV_ARG(IStorage, ppstg));
|
||
|
if (FAILED(hr))
|
||
|
{
|
||
|
(*ppsf)->Release();
|
||
|
*ppsf = NULL;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
HIDA_ReleaseStgMedium(pida, &medium);
|
||
|
}
|
||
|
return hr;
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
HRESULT CStgDropTarget::_Transfer(IDataObject *pdtobj, UINT uiCmd)
|
||
|
{
|
||
|
#if 0
|
||
|
IShellFolder *psf;
|
||
|
IStorage *pstg;
|
||
|
HRESULT hr = StorageFromDataObj(pdtobj, &psf, &pstg);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
DWORD grfModeCreated = STGM_READWRITE;
|
||
|
HRESULT hr = _pstgf->_EnsureStorage(grfModeCreated);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = pstg->CopyTo(0, NULL, 0, _pstgf->_pstg);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = _pstgf->_pstg->Commit(STGC_DEFAULT);
|
||
|
}
|
||
|
SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST|SHCNF_FLUSH|SHCNF_FLUSHNOWAIT, _pstgf->_pidl, NULL);
|
||
|
}
|
||
|
pstg->Release();
|
||
|
psf->Release();
|
||
|
}
|
||
|
#else
|
||
|
HRESULT hr;
|
||
|
STGMEDIUM medium = {0};
|
||
|
LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
|
||
|
if (pida)
|
||
|
{
|
||
|
IShellFolder *psf;
|
||
|
hr = SHBindToObjectEx(NULL, IDA_GetIDListPtr(pida, (UINT)-1), NULL, IID_PPV_ARG(IShellFolder, &psf));
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
IStorage *pstgSrc;
|
||
|
hr = psf->QueryInterface(IID_PPV_ARG(IStorage, &pstgSrc));
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
hr = _pstgf->_EnsureStorage(STGM_READWRITE);
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
for (UINT i = 0; i < pida->cidl; i++)
|
||
|
{
|
||
|
WCHAR szName[MAX_PATH];
|
||
|
hr = DisplayNameOf(psf, IDA_GetIDListPtr(pida, i), SHGDN_FORPARSING | SHGDN_INFOLDER, szName, ARRAYSIZE(szName));
|
||
|
if (SUCCEEDED(hr))
|
||
|
{
|
||
|
DWORD grfFlags = (uiCmd == DDIDM_COPY) ? STGMOVE_COPY : STGMOVE_MOVE;
|
||
|
hr = pstgSrc->MoveElementTo(szName, _pstgf->_pstg, szName, grfFlags);
|
||
|
if (SUCCEEDED(hr))
|
||
|
hr = _pstgf->_pstg->Commit(STGC_DEFAULT);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
pstgSrc->Release();
|
||
|
}
|
||
|
psf->Release();
|
||
|
}
|
||
|
HIDA_ReleaseStgMedium(pida, &medium);
|
||
|
SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST|SHCNF_FLUSH|SHCNF_FLUSHNOWAIT, _pstgf->_pidl, NULL);
|
||
|
}
|
||
|
else
|
||
|
hr = E_FAIL;
|
||
|
#endif
|
||
|
return hr;
|
||
|
}
|
||
|
|
||
|
DWORD CStgDropTarget::_GetDropEffect(DWORD *pdwEffect, DWORD grfKeyState)
|
||
|
{
|
||
|
DWORD dwEffectReturned = DROPEFFECT_NONE;
|
||
|
switch (grfKeyState & (MK_CONTROL | MK_SHIFT | MK_ALT))
|
||
|
{
|
||
|
case MK_CONTROL: dwEffectReturned = DROPEFFECT_COPY; break;
|
||
|
case MK_SHIFT: dwEffectReturned = DROPEFFECT_MOVE; break;
|
||
|
case MK_SHIFT | MK_CONTROL: dwEffectReturned = DROPEFFECT_LINK; break;
|
||
|
case MK_ALT: dwEffectReturned = DROPEFFECT_LINK; break;
|
||
|
|
||
|
default:
|
||
|
{
|
||
|
// no modifier keys:
|
||
|
// if the data object contains a preferred drop effect, try to use it
|
||
|
DWORD dwPreferred = DataObj_GetDWORD(_pdtobj, g_cfPreferredDropEffect, DROPEFFECT_NONE) & *pdwEffect;
|
||
|
if (dwPreferred)
|
||
|
{
|
||
|
if (dwPreferred & DROPEFFECT_MOVE)
|
||
|
{
|
||
|
dwEffectReturned = DROPEFFECT_MOVE;
|
||
|
}
|
||
|
else if (dwPreferred & DROPEFFECT_COPY)
|
||
|
{
|
||
|
dwEffectReturned = DROPEFFECT_COPY;
|
||
|
}
|
||
|
else if (dwPreferred & DROPEFFECT_LINK)
|
||
|
{
|
||
|
dwEffectReturned = DROPEFFECT_LINK;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
dwEffectReturned = DROPEFFECT_COPY;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
return dwEffectReturned;
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP CStgDropTarget::DragEnter(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
|
||
|
{
|
||
|
IUnknown_Set((IUnknown **)&_pdtobj, pdtobj);
|
||
|
|
||
|
_grfKeyStateLast = grfKeyState;
|
||
|
|
||
|
if (pdwEffect)
|
||
|
*pdwEffect = _dwEffectLastReturned = _GetDropEffect(pdwEffect, grfKeyState);
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP CStgDropTarget::DragOver(DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
|
||
|
{
|
||
|
// has the key state changed? if not then lets return the previously cached
|
||
|
// version, otherwise recompute.
|
||
|
|
||
|
if (_grfKeyStateLast == grfKeyState)
|
||
|
{
|
||
|
if (*pdwEffect)
|
||
|
*pdwEffect = _dwEffectLastReturned;
|
||
|
}
|
||
|
else if (*pdwEffect)
|
||
|
{
|
||
|
*pdwEffect = _GetDropEffect(pdwEffect, grfKeyState);
|
||
|
}
|
||
|
|
||
|
_dwEffectLastReturned = *pdwEffect;
|
||
|
_grfKeyStateLast = grfKeyState;
|
||
|
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
STDMETHODIMP CStgDropTarget::DragLeave()
|
||
|
{
|
||
|
ATOMICRELEASE(_pdtobj);
|
||
|
return S_OK;
|
||
|
}
|
||
|
|
||
|
STDMETHODIMP CStgDropTarget::Drop(IDataObject *pdtobj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
|
||
|
{
|
||
|
*pdwEffect = DROPEFFECT_NONE; // incase of failure
|
||
|
|
||
|
// determine the type of operation to performed, if the right button is down
|
||
|
// then lets display the menu, otherwise base it on the drop effect
|
||
|
|
||
|
UINT idCmd = 0; // Choice from drop popup menu
|
||
|
if (!(_grfKeyStateLast & MK_LBUTTON))
|
||
|
{
|
||
|
HMENU hMenu = SHLoadPopupMenu(HINST_THISDLL, POPUP_NONDEFAULTDD);
|
||
|
if (!hMenu)
|
||
|
{
|
||
|
DragLeave();
|
||
|
return E_FAIL;
|
||
|
}
|
||
|
|
||
|
SetMenuDefaultItem(hMenu, POPUP_NONDEFAULTDD, FALSE);
|
||
|
idCmd = TrackPopupMenu(hMenu,
|
||
|
TPM_RETURNCMD|TPM_RIGHTBUTTON|TPM_LEFTALIGN,
|
||
|
pt.x, pt.y, 0, _hwnd, NULL);
|
||
|
DestroyMenu(hMenu);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
switch (_GetDropEffect(pdwEffect, grfKeyState))
|
||
|
{
|
||
|
case DROPEFFECT_COPY: idCmd = DDIDM_COPY; break;
|
||
|
case DROPEFFECT_MOVE: idCmd = DDIDM_MOVE; break;
|
||
|
case DROPEFFECT_LINK: idCmd = DDIDM_LINK; break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// now perform the operation, based on the command ID we have.
|
||
|
|
||
|
HRESULT hr = E_FAIL;
|
||
|
switch (idCmd)
|
||
|
{
|
||
|
case DDIDM_COPY:
|
||
|
case DDIDM_MOVE:
|
||
|
hr = _Transfer(pdtobj, idCmd);
|
||
|
if (SUCCEEDED(hr))
|
||
|
*pdwEffect = (idCmd == DDIDM_COPY) ? DROPEFFECT_COPY : DROPEFFECT_MOVE;
|
||
|
else
|
||
|
*pdwEffect = 0;
|
||
|
break;
|
||
|
|
||
|
case DDIDM_LINK:
|
||
|
{
|
||
|
WCHAR wzPath[MAX_PATH];
|
||
|
SHGetNameAndFlags(_pstgf->_pidl, SHGDN_FORPARSING, wzPath, ARRAYSIZE(wzPath), NULL);
|
||
|
|
||
|
hr = SHCreateLinks(_hwnd, wzPath, pdtobj, 0, NULL);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// success so lets populate the new changes to the effect
|
||
|
|
||
|
if (SUCCEEDED(hr) && *pdwEffect)
|
||
|
{
|
||
|
DataObj_SetDWORD(pdtobj, g_cfLogicalPerformedDropEffect, *pdwEffect);
|
||
|
DataObj_SetDWORD(pdtobj, g_cfPerformedDropEffect, *pdwEffect);
|
||
|
}
|
||
|
|
||
|
DragLeave();
|
||
|
return hr;
|
||
|
}
|