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