From 1961d708e750b000f966b0be624dfc07cc314e8e Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Tue, 12 Sep 2023 06:01:09 +0900 Subject: [PATCH] [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 + .../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(-) create mode 100644 modules/rostests/apitests/shdocvw/CMakeLists.txt create mode 100644 modules/rostests/apitests/shdocvw/MRUList.cpp create mode 100644 modules/rostests/apitests/shdocvw/testlist.c diff --git a/dll/win32/shdocvw/mrulist.cpp b/dll/win32/shdocvw/mrulist.cpp index b140779cb9cee..33e91aa0936de 100644 --- a/dll/win32/shdocvw/mrulist.cpp +++ b/dll/win32/shdocvw/mrulist.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include "shdocvw.h" #include @@ -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 @@ class CMruBase HRESULT _GetSlotItem(UINT iSlot, SLOTITEMDATA **ppItem); void _CheckUsedSlots(); + HRESULT _UseEmptySlot(UINT *piSlot); public: - CMruBase() - { - } + CMruBase(); virtual ~CMruBase(); // IUnknown methods @@ -103,7 +103,7 @@ class CMruBase 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 @@ class CMruBase } }; +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(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 @@ class CMruLongList 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 @@ class CMruLongList 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(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(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(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 1d8f807d8de71..d58ee69384fec 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 0ca9d5827a544..8cab06cce7772 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 +#include /* 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 160c7e8c6c5c4..9ad6c4ecd37e6 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 407b595596467..ebc0bdfd4b59d 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 0000000000000..72fcc69af22a9 --- /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 0000000000000..73c110dc68cf7 --- /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 + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 0000000000000..1a34744c0df4c --- /dev/null +++ b/modules/rostests/apitests/shdocvw/testlist.c @@ -0,0 +1,10 @@ +#define STANDALONE +#include + +extern void func_MRUList(void); + +const struct test winetest_testlist[] = +{ + { "MRUList", func_MRUList }, + { 0, 0 } +};