1510 lines
38 KiB
C++
1510 lines
38 KiB
C++
|
|
#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;
|
|
}
|