|
|
#include "shellprv.h"
#include "cowsite.h"
#include "enumidlist.h"
typedef enum { MAYBEBOOL_MAYBE = 0, MAYBEBOOL_TRUE, MAYBEBOOL_FALSE, } MAYBEBOOL;
#define _GetBindWindow(p) NULL
class CShellItem : public IShellItem , public IPersistIDList , public IParentAndItem { public: CShellItem(); // IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release();
// IShellItem
STDMETHODIMP BindToHandler(IBindCtx *pbc, REFGUID rguidHandler, REFIID riid, void **ppv); STDMETHODIMP GetParent(IShellItem **ppsi); STDMETHODIMP GetDisplayName(SIGDN sigdnName, LPOLESTR *ppszName); STDMETHODIMP GetAttributes(SFGAOF sfgaoMask, SFGAOF *psfgaoFlags); STDMETHODIMP Compare(IShellItem *psi, SICHINTF hint, int *piOrder);
// IPersist
STDMETHODIMP GetClassID(LPCLSID lpClassID) {*lpClassID = CLSID_ShellItem; return S_OK;} // IPersistIDList
STDMETHODIMP SetIDList(LPCITEMIDLIST pidl); STDMETHODIMP GetIDList(LPITEMIDLIST *ppidl);
// IParentAndItem
STDMETHODIMP SetParentAndItem(LPCITEMIDLIST pidlParent, IShellFolder *psf, LPCITEMIDLIST pidlChild); STDMETHODIMP GetParentAndItem(LPITEMIDLIST *ppidlParent, IShellFolder **ppsf, LPITEMIDLIST *ppidlChild);
#if 0
// IPersistStream
STDMETHODIMP IsDirty(void); STDMETHODIMP Load(IStream *pStm); STDMETHODIMP Save(IStream *pStm, BOOL fClearDirty); STDMETHODIMP GetSizeMax(ULARGE_INTEGER *pcbSize);
// implement or we cant ask for the IShellFolder in GetParentAndItem()
// IMarshal
STDMETHODIMP GetUnmarshalClass( REFIID riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, CLSID *pCid);
STDMETHODIMP GetMarshalSizeMax( REFIID riid, void *pv, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, DWORD *pSize);
STDMETHODIMP MarshalInterface( IStream *pStm, REFIID riid, void *pv, dwDestContext, void *pvDestContext, DWORD mshlflags);
STDMETHODIMP UnmarshalInterface( IStream *pStm, REFIID riid, void **ppv);
STDMETHODIMP ReleaseMarshalData(IStream *pStm);
STDMETHODIMP DisconnectObject(DWORD dwReserved); #endif // 0
private: // methods
~CShellItem();
void _Reset(void); // BindToHandler() helpers
HRESULT _BindToParent(REFIID riid, void **ppv); HRESULT _BindToSelf(REFIID riid, void **ppv); // GetAttributes() helpers
inline BOOL _IsAttrib(SFGAOF sfgao); // GetDisplayName() helpers
BOOL _SupportedName(SIGDN sigdnName, SHGDNF *pflags); HRESULT _FixupName(SIGDN sigdnName, LPOLESTR *ppszName); void _FixupAttributes(IShellFolder *psf, SFGAOF sfgaoMask);
LONG _cRef; LPITEMIDLIST _pidlSelf; LPCITEMIDLIST _pidlChild; LPITEMIDLIST _pidlParent; IShellFolder *_psfSelf; IShellFolder *_psfParent; BOOL _fInited; SFGAOF _sfgaoTried; SFGAOF _sfgaoKnown; };
CShellItem::CShellItem() : _cRef(1) { ASSERT(!_pidlSelf); ASSERT(!_pidlChild); ASSERT(!_pidlParent); ASSERT(!_psfSelf); ASSERT(!_psfParent); }
CShellItem::~CShellItem() { _Reset(); }
void CShellItem::_Reset(void) { ATOMICRELEASE(_psfSelf); ATOMICRELEASE(_psfParent);
ILFree(_pidlSelf); ILFree(_pidlParent);
_pidlSelf = NULL; _pidlParent = NULL; _pidlChild = NULL; // alias into _pidlParent
} STDMETHODIMP CShellItem::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CShellItem, IShellItem), QITABENT(CShellItem, IPersistIDList), QITABENT(CShellItem, IParentAndItem), { 0 }, };
return QISearch(this, qit, riid, ppv); }
STDMETHODIMP_(ULONG) CShellItem::AddRef() { return InterlockedIncrement(&_cRef); }
STDMETHODIMP_(ULONG) CShellItem::Release() { if (InterlockedDecrement(&_cRef)) return _cRef;
delete this; return 0; }
STDMETHODIMP CShellItem::SetIDList(LPCITEMIDLIST pidl) { if (!pidl) { RIPMSG(0, "Tried to Call SetIDList with a NULL pidl"); return E_INVALIDARG; }
_Reset();
HRESULT hr = SHILClone(pidl, &_pidlSelf); if (SUCCEEDED(hr)) { // possible this item is the desktop in which case
// there is no parent.
if (ILIsEmpty(_pidlSelf)) { _pidlParent = NULL; _pidlChild = _pidlSelf; } else { _pidlParent = ILCloneParent(_pidlSelf); _pidlChild = ILFindLastID(_pidlSelf);
if (NULL == _pidlParent) { hr = E_OUTOFMEMORY; } } } return hr; }
STDMETHODIMP CShellItem::GetIDList(LPITEMIDLIST *ppidl) { HRESULT hr = E_UNEXPECTED; if (_pidlSelf) { hr = SHILClone(_pidlSelf, ppidl); }
return hr; }
HRESULT CShellItem::_BindToParent(REFIID riid, void **ppv) { ASSERT(_pidlChild); // we should already have a child setup
if (!_psfParent && _pidlParent && _pidlSelf) // check pidlParent to check in case the item is the desktop
{ HRESULT hr; LPCITEMIDLIST pidlChild;
hr = SHBindToIDListParent(_pidlSelf, IID_PPV_ARG(IShellFolder, &_psfParent), &pidlChild);
#ifdef DEBUG
if (SUCCEEDED(hr)) { ASSERT(pidlChild == _pidlChild); } #endif // DEBUG
}
if (_psfParent) { return _psfParent->QueryInterface(riid, ppv); }
return E_FAIL; }
HRESULT CShellItem::_BindToSelf(REFIID riid, void **ppv) { HRESULT hr = E_FAIL;
if (!_psfSelf) { hr = BindToHandler(NULL, BHID_SFObject, IID_PPV_ARG(IShellFolder, &_psfSelf)); }
if (_psfSelf) { hr = _psfSelf->QueryInterface(riid, ppv); }
return hr; }
HRESULT _CreateLinkTargetItem(IShellItem *psi, IBindCtx *pbc, REFGUID rbhid, REFIID riid, void **ppv) { SFGAOF flags = SFGAO_LINK; if (SUCCEEDED(psi->GetAttributes(flags, &flags)) && (flags & SFGAO_LINK)) { // this is indeed a link
// get the target and
IShellLink *psl; HRESULT hr = psi->BindToHandler(pbc, BHID_SFUIObject, IID_PPV_ARG(IShellLink, &psl));
if (SUCCEEDED(hr)) { DWORD slr = 0; HWND hwnd = _GetBindWindow(pbc); if (pbc) { BIND_OPTS2 bo; bo.cbStruct = sizeof(BIND_OPTS2); // Requires size filled in.
if (SUCCEEDED(pbc->GetBindOptions(&bo))) { // these are the flags to pass to resolve
slr = bo.dwTrackFlags; } }
hr = psl->Resolve(hwnd, slr);
if (S_OK == hr) { LPITEMIDLIST pidl; hr = psl->GetIDList(&pidl);
if (SUCCEEDED(hr)) { IShellItem *psiTarget; hr = SHCreateShellItem(NULL, NULL, pidl, &psiTarget);
if (SUCCEEDED(hr)) { hr = psiTarget->QueryInterface(riid, ppv); psiTarget->Release(); } ILFree(pidl); } } else if (SUCCEEDED(hr)) hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
psl->Release(); }
return hr; }
return E_INVALIDARG; }
BOOL _IsWebfolders(IShellItem *psi); HRESULT _CreateStorageHelper(IShellItem *psi, IBindCtx *pbc, REFGUID rbhid, REFIID riid, void **ppv); HRESULT _CreateStream(IShellItem *psi, IBindCtx *pbc, REFGUID rbhid, REFIID riid, void **ppv); HRESULT _CreateEnumHelper(IShellItem *psi, IBindCtx *pbc, REFGUID rbhid, REFIID riid, void **ppv);
HRESULT _CreateHelperInstance(IShellItem *psi, IBindCtx *pbc, REFGUID rbhid, REFIID riid, void **ppv) { IItemHandler *pih; HRESULT hr = SHCoCreateInstance(NULL, &rbhid, NULL, IID_PPV_ARG(IItemHandler, &pih));
if (SUCCEEDED(hr)) { hr = pih->SetItem(psi);
if (SUCCEEDED(hr)) { hr = pih->QueryInterface(riid, ppv); } pih->Release(); }
return hr; } enum { BNF_OBJECT = 0x0001, BNF_UIOBJECT = 0x0002, BNF_VIEWOBJECT = 0x0004, BNF_USE_RIID = 0x0008, BNF_REFLEXIVE = 0x0010, }; typedef DWORD BNF;
typedef HRESULT (* PFNCREATEHELPER)(IShellItem *psi, IBindCtx *pbc, REFGUID rbhid, REFIID riid, void **ppv);
typedef struct { const GUID *pbhid; BNF bnf; const IID *piid; PFNCREATEHELPER pfn; } BINDNONSENSE;
#define BINDHANDLER(bhid, flags, piid, pfn) { &bhid, flags, piid, pfn},
#define SFBINDHANDLER(bhid, flags, piid) BINDHANDLER(bhid, flags, piid, NULL)
#define BINDHELPER(bhid, flags, pfn) BINDHANDLER(bhid, flags, NULL, pfn)
const BINDNONSENSE c_bnList[] = { SFBINDHANDLER(BHID_SFObject, BNF_OBJECT | BNF_USE_RIID, NULL) SFBINDHANDLER(BHID_SFUIObject, BNF_UIOBJECT | BNF_USE_RIID, NULL) SFBINDHANDLER(BHID_SFViewObject, BNF_VIEWOBJECT | BNF_USE_RIID, NULL) BINDHELPER(BHID_LinkTargetItem, 0, _CreateLinkTargetItem) BINDHELPER(BHID_LocalCopyHelper, 0, _CreateHelperInstance) BINDHELPER(BHID_Storage, BNF_OBJECT | BNF_USE_RIID, _CreateStorageHelper) BINDHELPER(BHID_Stream, BNF_OBJECT | BNF_USE_RIID, NULL) BINDHELPER(BHID_StorageEnum, 0, _CreateEnumHelper) }; HRESULT _GetBindNonsense(const GUID *pbhid, const IID *piid, BINDNONSENSE *pbn) { HRESULT hr = MK_E_NOOBJECT; for (int i = 0; i < ARRAYSIZE(c_bnList); i++) { if (IsEqualGUID(*pbhid, *(c_bnList[i].pbhid))) { *pbn = c_bnList[i]; hr = S_OK;
if (pbn->bnf & BNF_USE_RIID) { pbn->piid = piid; }
if (pbn->piid && IsEqualGUID(*(pbn->piid), *piid)) pbn->bnf |= BNF_REFLEXIVE;
break; } } return hr; }
// the SafeBC functions will use the pbc passed in or
// create a new one if necessary. either way, if
// the *ppbc is returned non-NULL then it is ref'd
STDAPI SHSafeRegisterObjectParam(LPCWSTR psz, IUnknown *punk, IBindCtx *pbcIn, IBindCtx **ppbc) { IBindCtx *pbc = pbcIn; if (!pbc) CreateBindCtx(0, &pbc); else pbc->AddRef();
*ppbc = NULL; HRESULT hr; if (pbc) { hr = pbc->RegisterObjectParam((LPOLESTR)psz, punk); if (SUCCEEDED(hr)) { // pass our ref to the caller
*ppbc = pbc; } else { pbc->Release(); } } else { hr = E_OUTOFMEMORY; } return hr; }
STDMETHODIMP CShellItem::BindToHandler(IBindCtx *pbc, REFGUID rbhid, REFIID riid, void **ppv) { // look up handler for bind flags
// use the flags to determine BTO GUIO BTS CVO
BINDNONSENSE bn = {0}; HRESULT hr = _GetBindNonsense(&rbhid, &riid, &bn);
*ppv = NULL; if (SUCCEEDED(hr)) { hr = E_NOINTERFACE;
if (_pidlParent && (bn.bnf & (BNF_OBJECT | BNF_UIOBJECT))) { IShellFolder *psf; if (SUCCEEDED(_BindToParent(IID_PPV_ARG(IShellFolder, &psf)))) { if (bn.bnf & BNF_OBJECT) { hr = psf->BindToObject(_pidlChild, pbc, *(bn.piid), ppv); } if (FAILED(hr) && (bn.bnf & BNF_UIOBJECT)) { HWND hwnd = _GetBindWindow(pbc); hr = psf->GetUIObjectOf(hwnd, 1, &_pidlChild, *(bn.piid), NULL, ppv); } psf->Release(); } }
// if don't have a parent pidl then we are the desktop.
if (FAILED(hr) && (NULL == _pidlParent) && (bn.bnf & BNF_OBJECT)) { IShellFolder *psf; if (SUCCEEDED(SHGetDesktopFolder(&psf))) { hr = psf->QueryInterface(riid,ppv); psf->Release(); } }
if (FAILED(hr) && (bn.bnf & BNF_VIEWOBJECT)) { IShellFolder *psf;
if (SUCCEEDED(_BindToSelf(IID_PPV_ARG(IShellFolder, &psf)))) { HWND hwnd = _GetBindWindow(pbc); hr = psf->CreateViewObject(hwnd, *(bn.piid), ppv); psf->Release(); } }
if (SUCCEEDED(hr)) { if (!(bn.bnf & BNF_REFLEXIVE)) { IUnknown *punk = (IUnknown *)*ppv; hr = punk->QueryInterface(riid, ppv); punk->Release(); } // else riid is the same as bn.piid
} else if (bn.pfn) { hr = bn.pfn(this, pbc, rbhid, riid, ppv); } }
return hr; }
STDMETHODIMP CShellItem::GetParent(IShellItem **ppsi) { HRESULT hr = MK_E_NOOBJECT;
if (_pidlParent) { if (!ILIsEmpty(_pidlSelf)) { CShellItem *psi = new CShellItem(); if (psi) { // may already have the _psf Parent here so be nice
// to have a way to do this in a set.
hr = psi->SetIDList(_pidlParent); if (SUCCEEDED(hr)) hr = psi->QueryInterface(IID_PPV_ARG(IShellItem, ppsi)); psi->Release(); } else hr = E_OUTOFMEMORY; } }
return hr; }
BOOL CShellItem::_IsAttrib(SFGAOF sfgao) { HRESULT hr = GetAttributes(sfgao, &sfgao); return hr == S_OK; }
#define SHGDNF_MASK 0xFFFF // bottom word
BOOL CShellItem::_SupportedName(SIGDN sigdn, SHGDNF *pflags) { *pflags = (sigdn & SHGDNF_MASK); // block this completely
// to avoid doing any binding at all
if (sigdn == SIGDN_FILESYSPATH && !_IsAttrib(SFGAO_FILESYSTEM)) return FALSE;
return TRUE; }
HRESULT CShellItem::_FixupName(SIGDN sigdnName, LPOLESTR *ppszName) { HRESULT hr = S_OK; if (sigdnName == SIGDN_URL && !UrlIsW(*ppszName, URLIS_URL)) { WCHAR sz[MAX_URL_STRING]; DWORD cch = ARRAYSIZE(sz); if (SUCCEEDED(UrlCreateFromPathW(*ppszName, sz, &cch, 0))) { CoTaskMemFree(*ppszName); hr = SHStrDupW(sz, ppszName); } }
return hr; }
STDMETHODIMP CShellItem::GetDisplayName(SIGDN sigdnName, LPOLESTR *ppszName) { SHGDNF flags; if (_SupportedName(sigdnName, &flags)) { IShellFolder *psf; HRESULT hr = _BindToParent(IID_PPV_ARG(IShellFolder, &psf));
if (SUCCEEDED(hr)) { STRRET str; hr = IShellFolder_GetDisplayNameOf(psf, _pidlChild, flags, &str, 0);
if (SUCCEEDED(hr)) { hr = StrRetToStrW(&str, _pidlChild, ppszName);
if (SUCCEEDED(hr) && (int)flags != (int)sigdnName) { hr = _FixupName(sigdnName, ppszName); } } psf->Release(); }
return hr; } return E_INVALIDARG; }
void CShellItem::_FixupAttributes(IShellFolder *psf, SFGAOF sfgaoMask) { // APPCOMPAT: The following if statement and its associated body is an APP HACK for pagis pro
// folder. Which specifies SFGAO_FOLDER and SFGAO_FILESYSTEM but it doesn't specify SFGAO_STORAGEANCESTOR
// This APP HACK basically checks for this condition and provides SFGAO_STORAGEANCESTOR bit.
if (_sfgaoKnown & SFGAO_FOLDER) { if ((!(_sfgaoKnown & SFGAO_FILESYSANCESTOR) && (sfgaoMask & SFGAO_FILESYSANCESTOR)) || ((_sfgaoKnown & SFGAO_CANMONIKER) && !(_sfgaoKnown & SFGAO_STORAGEANCESTOR) && (sfgaoMask & SFGAO_STORAGEANCESTOR))) { OBJCOMPATFLAGS ocf = SHGetObjectCompatFlags(psf, NULL); if (ocf & OBJCOMPATF_NEEDSFILESYSANCESTOR) { _sfgaoKnown |= SFGAO_FILESYSANCESTOR; } if (ocf & OBJCOMPATF_NEEDSSTORAGEANCESTOR) { // switch SFGAO_CANMONIKER -> SFGAO_STORAGEANCESTOR
_sfgaoKnown |= SFGAO_STORAGEANCESTOR; _sfgaoKnown &= ~SFGAO_CANMONIKER; } } } }
STDMETHODIMP CShellItem::GetAttributes(SFGAOF sfgaoMask, SFGAOF *psfgaoFlags) { HRESULT hr = S_OK;
// see if we cached this bits before...
if ((sfgaoMask & _sfgaoTried) != sfgaoMask) { IShellFolder *psf; hr = _BindToParent(IID_PPV_ARG(IShellFolder, &psf));
if (SUCCEEDED(hr)) { // we cache all the bits except VALIDATE
_sfgaoTried |= (sfgaoMask & ~SFGAO_VALIDATE); SFGAOF sfgao = sfgaoMask;
hr = psf->GetAttributesOf(1, &_pidlChild, &sfgao);
if (SUCCEEDED(hr)) { // we cache all the bits except VALIDATE
_sfgaoKnown |= (sfgao & ~SFGAO_VALIDATE); _FixupAttributes(psf, sfgaoMask); }
psf->Release(); } }
*psfgaoFlags = _sfgaoKnown & sfgaoMask;
if (SUCCEEDED(hr)) { // we return S_OK
// only if the bits set match
// exactly the bits requested
if (*psfgaoFlags == sfgaoMask) hr = S_OK; else hr = S_FALSE; } return hr; }
STDMETHODIMP CShellItem::Compare(IShellItem *psi, SICHINTF hint, int *piOrder) { *piOrder = 0; HRESULT hr = IsSameObject(SAFECAST(this, IShellItem *), psi) ? S_OK : E_FAIL; if (FAILED(hr)) { IShellFolder *psf; hr = _BindToParent(IID_PPV_ARG(IShellFolder, &psf)); if (SUCCEEDED(hr)) { IParentAndItem *pfai; hr = psi->QueryInterface(IID_PPV_ARG(IParentAndItem, &pfai)); if (SUCCEEDED(hr)) { IShellFolder *psfOther; LPITEMIDLIST pidlParent, pidlChild; hr = pfai->GetParentAndItem(&pidlParent, &psfOther, &pidlChild); if (SUCCEEDED(hr)) { if (IsSameObject(psf, psfOther) || ILIsEqual(_pidlParent, pidlParent)) { hr = psf->CompareIDs(hint & 0xf0000000, _pidlChild, pidlChild); } else { // these items have a different parent
// compare the absolute pidls
LPITEMIDLIST pidlOther; hr = SHGetIDListFromUnk(psi, &pidlOther); if (SUCCEEDED(hr)) { IShellFolder *psfDesktop; hr = SHGetDesktopFolder(&psfDesktop); if (SUCCEEDED(hr)) { hr = psfDesktop->CompareIDs(hint & 0xf0000000, _pidlSelf, pidlOther); psfDesktop->Release(); } ILFree(pidlOther); } } if (SUCCEEDED(hr)) { *piOrder = ShortFromResult(hr); if (*piOrder) hr = S_FALSE; else hr = S_OK; } psfOther->Release(); ILFree(pidlParent); ILFree(pidlChild); } pfai->Release(); } psf->Release(); } }
return hr; }
// IParentAndItem
STDMETHODIMP CShellItem::SetParentAndItem(LPCITEMIDLIST pidlParent, IShellFolder *psfParent, LPCITEMIDLIST pidlChild) { // require to have a Parent if making this call. If don't then use SetIDList
if (!pidlParent && !psfParent) { RIPMSG(0, "Tried to Call SetParent without a parent"); return E_INVALIDARG; }
LPITEMIDLIST pidlFree = NULL;
if ((NULL == pidlParent) && psfParent) { if (SUCCEEDED(SHGetIDListFromUnk(psfParent, &pidlFree))) { pidlParent = pidlFree; } } if (!ILIsEmpty(_ILNext(pidlChild))) { // if more than on item in the child pidl don't use the parent IShellFolder*
// could revist and bind from this parent to get a new parent so don't have
// to BindObject through the entire pidl path.
psfParent = NULL; }
HRESULT hr = E_FAIL; if (pidlParent) { _Reset();
hr = SHILCombine(pidlParent, pidlChild, &_pidlSelf); if (SUCCEEDED(hr)) { // setup pidls so _pidlChild is a single item.
if (_pidlParent = ILCloneParent(_pidlSelf)) { _pidlChild = ILFindLastID(_pidlSelf);
PPUNK_SET(&_psfParent, psfParent);
#ifdef DEBUG
if (psfParent) { LPITEMIDLIST pidlD; if (SUCCEEDED(SHGetIDListFromUnk(psfParent, &pidlD))) { ASSERT(ILIsEqual(pidlD, pidlParent)); ILFree(pidlD); } } #endif //DEBUG
} else { hr = E_OUTOFMEMORY; } } }
ILFree(pidlFree); // maybe NULL
return hr; }
STDMETHODIMP CShellItem::GetParentAndItem(LPITEMIDLIST *ppidlParent, IShellFolder **ppsf, LPITEMIDLIST *ppidl) { if (ppsf) { _BindToParent(IID_PPV_ARG(IShellFolder, ppsf)); } if (ppidlParent) { if (_pidlParent) { *ppidlParent = ILClone(_pidlParent); } else { *ppidlParent = NULL; } } if (ppidl) *ppidl = ILClone(_pidlChild);
HRESULT hr = S_OK; if ((ppidlParent && !*ppidlParent) || (ppsf && !*ppsf) || (ppidl && !*ppidl)) { // this is failure
// but we dont know what failed
if (ppsf && *ppsf) { (*ppsf)->Release(); *ppsf = NULL; }
if (ppidlParent) { ILFree(*ppidlParent); *ppidlParent = NULL; }
if (ppidl) { ILFree(*ppidl); *ppidl = NULL; } hr = E_OUTOFMEMORY; } return hr; }
STDAPI CShellItem_CreateInstance(IUnknown *punkOuter, REFIID riid, void **ppv) { CShellItem *psi = new CShellItem(); if (psi) { HRESULT hr = psi->QueryInterface(riid, ppv); psi->Release(); return hr; } return E_OUTOFMEMORY; }
class CShellItemEnum : IEnumShellItems, public CObjectWithSite { public: CShellItemEnum(); STDMETHODIMP Initialize(LPCITEMIDLIST pidlFolder,IShellFolder *psf, DWORD dwFlags,UINT cidl,LPCITEMIDLIST *apidl); // IUnknown methods
STDMETHODIMP QueryInterface(REFIID riid, void **ppvOut); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release();
STDMETHODIMP Next(ULONG celt, IShellItem **rgelt, ULONG *pceltFetched); STDMETHODIMP Skip(ULONG celt); STDMETHODIMP Reset(); STDMETHODIMP Clone(IEnumShellItems **ppenum);
private:
virtual ~CShellItemEnum(); HRESULT _EnsureEnum();
LONG _cRef; DWORD _dwFlags;
IShellFolder *_psf; IEnumIDList *_penum; LPITEMIDLIST _pidlFolder; };
STDMETHODIMP CShellItemEnum::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CShellItemEnum, IEnumShellItems), QITABENT(CShellItemEnum, IObjectWithSite), { 0 }, };
return QISearch(this, qit, riid, ppv); }
STDMETHODIMP_(ULONG) CShellItemEnum::AddRef() { return InterlockedIncrement(&_cRef); }
STDMETHODIMP_(ULONG) CShellItemEnum::Release() { if (InterlockedDecrement(&_cRef)) return _cRef;
delete this; return 0; }
STDMETHODIMP CShellItemEnum::Next(ULONG celt, IShellItem **rgelt, ULONG *pceltFetched) { HRESULT hr = _EnsureEnum(); if (FAILED(hr)) return hr;
ULONG uTemp; if (!pceltFetched) pceltFetched = &uTemp; *pceltFetched = 0; while (celt--) { LPITEMIDLIST pidl; ULONG cFetched; hr = _penum->Next(1, &pidl, &cFetched); if (S_OK == hr) { hr = SHCreateShellItem(_pidlFolder, _psf, pidl, &rgelt[*pceltFetched]); if (SUCCEEDED(hr)) (*pceltFetched)++; ILFree(pidl); }
if (S_OK != hr) break; }
if (SUCCEEDED(hr)) { hr = *pceltFetched ? S_OK : S_FALSE; } else { for (UINT i = 0; i < *pceltFetched; i++) { ATOMICRELEASE(rgelt[i]); } *pceltFetched = 0; }
return hr; }
STDMETHODIMP CShellItemEnum::Skip(ULONG celt) { HRESULT hr = _EnsureEnum(); if (SUCCEEDED(hr)) hr = _penum->Skip(celt);
return hr; }
STDMETHODIMP CShellItemEnum::Reset() { HRESULT hr = _EnsureEnum(); if (SUCCEEDED(hr)) hr = _penum->Reset();
return hr; }
STDMETHODIMP CShellItemEnum::Clone(IEnumShellItems **ppenum) { return E_NOTIMPL; }
HRESULT CShellItemEnum::_EnsureEnum() { if (_penum) return S_OK;
HRESULT hr = E_FAIL;
if (_psf) { HWND hwnd = NULL; IUnknown_GetWindow(_punkSite, &hwnd);
// if didn't get an enum in Initialize then enumerate the
// entire folder.
hr = _psf->EnumObjects(hwnd, _dwFlags, &_penum); }
return hr; }
CShellItemEnum::CShellItemEnum() : _cRef(1) { ASSERT(NULL == _psf); ASSERT(NULL == _penum); ASSERT(NULL == _pidlFolder); }
STDMETHODIMP CShellItemEnum::Initialize(LPCITEMIDLIST pidlFolder, IShellFolder *psf, DWORD dwFlags, UINT cidl, LPCITEMIDLIST *apidl) { HRESULT hr = E_FAIL;
_dwFlags = dwFlags;
_psf = psf; _psf->AddRef();
if (NULL == _pidlFolder) { hr = SHGetIDListFromUnk(_psf, &_pidlFolder); } else { hr = SHILClone(pidlFolder, &_pidlFolder); }
if (SUCCEEDED(hr) && cidl) { ASSERT(apidl);
// if want to enum with other flags or combos need to implement the filter
ASSERT(_dwFlags == (SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN));
hr = CreateIEnumIDListOnIDLists(apidl, cidl, &_penum); }
// on error let our destructor do the cleanup
return hr; }
CShellItemEnum::~CShellItemEnum() { ATOMICRELEASE(_penum); ATOMICRELEASE(_psf); ILFree(_pidlFolder); }
HRESULT _CreateShellItemEnum(LPCITEMIDLIST pidlFolder,IShellFolder *psf,IBindCtx *pbc, REFGUID rbhid, UINT cidl, LPCITEMIDLIST *apidl, REFIID riid, void **ppv) { DWORD dwFlags; HRESULT hr = E_FAIL; LPCITEMIDLIST *pidlEnum = NULL;
UINT mycidl = 0; LPITEMIDLIST *myppidl = NULL;;
if (IsEqualGUID(rbhid, BHID_StorageEnum)) dwFlags = SHCONTF_STORAGE; else dwFlags = SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN;
CShellItemEnum *psie = new CShellItemEnum();
if (psie) { hr = psie->Initialize(pidlFolder, psf, dwFlags, cidl, apidl);
if (SUCCEEDED(hr)) { hr = psie->QueryInterface(riid, ppv); }
psie->Release(); } else { hr = E_OUTOFMEMORY; }
return hr; }
HRESULT _CreateEnumHelper(IShellItem *psi, IBindCtx *pbc, REFGUID rbhid, REFIID riid, void **ppv) { HRESULT hr = E_FAIL; IShellFolder *psf;
ASSERT(psi); if (psi) { hr = psi->BindToHandler(NULL, BHID_SFObject, IID_PPV_ARG(IShellFolder, &psf));
if (SUCCEEDED(hr)) { hr = _CreateShellItemEnum(NULL,psf,pbc,rbhid,0,NULL,riid,ppv); psf->Release(); } }
return hr; }
class CShellItemArray : public IShellItemArray { public: CShellItemArray(); ~CShellItemArray(); HRESULT Initialize(LPCITEMIDLIST pidlParent,IShellFolder *psf,UINT cidl,LPCITEMIDLIST *ppidl);
// IUnknown
STDMETHODIMP QueryInterface(REFIID riid, void **ppv); STDMETHODIMP_(ULONG) AddRef(void) ; STDMETHODIMP_(ULONG) Release(void);
// IShellItemArray
STDMETHODIMP BindToHandler( IBindCtx *pbc, REFGUID rbhid, REFIID riid, void **ppvOut);
STDMETHODIMP GetAttributes( SIATTRIBFLAGS dwAttribFlags, SFGAOF sfgaoMask, SFGAOF *psfgaoAttribs);
STDMETHODIMP GetCount(DWORD *pdwNumItems); STDMETHODIMP GetItemAt(DWORD dwIndex,IShellItem **ppsi); STDMETHODIMP EnumItems(IEnumShellItems **ppenumShellItems);
private: HRESULT _CloneIDListArray(UINT cidl, LPCITEMIDLIST *apidl, UINT *pcidl, LPITEMIDLIST **papidl);
IShellFolder *_pshf; LPITEMIDLIST _pidlParent; LPITEMIDLIST *_ppidl; UINT _cidl; LONG _cRef; IDataObject *_pdo; // cached data object.
DWORD _dwAttribAndCacheResults; DWORD _dwAttribAndCacheMask; DWORD _dwAttribCompatCacheResults; DWORD _dwAttribCompatCacheMask; BOOL _fItemPidlsRagged; // set to true if have any rugged pidls.
};
CShellItemArray::CShellItemArray() { ASSERT(0 == _cidl); ASSERT(NULL == _ppidl); ASSERT(NULL == _pshf); ASSERT(NULL == _pdo);
_fItemPidlsRagged = TRUE; _cRef = 1; }
CShellItemArray::~CShellItemArray() { ATOMICRELEASE(_pdo); ATOMICRELEASE(_pshf);
ILFree(_pidlParent); // may be null
if (NULL != _ppidl) { FreeIDListArray(_ppidl,_cidl); } }
HRESULT CShellItemArray::Initialize(LPCITEMIDLIST pidlParent, IShellFolder *psf, UINT cidl, LPCITEMIDLIST *ppidl) { if ((cidl > 1) && !ppidl || !psf) { return E_INVALIDARG; }
if (pidlParent) { _pidlParent = ILClone(pidlParent); // proceed on alloc failure, just won't use.
}
_pshf = psf; _pshf->AddRef();
HRESULT hr = S_OK; if (cidl) { // if there are items then make a copy
hr = _CloneIDListArray(cidl, ppidl, &_cidl, &_ppidl); }
// on error rely on destructor to do the cleanup
return hr; }
// IUnknown
STDMETHODIMP CShellItemArray::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CShellItemArray, IShellItemArray), { 0 }, };
return QISearch(this, qit, riid, ppv); }
STDMETHODIMP_(ULONG) CShellItemArray::AddRef() { return InterlockedIncrement(&_cRef); }
STDMETHODIMP_(ULONG) CShellItemArray::Release() { if (InterlockedDecrement(&_cRef)) return _cRef;
delete this; return 0; }
STDMETHODIMP CShellItemArray::BindToHandler(IBindCtx *pbc, REFGUID rbhid, REFIID riid, void **ppvOut) { HRESULT hr = E_FAIL;
if (_pshf) { // currently only allow bind to IDataObject and
// cache the result.
if (BHID_DataObject == rbhid) { if (NULL == _pdo) { _pshf->GetUIObjectOf(NULL, _cidl, (LPCITEMIDLIST *)_ppidl, IID_PPV_ARG_NULL(IDataObject, &_pdo)); }
if (_pdo) { hr = _pdo->QueryInterface(riid, ppvOut); } } else { hr = E_NOINTERFACE; } }
return hr; }
// This should probably take a flag that does an or'ing of attributes but this
// currrently isn't implemented. Do have comments on what the changes would be.
HRESULT CShellItemArray::GetAttributes(SIATTRIBFLAGS dwAttribFlags, SFGAOF sfgaoMask, SFGAOF *psfgaoAttribs) { DWORD dwAttrib; HRESULT hr = E_FAIL; if (dwAttribFlags > (dwAttribFlags & SIATTRIBFLAGS_MASK)) { ASSERT(dwAttribFlags <= (dwAttribFlags & SIATTRIBFLAGS_MASK)); return E_INVALIDARG; } if (SIATTRIBFLAGS_OR == dwAttribFlags) { ASSERT(SIATTRIBFLAGS_OR != dwAttribFlags); // or'ing is currently not implemented.
return E_INVALIDARG; }
if (_pshf) { DWORD dwAttribMask = sfgaoMask; DWORD *pdwCacheMask = NULL; DWORD *pdwCacheResults = NULL;
// setup to point to proper Cached values.
switch(dwAttribFlags) { case SIATTRIBFLAGS_AND: pdwCacheMask = &_dwAttribAndCacheMask; pdwCacheResults = &_dwAttribAndCacheResults; break; case SIATTRIBFLAGS_APPCOMPAT: pdwCacheMask = &_dwAttribCompatCacheMask; pdwCacheResults = &_dwAttribCompatCacheResults; break; default: ASSERT(0); // i don't know how to handle this flag.
break; }
dwAttribMask &= ~(*pdwCacheMask); // only ask for the bits we don't already have.
dwAttrib = dwAttribMask;
if (dwAttrib) { if (0 == _cidl) { dwAttrib = 0; } else { // if know this is not a ragged pidl and calling with the APPCOMPAT flag
// then calls GetAttributesOf for all the items in one call to the
// shellFolder.
if (!_fItemPidlsRagged && (SIATTRIBFLAGS_APPCOMPAT == dwAttribFlags)) { hr = _pshf->GetAttributesOf(_cidl, (LPCITEMIDLIST *)_ppidl, &dwAttrib); } else { LPITEMIDLIST *pCurItem = _ppidl; UINT itemCount = _cidl; DWORD dwAttribLoopResult = -1; // set all result bits for and, if going to or set to zero
while (itemCount--) { DWORD dwAttribTemp = dwAttrib; IShellFolder *psfNew; LPCITEMIDLIST pidlChild;
hr = SHBindToFolderIDListParent(_pshf, *pCurItem, IID_PPV_ARG(IShellFolder, &psfNew), &pidlChild);
if (SUCCEEDED(hr)) { hr = psfNew->GetAttributesOf(1, &pidlChild, &dwAttribTemp); psfNew->Release(); }
if (FAILED(hr)) { break; }
dwAttribLoopResult &= dwAttribTemp; // could also do an or'ing here
if (0 == dwAttribLoopResult) // if no attribs set and doing an and we can stop.
{ break; }
++pCurItem; }
dwAttrib = dwAttribLoopResult; // update the attrib
} } } else { hr = S_OK; }
if (SUCCEEDED(hr)) { // remember those bits that we just got +
// those that we computed before
*pdwCacheResults = dwAttrib | (*pdwCacheResults & *pdwCacheMask);
// we know these are now valid, keep track of those +
// if they gave us more than we asked for, cache them too
*pdwCacheMask |= dwAttribMask | dwAttrib;
// don't return anything that wasn't asked for. defview code relies on this.
*psfgaoAttribs = (*pdwCacheResults & sfgaoMask); } }
return hr; }
STDMETHODIMP CShellItemArray::GetCount(DWORD *pdwNumItems) { *pdwNumItems = _cidl; return S_OK; }
// way to get zero based index ShellItem without having to
// go through enumerator overhead.
STDMETHODIMP CShellItemArray::GetItemAt(DWORD dwIndex, IShellItem **ppsi) { *ppsi = NULL;
if (dwIndex >= _cidl) { return E_FAIL; } ASSERT(_ppidl);
LPITEMIDLIST pidl = *(_ppidl + dwIndex);
// if GetItemAt is called a lot may want to
// a) get the pshf pidl to pass to SHCreateshellItem so doesn't have to create each time
// b) see if always asking for first item and is so maybe cache the shellItem
return SHCreateShellItem(NULL, _pshf, pidl, ppsi); }
STDMETHODIMP CShellItemArray::EnumItems(IEnumShellItems **ppenumShellItems) { return _CreateShellItemEnum(_pidlParent, _pshf, NULL, GUID_NULL, _cidl, (LPCITEMIDLIST *) _ppidl, IID_PPV_ARG(IEnumShellItems, ppenumShellItems)); }
HRESULT CShellItemArray::_CloneIDListArray(UINT cidl, LPCITEMIDLIST *apidl, UINT *pcidl, LPITEMIDLIST **papidl) { HRESULT hr; LPITEMIDLIST *ppidl;
*papidl = NULL;
_fItemPidlsRagged = FALSE;
if (cidl && apidl) { ppidl = (LPITEMIDLIST *)LocalAlloc(LPTR, cidl * sizeof(*ppidl)); if (ppidl) { LPITEMIDLIST *apidlFrom = (LPITEMIDLIST *) apidl; LPITEMIDLIST *apidlTo = ppidl;
hr = S_OK; for (UINT i = 0; i < cidl ; i++) { hr = SHILClone(*apidlFrom, apidlTo); if (FAILED(hr)) { FreeIDListArray(ppidl, i); ppidl = NULL; break; } // if more than one item in list then set singeItemPidls to false
if (!ILIsEmpty(_ILNext(*apidlTo))) { _fItemPidlsRagged = TRUE; }
++apidlFrom; ++apidlTo; } } else hr = E_OUTOFMEMORY; } else { ppidl = NULL; hr = S_FALSE; // success by empty
}
if (SUCCEEDED(hr)) { *papidl = ppidl; *pcidl = cidl; } else { _fItemPidlsRagged = TRUE; } return hr; }
SHSTDAPI SHCreateShellItemArray(LPCITEMIDLIST pidlParent, IShellFolder *psf, UINT cidl, LPCITEMIDLIST *ppidl, IShellItemArray **ppsiItemArray) { HRESULT hr = E_OUTOFMEMORY; CShellItemArray *pItemArray = new CShellItemArray(); if (pItemArray) { hr = pItemArray->Initialize(pidlParent, psf, cidl, ppidl); if (FAILED(hr)) { pItemArray->Release(); pItemArray = NULL; } } *ppsiItemArray = pItemArray; return hr; }
|