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