|
|
#include <shellprv.h>
#include "enumidlist.h"
CEnumIDListBase::CEnumIDListBase() : _cRef(1) { }
CEnumIDListBase::~CEnumIDListBase() { }
STDMETHODIMP CEnumIDListBase::QueryInterface(REFIID riid, void **ppv) { static const QITAB qit[] = { QITABENT(CEnumIDListBase, IEnumIDList), // IID_IEnumIDList
QITABENT(CEnumIDListBase, IObjectWithSite), // IID_IObjectWithSite
{ 0 }, }; return QISearch(this, qit, riid, ppv); }
STDMETHODIMP_(ULONG) CEnumIDListBase::AddRef() { return InterlockedIncrement(&_cRef); }
STDMETHODIMP_(ULONG) CEnumIDListBase::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { delete this; } return cRef; }
class CEnumArray : public CEnumIDListBase { public: CEnumArray(); HRESULT Init(const LPCITEMIDLIST rgpidl[], UINT cidl, UINT ulIndex); HRESULT InitFromPaths(LPCTSTR pszPaths); HRESULT InitFromCSIDLArray(const LPCTSTR rgcsidl[], UINT ccsidls, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2);
// IEnumIDList
STDMETHODIMP Next(ULONG celt, LPITEMIDLIST *rgelt, ULONG *pceltFetched); STDMETHODIMP Skip(ULONG celt); STDMETHODIMP Reset(); STDMETHODIMP Clone(IEnumIDList **ppenum);
protected: virtual ~CEnumArray(); LPITEMIDLIST *_ppidl;
BOOL _InitFolderParent(LPITEMIDLIST rgItems[], UINT cMaxItems, UINT *pcItems, LPCITEMIDLIST pidlFolder, LPITEMIDLIST *ppidlParent); LPITEMIDLIST _ILLogical(LPCITEMIDLIST pidl); BOOL _ShouldEnumCSIDL(int csidl);
private: LONG _cRef; ULONG _ulIndex; UINT _cItems; };
CEnumArray::CEnumArray() : CEnumIDListBase() { }
CEnumArray::~CEnumArray() { if (_ppidl) FreeIDListArray(_ppidl, _cItems); }
HRESULT CEnumArray::Init(const LPCITEMIDLIST rgpidl[], UINT cidl, UINT ulIndex) { _ulIndex = ulIndex; HRESULT hr = CloneIDListArray(cidl, rgpidl, &_cItems, &_ppidl); if (S_FALSE == hr) hr = S_OK; // S_FALSE to S_OK
return hr; }
HRESULT CEnumArray::InitFromCSIDLArray(const LPCTSTR rgcsidl[], UINT ccsidls, LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlItem) { LPITEMIDLIST rgItems[32] = {0}; // reasonable max size, grow as needed
UINT cItems = 0;
LPITEMIDLIST pidlParent = NULL; // pidlFolder's pidlParent (filesystem or logical pidl)
LPITEMIDLIST pidlParentLogical = NULL; // pidlFolder's pidlParent (logical pidl -- if exists)
// Initialize pidlFolder's parent pidl.
if (_InitFolderParent(rgItems, ARRAYSIZE(rgItems), &cItems, pidlFolder, &pidlParent)) { // Retrieve pidlFolder's logical parent pidl.
pidlParentLogical = _ILLogical(pidlParent); }
// Initialize pidlItem.
if (pidlItem && (!pidlParent || !ILIsEqual(pidlItem, pidlParent)) && (!pidlParentLogical || !ILIsEqual(pidlItem, pidlParentLogical))) { if (rgItems[cItems] = ILClone(pidlItem)) { cItems++; } }
// Initialize CSIDLs.
for (UINT i = 0; (i < ccsidls) && (cItems < ARRAYSIZE(rgItems)); i++) { LPITEMIDLIST pidl; if (IS_INTRESOURCE(rgcsidl[i])) { int csidl = LOWORD((UINT_PTR)rgcsidl[i]);
if (_ShouldEnumCSIDL(csidl)) SHGetSpecialFolderLocation(NULL, csidl, &pidl); else pidl = NULL; } else { SHParseDisplayName((LPTSTR)rgcsidl[i], NULL, &pidl, 0, NULL); }
if (pidl) { DWORD dwAttribs = SFGAO_NONENUMERATED; if ((pidlFolder && ILIsEqual(pidlFolder, pidl)) || // if pidl is not myself
(pidlParent && ILIsEqual(pidlParent, pidl)) || // if pidl is not my parent
(pidlParentLogical && ILIsEqual(pidlParentLogical, pidl)) || // (need to check logical parent too)
(pidlItem && ILIsEqual(pidlItem, pidl)) || // if pidl is not pidlItem
FAILED(SHGetNameAndFlags(pidl, 0, NULL, 0, &dwAttribs)) || // if pidl is not SFGAO_NONENUMERATED
(SFGAO_NONENUMERATED & dwAttribs)) { ILFree(pidl); } else { rgItems[cItems++] = pidl; // then add pidl
} } }
// Initialize CEnumArray with collected pidls.
HRESULT hr = Init(rgItems, cItems, 0);
// Cleanup.
for (i = 0; i < cItems; i++) { ILFree(rgItems[i]); } ILFree(pidlParentLogical);
return hr; }
BOOL CEnumArray::_InitFolderParent(LPITEMIDLIST rgItems[], UINT cMaxItems, UINT *pcItems, LPCITEMIDLIST pidlFolder, LPITEMIDLIST *ppidlParent) { ASSERT(*pcItems == 0); // Currently we expect to add the parent pidl as the FIRST entry.
ASSERT(cMaxItems > 0); // Sanity check.
// If there is a pidlFolder and it's NOT the Desktop pidl, add its parent
// as the first entry in the rgItems array. Note that the reason we
// exclude the Desktop pidl from having its parent added to the array is
// because its parent is itself, and we don't want the folder we're
// currently in appearing in rgItems since we're already there!
if (pidlFolder && !ILIsEmpty(pidlFolder)) { *ppidlParent = ILCloneParent(pidlFolder); if (*ppidlParent) { rgItems[*pcItems] = *ppidlParent; (*pcItems)++; } } else { *ppidlParent = NULL; }
return (*ppidlParent != NULL); }
// Description:
// _ILLogical() will return NULL in three cases:
// 1. out of memory
// 2. pidl has no logical pidl equivalent
// 3. pidl is SAME as logical pidl equivalent
// (thus we already have the logical pidl)
//
// Note:
// ILFree() must be called on returned pidls.
//
LPITEMIDLIST CEnumArray::_ILLogical(LPCITEMIDLIST pidl) { LPITEMIDLIST pidlLogical = SHLogILFromFSIL(pidl); if (pidlLogical && ILIsEqual(pidl, pidlLogical)) { // If the pidl argument is logical, then we already
// have the logical pidl so don't return another one.
ILFree(pidlLogical); pidlLogical = NULL; } return pidlLogical; }
STDMETHODIMP CEnumArray::Next(ULONG celt, LPITEMIDLIST *ppidl, ULONG *pceltFetched) { HRESULT hr = S_FALSE; if (_ppidl && (_ulIndex < _cItems)) { hr = SHILClone(_ppidl[_ulIndex++], ppidl); } if (pceltFetched) *pceltFetched = (hr == S_OK) ? 1 : 0;
return hr; }
STDMETHODIMP CEnumArray::Skip(ULONG celt) { _ulIndex = min(_cItems, _ulIndex + celt); return S_OK; }
STDMETHODIMP CEnumArray::Reset() { _ulIndex = 0; return S_OK; }
HRESULT _CreateIEnumIDListOnIDLists(const LPCITEMIDLIST rgpidl[], UINT cItems, UINT ulIndex, IEnumIDList **ppenum) { HRESULT hr = E_OUTOFMEMORY; *ppenum = NULL;
CEnumArray *p = new CEnumArray(); if (p) { hr = p->Init(rgpidl, cItems, ulIndex); if (SUCCEEDED(hr)) { hr = p->QueryInterface(IID_PPV_ARG(IEnumIDList, ppenum)); } p->Release(); } return hr; }
STDMETHODIMP CEnumArray::Clone(IEnumIDList **ppenum) { return _CreateIEnumIDListOnIDLists(_ppidl, _cItems, _ulIndex, ppenum); }
// Depending on the current state of the world, certain we may not want
// to allow certain CSIDLs to be enumerated (i.e. we want to hide them).
//
BOOL CEnumArray::_ShouldEnumCSIDL(int csidl) { BOOL bEnum;
switch (csidl) { case CSIDL_COMMON_DOCUMENTS: case CSIDL_COMMON_MUSIC: case CSIDL_COMMON_PICTURES: case CSIDL_COMMON_VIDEO: bEnum = SHShowSharedFolders(); break;
default: bEnum = TRUE; break; }
return bEnum; }
STDAPI CreateIEnumIDListOnIDLists(const LPCITEMIDLIST rgpidl[], UINT cItems, IEnumIDList **ppenum) { return _CreateIEnumIDListOnIDLists(rgpidl, cItems, 0, ppenum); }
STDAPI CreateIEnumIDListOnCSIDLs(LPCITEMIDLIST pidlFolder, const LPCTSTR rgcsidl[], UINT cItems, IEnumIDList **ppenum) { return CreateIEnumIDListOnCSIDLs2(pidlFolder, NULL, rgcsidl, cItems, ppenum); }
STDAPI CreateIEnumIDListOnCSIDLs2(LPCITEMIDLIST pidlFolder, LPCITEMIDLIST pidlItem, const LPCTSTR rgcsidl[], UINT cItems, IEnumIDList **ppenum) { HRESULT hr = E_OUTOFMEMORY; *ppenum = NULL;
CEnumArray *p = new CEnumArray(); if (p) { hr = p->InitFromCSIDLArray(rgcsidl, cItems, pidlFolder, pidlItem); if (SUCCEEDED(hr)) { hr = p->QueryInterface(IID_PPV_ARG(IEnumIDList, ppenum)); } p->Release(); } return hr; }
STDAPI CreateIEnumIDListPaths(LPCTSTR pszPaths, IEnumIDList **ppenum) { *ppenum = NULL; HRESULT hr = E_FAIL;
LPITEMIDLIST rgItems[32] = {0}; TCHAR szPath[MAX_PATH]; LPCTSTR pszNext = pszPaths; int cItems = 0;
while ((cItems < ARRAYSIZE(rgItems)) && (pszNext = NextPath(pszNext, szPath, ARRAYSIZE(szPath)))) { PathRemoveBackslash(szPath); TCHAR szExpanded[MAX_PATH]; if (SHExpandEnvironmentStrings(szPath, szExpanded, ARRAYSIZE(szExpanded))) { if (SUCCEEDED(SHParseDisplayName(szExpanded, NULL, &rgItems[cItems], 0, NULL))) { cItems++; } } }
if (cItems > 0) { hr = _CreateIEnumIDListOnIDLists(rgItems, cItems, 0, ppenum);
for (int i = 0; i < cItems; i++) ILFree(rgItems[i]); } return hr; }
|