windows-nt/Source/XPSP1/NT/shell/shell32/sdspatch/sdfldits.cpp
2020-09-26 16:20:57 +08:00

860 lines
22 KiB
C++

#include "precomp.h"
#include "shconv.h"
#pragma hdrstop
class CEnumFolderItems;
class CFolderItems : public FolderItems3,
public IPersistFolder,
public CObjectSafety,
public CObjectWithSite,
protected CImpIDispatch
{
friend CEnumFolderItems;
public:
CFolderItems(CFolder *psdf, BOOL fSelected);
// IUnknown
STDMETHOD(QueryInterface)(REFIID riid, void ** ppv);
STDMETHOD_(ULONG, AddRef)(void);
STDMETHOD_(ULONG, Release)(void);
// IDispatch
STDMETHODIMP GetTypeInfoCount(UINT *pctinfo)
{ return CImpIDispatch::GetTypeInfoCount(pctinfo); }
STDMETHODIMP GetTypeInfo(UINT itinfo, LCID lcid, ITypeInfo **pptinfo)
{ return CImpIDispatch::GetTypeInfo(itinfo, lcid, pptinfo); }
STDMETHODIMP GetIDsOfNames(REFIID riid, OLECHAR ** rgszNames, UINT cNames, LCID lcid, DISPID *rgdispid)
{ return CImpIDispatch::GetIDsOfNames(riid, rgszNames, cNames, lcid, rgdispid); }
STDMETHODIMP Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS * pdispparams, VARIANT * pvarResult, EXCEPINFO * pexcepinfo, UINT * puArgErr)
{ return CImpIDispatch::Invoke(dispidMember, riid, lcid, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr); }
// FolderItems
STDMETHODIMP get_Application(IDispatch **ppid);
STDMETHODIMP get_Parent (IDispatch **ppid);
STDMETHODIMP get_Count(long *pCount);
STDMETHODIMP Item(VARIANT, FolderItem**);
STDMETHODIMP _NewEnum(IUnknown **);
// FolderItems2
STDMETHODIMP InvokeVerbEx(VARIANT vVerb, VARIANT vArgs);
// FolderItems3
STDMETHODIMP Filter(long grfFlags, BSTR bstrFilter);
STDMETHODIMP get_Verbs(FolderItemVerbs **ppfic);
// IPersist
STDMETHODIMP GetClassID(CLSID *pClassID);
// IPersistFolder
STDMETHODIMP Initialize(LPCITEMIDLIST pidl);
protected:
HDPA _GetHDPA();
UINT _GetHDPACount();
BOOL_PTR _CopyItem(UINT iItem, LPCITEMIDLIST pidl);
LONG _cRef;
CFolder *_psdf;
HDPA _hdpa;
BOOL _fSelected;
BOOL _fGotAllItems;
IEnumIDList *_penum;
UINT _cNumEnumed;
LONG _grfFlags;
LPTSTR _pszFileSpec;
HRESULT _SecurityCheck();
void _ResetIDListArray();
virtual ~CFolderItems(void);
BOOL _IncludeItem(IShellFolder *psf, LPCITEMIDLIST pidl);
virtual HRESULT _EnsureItem(UINT iItemNeeded, LPCITEMIDLIST *ppidl);
STDMETHODIMP _GetUIObjectOf(REFIID riid, void ** ppv);
};
// CFolderItemsF(rom)D(ifferent)F(olders)
class CFolderItemsFDF : public CFolderItems,
public IInsertItem
{
public:
// TODO: add callback pointer to constructor
CFolderItemsFDF(CFolder *psdf);
// IUnknown override
STDMETHOD(QueryInterface)(REFIID riid, void ** ppv);
STDMETHOD_(ULONG, AddRef)(void) {return CFolderItems::AddRef();};
STDMETHOD_(ULONG, Release)(void) {return CFolderItems::Release();};
// IInsertItem
STDMETHOD(InsertItem)(LPCITEMIDLIST pidl);
protected:
virtual HRESULT _EnsureItem(UINT iItemNeeded, LPCITEMIDLIST *ppidl);
};
//Enumerator of whatever is held in the collection
class CEnumFolderItems : public IEnumVARIANT
{
public:
CEnumFolderItems(CFolderItems *pfdritms);
// IUnknown
STDMETHODIMP QueryInterface(REFIID, void **);
STDMETHODIMP_(ULONG) AddRef(void);
STDMETHODIMP_(ULONG) Release(void);
// IEnumFORMATETC
STDMETHODIMP Next(ULONG, VARIANT *, ULONG *);
STDMETHODIMP Skip(ULONG);
STDMETHODIMP Reset(void);
STDMETHODIMP Clone(IEnumVARIANT **);
private:
~CEnumFolderItems();
LONG _cRef;
CFolderItems *_pfdritms;
UINT _iCur;
};
HRESULT CFolderItems_Create(CFolder *psdf, BOOL fSelected, FolderItems **ppitems)
{
*ppitems = NULL;
HRESULT hr = E_OUTOFMEMORY;
CFolderItems* psdfi = new CFolderItems(psdf, fSelected);
if (psdfi)
{
hr = psdfi->QueryInterface(IID_PPV_ARG(FolderItems, ppitems));
psdfi->Release();
}
return hr;
}
CFolderItems::CFolderItems(CFolder *psdf, BOOL fSelected) :
_cRef(1),
_psdf(psdf),
_fSelected(fSelected),
_grfFlags(SHCONTF_FOLDERS | SHCONTF_NONFOLDERS),
CImpIDispatch(SDSPATCH_TYPELIB, IID_FolderItems3)
{
DllAddRef();
if (_psdf)
_psdf->AddRef();
ASSERT(_hdpa == NULL);
ASSERT(_pszFileSpec == NULL);
}
HRESULT CFolderItems::GetClassID(CLSID *pClassID)
{
*pClassID = CLSID_FolderItemsFDF;
return S_OK;
}
HRESULT CFolderItems::Initialize(LPCITEMIDLIST pidl)
{
ASSERT(_psdf == NULL);
return CFolder_Create2(NULL, pidl, NULL, &_psdf);
}
int FreePidlCallBack(void *pv, void *)
{
ILFree((LPITEMIDLIST)pv);
return 1;
}
HRESULT CFolderItems::_SecurityCheck()
{
if (!_dwSafetyOptions)
return S_OK;
return _psdf->_SecurityCheck();
}
void CFolderItems::_ResetIDListArray(void)
{
// destory the DPA, and lets reset the counters and pointers
if (_hdpa)
{
DPA_DestroyCallback(_hdpa, FreePidlCallBack, 0);
_hdpa = NULL;
}
_fGotAllItems = FALSE;
_cNumEnumed = 0;
ATOMICRELEASE(_penum); // may be NULL
}
CFolderItems::~CFolderItems(void)
{
_ResetIDListArray();
Str_SetPtr(&_pszFileSpec, NULL);
if (_psdf)
_psdf->Release();
DllRelease();
}
HDPA CFolderItems::_GetHDPA()
{
if (NULL == _hdpa)
_hdpa = ::DPA_Create(0);
return _hdpa;
}
UINT CFolderItems::_GetHDPACount()
{
UINT count = 0;
HDPA hdpa = _GetHDPA();
if (hdpa)
count = DPA_GetPtrCount(hdpa);
return count;
}
BOOL_PTR CFolderItems::_CopyItem(UINT iItem, LPCITEMIDLIST pidl)
{
LPITEMIDLIST pidlT = ILClone(pidl);
if (pidlT)
{
if ( -1 == ::DPA_InsertPtr(_hdpa, iItem, pidlT) )
{
ILFree(pidlT);
pidlT = NULL; // failure
}
}
return (BOOL_PTR)pidlT;
}
// check the item name against the file spec and see if we should include it
BOOL CFolderItems::_IncludeItem(IShellFolder *psf, LPCITEMIDLIST pidl)
{
BOOL fInclude = TRUE; // by default include it...
if ( _pszFileSpec )
{
// see if we can resolve the link on this object
LPITEMIDLIST pidlFromLink = NULL;
IShellLink *psl;
if ( SUCCEEDED(psf->GetUIObjectOf(NULL, 1, &pidl, IID_IShellLink, NULL, (void **)&psl)) )
{
psl->GetIDList(&pidlFromLink);
psl->Release();
pidl = pidlFromLink;
}
// then apply the file spec
TCHAR szName[MAX_PATH];
SHGetNameAndFlags(pidl, SHGDN_INFOLDER|SHGDN_FORPARSING, szName, ARRAYSIZE(szName), NULL);
fInclude = PathMatchSpec(szName, _pszFileSpec);
ILFree(pidlFromLink);
}
return fInclude;
}
// in:
// iItemNeeded zero based index
//
//
// returns:
// S_OK in range value
// S_FALSE out of range value
//
HRESULT CFolderItems::_EnsureItem(UINT iItemNeeded, LPCITEMIDLIST *ppidlItem)
{
HRESULT hr = S_FALSE; // assume out of range
if (_GetHDPA())
{
LPCITEMIDLIST pidl = (LPCITEMIDLIST)DPA_GetPtr(_hdpa, iItemNeeded);
if (pidl)
{
if (ppidlItem)
*ppidlItem = pidl;
hr = S_OK;
}
else if (!_fGotAllItems)
{
IShellFolderView *psfv;
if (SUCCEEDED(_psdf->GetShellFolderView(&psfv)))
{
if (_fSelected)
{
// we can only request the entire selection, therefore
// do so and populate our array with those.
UINT cItems;
LPCITEMIDLIST *ppidl = NULL;
if (SUCCEEDED(psfv->GetSelectedObjects(&ppidl, &cItems)) && ppidl)
{
for (UINT i = 0; i < cItems; i++)
_CopyItem(i, ppidl[i]);
LocalFree(ppidl);
}
_fGotAllItems = TRUE;
hr = _EnsureItem(iItemNeeded, ppidlItem);
}
else
{
UINT cItems;
if (SUCCEEDED(psfv->GetObjectCount(&cItems)))
{
// if there is no file spec then we can just request the item
// that we want, otherwise we must collect them all
// from the view.
if (iItemNeeded < cItems)
{
LPCITEMIDLIST pidl;
if (SUCCEEDED(GetObjectSafely(psfv, &pidl, iItemNeeded)))
{
if (_CopyItem(iItemNeeded, pidl))
{
hr = _EnsureItem(iItemNeeded, ppidlItem);
}
}
}
}
}
psfv->Release();
}
else
{
// we don't have an enumerator, so lets request it
if (NULL == _penum)
_psdf->_psf->EnumObjects(NULL, _grfFlags, &_penum);
if (NULL == _penum)
{
_fGotAllItems = TRUE; // enum empty, we are done
}
else
{
// get more while our count is less than the index
while (_cNumEnumed <= iItemNeeded)
{
LPITEMIDLIST pidl;
if (S_OK == _penum->Next(1, &pidl, NULL))
{
if ( _IncludeItem(_psdf->_psf, pidl) &&
(-1 != ::DPA_AppendPtr(_hdpa, pidl)) )
{
_cNumEnumed++;
}
else
{
ILFree(pidl);
}
}
else
{
ATOMICRELEASE(_penum);
_fGotAllItems = TRUE;
break;
}
}
}
hr = _EnsureItem(iItemNeeded, ppidlItem);
}
}
}
else
hr = E_OUTOFMEMORY;
return hr;
}
STDMETHODIMP CFolderItems::QueryInterface(REFIID riid, void ** ppv)
{
static const QITAB qit[] = {
QITABENT(CFolderItems, FolderItems3),
QITABENTMULTI(CFolderItems, FolderItems, FolderItems3),
QITABENTMULTI(CFolderItems, IDispatch, FolderItems3),
QITABENT(CFolderItems, IObjectSafety),
QITABENT(CFolderItems, IPersistFolder),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
STDMETHODIMP_(ULONG) CFolderItems::AddRef(void)
{
return InterlockedIncrement(&_cRef);
}
STDMETHODIMP_(ULONG) CFolderItems::Release(void)
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
// FolderItems implementation
STDMETHODIMP CFolderItems::get_Application(IDispatch **ppid)
{
// let the folder object do the work...
return _psdf->get_Application(ppid);
}
STDMETHODIMP CFolderItems::get_Parent(IDispatch **ppid)
{
*ppid = NULL;
return E_NOTIMPL;
}
STDMETHODIMP CFolderItems::get_Count(long *pCount)
{
HRESULT hr = _SecurityCheck();
if (SUCCEEDED(hr))
{
hr = E_FAIL;
IShellFolderView *psfv = NULL;
// get the items from the view, we can do this if we don't have
// a spec.
if ( !_pszFileSpec )
{
hr = _psdf->GetShellFolderView(&psfv);
if (SUCCEEDED(hr))
{
UINT cCount;
hr = _fSelected ? psfv->GetSelectedCount(&cCount) : psfv->GetObjectCount(&cCount);
*pCount = cCount;
psfv->Release();
}
}
// either we failed to get to the view, or the file spec won't allow us
if ( _pszFileSpec || FAILED(hr) )
{
// Well it looks like we need to finish the iteration now to get this!
*pCount = SUCCEEDED(_EnsureItem(-1, NULL)) ? _GetHDPACount() : 0;
hr = S_OK;
}
}
return hr;
}
// Folder.Items.Item(1)
// Folder.Items.Item("file name")
// Folder.Items.Item() - same as Folder.Self
STDMETHODIMP CFolderItems::Item(VARIANT index, FolderItem **ppid)
{
HRESULT hr = _SecurityCheck();
if (SUCCEEDED(hr))
{
hr = S_FALSE;
*ppid = NULL;
// This is sortof gross, but if we are passed a pointer to another variant, simply
// update our copy here...
if (index.vt == (VT_BYREF | VT_VARIANT) && index.pvarVal)
index = *index.pvarVal;
switch (index.vt)
{
case VT_ERROR:
{
// No Parameters, generate a folder item for the folder itself...
Folder * psdfParent;
hr = _psdf->get_ParentFolder(&psdfParent);
if (SUCCEEDED(hr) && psdfParent)
{
hr = CFolderItem_Create((CFolder*)psdfParent, ILFindLastID(_psdf->_pidl), ppid);
psdfParent->Release();
}
}
break;
case VT_I2:
index.lVal = (long)index.iVal;
// And fall through...
case VT_I4:
{
LPCITEMIDLIST pidl;
hr = _EnsureItem(index.lVal, &pidl); // Get the asked for item...
if (S_OK == hr)
hr = CFolderItem_Create(_psdf, pidl, ppid);
}
break;
case VT_BSTR:
{
LPITEMIDLIST pidl;
hr = _psdf->_psf->ParseDisplayName(NULL, NULL, index.bstrVal, NULL, &pidl, NULL);
if (SUCCEEDED(hr))
{
hr = CFolderItem_Create(_psdf, pidl, ppid);
ILFree(pidl);
}
}
break;
default:
return E_NOTIMPL;
}
if (hr != S_OK) // Error values cause problems in Java script
{
*ppid = NULL;
hr = S_FALSE;
}
else if (ppid && _dwSafetyOptions)
{
hr = MakeSafeForScripting((IUnknown**)ppid);
}
}
return hr;
}
STDMETHODIMP CFolderItems::InvokeVerbEx(VARIANT vVerb, VARIANT vArgs)
{
long cItems;
// Note: if not safe, we'll fail in get_Count with E_ACCESSDENIED
HRESULT hr = get_Count(&cItems);
if (SUCCEEDED(hr) && cItems)
{
LPCITEMIDLIST *ppidl = (LPCITEMIDLIST *)LocalAlloc(LPTR, SIZEOF(*ppidl) * cItems);
if (ppidl)
{
for (int i = 0; i < cItems; i++)
{
_EnsureItem(i, &ppidl[i]);
}
// BUGBUG: shouldn't worry about items from different folders here?
hr = _psdf->InvokeVerbHelper(vVerb, vArgs, ppidl, cItems, _dwSafetyOptions);
LocalFree(ppidl);
}
else
hr = E_OUTOFMEMORY;
}
return hr;
}
//
// fIncludeFolders => includ folders in the enumeration (TRUE by default)
// bstrFilter = filespec to apply while enumerating
//
STDMETHODIMP CFolderItems::Filter(LONG grfFlags, BSTR bstrFileSpec)
{
HRESULT hr = _SecurityCheck();
if (SUCCEEDED(hr))
{
_grfFlags = grfFlags;
Str_SetPtr(&_pszFileSpec, bstrFileSpec);
_ResetIDListArray();
}
return hr;
}
STDMETHODIMP CFolderItems::_GetUIObjectOf(REFIID riid, void ** ppv)
{
*ppv = NULL;
HRESULT hr = E_FAIL;
long cItems;
if (SUCCEEDED(get_Count(&cItems)) && cItems)
{
LPCITEMIDLIST *ppidl = (LPCITEMIDLIST *)LocalAlloc(LPTR, SIZEOF(*ppidl) * cItems);
if (ppidl)
{
for (int i = 0; i < cItems; i++)
{
_EnsureItem(i, &ppidl[i]);
}
// BUGBUG: shouldn't worry about items from different folders here?
hr = _psdf->_psf->GetUIObjectOf(_psdf->_hwnd, cItems, ppidl, riid, NULL, ppv);
LocalFree(ppidl);
}
else
hr = E_OUTOFMEMORY;
}
return hr;
}
STDMETHODIMP CFolderItems::get_Verbs(FolderItemVerbs **ppfic)
{
*ppfic = NULL;
HRESULT hr = _SecurityCheck();
if (SUCCEEDED(hr))
{
hr = S_FALSE;
IContextMenu *pcm;
if (SUCCEEDED(_GetUIObjectOf(IID_PPV_ARG(IContextMenu, &pcm))))
{
hr = CFolderItemVerbs_Create(pcm, ppfic);
if (SUCCEEDED(hr) && _dwSafetyOptions)
{
hr = MakeSafeForScripting((IUnknown**)ppfic);
if (SUCCEEDED(hr))
{
// Set the folder's site to FolderItemVerbs
IUnknown_SetSite(*ppfic, _psdf->_punkSite);
}
}
pcm->Release();
}
}
return hr;
}
// supports VB "For Each" statement
STDMETHODIMP CFolderItems::_NewEnum(IUnknown **ppunk)
{
*ppunk = NULL;
HRESULT hr = _SecurityCheck();
if (SUCCEEDED(hr))
{
hr = E_OUTOFMEMORY;
CEnumFolderItems *pNew = new CEnumFolderItems(this);
if (pNew)
{
hr = pNew->QueryInterface(IID_PPV_ARG(IUnknown, ppunk));
pNew->Release();
if (SUCCEEDED(hr) && _dwSafetyOptions)
{
hr = MakeSafeForScripting(ppunk);
}
}
}
return hr;
}
HRESULT CFolderItemsFDF_Create(CFolder *psdf, FolderItems **ppitems)
{
*ppitems = NULL;
HRESULT hr = E_OUTOFMEMORY;
CFolderItemsFDF* psdfi = new CFolderItemsFDF(psdf);
if (psdfi)
{
hr = psdfi->QueryInterface(IID_PPV_ARG(FolderItems, ppitems));
psdfi->Release();
}
return hr;
}
STDAPI CFolderItemsFDF_CreateInstance(IUnknown *punk, REFIID riid, void **ppv)
{
HRESULT hr = E_OUTOFMEMORY;
*ppv = NULL;
CFolderItemsFDF *pfi = new CFolderItemsFDF(NULL);
if (pfi)
{
hr = pfi->QueryInterface(riid, ppv);
pfi->Release();
}
return hr;
}
CFolderItemsFDF::CFolderItemsFDF(CFolder *psdf) : CFolderItems(psdf, FALSE)
{
}
HRESULT CFolderItemsFDF::QueryInterface(REFIID riid, void ** ppv)
{
HRESULT hr = CFolderItems::QueryInterface(riid, ppv);
if (FAILED(hr))
{
static const QITAB qit[] = {
QITABENT(CFolderItemsFDF, IInsertItem),
{ 0 },
};
hr = QISearch(this, qit, riid, ppv);
}
return hr;
}
HRESULT CFolderItemsFDF::InsertItem(LPCITEMIDLIST pidl)
{
HRESULT hr = S_FALSE;
if (_GetHDPA())
{
LPITEMIDLIST pidlTemp;
hr = SHILClone(pidl, &pidlTemp);
if (SUCCEEDED(hr))
{
if (DPA_AppendPtr(_hdpa, pidlTemp) == -1)
{
ILFree(pidlTemp);
hr = E_FAIL;
}
}
}
return hr;
}
HRESULT CFolderItemsFDF::_EnsureItem(UINT iItemNeeded, LPCITEMIDLIST *ppidlItem)
{
HRESULT hr = S_FALSE; // assume out of range
if (ppidlItem)
*ppidlItem = NULL;
if (_GetHDPA())
{
LPCITEMIDLIST pidl = (LPCITEMIDLIST)DPA_GetPtr(_hdpa, iItemNeeded);
if (pidl)
{
if (ppidlItem)
*ppidlItem = pidl;
hr = S_OK;
}
}
return hr;
}
// CEnumFolderItems implementation of IEnumVARIANT
CEnumFolderItems::CEnumFolderItems(CFolderItems *pfdritms) :
_cRef(1),
_pfdritms(pfdritms),
_iCur(0)
{
_pfdritms->AddRef();
DllAddRef();
}
CEnumFolderItems::~CEnumFolderItems(void)
{
_pfdritms->Release();
DllRelease();
}
STDMETHODIMP CEnumFolderItems::QueryInterface(REFIID riid, void ** ppv)
{
static const QITAB qit[] = {
QITABENT(CEnumFolderItems, IEnumVARIANT),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
STDMETHODIMP_(ULONG) CEnumFolderItems::AddRef(void)
{
return InterlockedIncrement(&_cRef);
}
STDMETHODIMP_(ULONG) CEnumFolderItems::Release(void)
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
STDMETHODIMP CEnumFolderItems::Next(ULONG cVar, VARIANT *pVar, ULONG *pulVar)
{
ULONG cReturn = 0;
HRESULT hr = S_OK;
if (!pulVar && (cVar != 1))
return E_POINTER;
while (cVar)
{
LPCITEMIDLIST pidl;
if (S_OK == _pfdritms->_EnsureItem(_iCur + cVar - 1, &pidl))
{
FolderItem *pid;
hr = CFolderItem_Create(_pfdritms->_psdf, pidl, &pid);
_iCur++;
if (_pfdritms->_dwSafetyOptions && SUCCEEDED(hr))
hr = MakeSafeForScripting((IUnknown**)&pid);
if (SUCCEEDED(hr))
{
pVar->pdispVal = pid;
pVar->vt = VT_DISPATCH;
pVar++;
cReturn++;
cVar--;
}
else
break;
}
else
break;
}
if (SUCCEEDED(hr))
{
if (pulVar)
*pulVar = cReturn;
hr = cReturn ? S_OK : S_FALSE;
}
return hr;
}
STDMETHODIMP CEnumFolderItems::Skip(ULONG cSkip)
{
if ((_iCur + cSkip) >= _pfdritms->_GetHDPACount())
return S_FALSE;
_iCur += cSkip;
return NOERROR;
}
STDMETHODIMP CEnumFolderItems::Reset(void)
{
_iCur = 0;
return NOERROR;
}
STDMETHODIMP CEnumFolderItems::Clone(IEnumVARIANT **ppenum)
{
*ppenum = NULL;
HRESULT hr = E_OUTOFMEMORY;
CEnumFolderItems *pNew = new CEnumFolderItems(_pfdritms);
if (pNew)
{
hr = pNew->QueryInterface(IID_PPV_ARG(IEnumVARIANT, ppenum));
pNew->Release();
}
return hr;
}