Windows2000/private/shell/shell32/delfldr.cpp
2020-09-30 17:12:32 +02:00

1281 lines
35 KiB
C++

#include "shellprv.h"
#include "clsobj.h"
#include "caggunk.h"
#pragma hdrstop
// Delegated IShellFolder object. Takes an ISF inner and adds the list of
// delegated shell folders to its namespace, wrapping the IDLISTs as required.
class CDelegateFolder : public CAggregatedUnknown, IDelegateShellFolder, IShellFolder2, IPersistFreeThreadedObject
{
public:
// *** 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(); };
// IPersistFreeThreadedObject
STDMETHODIMP GetClassID(CLSID *pCLSID);
// *** IShellFolder methods ***
STDMETHODIMP ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pszDisplayName,
ULONG *pchEaten, LPITEMIDLIST *ppidl, ULONG *pdwAttributes);
STDMETHODIMP EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList);
STDMETHODIMP BindToObject(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppvOut);
STDMETHODIMP BindToStorage(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv);
STDMETHODIMP CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
STDMETHODIMP CreateViewObject (HWND hwnd, REFIID riid, void **ppvOut);
STDMETHODIMP GetAttributesOf(UINT cidl, LPCITEMIDLIST *apidl, ULONG *rgfInOut);
STDMETHODIMP GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST * apidl,
REFIID riid, UINT * prgfInOut, void **ppvOut);
STDMETHODIMP GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, STRRET *pName);
STDMETHODIMP SetNameOf(HWND hwnd, LPCITEMIDLIST pidl, LPCOLESTR pszName,
DWORD uFlags, LPITEMIDLIST *ppidlOut);
// *** IShellFolder2 methods ***
STDMETHODIMP GetDefaultSearchGUID(LPGUID lpGuid);
STDMETHODIMP EnumSearches(LPENUMEXTRASEARCH *ppenum);
STDMETHODIMP GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay);
STDMETHODIMP GetDefaultColumnState(UINT iColumn, DWORD *pbState);
STDMETHODIMP GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv);
STDMETHODIMP GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails);
STDMETHODIMP MapNameToSCID(LPCWSTR pwszName, SHCOLUMNID *pscid);
// *** IDelegateShellFolder ***
STDMETHODIMP Initialize(WORD id, IShellFolder2* psf);
STDMETHODIMP AddFolders(CLSID* aCLISD, INT count);
protected:
CDelegateFolder(IUnknown *punkOuter);
~CDelegateFolder();
// used by the CAggregatedUnknown stuff
HRESULT v_InternalQueryInterface(REFIID riid,void **ppv);
LPCITEMIDLIST _GetFolderIDList();
PDELEGATEITEMID _IsDelegateObject(LPCITEMIDLIST pidl, LPCLSID pclsid);
HRESULT _InitFolder(IUnknown *punk);
HRESULT _GetDelegateFolder(LPCITEMIDLIST pidl, REFIID riid, void **ppv);
HRESULT _GetItemFolder(LPCITEMIDLIST pidl, IShellFolder **ppsf);
HRESULT _GetItemFolder2(LPCITEMIDLIST pidl, IShellFolder2 **ppsf);
private:
HDCA _dcaDelegates; // array of CLSIDs we are using
IShellFolder2 *_psfOuter; // IShellFolder2 of the inner object
WORD _id; // ID used for marking our IDLISTs
BOOL _fFreeThread:1; // supports the IPersistFreeThreadedObject iface
LPITEMIDLIST _pidl; // IDLIST passed to our ::Initialize method
friend HRESULT CDelegateFolder_CreateInstance(IUnknown* punkOuter, REFIID riid, void **ppvOut);
};
// Stuff
// flow control helpers
#define ExitGracefully(_hr, _r) \
{ _hr = (_r); goto exit; }
#define FailGracefully(_hr) \
{ if (FAILED(hr)) goto exit; }
STDAPI CDelegateMalloc_Create(void *pv, UINT cbSize, WORD wOuter, IMalloc **ppmalloc);
class CDelegateFolderEnum : public IEnumIDList
{
public:
CDelegateFolderEnum(HWND hwnd, DWORD grfFlags, HDPA dpaFolders);
~CDelegateFolderEnum();
// *** IUnknown methods ***
STDMETHOD(QueryInterface) (REFIID riid, void **ppv);
STDMETHOD_(ULONG,AddRef) (THIS);
STDMETHOD_(ULONG,Release) (THIS);
// *** IEnumIDList methods ***
STDMETHOD(Next) (ULONG celt,
LPITEMIDLIST *rgelt,
ULONG *pceltFetched);
STDMETHOD(Skip) (ULONG celt);
STDMETHOD(Reset) (THIS);
STDMETHOD(Clone) (IEnumIDList **ppenum);
private:
LONG _cRef;
HWND _hwnd;
DWORD _grfFlags;
HDPA _dpaFolders;
INT _index;
IEnumIDList* _pCurrentEnum;
};
// get the clsid of the delegate from the pidl.
#define _GetDelegateCLSID(pdi) \
(*(UNALIGNED CLSID*)&(((PDELEGATEITEMID)(pdi))->rgb[((PDELEGATEITEMID)(pdi))->cbInner]))
// callback used to comapre two delegate item idlists CLSID
INT _CompareDelegateItem(void *pv1, void *pv2, LPARAM lParam)
{
PDELEGATEITEMID pdi1 = (PDELEGATEITEMID)pv1;
PDELEGATEITEMID pdi2 = (PDELEGATEITEMID)pv2;
return memcmp((UNALIGNED CLSID*)&(pdi1->rgb[pdi1->cbInner]),
(UNALIGNED CLSID*)&(pdi2->rgb[pdi2->cbInner]),
SIZEOF(CLSID));
}
// callback used to destroy the IUnkown DPA.
static INT _ReleaseCB(void *pItem, void *pData)
{
IUnknown *pUnknown = (IUnknown *)pItem;
pUnknown->Release();
return 1;
}
// Constructors etc
// Constructor
CDelegateFolder::CDelegateFolder(IUnknown *punkOuter) :
CAggregatedUnknown(punkOuter),
_dcaDelegates(NULL),
_psfOuter(NULL),
_id(0),
_fFreeThread(FALSE),
_pidl(NULL)
{
DllAddRef();
}
CDelegateFolder::~CDelegateFolder()
{
if ( _dcaDelegates )
DCA_Destroy(_dcaDelegates);
ILFree(_pidl);
DllRelease();
}
// get the pidl used to initialize this namespace
LPCITEMIDLIST CDelegateFolder::_GetFolderIDList()
{
if (!_pidl)
SHGetIDListFromUnk(_psfOuter, &_pidl);
return _pidl;
}
// aggregated unknown handling
HRESULT CDelegateFolder::v_InternalQueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] = {
QITABENT(CDelegateFolder, IDelegateShellFolder), // IID_IDelegateShellFolder
QITABENTMULTI(CDelegateFolder, IShellFolder, IShellFolder2), // IID_IShellFolder
QITABENT(CDelegateFolder, IShellFolder2), // IID_IShellFolder2
QITABENT(CDelegateFolder, IPersistFreeThreadedObject), // IID_IPersistFreeThreadedObject
{ 0 },
};
if ( IsEqualIID(riid, IID_IPersistFreeThreadedObject) && !_fFreeThread )
return E_NOINTERFACE;
return QISearch(this, qit, riid, ppv);
}
// IDelegateShellFolder
STDMETHODIMP CDelegateFolder::Initialize(WORD id, IShellFolder2* psf)
{
// ensure we have the DCA for storing the delegate folders into, then lets grav the ISF
// we have been given.
if ( !_dcaDelegates )
{
_dcaDelegates = DCA_Create();
if ( !_dcaDelegates )
return E_OUTOFMEMORY;
}
ASSERT(NULL != psf);
// NOTE: AddRef() is not called because psf is required to be an interface on the
// same aggregated object
_psfOuter = psf;
_id = id;
// use the CLSID (from our delegate) as the location in the registry
// where we look for the delegates to load
CLSID clsid;
if (SUCCEEDED(GetClassID(&clsid)))
{
HKEY hKey;
if (SUCCEEDED(SHRegGetCLSIDKey(clsid, NULL, FALSE, FALSE, &hKey)))
{
DCA_AddItemsFromKey(_dcaDelegates, hKey, TEXT("shellex\\DelegateShellFolders"));
RegCloseKey(hKey);
}
}
// is the inner object free threaded? if so we must honor this IID as the regitems
// code uses it to cache us.
IPersistFreeThreadedObject* ppfto;
if ( SUCCEEDED(_psfOuter->QueryInterface(IID_IPersistFreeThreadedObject, (void **)&ppfto)))
{
_fFreeThread = TRUE;
ppfto->Release();
}
return S_OK;
}
// given an array of CLISDs add them to the delegate object list
STDMETHODIMP CDelegateFolder::AddFolders(CLSID* aCLISD, INT count)
{
INT i;
// fail if either we don't have a delegate list to ad items to or, we have
// no array of CLSIDs to add.
if ( !aCLISD || !_dcaDelegates )
return E_INVALIDARG;
for ( i = 0 ; i < count ; i++ )
{
if ( !DCA_AddItem(_dcaDelegates, aCLISD[i]) )
return E_FAIL;
}
return S_OK;
}
/*
/ Crack the pidl and see if it looks like a delegate object, we return S_OK
/ if it is, otherwise we hand back S_FALSE.
/ In:
/ pidl -> pidl to be cracked
/ pclsid -> receives the CLSID for the object (if non-zero)
/ Out:
/ HRESULT
*/
PDELEGATEITEMID CDelegateFolder::_IsDelegateObject(LPCITEMIDLIST pidl, LPCLSID pclsid)
{
PDELEGATEITEMID pdi = (PDELEGATEITEMID)pidl;
if ( pdi && (pdi->cbSize > SIZEOF(DELEGATEITEMID)-1) && (pdi->wOuter == _id) )
{
if ( pclsid )
*pclsid = *((UNALIGNED CLSID*)&(pdi->rgb[pdi->cbInner]));
return pdi;
}
return NULL;
}
HRESULT CDelegateFolder::_InitFolder(IUnknown *punk)
{
HRESULT hr = S_OK;
IPersistFolder* ppf;
if ( SUCCEEDED(punk->QueryInterface(IID_IPersistFolder, (void **)&ppf)) )
{
hr = ppf->Initialize(_GetFolderIDList());
ppf->Release();
}
return hr;
}
/*
/ Extract the delegate CLSID from the pidl we see, if the type is not a delegate
/ item then return failure. if ppv is non-NULL then create an object from that
/ CLSID that maps to the thing we want.
/ In:
/ pidl = pidl to look into
/ pCLSID = receives the clisd
/ riid, ppv -> if you want an instance then pass the IID and a ppv
/ Out:
/ HRESULT
*/
HRESULT CDelegateFolder::_GetDelegateFolder(LPCITEMIDLIST pidl, REFIID riid, void **ppv)
{
HRESULT hr;
CLSID clsid;
PDELEGATEITEMID pdi = _IsDelegateObject(pidl, &clsid);
if (!pdi)
return E_FAIL;
// the caller wants an instance of this delegate folder, so lets co-create it and set
// its allocator as required.
IDelegateFolder* pDelegateFolder;
hr = SHCoCreateInstance(NULL, &clsid, NULL, IID_IDelegateFolder, (void **)&pDelegateFolder);
FailGracefully(hr);
IMalloc* pAlloc;
if ( SUCCEEDED(CDelegateMalloc_Create(&clsid, SIZEOF(CLSID), _id, &pAlloc)) )
{
pDelegateFolder->SetItemAlloc(pAlloc);
pAlloc->Release();
}
hr = pDelegateFolder->QueryInterface(riid, ppv);
pDelegateFolder->Release();
// the caller is requesting IShellFolder for this object, therefore we need to bind
// and ensure that its parent is correctly initialized (by calling IPersistFolder),
// note that at this point just calling Initialize with our cached PIDL should be
// sufficent.
if ( SUCCEEDED(hr) && _GetFolderIDList() &&
(IsEqualIID(riid, IID_IShellFolder) || IsEqualIID(riid, IID_IShellFolder2)) )
{
_InitFolder((IUnknown *)*ppv);
}
hr = S_OK; // success
exit:
return hr;
}
HRESULT CDelegateFolder::_GetItemFolder(LPCITEMIDLIST pidl, IShellFolder **ppsf)
{
HRESULT hr;
if (_IsDelegateObject(pidl, NULL))
{
hr = _GetDelegateFolder(pidl, IID_IShellFolder, (void **)ppsf);
}
else
{
*ppsf = _psfOuter;
_psfOuter->AddRef();
hr = S_OK;
}
return hr;
}
HRESULT CDelegateFolder::_GetItemFolder2(LPCITEMIDLIST pidl, IShellFolder2 **ppsf)
{
HRESULT hr;
if (_IsDelegateObject(pidl, NULL))
{
hr = _GetDelegateFolder(pidl, IID_IShellFolder2, (void **)ppsf);
}
else
{
*ppsf = _psfOuter;
_psfOuter->AddRef();
hr = S_OK;
}
return hr;
}
// IPersist method
STDMETHODIMP CDelegateFolder::GetClassID(CLSID *pCLSID)
{
IPersist* pps;
HRESULT hr = _psfOuter->QueryInterface(IID_IPersist, (void **)&pps);
if ( SUCCEEDED(hr) )
{
hr = pps->GetClassID(pCLSID);
pps->Release();
}
return hr;
}
// IShellFolder methods
STDMETHODIMP CDelegateFolder::ParseDisplayName(HWND hwnd, LPBC pbc, LPOLESTR pszDisplayName,
ULONG *pchEaten, LPITEMIDLIST *ppidl, ULONG *pdwAttributes)
{
// round robbin the delegate namespaces, seeing if any want to parse the string,
// if they don't they will return E_NOTIMPL or E_INVALIDARG. If they return anything
// other than that then we return their result, otherwise we continue.
for (int i = 0 ; i < DCA_GetItemCount(_dcaDelegates) ; i++)
{
IShellFolder* psf;
if ( SUCCEEDED(DCA_CreateInstance(_dcaDelegates, i, IID_IShellFolder, (void**)&psf)) )
{
IDelegateFolder* pdf;
if ( SUCCEEDED(psf->QueryInterface(IID_IDelegateFolder, (void**)&pdf)) )
{
IMalloc* pAlloc;
if ( SUCCEEDED(CDelegateMalloc_Create((LPVOID)DCA_GetItem(_dcaDelegates, i), SIZEOF(CLSID), _id, &pAlloc)) )
{
pdf->SetItemAlloc(pAlloc);
pAlloc->Release();
}
pdf->Release();
}
HRESULT hr = psf->ParseDisplayName(hwnd, pbc, pszDisplayName, pchEaten, ppidl, pdwAttributes);
psf->Release();
if ( (hr != E_INVALIDARG) && (hr != E_NOTIMPL) )
{
return hr;
}
}
}
// None of the delegates were interested so lets call the _psfOuter and let them have a crack at it!
return _psfOuter->ParseDisplayName(hwnd, pbc, pszDisplayName,
pchEaten, ppidl, pdwAttributes);
}
STDMETHODIMP CDelegateFolder::EnumObjects(HWND hwnd, DWORD grfFlags, IEnumIDList **ppenumIDList)
{
CDelegateFolderEnum* pEnum;
IDelegateFolder* pDelegateFolder;
IMalloc* pAlloc;
HDPA dpaFolders = NULL;
INT i;
*ppenumIDList = NULL; // in case of failure
// to construct the enumerator we are going to give out to the oustside world we
// must fill a DPA with the IShellFolder iface's we want to use. Therefore lets
// walk the list of delegate folders add those to the DPA (having both set their
// malloc and then initialized them).
dpaFolders = DPA_Create(4);
if ( !dpaFolders )
return E_OUTOFMEMORY;
for ( i = 0 ; i < DCA_GetItemCount(_dcaDelegates) ; i++ )
{
if ( SUCCEEDED(DCA_CreateInstance(_dcaDelegates, i, IID_IDelegateFolder, (void **)&pDelegateFolder)) )
{
if ( SUCCEEDED(CDelegateMalloc_Create((LPVOID)DCA_GetItem(_dcaDelegates, i), SIZEOF(CLSID), _id, &pAlloc)) )
{
pDelegateFolder->SetItemAlloc(pAlloc);
pAlloc->Release();
_InitFolder((IUnknown *)pDelegateFolder);
IShellFolder* psf;
if ( SUCCEEDED(pDelegateFolder->QueryInterface(IID_IShellFolder, (void **)&psf)) )
{
if ( -1 == DPA_AppendPtr(dpaFolders, psf) )
{
psf->Release(); // failed to place into the DPA.
}
}
}
pDelegateFolder->Release();
}
}
_psfOuter->AddRef();
DPA_AppendPtr(dpaFolders, _psfOuter); // BUGBUG: what about failure
pEnum = new CDelegateFolderEnum(hwnd, grfFlags, dpaFolders);
if ( !pEnum )
{
DPA_DestroyCallback(dpaFolders, _ReleaseCB, NULL);
return E_OUTOFMEMORY;
}
*ppenumIDList = SAFECAST(pEnum, IEnumIDList*); // pass out the enumerator
return S_OK;
}
STDMETHODIMP CDelegateFolder::BindToObject(LPCITEMIDLIST pidl, LPBC pbc,
REFIID riid, void **ppvOut)
{
IShellFolder* psfItem;
HRESULT hr = _GetItemFolder(pidl, &psfItem);
if (SUCCEEDED(hr))
{
hr = psfItem->BindToObject(pidl, pbc, riid, ppvOut);
psfItem->Release();
}
return hr;
}
STDMETHODIMP CDelegateFolder::BindToStorage(LPCITEMIDLIST pidl, LPBC pbc,
REFIID riid, void **ppv)
{
IShellFolder* psfItem;
HRESULT hr = _GetItemFolder(pidl, &psfItem);
if (SUCCEEDED(hr))
{
hr = psfItem->BindToStorage(pidl, pbc, riid, ppv);
psfItem->Release();
}
return hr;
}
STDMETHODIMP CDelegateFolder::CompareIDs(LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
HRESULT hr;
LPCITEMIDLIST pidlNext1 = _ILNext(pidl1);
LPCITEMIDLIST pidlNext2 = _ILNext(pidl2);
PDELEGATEITEMID pdi1 = _IsDelegateObject(pidl1, NULL);
PDELEGATEITEMID pdi2 = _IsDelegateObject(pidl2, NULL);
IShellFolder* psfItem1 = NULL;
IShellFolder* psfItem2 = NULL;
// if we have two delegate objects then attempt to compare, if we have a single then
// the delegate comes first. if neither of the items are delegates then lets pass
// them onto the innerISF.
if ( pdi1 )
{
INT iResult = 0;
if ( pdi2 )
{
STRRET StrRet1, StrRet2;
TCHAR szItemName1[MAX_PATH];
TCHAR szItemName2[MAX_PATH];
hr = _GetDelegateFolder(pidl1, IID_IShellFolder, (void **)&psfItem1);
FailGracefully(hr);
hr = _GetDelegateFolder(pidl2, IID_IShellFolder, (void **)&psfItem2);
FailGracefully(hr);
if ( FAILED(psfItem1->GetDisplayNameOf(pidl1, SHGDN_NORMAL, &StrRet1)) ||
FAILED(psfItem2->GetDisplayNameOf(pidl2, SHGDN_NORMAL, &StrRet2)) )
{
ExitGracefully(hr, E_FAIL);
}
StrRetToStrN(szItemName1, ARRAYSIZE(szItemName1), &StrRet1, pidl1);
StrRetToStrN(szItemName2, ARRAYSIZE(szItemName2), &StrRet2, pidl2);
iResult = lstrcmp(szItemName1,szItemName2);
}
else
{
iResult = 1;
}
// their names match so lets check out the rest of the data, starting with the CLSIDs,
// if they match then we must continue the binding process and let them be compared.
if ( !iResult )
iResult = memcmp(&pdi1->rgb[pdi1->cbInner], &pdi2->rgb[pdi2->cbInner], SIZEOF(CLSID));
if ( iResult )
ExitGracefully(hr, ResultFromShort(iResult));
if ( ILIsEmpty(pidlNext1) )
{
if ( ILIsEmpty(pidlNext2) )
ExitGracefully(hr, ResultFromShort(0)); // they are the same size
ExitGracefully(hr, ResultFromShort(-1)); // pidl1 is shorter
}
else if ( ILIsEmpty(pidlNext2) )
ExitGracefully(hr, ResultFromShort(1)); // pidl2 is shorter
// call into the delegate namespace that owns these IDLISTs and let it compare
if ( psfItem1 )
{
hr = _GetDelegateFolder(pidl1, IID_IShellFolder, (void **)&psfItem1);
FailGracefully(hr);
}
hr = psfItem1->CompareIDs(lParam, pidl1, pidl2); // done
}
else if ( pdi2 )
{
hr = ResultFromShort(-1); // only pidl2 was a delegate
}
else
{
// both items belong to the inner ISF, so lets pass them to that so they can be compared,
// having done that we can just return the result from there.
hr = _psfOuter->CompareIDs(lParam, pidl1, pidl2);
}
exit:
if ( psfItem1 )
psfItem1->Release();
if ( psfItem2 )
psfItem2->Release();
return hr;
}
STDMETHODIMP CDelegateFolder::CreateViewObject(HWND hwnd, REFIID riid, void **ppvOut)
{
return _psfOuter->CreateViewObject(hwnd, riid, ppvOut);
}
STDMETHODIMP CDelegateFolder::GetAttributesOf(UINT cidl, LPCITEMIDLIST * apidl, ULONG * rgfInOut)
{
HRESULT hr = E_FAIL;
IShellFolder* psfItem;
LPCITEMIDLIST* apidl2 = NULL;
HDPA dpa = NULL;
CLSID clsid;
ULONG i, cidl2;
if ( cidl == 1 )
{
// pass real IDLs through, otherwise get the delegate folder that
// this idlist maps to and get attributes from it.
IShellFolder* psfItem;
hr = _GetItemFolder(*apidl, &psfItem);
if (SUCCEEDED(hr))
{
hr = psfItem->GetAttributesOf(cidl, apidl, rgfInOut);
psfItem->Release();
}
}
else if ( cidl > 1 )
{
// the selection is large, so we build two lists, the first contains the items that
// relate to the inner ISF, the second is a list of the delegate items. the items
// destined for the inner ISF are stored in a LocalAlloc, the delegates are in a
// DPA.
dpa = DPA_Create(4);
apidl2 = (LPCITEMIDLIST*)LocalAlloc(LPTR, SIZEOF(LPCITEMIDLIST)*cidl);
if ( !apidl2 || !dpa )
ExitGracefully(hr, E_OUTOFMEMORY);
for ( cidl2 = 0, i = 0 ; i != cidl ; i++ )
{
if ( !_IsDelegateObject(apidl[i], NULL) )
{
apidl2[cidl2++] = apidl[i];
}
else
{
if ( -1 == DPA_AppendPtr(dpa, (LPVOID)apidl[i]) )
ExitGracefully(hr, E_OUTOFMEMORY);
}
}
// call the innerISF with the items it is interested in.
if ( cidl2 )
{
hr = _psfOuter->GetAttributesOf(cidl2, apidl2, rgfInOut);
FailGracefully(hr);
}
// if we have any destined for the delegate namespace then lets
// first sort that list (basedon the CLSID) and bind to the
// namespace as required. the initial sort avoids us having
// to CoCreate the delegate object multiple times.
if ( DPA_GetPtrCount(dpa) )
{
DPA_Sort(dpa, _CompareDelegateItem, NULL);
for ( cidl2 = 0, i = 0 ; i != (ULONG)DPA_GetPtrCount(dpa) ; i++ )
{
// if we are no longer in the same CLSID and the list
// is long we pass them onto the namespace
if ( cidl2 &&
!IsEqualCLSID(clsid, _GetDelegateCLSID((PDELEGATEITEMID)DPA_GetPtr(dpa, i))) )
{
hr = _GetDelegateFolder(apidl2[0], IID_IShellFolder, (void **)&psfItem);
FailGracefully(hr);
hr = psfItem->GetAttributesOf(cidl2, apidl2, rgfInOut);
psfItem->Release();
cidl2 = 0; // no new items in the dpa copy yet!
}
// add this item to the list, if we have none
// then take a snapshot of the GUID
if ( !cidl2 )
clsid = _GetDelegateCLSID((PDELEGATEITEMID)DPA_GetPtr(dpa, i));
apidl2[cidl2++] = (LPCITEMIDLIST)DPA_GetPtr(dpa, i);
}
if ( cidl2 )
{
// on exit ensure that we have passed out the remainng items.
hr = _GetDelegateFolder(apidl2[0], IID_IShellFolder, (void **)&psfItem);
FailGracefully(hr);
hr = psfItem->GetAttributesOf(cidl2, apidl2, rgfInOut);
psfItem->Release();
}
}
}
exit:
if ( dpa )
DPA_Destroy(dpa);
if ( apidl2 )
LocalFree((HLOCAL)apidl2);
return hr;
}
STDMETHODIMP CDelegateFolder::GetDisplayNameOf(LPCITEMIDLIST pidl, DWORD uFlags, STRRET *pName)
{
IShellFolder* psfItem;
HRESULT hr = _GetItemFolder(pidl, &psfItem);
if (SUCCEEDED(hr))
{
hr = psfItem->GetDisplayNameOf(pidl, uFlags, pName);
psfItem->Release();
}
return hr;
}
STDMETHODIMP CDelegateFolder::SetNameOf(HWND hwnd, LPCITEMIDLIST pidl,
LPCOLESTR pszName, DWORD uFlags, LPITEMIDLIST *ppidlOut)
{
IShellFolder* psfItem;
HRESULT hr = _GetItemFolder(pidl, &psfItem);
if (SUCCEEDED(hr))
{
hr = psfItem->SetNameOf(hwnd, pidl, pszName, uFlags, ppidlOut);
psfItem->Release();
}
return hr;
}
STDMETHODIMP CDelegateFolder::GetUIObjectOf(HWND hwnd, UINT cidl, LPCITEMIDLIST *apidl,
REFIID riid, UINT *prgfInOut, void **ppvOut)
{
HRESULT hr = E_FAIL;
IShellFolder* psfItem;
BOOL fDelegate = FALSE;
UINT i, cidl2;
LPCITEMIDLIST* apidl2 = NULL;
if ( cidl == 1 )
{
// single selection is simple, we must just pass that down to the
// correct owner.
fDelegate = (_IsDelegateObject(*apidl, NULL) != NULL);
if ( fDelegate )
{
hr = _GetDelegateFolder(*apidl, IID_IShellFolder, (void **)&psfItem);
FailGracefully(hr);
hr = psfItem->GetUIObjectOf(hwnd, cidl, apidl, riid, prgfInOut, ppvOut);
psfItem->Release();
}
}
else if ( cidl > 1 )
{
// walk the list of IDLISTs and see what items we have, if there are any delegate
// items then this complicates things, if not then we can just pass to the
// inner ISF to get the information from that.
for ( i = 0 ; (i != cidl) && !fDelegate ; i++ )
fDelegate = (_IsDelegateObject(apidl[i], NULL) != NULL);
if ( fDelegate )
{
if ( IsEqualIID(riid, IID_IDataObject) )
{
hr = CIDLData_CreateFromIDArray(_GetFolderIDList(), cidl, apidl, (IDataObject **)ppvOut);
}
else if ( IsEqualIID(riid, IID_IContextMenu) )
{
// the selection is large, there is at least one delegate item in it
// so lets build an alternate list where all the items match the
// first (clsid and delegate-ness).
apidl2 = (LPCITEMIDLIST*)LocalAlloc(LPTR, SIZEOF(LPCITEMIDLIST)*cidl);
if ( !apidl2 )
ExitGracefully(hr, E_OUTOFMEMORY);
for ( cidl2 = 0, i = 0 ; i != cidl ; i++ )
{
if ( (_IsDelegateObject(apidl[0], NULL) == _IsDelegateObject(apidl[i], NULL)) &&
( !_IsDelegateObject(apidl[0], NULL) ||
IsEqualCLSID(_GetDelegateCLSID(apidl[0]), _GetDelegateCLSID(apidl[i]))) )
{
apidl2[cidl2++] = apidl[i];
}
}
// if there is a delegate in the first the bind to it and call it, otherwise
// just call the innerISF.
if ( !_IsDelegateObject(apidl2[0], NULL) )
{
hr = _psfOuter->GetUIObjectOf(hwnd, cidl2, apidl2, riid, prgfInOut, ppvOut);
}
else
{
hr = _GetDelegateFolder(apidl2[0], IID_IShellFolder, (void **)&psfItem);
FailGracefully(hr);
hr = psfItem->GetUIObjectOf(hwnd, cidl2, apidl2, riid, prgfInOut, ppvOut);
psfItem->Release();
}
fDelegate = TRUE; // already handled
}
else
{
ExitGracefully(hr, E_NOTIMPL); // BUGBUG: we must expand on this guy!
}
}
}
// handled yet? if not then pass onto the inner ISF
if ( !fDelegate )
hr = _psfOuter->GetUIObjectOf(hwnd, cidl, apidl, riid, prgfInOut, ppvOut);
exit:
if ( apidl2 )
LocalFree(apidl2);
return hr;
}
STDMETHODIMP CDelegateFolder::GetDefaultSearchGUID(LPGUID lpGUID)
{
return _psfOuter->GetDefaultSearchGUID(lpGUID);
}
STDMETHODIMP CDelegateFolder::EnumSearches(LPENUMEXTRASEARCH *ppenum)
{
return _psfOuter->EnumSearches(ppenum);
}
STDMETHODIMP CDelegateFolder::GetDefaultColumn(DWORD dwRes, ULONG *pSort, ULONG *pDisplay)
{
return _psfOuter->GetDefaultColumn(dwRes, pSort, pDisplay);
}
STDMETHODIMP CDelegateFolder::GetDefaultColumnState(UINT iColumn, DWORD *pbState)
{
return _psfOuter->GetDefaultColumnState(iColumn, pbState);
}
STDMETHODIMP CDelegateFolder::GetDetailsEx(LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv)
{
IShellFolder2* psfItem;
HRESULT hr = _GetItemFolder2(pidl, &psfItem);
if (SUCCEEDED(hr))
{
hr = psfItem->GetDetailsEx(pidl, pscid, pv);
psfItem->Release();
}
return hr;
}
STDMETHODIMP CDelegateFolder::GetDetailsOf(LPCITEMIDLIST pidl, UINT iColumn, SHELLDETAILS *pDetails)
{
IShellFolder2* psfItem;
HRESULT hr = _GetItemFolder2(pidl, &psfItem);
if (SUCCEEDED(hr))
{
hr = psfItem->GetDetailsOf(pidl, iColumn, pDetails);
psfItem->Release();
}
return hr;
}
STDMETHODIMP CDelegateFolder::MapNameToSCID(LPCWSTR pwszName, SHCOLUMNID *pscid)
{
return _psfOuter->MapNameToSCID(pwszName, pscid);
}
/*
/ Instance creation
*/
STDAPI CDelegateFolder_CreateInstance(IUnknown* punkOuter, REFIID riid, void **ppvOut)
{
// we only suport being created as an agregate
if ( !punkOuter || !IsEqualIID(riid, IID_IUnknown))
{
ASSERT(0);
return E_FAIL;
}
CDelegateFolder *pdelisf = new CDelegateFolder(punkOuter);
if ( !pdelisf )
return E_OUTOFMEMORY;
*ppvOut = pdelisf->_GetInner();
return S_OK;
}
// Enumerator, this handles enumerating the IF and bringing in the delegate
// folder objects.
CDelegateFolderEnum::CDelegateFolderEnum(HWND hwnd, DWORD grfFlags, HDPA dpaFolders) :
_cRef(1),
_hwnd(hwnd),
_grfFlags(grfFlags),
_dpaFolders(dpaFolders),
_index(0),
_pCurrentEnum(NULL)
{
DllAddRef();
}
CDelegateFolderEnum::~CDelegateFolderEnum()
{
if ( _dpaFolders )
DPA_DestroyCallback(_dpaFolders, _ReleaseCB, NULL);
if ( _pCurrentEnum )
_pCurrentEnum->Release();
DllRelease();
}
// IUnknown goop
STDMETHODIMP CDelegateFolderEnum::QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] = {
QITABENT(CDelegateFolderEnum, IEnumIDList),
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
STDMETHODIMP_(ULONG) CDelegateFolderEnum::AddRef()
{
return InterlockedIncrement(&_cRef);
}
STDMETHODIMP_(ULONG) CDelegateFolderEnum::Release()
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
// IEnumIDList
STDMETHODIMP CDelegateFolderEnum::Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched)
{
HRESULT hr = S_FALSE;
ULONG ulDummy;
if (pceltFetched == NULL)
pceltFetched = &ulDummy;
*pceltFetched = 0; // nothing has been returned yet
// whilst we have an enumerator and there have been no items returned
// lets go around to see how many objects we should be removing.
while ( !*pceltFetched )
{
// no enumerator, lets see if we can get an instance of the next
// one to enumerate from.
if ( !_pCurrentEnum )
{
while ( _index < DPA_GetPtrCount(_dpaFolders) )
{
IShellFolder* pShellFolder = (IShellFolder*)DPA_GetPtr(_dpaFolders, _index++);
ASSERT(pShellFolder);
if ( SUCCEEDED(pShellFolder->EnumObjects(_hwnd, _grfFlags, &_pCurrentEnum)) )
break;
}
if ( !_pCurrentEnum )
break;
}
// do we have an enumerator now? if so then lets call it and return the items back
// to the caller.
if ( _pCurrentEnum )
{
hr = _pCurrentEnum->Next(celt, rgelt, pceltFetched);
if ( hr == S_FALSE )
{
_pCurrentEnum->Release();
_pCurrentEnum = NULL;
if ( !*pceltFetched )
continue;
}
break;
}
}
return hr;
}
STDMETHODIMP CDelegateFolderEnum::Skip(ULONG celt)
{
return E_NOTIMPL;
}
STDMETHODIMP CDelegateFolderEnum::Reset(THIS)
{
// lets start the enumeration process again, so set the index back to the head
// and release the enumerator we are currently using.
_index = 0;
ATOMICRELEASE(_pCurrentEnum);
return S_OK;
}
STDMETHODIMP CDelegateFolderEnum::Clone(IEnumIDList **ppenum)
{
return E_NOTIMPL;
}
// This code used to be in shdocvw, it is the implementation of a IMalloc for handling
// delegate folder items
class CDelagateMalloc : public IMalloc
{
public:
// IUnknown
virtual STDMETHODIMP QueryInterface(REFIID,void **);
virtual STDMETHODIMP_(ULONG) AddRef(void);
virtual STDMETHODIMP_(ULONG) Release(void);
// IMalloc
virtual STDMETHODIMP_(LPVOID) Alloc(ULONG cb);
virtual STDMETHODIMP_(LPVOID) Realloc(void *pv, ULONG cb);
virtual STDMETHODIMP_(void) Free(void *pv);
virtual STDMETHODIMP_(ULONG) GetSize(void *pv);
virtual STDMETHODIMP_(int) DidAlloc(void *pv);
virtual STDMETHODIMP_(void) HeapMinimize();
private:
CDelagateMalloc(void *pv, UINT cbSize, WORD wOuter);
~CDelagateMalloc();
void* operator new(size_t cbClass, UINT cbSize);
friend HRESULT CDelegateMalloc_Create(void *pv, UINT cbSize, WORD wOuter, IMalloc **ppmalloc);
protected:
LONG _cRef;
WORD _wOuter; // delegate item outer signature
WORD _wUnused; // to allign
#ifdef DEBUG
UINT _cAllocs;
#endif
UINT _cb;
BYTE _data[];
};
void* CDelagateMalloc::operator new(size_t cbClass, UINT cbSize)
{
return ::operator new(cbClass + cbSize);
}
CDelagateMalloc::CDelagateMalloc(void *pv, UINT cbSize, WORD wOuter)
{
_cRef = 1;
_wOuter = wOuter;
_cb = cbSize;
memcpy(_data, pv, _cb);
}
CDelagateMalloc::~CDelagateMalloc()
{
DEBUG_CODE( TraceMsg(DM_TRACE, "DelegateMalloc destroyed with %d allocs performed", _cAllocs); )
}
HRESULT CDelagateMalloc::QueryInterface(REFIID riid, void **ppv)
{
static const QITAB qit[] = {
QITABENT(CDelagateMalloc, IMalloc), // IID_IMalloc
{ 0 },
};
return QISearch(this, qit, riid, ppv);
}
ULONG CDelagateMalloc::AddRef()
{
return InterlockedIncrement(&_cRef);
}
ULONG CDelagateMalloc::Release()
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
void *CDelagateMalloc::Alloc(ULONG cb)
{
WORD cbActualSize = (WORD)(
SIZEOF(DELEGATEITEMID) - 1 + // header (-1 sizeof(rgb[0])
cb + // inner
_cb); // outer data
PDELEGATEITEMID pidl = (PDELEGATEITEMID)SHAlloc(cbActualSize + 2); // +2 for pidl term
if (pidl)
{
pidl->cbSize = cbActualSize;
pidl->wOuter = _wOuter;
pidl->cbInner = (WORD)cb;
memcpy(&pidl->rgb[cb], _data, _cb);
*(WORD *)&(((BYTE *)pidl)[cbActualSize]) = 0;
#ifdef DEBUG
_cAllocs++;
#endif
}
return pidl;
}
void *CDelagateMalloc::Realloc(void *pv, ULONG cb)
{
return NULL;
}
void CDelagateMalloc::Free(void *pv)
{
SHFree(pv);
}
ULONG CDelagateMalloc::GetSize(void *pv)
{
return (ULONG)-1;
}
int CDelagateMalloc::DidAlloc(void *pv)
{
return -1;
}
void CDelagateMalloc::HeapMinimize()
{
}
STDAPI CDelegateMalloc_Create(void *pv, UINT cbSize, WORD wOuter, IMalloc **ppmalloc)
{
CDelagateMalloc *pdm = new(cbSize) CDelagateMalloc(pv, cbSize, wOuter);
if (pdm)
{
HRESULT hres = pdm->QueryInterface(IID_IMalloc, (void **)ppmalloc);
pdm->Release();
return hres;
}
return E_OUTOFMEMORY;
}