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