[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
This commit is contained in:
Katayama Hirofumi MZ 2023-09-12 06:01:09 +09:00 committed by GitHub
parent 2a16fc5e19
commit 1961d708e7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 675 additions and 28 deletions

View file

@ -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;
}

View file

@ -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 */

View file

@ -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:

View file

@ -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'
{

View file

@ -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)

View file

@ -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)

View file

@ -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@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();
}

View file

@ -0,0 +1,10 @@
#define STANDALONE
#include <apitest.h>
extern void func_MRUList(void);
const struct test winetest_testlist[] =
{
{ "MRUList", func_MRUList },
{ 0, 0 }
};