https://git.reactos.org/?p=reactos.git;a=commitdiff;h=1961d708e750b000f966b0be624dfc07cc314e8e

commit 1961d708e750b000f966b0be624dfc07cc314e8e
Author:     Katayama Hirofumi MZ <katayama.hirofumi...@gmail.com>
AuthorDate: Tue Sep 12 06:01:09 2023 +0900
Commit:     GitHub <nore...@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...@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 }
+};

Reply via email to