[SHDOCVW] Implement MRU List for Shell Bag, Part 5 (#5691)

Follow-up to #5686. 
Implement CSafeMutex and CMruPidlList classes.
CORE-9283
This commit is contained in:
Katayama Hirofumi MZ 2023-09-17 19:52:50 +09:00 committed by GitHub
parent 8650eac76c
commit fd626dc163
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 266 additions and 93 deletions

View file

@ -24,6 +24,7 @@
WINE_DEFAULT_DEBUG_CHANNEL(shdocvw);
class CSafeMutex;
class CMruBase;
class CMruShortList;
class CMruLongList;
@ -57,6 +58,35 @@ BOOL IEILIsEqual(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, BOOL bUnknown)
#define COMPARE_BY_IEILISEQUAL 0x3
#define COMPARE_BY_MASK 0xF
class CSafeMutex
{
protected:
HANDLE m_hMutex;
public:
CSafeMutex() : m_hMutex(NULL)
{
}
~CSafeMutex()
{
if (m_hMutex)
{
::ReleaseMutex(m_hMutex);
m_hMutex = NULL;
}
}
HRESULT Enter(HANDLE hMutex)
{
DWORD wait = ::WaitForSingleObject(hMutex, 500);
if (wait != WAIT_OBJECT_0)
return E_FAIL;
m_hMutex = hMutex;
return S_OK;
}
};
class CMruBase
: public IMruDataList
{
@ -72,7 +102,7 @@ protected:
SLOTITEMDATA * m_pSlots = NULL; // Slot data
HRESULT _LoadItem(UINT iSlot);
HRESULT _AddItem(UINT iSlot, const BYTE *pbData, DWORD cbData);
HRESULT _AddItem(UINT iSlot, LPCVOID pvData, DWORD cbData);
HRESULT _GetItem(UINT iSlot, SLOTITEMDATA **ppItem);
void _DeleteItem(UINT iSlot);
@ -96,9 +126,9 @@ public:
STDMETHODIMP InitData(UINT cCapacity, UINT flags, HKEY hKey,
LPCWSTR pszSubKey OPTIONAL,
SLOTCOMPARE fnCompare OPTIONAL) override;
STDMETHODIMP AddData(const BYTE *pbData, DWORD cbData, UINT *piSlot) override;
STDMETHODIMP FindData(const BYTE *pbData, DWORD cbData, UINT *piSlot) override;
STDMETHODIMP GetData(UINT iSlot, BYTE *pbData, DWORD cbData) override;
STDMETHODIMP AddData(LPCVOID pvData, DWORD cbData, UINT *piSlot) override;
STDMETHODIMP FindData(LPCVOID pvData, DWORD cbData, UINT *piSlot) override;
STDMETHODIMP GetData(UINT iSlot, LPVOID pvData, DWORD cbData) override;
STDMETHODIMP QueryInfo(UINT iSlot, UINT *puSlot, DWORD *pcbData) override;
STDMETHODIMP Delete(UINT iSlot) override;
@ -241,14 +271,14 @@ void CMruBase::_CheckUsedSlots()
m_bChecked = TRUE;
}
HRESULT CMruBase::_AddItem(UINT iSlot, const BYTE *pbData, DWORD cbData)
HRESULT CMruBase::_AddItem(UINT iSlot, LPCVOID pvData, DWORD cbData)
{
SLOTITEMDATA *pItem = &m_pSlots[iSlot];
WCHAR szBuff[12];
_SlotString(iSlot, szBuff, _countof(szBuff));
if (SHSetValueW(m_hKey, NULL, szBuff, REG_BINARY, pbData, cbData) != ERROR_SUCCESS)
if (SHSetValueW(m_hKey, NULL, szBuff, REG_BINARY, pvData, cbData) != ERROR_SUCCESS)
return E_OUTOFMEMORY;
if (cbData >= pItem->cbData || !pItem->pvData)
@ -262,7 +292,7 @@ HRESULT CMruBase::_AddItem(UINT iSlot, const BYTE *pbData, DWORD cbData)
pItem->cbData = cbData;
pItem->dwFlags = (SLOT_LOADED | SLOT_SET);
CopyMemory(pItem->pvData, pbData, cbData);
CopyMemory(pItem->pvData, pvData, cbData);
return S_OK;
}
@ -293,14 +323,14 @@ CMruBase::InitData(
return _InitSlots();
}
STDMETHODIMP CMruBase::AddData(const BYTE *pbData, DWORD cbData, UINT *piSlot)
STDMETHODIMP CMruBase::AddData(LPCVOID pvData, DWORD cbData, UINT *piSlot)
{
UINT iSlot;
HRESULT hr = FindData(pbData, cbData, &iSlot);
HRESULT hr = FindData(pvData, cbData, &iSlot);
if (FAILED(hr))
{
iSlot = _UpdateSlots(m_cSlots);
hr = _AddItem(iSlot, pbData, cbData);
hr = _AddItem(iSlot, pvData, cbData);
if (FAILED(hr))
return hr;
}
@ -316,14 +346,14 @@ STDMETHODIMP CMruBase::AddData(const BYTE *pbData, DWORD cbData, UINT *piSlot)
return hr;
}
STDMETHODIMP CMruBase::FindData(const BYTE *pbData, DWORD cbData, UINT *piSlot)
STDMETHODIMP CMruBase::FindData(LPCVOID pvData, DWORD cbData, UINT *piSlot)
{
if (m_cSlots <= 0)
return E_FAIL;
UINT iSlot = 0;
SLOTITEMDATA *pItem;
while (FAILED(_GetItem(iSlot, &pItem)) || !_IsEqual(pItem, pbData, cbData))
while (FAILED(_GetItem(iSlot, &pItem)) || !_IsEqual(pItem, pvData, cbData))
{
if (++iSlot >= m_cSlots)
return E_FAIL;
@ -333,7 +363,7 @@ STDMETHODIMP CMruBase::FindData(const BYTE *pbData, DWORD cbData, UINT *piSlot)
return S_OK;
}
STDMETHODIMP CMruBase::GetData(UINT iSlot, BYTE *pbData, DWORD cbData)
STDMETHODIMP CMruBase::GetData(UINT iSlot, LPVOID pvData, DWORD cbData)
{
SLOTITEMDATA *pItem;
HRESULT hr = _GetItem(iSlot, &pItem);
@ -343,7 +373,7 @@ STDMETHODIMP CMruBase::GetData(UINT iSlot, BYTE *pbData, DWORD cbData)
if (cbData < pItem->cbData)
return 0x8007007A; // FIXME: Magic number
CopyMemory(pbData, pItem->pvData, pItem->cbData);
CopyMemory(pvData, pItem->pvData, pItem->cbData);
return hr;
}
@ -694,7 +724,7 @@ void CMruLongList::_ImportShortList()
if (FAILED(hr))
break;
_AddItem(iSlot, (const BYTE*)pItem->pvData, pItem->cbData);
_AddItem(iSlot, pItem->pvData, pItem->cbData);
pShortList->_DeleteItem(iSlot);
m_puSlotData[m_cSlots++] = iSlot;
@ -751,10 +781,10 @@ public:
HRESULT BindToSlot(UINT iSlot, IShellFolder **ppSF);
HRESULT GetNode(BOOL bAdd, LPCITEMIDLIST pidl, CMruNode **pNewNode);
HRESULT GetNodeSlot(UINT *piSlot);
HRESULT SetNodeSlot(UINT iSlot);
HRESULT GetNodeSlot(UINT *pnNodeSlot);
HRESULT SetNodeSlot(UINT nNodeSlot);
HRESULT RemoveLeast(UINT *piSlot);
HRESULT RemoveLeast(UINT *pnNodeSlot);
HRESULT Clear(CMruPidlList *pList);
};
@ -793,10 +823,10 @@ HRESULT CMruNode::_CreateNode(UINT iSlot, CMruNode **ppNewNode)
if (!pNewNode)
return E_OUTOFMEMORY;
WCHAR szBuff[12];
_SlotString(iSlot, szBuff, _countof(szBuff));
WCHAR szSubKey[12];
_SlotString(iSlot, szSubKey, _countof(szSubKey));
HRESULT hr = pNewNode->InitData(m_cSlotRooms, 0, m_hKey, szBuff, NULL);
HRESULT hr = pNewNode->InitData(m_cSlotRooms, 0, m_hKey, szSubKey, NULL);
if (FAILED(hr))
pNewNode->Release();
else
@ -876,19 +906,19 @@ BOOL CMruNode::_IsEqual(SLOTITEMDATA *pItem, LPCVOID pvData, UINT cbData)
(LPCITEMIDLIST)pvData) == 0;
}
HRESULT CMruNode::GetNodeSlot(UINT *piSlot)
HRESULT CMruNode::GetNodeSlot(UINT *pnNodeSlot)
{
DWORD dwData, cbData = sizeof(dwData);
DWORD error = SHGetValueW(m_hKey, NULL, L"NodeSlot", NULL, &dwData, (piSlot ? &cbData : NULL));
DWORD error = SHGetValueW(m_hKey, NULL, L"NodeSlot", NULL, &dwData, (pnNodeSlot ? &cbData : NULL));
if (error != ERROR_SUCCESS)
return E_FAIL;
*piSlot = (UINT)dwData;
*pnNodeSlot = (UINT)dwData;
return S_OK;
}
HRESULT CMruNode::SetNodeSlot(UINT iSlot)
HRESULT CMruNode::SetNodeSlot(UINT nNodeSlot)
{
DWORD dwData = iSlot;
DWORD dwData = nNodeSlot;
if (SHSetValueW(m_hKey, NULL, L"NodeSlot", REG_DWORD, &dwData, sizeof(dwData)) != ERROR_SUCCESS)
return E_FAIL;
return S_OK;
@ -896,7 +926,7 @@ HRESULT CMruNode::SetNodeSlot(UINT iSlot)
HRESULT CMruNode::_AddPidl(UINT iSlot, LPCITEMIDLIST pidl)
{
return CMruBase::_AddItem(iSlot, (const BYTE*)pidl, sizeof(WORD) + pidl->mkid.cb);
return CMruBase::_AddItem(iSlot, pidl, sizeof(WORD) + pidl->mkid.cb);
}
DWORD CMruNode::_DeleteValue(LPCWSTR pszValue)
@ -907,7 +937,7 @@ DWORD CMruNode::_DeleteValue(LPCWSTR pszValue)
HRESULT CMruNode::_FindPidl(LPCITEMIDLIST pidl, UINT *piSlot)
{
return FindData((const BYTE *)pidl, sizeof(WORD) + pidl->mkid.cb, piSlot);
return FindData(pidl, sizeof(WORD) + pidl->mkid.cb, piSlot);
}
HRESULT CMruNode::_GetPidlSlot(LPCITEMIDLIST pidl, BOOL bAdd, UINT *piSlot)
@ -933,11 +963,11 @@ HRESULT CMruNode::_GetPidlSlot(LPCITEMIDLIST pidl, BOOL bAdd, UINT *piSlot)
return hr;
}
HRESULT CMruNode::RemoveLeast(UINT *piSlot)
HRESULT CMruNode::RemoveLeast(UINT *pnNodeSlot)
{
if (!m_cSlots)
{
CMruNode::GetNodeSlot(piSlot);
GetNodeSlot(pnNodeSlot);
return S_FALSE;
}
@ -947,10 +977,10 @@ HRESULT CMruNode::RemoveLeast(UINT *piSlot)
return hr;
CMruNode *pNode;
hr = CMruNode::_CreateNode(uSlot, &pNode);
hr = _CreateNode(uSlot, &pNode);
if (SUCCEEDED(hr))
{
hr = pNode->RemoveLeast(piSlot);
hr = pNode->RemoveLeast(pnNodeSlot);
pNode->Release();
}
@ -965,42 +995,24 @@ HRESULT CMruNode::RemoveLeast(UINT *piSlot)
}
class CMruPidlList
: public IMruPidlList
, public CMruNode
: public CMruNode
, public IMruPidlList
{
protected:
LPBYTE m_pbSlots = NULL; // The data
DWORD m_cbSlots = 0; // The data size
LPBYTE m_pbNodeSlots = NULL; // The node slots (contains SLOT_... flags)
DWORD m_cMaxNodeSlots = 0; // The upper bound of the node slot index
HANDLE m_hMutex = NULL; // The mutex (for sync)
BOOL _LoadNodeSlots()
{
DWORD cbSlots = m_cbSlots;
if (SHGetValueW(m_hKey, NULL, L"NodeSlots", NULL, m_pbSlots, &cbSlots) != ERROR_SUCCESS)
return FALSE;
m_cbSlots = cbSlots;
return TRUE;
}
void _SaveNodeSlots()
{
SHSetValueW(m_hKey, NULL, L"NodeSlots", REG_BINARY, m_pbSlots, m_cbSlots);
}
BOOL _LoadNodeSlots();
void _SaveNodeSlots();
HRESULT _InitNodeSlots();
public:
CMruPidlList()
{
}
CMruPidlList() { }
~CMruPidlList() override;
virtual ~CMruPidlList()
{
m_pbSlots = (LPBYTE)::LocalFree(m_pbSlots);
if (m_hMutex)
{
::CloseHandle(m_hMutex);
m_hMutex = NULL;
}
}
HRESULT GetEmptySlot(UINT *pnNodeSlot);
void EmptyNodeSlot(UINT nNodeSlot);
// IUnknown methods
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj) override;
@ -1014,18 +1026,26 @@ public:
}
// IMruPidlList methods
STDMETHODIMP InitList(UINT cMRUSize, HKEY hKey, LPCWSTR pszName) override;
STDMETHODIMP UsePidl(LPCITEMIDLIST pidl, UINT *puSlots) override;
STDMETHODIMP InitList(UINT cMRUSize, HKEY hKey, LPCWSTR pszSubKey) override;
STDMETHODIMP UsePidl(LPCITEMIDLIST pidl, UINT *pnNodeSlot) override;
STDMETHODIMP QueryPidl(
LPCITEMIDLIST pidl,
UINT cSlots,
UINT *puSlots,
UINT *pcSlots) override;
UINT *pnNodeSlots,
UINT *pcNodeSlots) override;
STDMETHODIMP PruneKids(LPCITEMIDLIST pidl) override;
void EmptyNodeSlot(UINT iSlot);
};
CMruPidlList::~CMruPidlList()
{
m_pbNodeSlots = (LPBYTE)::LocalFree(m_pbNodeSlots);
if (m_hMutex)
{
::CloseHandle(m_hMutex);
m_hMutex = NULL;
}
}
STDMETHODIMP CMruPidlList::QueryInterface(REFIID riid, void **ppvObj)
{
if (!ppvObj)
@ -1042,32 +1062,182 @@ STDMETHODIMP CMruPidlList::QueryInterface(REFIID riid, void **ppvObj)
return E_NOINTERFACE;
}
STDMETHODIMP CMruPidlList::InitList(UINT cMRUSize, HKEY hKey, LPCWSTR pszName)
BOOL CMruPidlList::_LoadNodeSlots()
{
FIXME("Stub\n");
return E_NOTIMPL;
DWORD cbNodeSlots = m_cSlotRooms * sizeof(BYTE);
if (SHGetValueW(m_hKey, NULL, L"NodeSlots", NULL, m_pbNodeSlots, &cbNodeSlots) != ERROR_SUCCESS)
return FALSE;
m_cMaxNodeSlots = m_cSlotRooms;
return TRUE;
}
STDMETHODIMP CMruPidlList::UsePidl(LPCITEMIDLIST pidl, UINT *puSlots)
void CMruPidlList::_SaveNodeSlots()
{
FIXME("Stub\n");
return E_NOTIMPL;
DWORD cbNodeSlots = m_cSlotRooms * sizeof(BYTE);
SHSetValueW(m_hKey, NULL, L"NodeSlots", REG_BINARY, m_pbNodeSlots, cbNodeSlots);
}
HRESULT CMruPidlList::_InitNodeSlots()
{
m_pbNodeSlots = (BYTE*)LocalAlloc(LPTR, m_cSlotRooms * sizeof(BYTE));
if (!m_pbNodeSlots)
return E_OUTOFMEMORY;
_LoadNodeSlots();
m_bNeedSave = TRUE;
_SaveNodeSlots();
return S_OK;
}
HRESULT CMruPidlList::GetEmptySlot(UINT *pnNodeSlot)
{
*pnNodeSlot = 0;
if (!_LoadNodeSlots())
return E_FAIL;
if (m_cMaxNodeSlots < m_cSlotRooms)
{
m_pbNodeSlots[m_cMaxNodeSlots] = SLOT_SET;
*pnNodeSlot = ++m_cMaxNodeSlots;
_SaveNodeSlots();
return S_OK;
}
for (UINT iNodeSlot = 0; iNodeSlot < m_cMaxNodeSlots; ++iNodeSlot)
{
if (m_pbNodeSlots[iNodeSlot] & SLOT_SET)
continue;
m_pbNodeSlots[iNodeSlot] = SLOT_SET;
*pnNodeSlot = iNodeSlot + 1; // nNodeSlot is 1-base
_SaveNodeSlots();
return S_OK;
}
HRESULT hr = E_FAIL;
if (SUCCEEDED(RemoveLeast(pnNodeSlot)) && *pnNodeSlot)
hr = S_OK;
_SaveNodeSlots();
return hr;
}
STDMETHODIMP CMruPidlList::InitList(UINT cMRUSize, HKEY hKey, LPCWSTR pszSubKey)
{
HRESULT hr = InitData(cMRUSize, 0, hKey, pszSubKey, NULL);
if (FAILED(hr))
return hr;
hr = _InitNodeSlots();
if (FAILED(hr))
return hr;
m_hMutex = ::CreateMutexW(NULL, FALSE, L"Shell.CMruPidlList");
if (!m_hMutex)
hr = HRESULT_FROM_WIN32(GetLastError());
return hr;
}
STDMETHODIMP CMruPidlList::UsePidl(LPCITEMIDLIST pidl, UINT *pnNodeSlot)
{
CSafeMutex mutex;
HRESULT hr = mutex.Enter(m_hMutex);
if (FAILED(hr))
return hr;
*pnNodeSlot = 0;
CMruNode *pNode;
hr = GetNode(TRUE, pidl, &pNode);
if (FAILED(hr))
return hr;
hr = pNode->GetNodeSlot(pnNodeSlot);
if (FAILED(hr))
{
hr = GetEmptySlot(pnNodeSlot);
if (SUCCEEDED(hr))
{
hr = pNode->SetNodeSlot(*pnNodeSlot);
}
}
pNode->Release();
return hr;
}
STDMETHODIMP CMruPidlList::QueryPidl(
LPCITEMIDLIST pidl,
UINT cSlots,
UINT *puSlots,
UINT *pcSlots)
UINT *pnNodeSlots,
UINT *pcNodeSlots)
{
FIXME("Stub\n");
return E_NOTIMPL;
CSafeMutex mutex;
HRESULT hr = mutex.Enter(m_hMutex);
if (FAILED(hr))
return hr;
*pcNodeSlots = 0;
CMruNode *pNode;
hr = GetNode(FALSE, pidl, &pNode);
if (FAILED(hr))
return hr;
while (pNode && *pcNodeSlots < cSlots)
{
CMruNode *pParent = pNode->GetParent();
if (SUCCEEDED(pNode->GetNodeSlot(&pnNodeSlots[*pcNodeSlots])))
++(*pcNodeSlots);
else if (hr == S_OK && !*pcNodeSlots)
hr = S_FALSE;
pNode->Release();
pNode = pParent;
}
if (pNode)
pNode->Release();
if (SUCCEEDED(hr) && !*pcNodeSlots)
hr = E_FAIL;
return hr;
}
STDMETHODIMP CMruPidlList::PruneKids(LPCITEMIDLIST pidl)
{
FIXME("Stub\n");
return E_NOTIMPL;
CSafeMutex mutex;
HRESULT hr = mutex.Enter(m_hMutex);
if (FAILED(hr))
return hr;
if (!_LoadNodeSlots())
return hr;
CMruNode *pNode;
hr = GetNode(FALSE, pidl, &pNode);
if (FAILED(hr))
return hr;
if (hr == S_OK)
hr = pNode->Clear(this);
else
hr = E_FAIL;
pNode->Release();
_SaveNodeSlots();
return hr;
}
void CMruPidlList::EmptyNodeSlot(UINT nNodeSlot)
{
m_pbNodeSlots[nNodeSlot - 1] = 0; // nNodeSlot is 1-base
m_bNeedSave = TRUE;
}
EXTERN_C HRESULT CMruPidlList_CreateInstance(DWORD_PTR dwUnused1, void **ppv, DWORD_PTR dwUnused3)
@ -1091,29 +1261,27 @@ EXTERN_C HRESULT CMruPidlList_CreateInstance(DWORD_PTR dwUnused1, void **ppv, DW
return S_OK;
}
void CMruPidlList::EmptyNodeSlot(UINT iSlot)
{
m_pbSlots[iSlot - 1] = 0;
m_bNeedSave = TRUE;
}
HRESULT CMruNode::Clear(CMruPidlList *pList)
{
UINT uSlot;
UINT uSlot, nNodeSlot;
HRESULT hr;
while (SUCCEEDED(_GetSlot(0, &uSlot)))
{
CMruNode *pNode;
if (SUCCEEDED(CMruNode::_CreateNode(uSlot, &pNode)))
hr = _CreateNode(uSlot, &pNode);
if (SUCCEEDED(hr))
{
UINT iSlot;
if (SUCCEEDED(pNode->GetNodeSlot(&iSlot)))
pList->EmptyNodeSlot(iSlot);
hr = pNode->GetNodeSlot(&nNodeSlot);
if (SUCCEEDED(hr))
pList->EmptyNodeSlot(nNodeSlot);
pNode->Clear(pList);
pNode->Release();
}
Delete(0);
}
return S_OK;
}

