https://git.reactos.org/?p=reactos.git;a=commitdiff;h=1961d708e750b000f966b…
commit 1961d708e750b000f966b0be624dfc07cc314e8e
Author: Katayama Hirofumi MZ <katayama.hirofumi.mz(a)gmail.com>
AuthorDate: Tue Sep 12 06:01:09 2023 +0900
Commit: GitHub <noreply(a)github.com>
CommitDate: Tue Sep 12 06:01:09 2023 +0900
[SHDOCVW][SHDOCVW_APITEST] Implement MRU List for Shell Bag, Part 3 (#5646)
Follow-up to #5634.
- Implement CMruBase::_UseEmptySlot.
- Implement CMruLongList and CMruShortList.
- Add CMruClassFactory class and modify
DllGetClassObject function by using it.
- Add shdocvw_apitest.exe.
CORE-9283
---
dll/win32/shdocvw/mrulist.cpp | 386 +++++++++++++++++++++--
dll/win32/shdocvw/shdocvw.h | 13 +-
dll/win32/shdocvw/shdocvw_main.c | 29 +-
dll/win32/shdocvw/shdocvw_v1.rgs | 4 +
modules/rostests/apitests/CMakeLists.txt | 1 +
modules/rostests/apitests/shdocvw/CMakeLists.txt | 10 +
modules/rostests/apitests/shdocvw/MRUList.cpp | 250 +++++++++++++++
modules/rostests/apitests/shdocvw/testlist.c | 10 +
8 files changed, 675 insertions(+), 28 deletions(-)
diff --git a/dll/win32/shdocvw/mrulist.cpp b/dll/win32/shdocvw/mrulist.cpp
index b140779cb9c..33e91aa0936 100644
--- a/dll/win32/shdocvw/mrulist.cpp
+++ b/dll/win32/shdocvw/mrulist.cpp
@@ -17,6 +17,7 @@
#include <shlguid_undoc.h>
#include <shlwapi.h>
#include <shlwapi_undoc.h>
+#include <strsafe.h>
#include "shdocvw.h"
#include <wine/debug.h>
@@ -40,7 +41,7 @@ BOOL IEILIsEqual(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, BOOL
bUnknown)
// The flags for SLOTITEMDATA.dwFlags
#define SLOT_LOADED 0x1
-#define SLOT_UNKNOWN_FLAG 0x2
+#define SLOT_SET 0x2
// The flags for CMruBase.m_dwFlags
#define COMPARE_BY_MEMCMP 0x0
@@ -55,7 +56,7 @@ class CMruBase
protected:
LONG m_cRefs = 1; // Reference count
DWORD m_dwFlags = 0; // The COMPARE_BY_... flags
- BOOL m_bFlag1 = FALSE; // ???
+ BOOL m_bNeedSave = FALSE; // The flag that indicates whether it
needs saving
BOOL m_bChecked = FALSE; // The checked flag
HKEY m_hKey = NULL; // A registry key
DWORD m_cSlotRooms = 0; // Rooms for slots
@@ -70,11 +71,10 @@ protected:
HRESULT _GetSlotItem(UINT iSlot, SLOTITEMDATA **ppItem);
void _CheckUsedSlots();
+ HRESULT _UseEmptySlot(UINT *piSlot);
public:
- CMruBase()
- {
- }
+ CMruBase();
virtual ~CMruBase();
// IUnknown methods
@@ -103,7 +103,7 @@ public:
virtual UINT _UpdateSlots(UINT iSlot) = 0;
virtual void _SlotString(DWORD dwSlot, LPWSTR psz, DWORD cch) = 0;
virtual HRESULT _GetSlot(UINT iSlot, UINT *puSlot) = 0;
- virtual HRESULT _RemoveSlot(UINT iSlot, UINT *uSlot) = 0;
+ virtual HRESULT _RemoveSlot(UINT iSlot, UINT *puSlot) = 0;
static void* operator new(size_t size)
{
@@ -115,6 +115,11 @@ public:
}
};
+CMruBase::CMruBase()
+{
+ ::InterlockedIncrement(&SHDOCVW_refCount);
+}
+
CMruBase::~CMruBase()
{
if (m_hKey)
@@ -133,13 +138,15 @@ CMruBase::~CMruBase()
::LocalFree(m_pSlots);
m_pSlots = NULL;
}
+
+ ::InterlockedDecrement(&SHDOCVW_refCount);
}
STDMETHODIMP CMruBase::QueryInterface(REFIID riid, void **ppvObj)
{
if (!ppvObj)
return E_POINTER;
- if (IsEqualGUID(riid, IID_IMruDataList))
+ if (IsEqualGUID(riid, IID_IMruDataList) || IsEqualGUID(riid, IID_IUnknown))
{
*ppvObj = static_cast<IMruDataList*>(this);
AddRef();
@@ -247,7 +254,7 @@ HRESULT CMruBase::_AddItem(UINT iSlot, const BYTE *pbData, DWORD
cbData)
return E_FAIL;
pItem->cbData = cbData;
- pItem->dwFlags = (SLOT_LOADED | SLOT_UNKNOWN_FLAG);
+ pItem->dwFlags = (SLOT_LOADED | SLOT_SET);
CopyMemory(pItem->pvData, pbData, cbData);
return S_OK;
}
@@ -397,6 +404,147 @@ HRESULT CMruBase::_DeleteValue(LPCWSTR pszValue)
return SHDeleteValueW(m_hKey, NULL, pszValue);
}
+HRESULT CMruBase::_UseEmptySlot(UINT *piSlot)
+{
+ if (!m_bChecked)
+ _CheckUsedSlots();
+
+ if (!m_cSlotRooms)
+ return E_FAIL;
+
+ UINT iSlot = 0;
+ for (SLOTITEMDATA *pItem = m_pSlots; (pItem->dwFlags & SLOT_SET); ++pItem)
+ {
+ if (++iSlot >= m_cSlotRooms)
+ return E_FAIL;
+ }
+
+ m_pSlots[iSlot].dwFlags |= SLOT_SET;
+ *piSlot = iSlot;
+ ++m_cSlots;
+
+ return S_OK;
+}
+
+class CMruShortList
+ : public CMruBase
+{
+protected:
+ LPWSTR m_pszSlotData = NULL;
+
+ HRESULT _InitSlots() override;
+ void _SaveSlots() override;
+ UINT _UpdateSlots(UINT iSlot) override;
+ void _SlotString(DWORD dwSlot, LPWSTR psz, DWORD cch) override;
+ HRESULT _GetSlot(UINT iSlot, UINT *puSlot) override;
+ HRESULT _RemoveSlot(UINT iSlot, UINT *puSlot) override;
+ friend class CMruLongList;
+
+public:
+ CMruShortList()
+ {
+ }
+
+ ~CMruShortList() override
+ {
+ m_pszSlotData = (LPWSTR)::LocalFree(m_pszSlotData);
+ }
+};
+
+HRESULT CMruShortList::_InitSlots()
+{
+ DWORD cbData = (m_cSlotRooms + 1) * sizeof(WCHAR);
+ m_pszSlotData = (LPWSTR)LocalAlloc(LPTR, cbData);
+ if (!m_pszSlotData)
+ return E_OUTOFMEMORY;
+
+ if (SHGetValueW(m_hKey, NULL, L"MRUList", NULL, m_pszSlotData, &cbData)
== ERROR_SUCCESS)
+ m_cSlots = (cbData / sizeof(WCHAR)) - 1;
+
+ m_pszSlotData[m_cSlots] = UNICODE_NULL;
+ return S_OK;
+}
+
+void CMruShortList::_SaveSlots()
+{
+ if (m_bNeedSave)
+ {
+ DWORD cbData = (m_cSlots + 1) * sizeof(WCHAR);
+ SHSetValueW(m_hKey, NULL, L"MRUList", REG_SZ, m_pszSlotData, cbData);
+ m_bNeedSave = FALSE;
+ }
+}
+
+// NOTE: MRUList uses lowercase alphabet for history of most recently used items.
+UINT CMruShortList::_UpdateSlots(UINT iSlot)
+{
+ UINT iData, cDataToMove = iSlot;
+
+ if (iSlot == m_cSlots)
+ {
+ if (SUCCEEDED(_UseEmptySlot(&iData)))
+ {
+ ++cDataToMove;
+ }
+ else
+ {
+ // This code is getting the item index from a lowercase letter.
+ iData = m_pszSlotData[m_cSlots - 1] - L'a';
+ --cDataToMove;
+ }
+ }
+ else
+ {
+ iData = m_pszSlotData[iSlot] - L'a';
+ }
+
+ if (cDataToMove)
+ {
+ MoveMemory(m_pszSlotData + 1, m_pszSlotData, cDataToMove * sizeof(WCHAR));
+ m_pszSlotData[0] = (WCHAR)(L'a' + iData);
+ m_bNeedSave = TRUE;
+ }
+
+ return iData;
+}
+
+void CMruShortList::_SlotString(DWORD dwSlot, LPWSTR psz, DWORD cch)
+{
+ if (cch >= 2)
+ {
+ psz[0] = (WCHAR)(L'a' + dwSlot);
+ psz[1] = UNICODE_NULL;
+ }
+}
+
+HRESULT CMruShortList::_GetSlot(UINT iSlot, UINT *puSlot)
+{
+ if (iSlot >= m_cSlots)
+ return E_FAIL;
+
+ UINT iData = m_pszSlotData[iSlot] - L'a';
+ if (iData >= m_cSlotRooms)
+ return E_FAIL;
+
+ *puSlot = iData;
+ m_pSlots[iData].dwFlags |= SLOT_SET;
+ return S_OK;
+}
+
+HRESULT CMruShortList::_RemoveSlot(UINT iSlot, UINT *puSlot)
+{
+ HRESULT hr = _GetSlot(iSlot, puSlot);
+ if (FAILED(hr))
+ return hr;
+
+ MoveMemory(&m_pszSlotData[iSlot], &m_pszSlotData[iSlot + 1], (m_cSlots -
iSlot) * sizeof(WCHAR));
+ --m_cSlots;
+ m_pSlots->dwFlags &= ~SLOT_SET;
+ m_bNeedSave = TRUE;
+
+ return hr;
+}
+
class CMruLongList
: public CMruBase
{
@@ -410,7 +558,7 @@ protected:
UINT _UpdateSlots(UINT iSlot) override;
void _SlotString(DWORD dwSlot, LPWSTR psz, DWORD cch) override;
HRESULT _GetSlot(UINT iSlot, UINT *puSlot) override;
- HRESULT _RemoveSlot(UINT iSlot, UINT *uSlot) override;
+ HRESULT _RemoveSlot(UINT iSlot, UINT *puSlot) override;
public:
CMruLongList()
@@ -429,51 +577,144 @@ public:
HRESULT CMruLongList::_InitSlots()
{
- FIXME("Stub\n");
- return E_NOTIMPL;
+ DWORD cbData = (m_cSlotRooms + 1) * sizeof(UINT);
+ m_puSlotData = (UINT*)LocalAlloc(LPTR, cbData);
+ if (!m_puSlotData)
+ return E_OUTOFMEMORY;
+
+ if (SHGetValueW(m_hKey, NULL, L"MRUListEx", NULL, m_puSlotData,
&cbData) == ERROR_SUCCESS)
+ m_cSlots = (cbData / sizeof(UINT)) - 1;
+ else
+ _ImportShortList();
+
+ m_puSlotData[m_cSlots] = MAXDWORD;
+ return S_OK;
}
void CMruLongList::_SaveSlots()
{
- FIXME("Stub\n");
+ if (m_bNeedSave)
+ {
+ SHSetValueW(m_hKey, NULL, L"MRUListEx", REG_BINARY, m_puSlotData,
+ (m_cSlots + 1) * sizeof(UINT));
+ m_bNeedSave = FALSE;
+ }
}
UINT CMruLongList::_UpdateSlots(UINT iSlot)
{
- FIXME("Stub\n");
- return E_NOTIMPL;
+ UINT cSlotsToMove, uSlotData;
+
+ cSlotsToMove = iSlot;
+ if (iSlot == m_cSlots)
+ {
+ if (SUCCEEDED(_UseEmptySlot(&uSlotData)))
+ {
+ ++cSlotsToMove;
+ }
+ else
+ {
+ uSlotData = m_puSlotData[m_cSlots - 1];
+ --cSlotsToMove;
+ }
+ }
+ else
+ {
+ uSlotData = m_puSlotData[iSlot];
+ }
+
+ if (cSlotsToMove > 0)
+ {
+ MoveMemory(m_puSlotData + 1, m_puSlotData, cSlotsToMove * sizeof(UINT));
+ m_puSlotData[0] = uSlotData;
+ m_bNeedSave = TRUE;
+ }
+
+ return uSlotData;
}
void CMruLongList::_SlotString(DWORD dwSlot, LPWSTR psz, DWORD cch)
{
- FIXME("Stub\n");
+ StringCchPrintfW(psz, cch, L"%d", dwSlot);
}
HRESULT CMruLongList::_GetSlot(UINT iSlot, UINT *puSlot)
{
- FIXME("Stub\n");
- return E_NOTIMPL;
+ if (iSlot >= m_cSlots)
+ return E_FAIL;
+
+ UINT uSlotData = m_puSlotData[iSlot];
+ if (uSlotData >= m_cSlotRooms)
+ return E_FAIL;
+
+ *puSlot = uSlotData;
+ m_pSlots[uSlotData].dwFlags |= SLOT_SET;
+ return S_OK;
}
-HRESULT CMruLongList::_RemoveSlot(UINT iSlot, UINT *uSlot)
+HRESULT CMruLongList::_RemoveSlot(UINT iSlot, UINT *puSlot)
{
- FIXME("Stub\n");
- return E_NOTIMPL;
+ HRESULT hr = _GetSlot(iSlot, puSlot);
+ if (FAILED(hr))
+ return hr;
+
+ MoveMemory(&m_puSlotData[iSlot], &m_puSlotData[iSlot + 1], (m_cSlots - iSlot)
* sizeof(UINT));
+ --m_cSlots;
+ m_pSlots[0].dwFlags &= ~SLOT_SET;
+ m_bNeedSave = TRUE;
+
+ return hr;
}
void CMruLongList::_ImportShortList()
{
- FIXME("Stub\n");
+ CMruShortList *pShortList = new CMruShortList();
+ if (!pShortList)
+ return;
+
+ HRESULT hr = pShortList->InitData(m_cSlotRooms, 0, m_hKey, NULL, NULL);
+ if (SUCCEEDED(hr))
+ {
+ for (;;)
+ {
+ UINT uSlot;
+ hr = pShortList->_GetSlot(m_cSlots, &uSlot);
+ if (FAILED(hr))
+ break;
+
+ SLOTITEMDATA *pItem;
+ hr = pShortList->_GetSlotItem(uSlot, &pItem);
+ if (FAILED(hr))
+ break;
+
+ _AddItem(uSlot, (const BYTE*)pItem->pvData, pItem->cbData);
+ pShortList->_DeleteItem(uSlot);
+
+ m_puSlotData[m_cSlots++] = uSlot;
+ }
+
+ m_bNeedSave = TRUE;
+ }
+
+ SHDeleteValueW(m_hKey, NULL, L"MRUList");
+ pShortList->Release();
}
EXTERN_C HRESULT
-CMruLongList_CreateInstance(DWORD dwUnused1, void **ppv, DWORD dwUnused3)
+CMruLongList_CreateInstance(DWORD_PTR dwUnused1, void **ppv, DWORD_PTR dwUnused3)
{
UNREFERENCED_PARAMETER(dwUnused1);
UNREFERENCED_PARAMETER(dwUnused3);
+ TRACE("%p %p %p\n", dwUnused1, ppv, dwUnused3);
+
+ if (!ppv)
+ return E_POINTER;
+
CMruLongList *pMruList = new CMruLongList();
*ppv = static_cast<IMruDataList*>(pMruList);
+ TRACE("%p\n", *ppv);
+
return S_OK;
}
@@ -626,11 +867,16 @@ STDMETHODIMP CMruPidlList::PruneKids(LPCITEMIDLIST pidl)
return E_NOTIMPL;
}
-EXTERN_C HRESULT CMruPidlList_CreateInstance(DWORD dwUnused1, void **ppv, DWORD
dwUnused3)
+EXTERN_C HRESULT CMruPidlList_CreateInstance(DWORD_PTR dwUnused1, void **ppv, DWORD_PTR
dwUnused3)
{
UNREFERENCED_PARAMETER(dwUnused1);
UNREFERENCED_PARAMETER(dwUnused3);
+ TRACE("%p %p %p\n", dwUnused1, ppv, dwUnused3);
+
+ if (!ppv)
+ return E_POINTER;
+
*ppv = NULL;
CMruPidlList *pMruList = new CMruPidlList();
@@ -638,5 +884,99 @@ EXTERN_C HRESULT CMruPidlList_CreateInstance(DWORD dwUnused1, void
**ppv, DWORD
return E_OUTOFMEMORY;
*ppv = static_cast<IMruPidlList*>(pMruList);
+ TRACE("%p\n", *ppv);
+ return S_OK;
+}
+
+class CMruClassFactory : public IClassFactory
+{
+protected:
+ LONG m_cRefs = 1;
+
+public:
+ CMruClassFactory()
+ {
+ ::InterlockedIncrement(&SHDOCVW_refCount);
+ }
+ virtual ~CMruClassFactory()
+ {
+ ::InterlockedDecrement(&SHDOCVW_refCount);
+ }
+
+ // IUnknown methods
+ STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj) override;
+ STDMETHODIMP_(ULONG) AddRef() override
+ {
+ return ::InterlockedIncrement(&m_cRefs);
+ }
+ STDMETHODIMP_(ULONG) Release()
+ {
+ if (::InterlockedDecrement(&m_cRefs) == 0)
+ {
+ delete this;
+ return 0;
+ }
+ return m_cRefs;
+ }
+
+ // IClassFactory methods
+ STDMETHODIMP CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject);
+ STDMETHODIMP LockServer(BOOL fLock);
+
+ static void* operator new(size_t size)
+ {
+ return ::LocalAlloc(LPTR, size);
+ }
+ static void operator delete(void *ptr)
+ {
+ ::LocalFree(ptr);
+ }
+};
+
+STDMETHODIMP CMruClassFactory::QueryInterface(REFIID riid, void **ppvObj)
+{
+ if (!ppvObj)
+ return E_POINTER;
+ if (IsEqualGUID(riid, IID_IClassFactory) || IsEqualGUID(riid, IID_IUnknown))
+ {
+ *ppvObj = static_cast<IClassFactory*>(this);
+ AddRef();
+ return S_OK;
+ }
+ ERR("%s: E_NOINTERFACE\n", debugstr_guid(&riid));
+ return E_NOINTERFACE;
+}
+
+STDMETHODIMP CMruClassFactory::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void
**ppvObject)
+{
+ if (pUnkOuter)
+ return CLASS_E_NOAGGREGATION;
+
+ if (IsEqualGUID(riid, IID_IMruDataList))
+ return CMruLongList_CreateInstance(0, ppvObject, 0);
+
+ if (IsEqualGUID(riid, IID_IMruPidlList))
+ return CMruPidlList_CreateInstance(0, ppvObject, 0);
+
+ return E_NOINTERFACE;
+}
+
+STDMETHODIMP CMruClassFactory::LockServer(BOOL fLock)
+{
+ if (fLock)
+ ::InterlockedIncrement(&SHDOCVW_refCount);
+ else
+ ::InterlockedDecrement(&SHDOCVW_refCount);
return S_OK;
}
+
+EXTERN_C HRESULT CMruClassFactory_CreateInstance(REFIID riid, void **ppv)
+{
+ CMruClassFactory *pFactory = new CMruClassFactory();
+ if (!pFactory)
+ return E_OUTOFMEMORY;
+
+ HRESULT hr = pFactory->QueryInterface(riid, ppv);
+ pFactory->Release();
+ return hr;
+}
diff --git a/dll/win32/shdocvw/shdocvw.h b/dll/win32/shdocvw/shdocvw.h
index 1d8f807d8de..d58ee69384f 100644
--- a/dll/win32/shdocvw/shdocvw.h
+++ b/dll/win32/shdocvw/shdocvw.h
@@ -46,13 +46,22 @@ extern HRESULT SHDOCVW_GetShellInstanceObjectClassObject(REFCLSID
rclsid,
/**********************************************************************
* Dll lifetime tracking declaration for shdocvw.dll
*/
+#ifdef __REACTOS__
+# ifdef __cplusplus
+EXTERN_C
+# else
+extern
+# endif
+LONG SHDOCVW_refCount;
+#else
extern LONG SHDOCVW_refCount DECLSPEC_HIDDEN;
+#endif
static inline void SHDOCVW_LockModule(void) { InterlockedIncrement( &SHDOCVW_refCount
); }
static inline void SHDOCVW_UnlockModule(void) { InterlockedDecrement(
&SHDOCVW_refCount ); }
#ifdef __REACTOS__
-EXTERN_C HRESULT CMruLongList_CreateInstance(DWORD dwUnused1, void **ppv, DWORD
dwUnused3);
-EXTERN_C HRESULT CMruPidlList_CreateInstance(DWORD dwUnused1, void **ppv, DWORD
dwUnused3);
+EXTERN_C HRESULT CMruLongList_CreateInstance(DWORD_PTR dwUnused1, void **ppv, DWORD_PTR
dwUnused3);
+EXTERN_C HRESULT CMruClassFactory_CreateInstance(REFIID riid, void **ppv);
#endif
#endif /* __WINE_SHDOCVW_H */
diff --git a/dll/win32/shdocvw/shdocvw_main.c b/dll/win32/shdocvw/shdocvw_main.c
index 0ca9d5827a5..8cab06cce77 100644
--- a/dll/win32/shdocvw/shdocvw_main.c
+++ b/dll/win32/shdocvw/shdocvw_main.c
@@ -31,6 +31,7 @@
#ifdef __REACTOS__
#include "winnls.h"
#include <shlguid_undoc.h>
+#include <rpcproxy.h> /* for __wine_register_resources /
__wine_unregister_resources */
#endif
#include "shlwapi.h"
#include "wininet.h"
@@ -44,6 +45,9 @@ LONG SHDOCVW_refCount = 0;
static HMODULE SHDOCVW_hshell32 = 0;
static HINSTANCE ieframe_instance;
+#ifdef __REACTOS__
+static HINSTANCE instance;
+#endif
static HINSTANCE get_ieframe_instance(void)
{
@@ -89,10 +93,18 @@ HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void
**ppv)
return get_ieframe_object(rclsid, riid, ppv);
#ifdef __REACTOS__
- if (IsEqualGUID(&CLSID_MruLongList, rclsid))
+ if (IsEqualGUID(riid, &IID_IClassFactory) || IsEqualGUID(riid,
&IID_IUnknown))
+ {
+ if (IsEqualGUID(rclsid, &CLSID_MruLongList) ||
+ IsEqualGUID(rclsid, &CLSID_MruPidlList))
+ {
+ return CMruClassFactory_CreateInstance(riid, ppv);
+ }
+ }
+ else if (IsEqualGUID(riid, &IID_IMruDataList))
+ {
return CMruLongList_CreateInstance(0, ppv, 0);
- if (IsEqualGUID(&CLSID_MruPidlList, rclsid))
- return CMruPidlList_CreateInstance(0, ppv, 0);
+ }
#endif
/* As a last resort, figure if the CLSID belongs to a 'Shell Instance Object'
*/
@@ -105,7 +117,11 @@ HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void
**ppv)
HRESULT WINAPI DllRegisterServer(void)
{
TRACE("\n");
+#ifdef __REACTOS__
+ return __wine_register_resources(instance);
+#else
return S_OK;
+#endif
}
/***********************************************************************
@@ -114,7 +130,11 @@ HRESULT WINAPI DllRegisterServer(void)
HRESULT WINAPI DllUnregisterServer(void)
{
TRACE("\n");
+#ifdef __REACTOS__
+ return __wine_unregister_resources(instance);
+#else
return S_OK;
+#endif
}
/******************************************************************
@@ -155,6 +175,9 @@ BOOL WINAPI DllMain(HINSTANCE hinst, DWORD fdwReason, LPVOID
fImpLoad)
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
+#ifdef __REACTOS__
+ instance = hinst;
+#endif
DisableThreadLibraryCalls(hinst);
break;
case DLL_PROCESS_DETACH:
diff --git a/dll/win32/shdocvw/shdocvw_v1.rgs b/dll/win32/shdocvw/shdocvw_v1.rgs
index 160c7e8c6c5..9ad6c4ecd37 100644
--- a/dll/win32/shdocvw/shdocvw_v1.rgs
+++ b/dll/win32/shdocvw/shdocvw_v1.rgs
@@ -199,6 +199,10 @@ HKCR
Version = s '1.1'
VersionIndependentProgId = s 'SearchAssistantOC.SearchAssistantOC'
}
+ '{53BD6B4E-3780-4693-AFC3-7161C2F3EE9C}' = s 'MruLongList'
+ {
+ InprocServer32 = s '%MODULE%' { val ThreadingModel = s
'Apartment' }
+ }
}
'Shell.Explorer.1' = s 'Microsoft Web Browser Version 1'
{
diff --git a/modules/rostests/apitests/CMakeLists.txt
b/modules/rostests/apitests/CMakeLists.txt
index 407b5955964..ebc0bdfd4b5 100644
--- a/modules/rostests/apitests/CMakeLists.txt
+++ b/modules/rostests/apitests/CMakeLists.txt
@@ -43,6 +43,7 @@ add_subdirectory(powrprof)
add_subdirectory(sdk)
add_subdirectory(setupapi)
add_subdirectory(sfc)
+add_subdirectory(shdocvw)
add_subdirectory(shell32)
add_subdirectory(shlwapi)
add_subdirectory(spoolss)
diff --git a/modules/rostests/apitests/shdocvw/CMakeLists.txt
b/modules/rostests/apitests/shdocvw/CMakeLists.txt
new file mode 100644
index 00000000000..72fcc69af22
--- /dev/null
+++ b/modules/rostests/apitests/shdocvw/CMakeLists.txt
@@ -0,0 +1,10 @@
+
+list(APPEND SOURCE
+ MRUList.cpp
+ testlist.c)
+
+add_executable(shdocvw_apitest ${SOURCE})
+set_module_type(shdocvw_apitest win32cui)
+target_link_libraries(shdocvw_apitest ${PSEH_LIB} uuid)
+add_importlibs(shdocvw_apitest shlwapi oleaut32 ole32 user32 advapi32 msvcrt kernel32)
+add_rostests_file(TARGET shdocvw_apitest)
diff --git a/modules/rostests/apitests/shdocvw/MRUList.cpp
b/modules/rostests/apitests/shdocvw/MRUList.cpp
new file mode 100644
index 00000000000..73c110dc68c
--- /dev/null
+++ b/modules/rostests/apitests/shdocvw/MRUList.cpp
@@ -0,0 +1,250 @@
+/*
+ * PROJECT: ReactOS api tests
+ * LICENSE: LGPL-2.1-or-later (
https://spdx.org/licenses/LGPL-2.1-or-later)
+ * PURPOSE: Tests for MRU List
+ * COPYRIGHT: Copyright 2023 Katayama Hirofumi MZ
<katayama.hirofumi.mz(a)gmail.com>
+ */
+
+#include <apitest.h>
+#include <winreg.h>
+#include <shlwapi.h>
+#include <shlobj.h>
+#include <shlobj_undoc.h>
+#include <shlguid_undoc.h>
+#include <stdio.h>
+#include <shlwapi_undoc.h>
+#include <versionhelpers.h>
+#include <strsafe.h>
+#include <wine/test.h>
+#include <pseh/pseh2.h>
+
+#define SUBKEY0 L"Software\\MRUListTest"
+#define TEXT0 L"This is a test."
+#define TEXT1 L"ReactOS rocks!"
+
+static void MRUList_List0(void)
+{
+ HRESULT hr;
+ IMruDataList *pList = NULL;
+ UINT iSlot1, iSlot2, iSlot3;
+ DWORD cbText;
+ WCHAR szText[MAX_PATH];
+
+ hr = CoCreateInstance(CLSID_MruLongList, NULL, CLSCTX_INPROC_SERVER,
+ IID_IMruDataList, (LPVOID*)&pList);
+ ok_hex(hr, S_OK);
+ if (pList == NULL)
+ {
+ skip("pList was NULL\n");
+ return;
+ }
+
+ hr = pList->InitData(26, 0, HKEY_CURRENT_USER, SUBKEY0, NULL);
+ ok_hex(hr, S_OK);
+
+ cbText = (wcslen(TEXT0) + 1) * sizeof(WCHAR);
+ hr = pList->AddData((BYTE*)TEXT0, cbText, &iSlot1);
+ ok_hex(hr, S_OK);
+ ok_int(iSlot1, 0);
+
+ hr = pList->FindData((BYTE*)TEXT0, cbText, &iSlot2);
+ ok_hex(hr, S_OK);
+ ok_int(iSlot1, iSlot2);
+
+ cbText = sizeof(szText);
+ hr = pList->GetData(iSlot1, (BYTE*)szText, cbText);
+ ok_hex(hr, S_OK);
+ ok_wstr(szText, TEXT0);
+
+ cbText = (wcslen(TEXT1) + 1) * sizeof(WCHAR);
+ hr = pList->AddData((BYTE*)TEXT1, cbText, &iSlot3);
+ ok_hex(hr, S_OK);
+ ok_int(iSlot3, 1);
+
+ pList->Release();
+}
+
+static void MRUList_List0_Check(void)
+{
+ BYTE abData[512];
+ DWORD cbData, dwType;
+
+ cbData = sizeof(abData);
+ LONG error = SHGetValueW(HKEY_CURRENT_USER, SUBKEY0, L"MRUListEx",
&dwType, abData, &cbData);
+ ok_long(error, ERROR_SUCCESS);
+ ok_long(dwType, REG_BINARY);
+#if 1
+ ok_int(memcmp(abData, "\x01\x00\x00\x00\x00\x00\x00\x00\xFF\xFF\xFF\xFF",
12), 0);
+#else
+ for (DWORD i = 0; i < cbData; ++i)
+ {
+ printf("%02X ", abData[i]);
+ }
+ printf("\n");
+#endif
+}
+
+static void MRUList_List1(void)
+{
+ HRESULT hr;
+ IMruDataList *pList = NULL;
+ UINT iSlot;
+
+ hr = CoCreateInstance(CLSID_MruLongList, NULL, CLSCTX_INPROC_SERVER,
+ IID_IMruDataList, (LPVOID*)&pList);
+ ok_hex(hr, S_OK);
+ if (pList == NULL)
+ {
+ skip("pList was NULL\n");
+ return;
+ }
+
+ hr = pList->InitData(26, 0, HKEY_CURRENT_USER, SUBKEY0, NULL);
+ ok_hex(hr, S_OK);
+
+ DWORD cbText = (wcslen(TEXT0) + 1) * sizeof(WCHAR);
+ hr = pList->FindData((BYTE*)TEXT0, cbText, &iSlot);
+ ok_hex(hr, S_OK);
+ ok_int(iSlot, 1);
+
+ hr = pList->Delete(iSlot);
+ ok_hex(hr, S_OK);
+
+ iSlot = 0xCAFE;
+ cbText = (wcslen(TEXT0) + 1) * sizeof(WCHAR);
+ hr = pList->FindData((BYTE*)TEXT0, cbText, &iSlot);
+ ok_hex(hr, E_FAIL);
+ ok_int(iSlot, 0xCAFE);
+
+ pList->Release();
+}
+
+static void MRUList_List1_Check(void)
+{
+ BYTE abData[512];
+ DWORD cbData, dwType;
+
+ cbData = sizeof(abData);
+ LONG error = SHGetValueW(HKEY_CURRENT_USER, SUBKEY0, L"MRUListEx",
&dwType, abData, &cbData);
+ ok_long(error, ERROR_SUCCESS);
+ ok_long(dwType, REG_BINARY);
+#if 1
+ ok_int(memcmp(abData, "\x01\x00\x00\x00\xFF\xFF\xFF\xFF", 8), 0);
+#else
+ for (DWORD i = 0; i < cbData; ++i)
+ {
+ printf("%02X ", abData[i]);
+ }
+ printf("\n");
+#endif
+}
+
+static void MRUList_List2(void)
+{
+ HRESULT hr;
+ IMruDataList *pList = NULL;
+
+ hr = CoCreateInstance(CLSID_MruLongList, NULL, CLSCTX_INPROC_SERVER,
+ IID_IMruDataList, (LPVOID*)&pList);
+ ok_hex(hr, S_OK);
+ if (pList == NULL)
+ {
+ skip("pList was NULL\n");
+ return;
+ }
+
+ hr = pList->InitData(26, 0, HKEY_CURRENT_USER, SUBKEY0, NULL);
+ ok_hex(hr, S_OK);
+
+ WCHAR szText[MAX_PATH];
+ DWORD cbText = sizeof(szText);
+ StringCchCopyW(szText, _countof(szText), L"====");
+ hr = pList->GetData(0, (BYTE*)szText, cbText);
+ ok_hex(hr, S_OK);
+ ok_wstr(szText, L"ABC");
+
+ StringCchCopyW(szText, _countof(szText), L"====");
+ cbText = sizeof(szText);
+ hr = pList->GetData(1, (BYTE*)szText, cbText);
+ ok_hex(hr, S_OK);
+ ok_wstr(szText, L"XYZ");
+
+ pList->Release();
+}
+
+static void MRUList_List2_Check(void)
+{
+ BYTE abData[512];
+ DWORD cbData, dwType;
+
+ cbData = sizeof(abData);
+ LONG error = SHGetValueW(HKEY_CURRENT_USER, SUBKEY0, L"MRUListEx",
&dwType, abData, &cbData);
+ ok_long(error, ERROR_SUCCESS);
+ ok_long(dwType, REG_BINARY);
+#if 1
+ ok_int(memcmp(abData, "\x00\x00\x00\x00\x01\x00\x00\x00\xFF\xFF\xFF\xFF",
12), 0);
+#else
+ for (DWORD i = 0; i < cbData; ++i)
+ {
+ printf("%02X ", abData[i]);
+ }
+ printf("\n");
+#endif
+}
+
+static void MRUList_List(void)
+{
+ if (IsWindowsVistaOrGreater())
+ {
+ skip("Vista+ doesn't support CLSID_MruLongList\n");
+ return;
+ }
+
+ SHDeleteKeyW(HKEY_CURRENT_USER, SUBKEY0);
+
+ LONG error;
+ error = SHSetValueW(HKEY_CURRENT_USER, SUBKEY0, NULL, REG_SZ, L"",
sizeof(UNICODE_NULL));
+ ok_long(error, ERROR_SUCCESS);
+
+ error = SHGetValueW(HKEY_CURRENT_USER, SUBKEY0, NULL, NULL, NULL, NULL);
+ ok_long(error, ERROR_SUCCESS);
+
+ MRUList_List0();
+ MRUList_List0_Check();
+
+ MRUList_List1();
+ MRUList_List1_Check();
+
+ error = SHDeleteValueW(HKEY_CURRENT_USER, SUBKEY0, L"MRUList");
+ ok_long(error, ERROR_FILE_NOT_FOUND);
+ error = SHDeleteValueW(HKEY_CURRENT_USER, SUBKEY0, L"MRUListEx");
+ ok_long(error, ERROR_SUCCESS);
+
+ error = SHSetValueW(HKEY_CURRENT_USER, SUBKEY0, L"MRUList", REG_SZ,
L"ab", 3 * sizeof(WCHAR));
+ ok_long(error, ERROR_SUCCESS);
+ error = SHSetValueW(HKEY_CURRENT_USER, SUBKEY0, L"a", REG_BINARY,
L"ABC", 4 * sizeof(WCHAR));
+ ok_long(error, ERROR_SUCCESS);
+ error = SHSetValueW(HKEY_CURRENT_USER, SUBKEY0, L"b", REG_BINARY,
L"XYZ", 4 * sizeof(WCHAR));
+ ok_long(error, ERROR_SUCCESS);
+
+ MRUList_List2();
+ MRUList_List2_Check();
+
+ error = SHDeleteValueW(HKEY_CURRENT_USER, SUBKEY0, L"MRUList");
+ ok_long(error, ERROR_FILE_NOT_FOUND);
+ error = SHDeleteValueW(HKEY_CURRENT_USER, SUBKEY0, L"MRUListEx");
+ ok_long(error, ERROR_SUCCESS);
+
+ SHDeleteKeyW(HKEY_CURRENT_USER, SUBKEY0);
+}
+
+START_TEST(MRUList)
+{
+ HRESULT hr = CoInitialize(NULL);
+ ok_hex(hr, S_OK);
+
+ MRUList_List();
+
+ if (SUCCEEDED(hr))
+ CoUninitialize();
+}
diff --git a/modules/rostests/apitests/shdocvw/testlist.c
b/modules/rostests/apitests/shdocvw/testlist.c
new file mode 100644
index 00000000000..1a34744c0df
--- /dev/null
+++ b/modules/rostests/apitests/shdocvw/testlist.c
@@ -0,0 +1,10 @@
+#define STANDALONE
+#include <apitest.h>
+
+extern void func_MRUList(void);
+
+const struct test winetest_testlist[] =
+{
+ { "MRUList", func_MRUList },
+ { 0, 0 }
+};