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

344 lines
9.4 KiB
C++

#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()
{
if (InterlockedDecrement(&_cRef))
return _cRef;
delete this;
return 0;
}
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;
}