1317 lines
31 KiB
C++
1317 lines
31 KiB
C++
|
#include "priv.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()
|
||
|
{
|
||
|
if (InterlockedDecrement(&_cRef))
|
||
|
return _cRef;
|
||
|
|
||
|
_SaveSlots();
|
||
|
|
||
|
delete this;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
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
|
||
|
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)
|
||
|
{
|
||
|
wnsprintfW(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
|
||
|
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;
|
||
|
}
|
||
|
|