View file

@ -61,6 +61,7 @@ static inline void SHDOCVW_UnlockModule(void) { InterlockedDecrement( &SHDOCVW_r
#ifdef __REACTOS__
EXTERN_C HRESULT CMruLongList_CreateInstance(DWORD_PTR dwUnused1, void **ppv, DWORD_PTR dwUnused3);
EXTERN_C HRESULT CMruPidlList_CreateInstance(DWORD_PTR dwUnused1, void **ppv, DWORD_PTR dwUnused3);
EXTERN_C HRESULT CMruClassFactory_CreateInstance(REFIID riid, void **ppv);
#endif

View file

@ -105,6 +105,10 @@ HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void **ppv)
{
return CMruLongList_CreateInstance(0, ppv, 0);
}
else if (IsEqualGUID(riid, &IID_IMruPidlList))
{
return CMruPidlList_CreateInstance(0, ppv, 0);
}
#endif
/* As a last resort, figure if the CLSID belongs to a 'Shell Instance Object' */

View file

@ -699,9 +699,9 @@ DECLARE_INTERFACE_(IMruDataList, IUnknown)
STDMETHOD_(ULONG,Release)(THIS) PURE;
/*** IMruDataList ***/
STDMETHOD(InitData)(THIS_ UINT, UINT, HKEY, LPCWSTR, SLOTCOMPARE) PURE;
STDMETHOD(AddData)(THIS_ const BYTE *, DWORD, UINT*) PURE;
STDMETHOD(FindData)(THIS_ const BYTE*, DWORD, UINT*) PURE;
STDMETHOD(GetData)(THIS_ UINT, BYTE*, DWORD) PURE;
STDMETHOD(AddData)(THIS_ LPCVOID , DWORD, UINT*) PURE;
STDMETHOD(FindData)(THIS_ LPCVOID , DWORD, UINT*) PURE;
STDMETHOD(GetData)(THIS_ UINT, LPVOID, DWORD) PURE;
STDMETHOD(QueryInfo)(THIS_ UINT, UINT*, DWORD*) PURE;
STDMETHOD(Delete)(THIS_ UINT) PURE;
};