|
|
#include "priv.h"
#include <strsafe.h>
#pragma hdrstop
// this is swiped from comctl32\mru.c
#define SLOT_LOADED 0x01
#define SLOT_USED 0x02
typedef struct _SLOTITEMDATA { DWORD state; DWORD cb; BYTE *p; } SLOTITEMDATA;
class CMruBase : public IMruDataList { public: CMruBase() : _cRef(1) {} // IUnknown methods
STDMETHODIMP QueryInterface(REFIID riid, void **ppvOut); STDMETHODIMP_(ULONG) AddRef(); STDMETHODIMP_(ULONG) Release();
// IMruDataList (maybe?)
STDMETHODIMP InitData( UINT uMax, MRULISTF flags, HKEY hKey, LPCWSTR pszSubKey, MRUDATALISTCOMPARE pfnCompare); STDMETHODIMP AddData(const BYTE *pData, DWORD cbData, DWORD *pdwSlot); STDMETHODIMP FindData(const BYTE *pData, DWORD cbData, int *piIndex); STDMETHODIMP GetData(int iIndex, BYTE *pData, DWORD cbData); STDMETHODIMP QueryInfo(int iIndex, DWORD *pdwSlot, DWORD *pcbData); STDMETHODIMP Delete(int iItem); protected: virtual ~CMruBase();
HRESULT _GetItem(int iIndex, SLOTITEMDATA **ppitem); HRESULT _GetSlotItem(DWORD dwSlot, SLOTITEMDATA **ppitem); HRESULT _LoadItem(DWORD dwSlot); HRESULT _AddItem(DWORD dwSlot, const BYTE *pData, DWORD cbData); void _DeleteItem(DWORD dwSlot); HRESULT _UseEmptySlot(DWORD *pdwSlot); void _CheckUsedSlots();
// virtuals that are optionally implemented
virtual BOOL _IsEqual(SLOTITEMDATA *pitem, const BYTE *pData, DWORD cbData); virtual void _DeleteValue(LPCWSTR psz);
// virtuals that must be implemented
virtual HRESULT _InitSlots() = 0; virtual void _SaveSlots() = 0; virtual DWORD _UpdateSlots(int iIndex) = 0; virtual void _SlotString(DWORD dwSlot, LPWSTR psz, DWORD cch) = 0; virtual HRESULT _GetSlot(int iIndex, DWORD *pdwSlot) = 0; virtual HRESULT _RemoveSlot(int iIndex, DWORD *pdwSlot) = 0;
protected: LONG _cRef; MRULISTF _flags; BOOL _fDirty; BOOL _fSlotsChecked; HKEY _hkMru; int _cMaxSlots; int _cUsedSlots; MRUDATALISTCOMPARE _pfnCompare; SLOTITEMDATA *_pItems; };
class CMruLongList : public CMruBase {
protected: virtual ~CMruLongList() { if (_rgdwSlots) { LocalFree(_rgdwSlots); _rgdwSlots = NULL; } }
void _ImportShortList();
virtual HRESULT _InitSlots(); virtual void _SaveSlots(); virtual DWORD _UpdateSlots(int iIndex); virtual void _SlotString(DWORD dwSlot, LPWSTR psz, DWORD cch); virtual HRESULT _GetSlot(int iIndex, DWORD *pdwSlot); virtual HRESULT _RemoveSlot(int iIndex, DWORD *pdwSlot);
private: DWORD *_rgdwSlots; };
STDMETHODIMP_(ULONG) CMruBase::AddRef() { return InterlockedIncrement(&_cRef); }
#define szMRUEX TEXT("MRUListEx")
#define szMRUEX_OLD TEXT("MRUList")
STDMETHODIMP_(ULONG) CMruBase::Release() { ASSERT( 0 != _cRef ); ULONG cRef = InterlockedDecrement(&_cRef); if ( 0 == cRef ) { _SaveSlots(); delete this; } return cRef; }
STDMETHODIMP CMruBase::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CMruBase, IMruDataList), // IID_IMruDataList
{ 0 }, };
return QISearch(this, qit, riid, ppvObj); }
CMruBase::~CMruBase() { if (_hkMru) RegCloseKey(_hkMru);
if (_pItems) { for (int i = 0; i < _cUsedSlots; i++) { if (_pItems[i].p) { LocalFree(_pItems[i].p); _pItems[i].p = NULL; } }
LocalFree(_pItems); _pItems = NULL; } }
class CMruShortList : public CMruBase { protected: virtual ~CMruShortList() { if (_rgchSlots) { LocalFree(_rgchSlots); _rgchSlots = NULL; } }
virtual HRESULT _InitSlots(); virtual void _SaveSlots(); virtual DWORD _UpdateSlots(int iIndex); virtual void _SlotString(DWORD dwSlot, LPWSTR psz, DWORD cch); virtual HRESULT _GetSlot(int iIndex, DWORD *pdwSlot); virtual HRESULT _RemoveSlot(int iIndex, DWORD *pdwSlot);
friend class CMruLongList; private: WCHAR *_rgchSlots; };
HRESULT CMruShortList::_InitSlots() { HRESULT hr = E_OUTOFMEMORY; DWORD cb = (_cMaxSlots + 1) * sizeof(_rgchSlots[0]); _rgchSlots = (WCHAR *) LocalAlloc(LPTR, cb);
if (_rgchSlots) { // Do we already have the new MRU Index?
// Then validate it. You can never trust the registry not to be corrupted.
// Must be at least the size of a DWORD
// Must be a multiple of DWORD in length
// Must end in a -1
if (NOERROR == SHGetValue(_hkMru, NULL, szMRUEX_OLD, NULL, (LPBYTE)_rgchSlots, &cb)) { ASSERT(!(cb % 2)); _cUsedSlots = (cb / sizeof(_rgchSlots[0])) - 1; ASSERT(_rgchSlots[_cUsedSlots] == 0); }
_rgchSlots[_cUsedSlots] = 0; hr = S_OK; }
return hr; }
void CMruShortList::_SaveSlots() { if (_fDirty) { SHSetValue(_hkMru, NULL, szMRUEX_OLD, REG_SZ, (BYTE *)_rgchSlots, sizeof(_rgchSlots[0]) * (_cUsedSlots + 1)); _fDirty = FALSE; } }
#define BASE_CHAR TEXT('a')
void CMruShortList::_SlotString(DWORD dwSlot, LPWSTR psz, DWORD cch) { if (cch > 1) { psz[0] = (WCHAR) dwSlot + BASE_CHAR; psz[1] = 0; } }
HRESULT CMruShortList::_GetSlot(int iIndex, DWORD *pdwSlot) { HRESULT hr = E_FAIL; if (iIndex < _cUsedSlots) { // its in our range of allocated slots
if (_rgchSlots[iIndex] - BASE_CHAR < _cMaxSlots) { *pdwSlot = _rgchSlots[iIndex] - BASE_CHAR; _pItems[*pdwSlot].state |= SLOT_USED; hr = S_OK; } }
return hr; }
HRESULT CMruShortList::_RemoveSlot(int iIndex, DWORD *pdwSlot) { HRESULT hr = _GetSlot(iIndex, pdwSlot);
if (SUCCEEDED(hr)) { // MoveMemory() handles overlapping ranges
// Sure it looks like you should use "_cUsedSlots - iIndex - 1" for the size, but
// _cUsedSlots is the highest used index not the size
MoveMemory(&_rgchSlots[iIndex], &_rgchSlots[iIndex+1], (_cUsedSlots - iIndex) * sizeof(_rgchSlots[0])); _cUsedSlots--; // unuse the slot
_pItems->state &= ~SLOT_USED; _fDirty = TRUE; } return hr; }
DWORD CMruShortList::_UpdateSlots(int iIndex) { // need to move this away
DWORD dwSlot; DWORD cb = iIndex * sizeof(_rgchSlots[0]);
if (iIndex != _cUsedSlots) dwSlot = _rgchSlots[iIndex] - BASE_CHAR; else { // we are at the end of the list
// see if we can grow
// find the first unused slot
if (SUCCEEDED(_UseEmptySlot(&dwSlot))) { // need to move the terminator
cb += sizeof(_rgchSlots[0]); } else { // dont move the the terminator
// and dont move the last slot
dwSlot = _rgchSlots[_cUsedSlots - 1] - BASE_CHAR; cb -= sizeof(_rgchSlots[0]); } }
if (cb) { // MoveMemory() handles overlapping ranges
MoveMemory(&_rgchSlots[1], &_rgchSlots[0], cb); _rgchSlots[0] = (WCHAR) dwSlot + BASE_CHAR; _fDirty = TRUE; }
return dwSlot; }
HRESULT CMruBase::InitData( UINT uMax, MRULISTF flags, HKEY hKey, LPCWSTR pszSubKey, MRUDATALISTCOMPARE pfnCompare) { HRESULT hr = E_FAIL; _flags = flags; _pfnCompare = pfnCompare; _cMaxSlots = uMax;
if (pszSubKey) { RegCreateKeyEx(hKey, pszSubKey, 0L, NULL, 0, MAXIMUM_ALLOWED, NULL, &_hkMru, NULL); } else _hkMru = SHRegDuplicateHKey(hKey);
if (_hkMru) { _pItems = (SLOTITEMDATA *) LocalAlloc(LPTR, sizeof(SLOTITEMDATA) * _cMaxSlots); if (_pItems) hr = _InitSlots(); else hr = E_OUTOFMEMORY; }
return hr; }
void CMruBase::_CheckUsedSlots() { ASSERT(!_fSlotsChecked); DWORD dwSlot; for (int i = 0; i < _cUsedSlots; i++) { _GetSlot(i, &dwSlot); } _fSlotsChecked = TRUE; }
HRESULT CMruBase::_AddItem(DWORD dwSlot, const BYTE *pData, DWORD cbData) { SLOTITEMDATA *pitem = &_pItems[dwSlot]; WCHAR szSlot[12]; _SlotString(dwSlot, szSlot, ARRAYSIZE(szSlot));
HRESULT hr = E_OUTOFMEMORY; if (NOERROR == SHSetValue(_hkMru, NULL, szSlot, REG_BINARY, pData, cbData)) { if (cbData >= pitem->cb || !pitem->p) { if (pitem->p) LocalFree(pitem->p);
// Binary data has the size at the begining so we'll need a little extra room.
pitem->p = (BYTE *)LocalAlloc(LPTR, cbData); }
if (pitem->p) { pitem->cb = cbData; pitem->state = (SLOT_LOADED | SLOT_USED); memcpy(pitem->p, pData, cbData); hr = S_OK; } }
return hr; }
HRESULT CMruBase::AddData(const BYTE *pData, DWORD cbData, DWORD *pdwSlot) { HRESULT hr = E_FAIL; int iIndex; DWORD dwSlot; if (SUCCEEDED(FindData(pData, cbData, &iIndex))) { dwSlot = _UpdateSlots(iIndex); hr = S_OK; } else { dwSlot = _UpdateSlots(_cUsedSlots);
hr = _AddItem(dwSlot, pData, cbData); }
if (SUCCEEDED(hr) && pdwSlot) *pdwSlot = dwSlot;
return hr; }
BOOL CMruBase::_IsEqual(SLOTITEMDATA *pitem, const BYTE *pData, DWORD cbData) { BOOL fRet = FALSE; if (_pfnCompare) { fRet = (0 == _pfnCompare(pData, pitem->p, cbData)); } else { switch (_flags & 0xf) { case MRULISTF_USE_MEMCMP: if (pitem->cb == cbData) fRet = (0 == memcmp(pData, pitem->p, min(cbData, pitem->cb))); break;
case MRULISTF_USE_STRCMPIW: fRet = (0 == StrCmpIW((LPCWSTR)pData, (LPCWSTR)pitem->p)); break; case MRULISTF_USE_STRCMPW: fRet = (0 == StrCmpW((LPCWSTR)pData, (LPCWSTR)pitem->p)); break; case MRULISTF_USE_ILISEQUAL: fRet = ILIsEqual((LPCITEMIDLIST)pData, (LPCITEMIDLIST)pitem->p); break; } }
return fRet; }
HRESULT CMruBase::FindData(const BYTE *pData, DWORD cbData, int *piIndex) { HRESULT hr = E_FAIL; for (int iIndex = 0; iIndex < _cUsedSlots ; iIndex++) { SLOTITEMDATA *pitem; if (SUCCEEDED(_GetItem(iIndex, &pitem))) { if (_IsEqual(pitem, pData, cbData)) { hr = S_OK; *piIndex = iIndex; break; } } }
return hr; }
HRESULT CMruBase::_LoadItem(DWORD dwSlot) { SLOTITEMDATA *pitem = &_pItems[dwSlot]; DWORD cb; WCHAR szSlot[12]; _SlotString(dwSlot, szSlot, ARRAYSIZE(szSlot));
ASSERT(!(pitem->state & SLOT_LOADED)); ASSERT(pitem->state & SLOT_USED); if (NOERROR == SHGetValue(_hkMru, NULL, szSlot, NULL, NULL, &cb) && cb) { // Binary data has the size at the begining so we'll need a little extra room.
pitem->p = (BYTE *)LocalAlloc(LPTR, cb);
if (pitem->p) { pitem->cb = cb;
if (NOERROR != SHGetValue(_hkMru, NULL, szSlot, NULL, pitem->p, &cb)) { LocalFree(pitem->p); pitem->p = NULL; } } } pitem->state |= SLOT_LOADED;
return pitem->p ? S_OK : E_FAIL; }
HRESULT CMruBase::_GetSlotItem(DWORD dwSlot, SLOTITEMDATA **ppitem) { HRESULT hr = S_OK; ASSERT(dwSlot < (DWORD)_cMaxSlots); if (!(_pItems[dwSlot].state & SLOT_LOADED)) _LoadItem(dwSlot);
if (_pItems[dwSlot].p) { *ppitem = &_pItems[dwSlot]; return S_OK; }
return E_OUTOFMEMORY; }
HRESULT CMruBase::_GetItem(int iIndex, SLOTITEMDATA **ppitem) { DWORD dwSlot; HRESULT hr = _GetSlot(iIndex, &dwSlot);
if (SUCCEEDED(hr)) { hr = _GetSlotItem(dwSlot, ppitem); }
return hr; } HRESULT CMruBase::GetData(int iIndex, BYTE *pData, DWORD cbData) { SLOTITEMDATA *pitem; HRESULT hr = _GetItem(iIndex, &pitem); if (SUCCEEDED(hr)) { if (pitem->cb <= cbData) { memcpy(pData, pitem->p, min(cbData, pitem->cb)); } else hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER); }
return hr; }
HRESULT CMruBase::QueryInfo(int iIndex, DWORD *pdwSlot, DWORD *pcbData) { DWORD dwSlot; HRESULT hr = _GetSlot(iIndex, &dwSlot);
if (SUCCEEDED(hr)) { if (pdwSlot) *pdwSlot = dwSlot; if (pcbData) { SLOTITEMDATA *pitem; hr = _GetSlotItem(dwSlot, &pitem); if (SUCCEEDED(hr)) { *pcbData = pitem->cb; } } }
return hr; } HRESULT CMruBase::_UseEmptySlot(DWORD *pdwSlot) { HRESULT hr = E_FAIL;
if (!_fSlotsChecked) _CheckUsedSlots(); for (DWORD dw = 0; dw < (DWORD) _cMaxSlots; dw++) { if (!(_pItems[dw].state & SLOT_USED)) { _pItems[dw].state |= SLOT_USED; *pdwSlot = dw; _cUsedSlots++; hr = S_OK; break; } }
return hr; }
void CMruBase::_DeleteValue(LPCWSTR psz) { SHDeleteValue(_hkMru, NULL, psz); }
void CMruBase::_DeleteItem(DWORD dwSlot) { ASSERT(dwSlot < (DWORD) _cMaxSlots);
WCHAR szSlot[12]; _SlotString(dwSlot, szSlot, ARRAYSIZE(szSlot)); _DeleteValue(szSlot);
if (_pItems[dwSlot].p) { LocalFree(_pItems[dwSlot].p); _pItems[dwSlot].p = NULL; } } HRESULT CMruBase::Delete(int iIndex) { DWORD dwSlot; HRESULT hr = _RemoveSlot(iIndex, &dwSlot);
if (SUCCEEDED(hr)) { _DeleteItem(dwSlot); }
return hr; }
void CMruLongList::_ImportShortList() { CMruShortList *pmru = new CMruShortList();
if (pmru) { if (SUCCEEDED(pmru->InitData(_cMaxSlots, 0, _hkMru, NULL, NULL))) { // we need to walk the list
DWORD dwSlot; SLOTITEMDATA *pitem; while (SUCCEEDED(pmru->_GetSlot(_cUsedSlots, &dwSlot)) && SUCCEEDED(pmru->_GetSlotItem(dwSlot, &pitem))) { // we just copy to ourselves
_AddItem(dwSlot, pitem->p, pitem->cb); pmru->_DeleteItem(dwSlot);
// dont use _UpdateSlots() here
_rgdwSlots[_cUsedSlots] = dwSlot; _cUsedSlots++; }
_fDirty = TRUE; }
pmru->Release();
// wipe it out
SHDeleteValue(_hkMru, NULL, szMRUEX_OLD); } }
HRESULT CMruLongList::_InitSlots() { HRESULT hr = E_OUTOFMEMORY; DWORD cb = (_cMaxSlots + 1) * sizeof(_rgdwSlots[0]); _rgdwSlots = (DWORD *) LocalAlloc(LPTR, cb);
if (_rgdwSlots) { // Do we already have the new MRU Index?
// Then validate it. You can never trust the registry not to be corrupted.
// Must be at least the size of a DWORD
// Must be a multiple of DWORD in length
// Must end in a -1
if (NOERROR == SHGetValue(_hkMru, NULL, szMRUEX, NULL, (LPBYTE)_rgdwSlots, &cb)) { ASSERT(!(cb % 4)); _cUsedSlots = (cb / sizeof(_rgdwSlots[0])) - 1; ASSERT(_rgdwSlots[_cUsedSlots] == -1); } else { _ImportShortList(); }
_rgdwSlots[_cUsedSlots] = (DWORD)-1; hr = S_OK; }
return hr; }
void CMruLongList::_SaveSlots() { if (_fDirty) { SHSetValue(_hkMru, NULL, szMRUEX, REG_BINARY, (BYTE *)_rgdwSlots, sizeof(_rgdwSlots[0]) * (_cUsedSlots + 1)); _fDirty = FALSE; } }
void CMruLongList::_SlotString(DWORD dwSlot, LPWSTR psz, DWORD cch) { StringCchPrintf(psz, cch, L"%d", dwSlot); }
HRESULT CMruLongList::_GetSlot(int iIndex, DWORD *pdwSlot) { HRESULT hr = E_FAIL; ASSERT(iIndex < _cMaxSlots); if (iIndex < _cUsedSlots) { // its in our range of allocated slots
if (_rgdwSlots[iIndex] < (DWORD) _cMaxSlots) { *pdwSlot = _rgdwSlots[iIndex]; _pItems[*pdwSlot].state |= SLOT_USED; hr = S_OK; } }
return hr; }
HRESULT CMruLongList::_RemoveSlot(int iIndex, DWORD *pdwSlot) { HRESULT hr = _GetSlot(iIndex, pdwSlot);
if (SUCCEEDED(hr)) { // MoveMemory() handles overlapping ranges
// Sure it looks like you should use "_cUsedSlots - iIndex - 1" for the size, but
// _cUsedSlots is the highest used index not the size
MoveMemory(&_rgdwSlots[iIndex], &_rgdwSlots[iIndex+1], (_cUsedSlots - iIndex) * sizeof(_rgdwSlots[0])); _cUsedSlots--; // unuse the slot
_pItems->state &= ~SLOT_USED; _fDirty = TRUE; } return hr; }
DWORD CMruLongList::_UpdateSlots(int iIndex) { // need to move this away
DWORD dwSlot; DWORD cb = iIndex * sizeof(_rgdwSlots[0]);
if (iIndex != _cUsedSlots) dwSlot = _rgdwSlots[iIndex]; else { // we are at the end of the list
// see if we can grow
// find the first unused slot
if (SUCCEEDED(_UseEmptySlot(&dwSlot))) { // need to move the terminator
cb += sizeof(_rgdwSlots[0]); } else { // dont move the the terminator
// and dont move the last slot
dwSlot = _rgdwSlots[_cUsedSlots - 1]; cb -= sizeof(_rgdwSlots[0]); } }
if (cb) { // MoveMemory() handles overlapping ranges
MoveMemory(&_rgdwSlots[1], &_rgdwSlots[0], cb); _rgdwSlots[0] = dwSlot; _fDirty = TRUE; }
return dwSlot; }
STDAPI CMruLongList_CreateInstance(IUnknown * punkOuter, IUnknown ** ppunk, LPCOBJECTINFO poi) { *ppunk = NULL;
CMruLongList *p = new CMruLongList(); if (p != NULL) { *ppunk = SAFECAST(p, IMruDataList *); return S_OK; }
return E_OUTOFMEMORY; }
class CMruPidlList;
class CMruNode : public CMruLongList { public: CMruNode(CMruNode *pnodeParent, DWORD dwSlot); HRESULT GetNode(BOOL fCreate, LPCITEMIDLIST pidlChild, CMruNode **ppnode); HRESULT RemoveLeast(DWORD *pdwSlotLeast); HRESULT BindToSlot(DWORD dwSlot, IShellFolder **ppsf); HRESULT Clear(CMruPidlList *proot);
CMruNode *GetParent() { if (_pnodeParent) _pnodeParent->AddRef(); return _pnodeParent;}
HRESULT GetNodeSlot(DWORD *pdwNodeSlot) { DWORD cb = sizeof(*pdwNodeSlot); return NOERROR == SHGetValue(_hkMru, NULL, L"NodeSlot", NULL, pdwNodeSlot, pdwNodeSlot ? &cb : NULL) ? S_OK : E_FAIL; }
HRESULT SetNodeSlot(DWORD dwNodeSlot) { return NOERROR == SHSetValue(_hkMru, NULL, L"NodeSlot", REG_DWORD, &dwNodeSlot, sizeof(dwNodeSlot)) ? S_OK : E_FAIL; }
protected: CMruNode() {} virtual ~CMruNode(); virtual BOOL _IsEqual(SLOTITEMDATA *pitem, const BYTE *pData, DWORD cbData); virtual void _DeleteValue(LPCWSTR psz);
HRESULT _GetPidlSlot(LPCITEMIDLIST pidlChild, BOOL fCreate, DWORD *pdwKidSlot); HRESULT _CreateNode(DWORD dwSlot, CMruNode **ppnode); BOOL _InitLate();
HRESULT _FindPidl(LPCITEMIDLIST pidl, int *piIndex) { return FindData((LPBYTE)pidl, pidl->mkid.cb + sizeof(pidl->mkid.cb), piIndex); }
HRESULT _AddPidl(DWORD dwSlot, LPCITEMIDLIST pidl) { return _AddItem(dwSlot, (LPBYTE)pidl, pidl->mkid.cb + sizeof(pidl->mkid.cb)); }
#ifdef DEBUG
HRESULT _GetSlotName(DWORD dwSlot, LPWSTR psz, DWORD cch); #endif
protected: DWORD _dwSlotSelf; CMruNode *_pnodeParent; IShellFolder *_psf; };
class CMruPidlList : public CMruNode , public IMruPidlList { public: CMruPidlList() {} // IUnknown methods
STDMETHODIMP QueryInterface(REFIID riid, void **ppvOut); STDMETHODIMP_(ULONG) AddRef() { return CMruBase::AddRef(); }
STDMETHODIMP_(ULONG) Release() { return CMruBase::Release(); }
// IMruPidlList
STDMETHODIMP InitList(UINT uMax, HKEY hKey, LPCWSTR pszSubKey); STDMETHODIMP UsePidl(LPCITEMIDLIST pidl, DWORD *pdwSlot); STDMETHODIMP QueryPidl(LPCITEMIDLIST pidl, DWORD cSlots, DWORD *rgdwSlots, DWORD *pcSlotsFetched); STDMETHODIMP PruneKids(LPCITEMIDLIST pidl);
HRESULT GetEmptySlot(DWORD *pdwSlot); void EmptyNodeSlot(DWORD dwNodeSlot);
protected: ~CMruPidlList() { if (_rgbNodeSlots) { LocalFree(_rgbNodeSlots); _rgbNodeSlots = NULL; }
if (_hMutex) CloseHandle(_hMutex); } BOOL _LoadNodeSlots(); void _SaveNodeSlots(); HRESULT _InitNodeSlots();
protected: BYTE *_rgbNodeSlots; int _cUsedNodeSlots ; HANDLE _hMutex; };
CMruNode::CMruNode(CMruNode *pnodeParent, DWORD dwSlot) : _pnodeParent(pnodeParent), _dwSlotSelf(dwSlot) { ASSERT(_cRef); _pnodeParent->AddRef(); }
CMruNode::~CMruNode() { if (_pnodeParent) _pnodeParent->Release(); if (_psf) _psf->Release(); }
HRESULT CMruNode::BindToSlot(DWORD dwSlot, IShellFolder **ppsf) { SLOTITEMDATA *pitem; HRESULT hr = _GetSlotItem(dwSlot, &pitem); if (SUCCEEDED(hr)) { hr = _psf->BindToObject((LPCITEMIDLIST)pitem->p, NULL, IID_PPV_ARG(IShellFolder, ppsf)); } return hr; }
#ifdef DEBUG
HRESULT CMruNode::_GetSlotName(DWORD dwSlot, LPWSTR psz, DWORD cch) { SLOTITEMDATA *pitem; HRESULT hr = _GetSlotItem(dwSlot, &pitem); if (SUCCEEDED(hr)) { hr = DisplayNameOf(_psf, (LPCITEMIDLIST)pitem->p, 0, psz, cch); } return hr; } #endif
BOOL CMruNode::_IsEqual(SLOTITEMDATA *pitem, const BYTE *pData, DWORD cbData) { return S_OK == IShellFolder_CompareIDs(_psf, SHCIDS_CANONICALONLY, (LPCITEMIDLIST)pitem->p, (LPCITEMIDLIST)pData); }
HRESULT CMruNode::_CreateNode(DWORD dwSlot, CMruNode **ppnode) { HRESULT hr = E_OUTOFMEMORY; CMruNode *pnode = new CMruNode(this, dwSlot); if (pnode) { WCHAR szSlot[12]; _SlotString(dwSlot, szSlot, ARRAYSIZE(szSlot)); hr = pnode->InitData(_cMaxSlots, 0, _hkMru, szSlot, NULL); if (SUCCEEDED(hr)) *ppnode = pnode; else pnode->Release(); } return hr; }
BOOL CMruNode::_InitLate() { if (!_psf) { if (_pnodeParent) { _pnodeParent->BindToSlot(_dwSlotSelf, &_psf); #ifdef DEBUG
WCHAR sz[MAX_PATH]; if (SUCCEEDED(_pnodeParent->_GetSlotName(_dwSlotSelf, sz, ARRAYSIZE(sz)))) SHSetValue(_hkMru, NULL, L"SlotName", REG_SZ, sz, CbFromCchW(lstrlen(sz) + 1)); #endif
} else SHGetDesktopFolder(&_psf); } return (_psf != NULL); }
HRESULT CMruNode::_GetPidlSlot(LPCITEMIDLIST pidlChild, BOOL fCreate, DWORD *pdwKidSlot) { HRESULT hr = E_OUTOFMEMORY; LPITEMIDLIST pidlFirst = ILCloneFirst(pidlChild); if (pidlFirst) { int iIndex; if (SUCCEEDED(_FindPidl(pidlFirst, &iIndex))) { *pdwKidSlot = _UpdateSlots(iIndex); hr = S_OK; } else if (fCreate) { *pdwKidSlot = _UpdateSlots(_cUsedSlots); hr = _AddPidl(*pdwKidSlot, pidlFirst); } ILFree(pidlFirst); } return hr; }
HRESULT CMruNode::GetNode(BOOL fCreate, LPCITEMIDLIST pidlChild, CMruNode **ppnode) { HRESULT hr = E_FAIL; if (ILIsEmpty(pidlChild)) { *ppnode = this; AddRef(); hr = S_OK; } else if (_InitLate()) { DWORD dwKidSlot; hr = _GetPidlSlot(pidlChild, fCreate, &dwKidSlot);
if (SUCCEEDED(hr)) { // need to make another CMruNode
CMruNode *pnode; hr = _CreateNode(dwKidSlot, &pnode); if (SUCCEEDED(hr)) { // need to save so that this node
// is updated so that it doesnt get
// deleted from under us.
_SaveSlots(); hr = pnode->GetNode(fCreate, _ILNext(pidlChild), ppnode); pnode->Release(); } }
if (FAILED(hr) && !fCreate) { *ppnode = this; AddRef(); hr = S_FALSE; } } return hr; }
void CMruNode::_DeleteValue(LPCWSTR psz) { CMruBase::_DeleteValue(psz); SHDeleteKey(_hkMru, psz); }
HRESULT CMruNode::RemoveLeast(DWORD *pdwSlotLeast) { // if this node has children
// then we attempt to call RemoveLeast on them
ASSERT(_cUsedSlots >= 0); HRESULT hr = S_FALSE; if (_cUsedSlots) { DWORD dwLocalSlot; hr = _GetSlot(_cUsedSlots - 1, &dwLocalSlot); if (SUCCEEDED(hr)) { CMruNode *pnode; hr = _CreateNode(dwLocalSlot, &pnode); if (SUCCEEDED(hr)) { hr = pnode->RemoveLeast(pdwSlotLeast); pnode->Release(); }
// S_FALSE means that this node needs
// needs deleting. it is empty.
if (hr == S_FALSE) { Delete(_cUsedSlots - 1);
// if we still have kids, or have a NodeSlot
// then we dont want to be deleted
if (_cUsedSlots || SUCCEEDED(GetNodeSlot(NULL))) hr = S_OK; } } } else { // this is the empty node
// delete me if you can
ASSERT(!*pdwSlotLeast); GetNodeSlot(pdwSlotLeast); } return hr; }
HRESULT CMruNode::Clear(CMruPidlList *proot) { DWORD dwLocalSlot; while (SUCCEEDED(_GetSlot(0, &dwLocalSlot))) { CMruNode *pnode; if (SUCCEEDED(_CreateNode(dwLocalSlot, &pnode))) { // tell the root about it
DWORD dwNodeSlot; if (SUCCEEDED(pnode->GetNodeSlot(&dwNodeSlot))) proot->EmptyNodeSlot(dwNodeSlot);
pnode->Clear(proot); pnode->Release(); }
Delete(0);
} return S_OK; }
class CSafeMutex { public: CSafeMutex() : _h(0) {} ~CSafeMutex() { if (_h) ReleaseMutex(_h); } HRESULT Enter(HANDLE hMutex) { // this is usually done on the UI thread
// wait for half a second or dont bother
HRESULT hr; DWORD dwWait = WaitForSingleObject(hMutex, 500); if (dwWait == WAIT_OBJECT_0) { _h = hMutex; hr = S_OK; } else hr = E_FAIL; return hr; } private: HANDLE _h; };
HRESULT CMruPidlList::UsePidl(LPCITEMIDLIST pidl, DWORD *pdwSlot) { CSafeMutex sm; HRESULT hr = sm.Enter(_hMutex); if (SUCCEEDED(hr)) { CMruNode *pnode; hr = GetNode(TRUE, pidl, &pnode); *pdwSlot = 0; if (SUCCEEDED(hr)) { ASSERT(hr == S_OK); hr = pnode->GetNodeSlot(pdwSlot);
if (FAILED(hr)) { hr = GetEmptySlot(pdwSlot); if (SUCCEEDED(hr)) { hr = pnode->SetNodeSlot(*pdwSlot); } } pnode->Release(); } } return hr; } HRESULT CMruPidlList::QueryPidl(LPCITEMIDLIST pidl, DWORD cSlots, DWORD *rgdwSlots, DWORD *pcSlotsFetched) { CSafeMutex sm; HRESULT hr = sm.Enter(_hMutex); if (SUCCEEDED(hr)) { CMruNode *pnode; hr = GetNode(FALSE, pidl, &pnode); *pcSlotsFetched = 0; if (SUCCEEDED(hr)) { while (*pcSlotsFetched < cSlots && pnode) { CMruNode *pnodeParent = pnode->GetParent(); if (SUCCEEDED(pnode->GetNodeSlot(&rgdwSlots[*pcSlotsFetched]))) { (*pcSlotsFetched)++; } else if (hr == S_OK && !*pcSlotsFetched) { // we found the exact node
// but we couldnt get the NodeSlot from it
hr = S_FALSE; } pnode->Release(); pnode = pnodeParent; }
if (pnode) pnode->Release(); }
if (SUCCEEDED(hr) && !*pcSlotsFetched) hr = E_FAIL; } return hr; }
HRESULT CMruPidlList::PruneKids(LPCITEMIDLIST pidl) { CSafeMutex sm; HRESULT hr = sm.Enter(_hMutex); if (SUCCEEDED(hr)) { if (_LoadNodeSlots()) { CMruNode *pnode; hr = GetNode(FALSE, pidl, &pnode); if (SUCCEEDED(hr)) { if (hr == S_OK) { hr = pnode->Clear(this); } else hr = E_FAIL; pnode->Release(); } _SaveNodeSlots(); } } return hr; }
STDMETHODIMP CMruPidlList::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CMruPidlList, IMruPidlList), // IID_IMruDataList
{ 0 }, };
return QISearch(this, qit, riid, ppvObj); }
HRESULT CMruPidlList::InitList(UINT uMax, HKEY hKey, LPCWSTR pszSubKey) { HRESULT hr = InitData(uMax, 0, hKey, pszSubKey, NULL); if (SUCCEEDED(hr)) { hr = _InitNodeSlots(); if (SUCCEEDED(hr)) { _hMutex = CreateMutex(NULL, FALSE, TEXT("Shell.CMruPidlList")); if (!_hMutex) hr = ResultFromLastError(); } } return hr; }
BOOL CMruPidlList::_LoadNodeSlots() { DWORD cb = (_cMaxSlots) * sizeof(_rgbNodeSlots[0]); if (NOERROR == SHGetValue(_hkMru, NULL, L"NodeSlots", NULL, _rgbNodeSlots , &cb)) { _cUsedNodeSlots = (cb / sizeof(_rgbNodeSlots[0])); return TRUE; } return FALSE; }
void CMruPidlList::_SaveNodeSlots() { SHSetValue(_hkMru, NULL, L"NodeSlots", REG_BINARY, _rgbNodeSlots , _cUsedNodeSlots); }
HRESULT CMruPidlList::_InitNodeSlots() { HRESULT hr = E_OUTOFMEMORY; DWORD cb = (_cMaxSlots) * sizeof(_rgbNodeSlots[0]); _rgbNodeSlots = (BYTE *) LocalAlloc(LPTR, cb);
if (_rgbNodeSlots) { _LoadNodeSlots(); _fDirty = TRUE; _SaveNodeSlots(); hr = S_OK; }
return hr; }
void CMruPidlList::EmptyNodeSlot(DWORD dwNodeSlot) { ASSERT(dwNodeSlot <= (DWORD)_cMaxSlots); _rgbNodeSlots[dwNodeSlot-1] = FALSE; _fDirty = TRUE; }
HRESULT CMruPidlList::GetEmptySlot(DWORD *pdwSlot) { HRESULT hr = E_FAIL; *pdwSlot = 0; if (_LoadNodeSlots()) { if (_cUsedNodeSlots < _cMaxSlots) { // then we can just use the next most natural
// node slot
_rgbNodeSlots[_cUsedNodeSlots] = SLOT_USED; *pdwSlot = ++_cUsedNodeSlots; hr = S_OK; } else { // if we can find an empty in the list...
for (int i = 0; i < _cUsedNodeSlots; i++) { if (!(_rgbNodeSlots[i] & SLOT_USED)) { _rgbNodeSlots[i] = SLOT_USED; *pdwSlot = i+1; hr = S_OK; break; } }
if (FAILED(hr)) { // we need to find the LRU slot
if (SUCCEEDED(RemoveLeast(pdwSlot)) && *pdwSlot) hr = S_OK; } } _SaveNodeSlots(); }
return hr; }
STDAPI CMruPidlList_CreateInstance(IUnknown * punkOuter, IUnknown ** ppunk, LPCOBJECTINFO poi) { *ppunk = NULL;
CMruPidlList *p = new CMruPidlList(); if (p != NULL) { *ppunk = SAFECAST(p, IMruPidlList *); return S_OK; }
return E_OUTOFMEMORY; }
|