https://git.reactos.org/?p=reactos.git;a=commitdiff;h=b4bc6f0a6a1ea3d6e9aed…
commit b4bc6f0a6a1ea3d6e9aed68c28b2b4787dd21fd9
Author: Katayama Hirofumi MZ <katayama.hirofumi.mz(a)gmail.com>
AuthorDate: Sat Aug 5 21:20:58 2023 +0900
Commit: GitHub <noreply(a)github.com>
CommitDate: Sat Aug 5 21:20:58 2023 +0900
[SHLWAPI][SHLWAPI_APITEST][SDK] SHCreatePropertyBagOnMemory (#5511)
- Add propbag.cpp.
- Add CBasePropertyBag / CMemPropertyBag classes.
- Implement SHCreatePropertyBagOnMemory.
- Strengthen SHPropertyBag testcase in shlwapi_apitest.
CORE-9283
---
dll/win32/shlwapi/CMakeLists.txt | 5 +-
dll/win32/shlwapi/precomp.h | 4 +
dll/win32/shlwapi/propbag.cpp | 253 +++++++++++++++++++++
dll/win32/shlwapi/shlwapi.spec | 2 +-
.../rostests/apitests/shlwapi/SHPropertyBag.cpp | 132 +++++++++++
sdk/include/reactos/shlwapi_undoc.h | 2 +
6 files changed, 396 insertions(+), 2 deletions(-)
diff --git a/dll/win32/shlwapi/CMakeLists.txt b/dll/win32/shlwapi/CMakeLists.txt
index fcb03a15c37..87551cd21c5 100644
--- a/dll/win32/shlwapi/CMakeLists.txt
+++ b/dll/win32/shlwapi/CMakeLists.txt
@@ -24,6 +24,7 @@ list(APPEND SOURCE
list(APPEND PCH_SKIP_SOURCE
assoc.c
+ propbag.cpp
wsprintf.c
${CMAKE_CURRENT_BINARY_DIR}/shlwapi_stubs.c)
@@ -36,7 +37,9 @@ add_library(shlwapi MODULE
# our C++ atlbase.h conflicts with the one from wine, so only use wine includes for C
# Unfortunately, we can't use different includes for C & C++ in VS generator, so
use an object library to achieve this
-target_include_directories(shlwapi BEFORE PRIVATE
${REACTOS_SOURCE_DIR}/sdk/include/reactos/wine)
+target_include_directories(shlwapi BEFORE PRIVATE
+ ${REACTOS_SOURCE_DIR}/sdk/include/reactos/wine
+ ${REACTOS_SOURCE_DIR}/sdk/lib/atl)
add_library(shlwapi_autocomp OBJECT autocomp.cpp)
target_link_libraries(shlwapi_autocomp PRIVATE atl_classes)
diff --git a/dll/win32/shlwapi/precomp.h b/dll/win32/shlwapi/precomp.h
index eff450aa861..86f3ea401fd 100644
--- a/dll/win32/shlwapi/precomp.h
+++ b/dll/win32/shlwapi/precomp.h
@@ -28,6 +28,10 @@
#include <wine/debug.h>
#include <wine/unicode.h>
+#ifdef __REACTOS__
+EXTERN_C HRESULT VariantChangeTypeForRead(_Inout_ VARIANTARG *pvarg, _In_ VARTYPE vt);
+#endif
+
#include "resource.h"
#endif /* !_SHLWAPI_PCH_ */
diff --git a/dll/win32/shlwapi/propbag.cpp b/dll/win32/shlwapi/propbag.cpp
new file mode 100644
index 00000000000..27434091fc3
--- /dev/null
+++ b/dll/win32/shlwapi/propbag.cpp
@@ -0,0 +1,253 @@
+/*
+ * PROJECT: ReactOS Shell
+ * LICENSE: LGPL-2.0-or-later (
https://spdx.org/licenses/LGPL-2.0-or-later)
+ * PURPOSE: Implement shell property bags
+ * COPYRIGHT: Copyright 2023 Katayama Hirofumi MZ
<katayama.hirofumi.mz(a)gmail.com>
+ */
+
+#define _ATL_NO_EXCEPTIONS
+#include "precomp.h"
+#include <shlwapi.h>
+#include <shlwapi_undoc.h>
+#include <atlstr.h> // for CStringW
+#include <atlsimpcoll.h> // for CSimpleMap
+#include <atlcomcli.h> // for CComVariant
+
+WINE_DEFAULT_DEBUG_CHANNEL(shell);
+
+class CBasePropertyBag
+ : public IPropertyBag
+#if (_WIN32_WINNT < _WIN32_WINNT_VISTA)
+ , public IPropertyBag2
+#endif
+{
+protected:
+ LONG m_cRefs; // reference count
+ DWORD m_dwMode; // STGM_* flags
+
+public:
+ CBasePropertyBag(DWORD dwMode)
+ : m_cRefs(0)
+ , m_dwMode(dwMode)
+ {
+ }
+
+ virtual ~CBasePropertyBag() { }
+
+ // IUnknown interface
+ STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) override
+ {
+#if (_WIN32_WINNT < _WIN32_WINNT_VISTA)
+ if (::IsEqualGUID(riid, IID_IPropertyBag2))
+ {
+ AddRef();
+ *ppvObject = static_cast<IPropertyBag2*>(this);
+ return S_OK;
+ }
+#endif
+ if (::IsEqualGUID(riid, IID_IUnknown) || ::IsEqualGUID(riid, IID_IPropertyBag))
+ {
+ AddRef();
+ *ppvObject = static_cast<IPropertyBag*>(this);
+ return S_OK;
+ }
+ return E_NOTIMPL;
+ }
+ STDMETHODIMP_(ULONG) AddRef() override
+ {
+ return ::InterlockedIncrement(&m_cRefs);
+ }
+ STDMETHODIMP_(ULONG) Release() override
+ {
+ if (::InterlockedDecrement(&m_cRefs) == 0)
+ {
+ delete this;
+ return 0;
+ }
+ return m_cRefs;
+ }
+
+#if (_WIN32_WINNT < _WIN32_WINNT_VISTA)
+ // IPropertyBag2 interface (stubs)
+ STDMETHODIMP Read(
+ _In_ ULONG cProperties,
+ _In_ PROPBAG2 *pPropBag,
+ _In_opt_ IErrorLog *pErrorLog,
+ _Out_ VARIANT *pvarValue,
+ _Out_ HRESULT *phrError) override
+ {
+ return E_NOTIMPL;
+ }
+ STDMETHODIMP Write(
+ _In_ ULONG cProperties,
+ _In_ PROPBAG2 *pPropBag,
+ _In_ VARIANT *pvarValue)
+ {
+ return E_NOTIMPL;
+ }
+ STDMETHODIMP CountProperties(_Out_ ULONG *pcProperties) override
+ {
+ return E_NOTIMPL;
+ }
+ STDMETHODIMP GetPropertyInfo(
+ _In_ ULONG iProperty,
+ _In_ ULONG cProperties,
+ _Out_ PROPBAG2 *pPropBag,
+ _Out_ ULONG *pcProperties) override
+ {
+ return E_NOTIMPL;
+ }
+ STDMETHODIMP LoadObject(
+ _In_z_ LPCWSTR pstrName,
+ _In_ DWORD dwHint,
+ _In_ IUnknown *pUnkObject,
+ _In_opt_ IErrorLog *pErrorLog) override
+ {
+ return E_NOTIMPL;
+ }
+#endif
+};
+
+struct CPropMapEqual
+{
+ static bool IsEqualKey(const ATL::CStringW& k1, const ATL::CStringW& k2)
+ {
+ return k1.CompareNoCase(k2) == 0;
+ }
+
+ static bool IsEqualValue(const ATL::CComVariant& v1, const ATL::CComVariant&
v2)
+ {
+ return false;
+ }
+};
+
+class CMemPropertyBag : public CBasePropertyBag
+{
+protected:
+ ATL::CSimpleMap<ATL::CStringW, ATL::CComVariant, CPropMapEqual> m_PropMap;
+
+public:
+ CMemPropertyBag(DWORD dwFlags) : CBasePropertyBag(dwFlags) { }
+
+ STDMETHODIMP Read(_In_z_ LPCWSTR pszPropName, _Inout_ VARIANT *pvari,
+ _Inout_opt_ IErrorLog *pErrorLog) override;
+ STDMETHODIMP Write(_In_z_ LPCWSTR pszPropName, _In_ VARIANT *pvari) override;
+};
+
+STDMETHODIMP
+CMemPropertyBag::Read(
+ _In_z_ LPCWSTR pszPropName,
+ _Inout_ VARIANT *pvari,
+ _Inout_opt_ IErrorLog *pErrorLog)
+{
+ UNREFERENCED_PARAMETER(pErrorLog);
+
+ TRACE("%p: %s %p %p\n", this, debugstr_w(pszPropName), pvari, pErrorLog);
+
+ VARTYPE vt = V_VT(pvari);
+
+ ::VariantInit(pvari);
+
+#if (_WIN32_WINNT < _WIN32_WINNT_VISTA)
+ if ((m_dwMode & (STGM_READ | STGM_WRITE | STGM_READWRITE)) == STGM_WRITE)
+ {
+ ERR("%p: 0x%X\n", this, m_dwMode);
+ return E_ACCESSDENIED;
+ }
+#endif
+
+ if (!pszPropName || !pvari)
+ {
+ ERR("%p: %s %p %p\n", this, debugstr_w(pszPropName), pvari,
pErrorLog);
+ return E_INVALIDARG;
+ }
+
+ INT iItem = m_PropMap.FindKey(pszPropName);
+ if (iItem == -1)
+ {
+ ERR("%p: %s %p %p\n", this, debugstr_w(pszPropName), pvari,
pErrorLog);
+ return E_FAIL;
+ }
+
+ HRESULT hr = ::VariantCopy(pvari, &m_PropMap.GetValueAt(iItem));
+ if (FAILED(hr))
+ {
+ ERR("%p: 0x%08X %p\n", this, hr, pvari);
+ return hr;
+ }
+
+ hr = ::VariantChangeTypeForRead(pvari, vt);
+ if (FAILED(hr))
+ {
+ ERR("%p: 0x%08X %p\n", this, hr, pvari);
+ return hr;
+ }
+
+ return hr;
+}
+
+STDMETHODIMP
+CMemPropertyBag::Write(
+ _In_z_ LPCWSTR pszPropName,
+ _In_ VARIANT *pvari)
+{
+ TRACE("%p: %s %p\n", this, debugstr_w(pszPropName), pvari);
+
+#if (_WIN32_WINNT < _WIN32_WINNT_VISTA)
+ if ((m_dwMode & (STGM_READ | STGM_WRITE | STGM_READWRITE)) == STGM_READ)
+ {
+ ERR("%p: 0x%X\n", this, m_dwMode);
+ return E_ACCESSDENIED;
+ }
+#endif
+
+ if (!pszPropName || !pvari)
+ {
+ ERR("%p: %s %p\n", this, debugstr_w(pszPropName), pvari);
+ return E_INVALIDARG;
+ }
+
+ ATL::CComVariant vari;
+ HRESULT hr = vari.Copy(pvari);
+ if (FAILED(hr))
+ {
+ ERR("%p: %s %p: 0x%08X\n", this, debugstr_w(pszPropName), pvari, hr);
+ return hr;
+ }
+
+ if (!m_PropMap.SetAt(pszPropName, vari))
+ {
+ ERR("%p: %s %p\n", this, debugstr_w(pszPropName), pvari);
+ return E_FAIL;
+ }
+
+ return hr;
+}
+
+/**************************************************************************
+ * SHCreatePropertyBagOnMemory (SHLWAPI.477)
+ *
+ * Creates a property bag object on memory.
+ *
+ * @param dwMode Specifies either STGM_READ, STGM_WRITE or STGM_READWRITE. Ignored on
Vista+.
+ * @param riid Specifies either IID_IUnknown, IID_IPropertyBag or IID_IPropertyBag2.
+ * Vista+ rejects IID_IPropertyBag2.
+ * @param ppvObj Receives an IPropertyBag pointer.
+ * @return An HRESULT value. S_OK on success, non-zero on failure.
+ * @see
http://undoc.airesoft.co.uk/shlwapi.dll/SHCreatePropertyBagOnMemory.php
+ */
+EXTERN_C HRESULT WINAPI
+SHCreatePropertyBagOnMemory(_In_ DWORD dwMode, _In_ REFIID riid, _Out_ void **ppvObj)
+{
+ TRACE("0x%08X, %s, %p\n", dwMode, debugstr_guid(&riid), ppvObj);
+
+ *ppvObj = NULL;
+
+ CComPtr<CMemPropertyBag> pMemBag(new CMemPropertyBag(dwMode));
+
+ HRESULT hr = pMemBag->QueryInterface(riid, ppvObj);
+ if (FAILED(hr))
+ ERR("0x%08X %s\n", hr, debugstr_guid(&riid));
+
+ return hr;
+}
diff --git a/dll/win32/shlwapi/shlwapi.spec b/dll/win32/shlwapi/shlwapi.spec
index 8dc01d454dc..0844af03d5d 100644
--- a/dll/win32/shlwapi/shlwapi.spec
+++ b/dll/win32/shlwapi/shlwapi.spec
@@ -474,7 +474,7 @@
474 stub -noname SHSetIniStringUTF7W
475 stdcall -noname GetShellSecurityDescriptor(ptr long)
476 stdcall -noname SHGetObjectCompatFlags(ptr ptr)
-477 stub -noname SHCreatePropertyBagOnMemory
+477 stdcall -noname SHCreatePropertyBagOnMemory(long ptr ptr)
478 stdcall -noname IUnknown_TranslateAcceleratorIO(ptr ptr)
479 stdcall -noname IUnknown_UIActivateIO(ptr long ptr)
480 stdcall -noname UrlCrackW(wstr long long ptr) wininet.InternetCrackUrlW
diff --git a/modules/rostests/apitests/shlwapi/SHPropertyBag.cpp
b/modules/rostests/apitests/shlwapi/SHPropertyBag.cpp
index 1aca2d256c0..2114ceff231 100644
--- a/modules/rostests/apitests/shlwapi/SHPropertyBag.cpp
+++ b/modules/rostests/apitests/shlwapi/SHPropertyBag.cpp
@@ -325,8 +325,140 @@ static void SHPropertyBag_WriteTest(void)
ok_int(s_cWrite, 1);
}
+static void SHPropertyBag_OnMemory(void)
+{
+ HRESULT hr;
+ VARIANT vari;
+
+ IPropertyBag *pPropBag = NULL;
+ hr = SHCreatePropertyBagOnMemory(STGM_READWRITE, IID_IPropertyBag,
(void**)&pPropBag);
+ ok_long(hr, S_OK);
+ if (pPropBag == NULL)
+ {
+ skip("pPropBag was NULL\n");
+ return;
+ }
+
+ VariantInit(&vari);
+ hr = pPropBag->Read(L"InvalidName", &vari, NULL);
+ ok_long(hr, E_FAIL);
+ VariantClear(&vari);
+
+ VariantInit(&vari);
+ V_VT(&vari) = VT_UI4;
+ V_UI4(&vari) = 0xDEADFACE;
+ hr = pPropBag->Write(L"Name1", &vari);
+ ok_long(hr, S_OK);
+ VariantClear(&vari);
+
+ VariantInit(&vari);
+ hr = pPropBag->Read(L"Name1", &vari, NULL);
+ ok_long(hr, S_OK);
+ ok_long(V_VT(&vari), VT_UI4);
+ ok_long(V_UI4(&vari), 0xDEADFACE);
+ VariantClear(&vari);
+
+ pPropBag->Release();
+ pPropBag = NULL;
+
+ hr = SHCreatePropertyBagOnMemory(STGM_READ, IID_IPropertyBag,
(void**)&pPropBag);
+ ok_long(hr, S_OK);
+
+ VariantInit(&vari);
+ V_VT(&vari) = VT_UI4;
+ V_UI4(&vari) = 0xDEADFACE;
+ hr = pPropBag->Write(L"Name1", &vari);
+ ok_long(hr, (IsWindowsVistaOrGreater() ? S_OK : E_ACCESSDENIED));
+ VariantClear(&vari);
+
+ VariantInit(&vari);
+ V_VT(&vari) = VT_UI4;
+ V_UI4(&vari) = 0xFEEDF00D;
+ hr = pPropBag->Read(L"Name1", &vari, NULL);
+ if (IsWindowsVistaOrGreater())
+ {
+ ok_long(hr, S_OK);
+ ok_int(V_VT(&vari), VT_UI4);
+ ok_long(V_UI4(&vari), 0xDEADFACE);
+ }
+ else
+ {
+ ok_long(hr, E_FAIL);
+ ok_int(V_VT(&vari), VT_EMPTY);
+ ok_long(V_UI4(&vari), 0xFEEDF00D);
+ }
+ VariantClear(&vari);
+
+ pPropBag->Release();
+ pPropBag = NULL;
+
+ hr = SHCreatePropertyBagOnMemory(STGM_WRITE, IID_IPropertyBag,
(void**)&pPropBag);
+ ok_long(hr, S_OK);
+
+ VariantInit(&vari);
+ V_VT(&vari) = VT_UI4;
+ V_UI4(&vari) = 0xDEADFACE;
+ hr = pPropBag->Write(L"Name1", &vari);
+ ok_long(hr, S_OK);
+ VariantClear(&vari);
+
+ VariantInit(&vari);
+ V_VT(&vari) = VT_UI4;
+ V_UI4(&vari) = 0xFEEDF00D;
+ hr = pPropBag->Read(L"Name1", &vari, NULL);
+ if (IsWindowsVistaOrGreater())
+ {
+ ok_long(hr, S_OK);
+ ok_int(V_VT(&vari), VT_UI4);
+ ok_long(V_UI4(&vari), 0xDEADFACE);
+ }
+ else
+ {
+ ok_long(hr, E_ACCESSDENIED);
+ ok_int(V_VT(&vari), VT_EMPTY);
+ ok_long(V_UI4(&vari), 0xFEEDF00D);
+ }
+ VariantClear(&vari);
+
+ pPropBag->Release();
+
+ hr = SHCreatePropertyBagOnMemory(STGM_READWRITE, IID_IPropertyBag2,
(void**)&pPropBag);
+ if (IsWindowsVistaOrGreater())
+ {
+ ok_long(hr, E_NOINTERFACE);
+ }
+ else
+ {
+ ok_long(hr, S_OK);
+ pPropBag->Release();
+ }
+
+ hr = SHCreatePropertyBagOnMemory(STGM_READ, IID_IPropertyBag2,
(void**)&pPropBag);
+ if (IsWindowsVistaOrGreater())
+ {
+ ok_long(hr, E_NOINTERFACE);
+ }
+ else
+ {
+ ok_long(hr, S_OK);
+ pPropBag->Release();
+ }
+
+ hr = SHCreatePropertyBagOnMemory(STGM_WRITE, IID_IPropertyBag2,
(void**)&pPropBag);
+ if (IsWindowsVistaOrGreater())
+ {
+ ok_long(hr, E_NOINTERFACE);
+ }
+ else
+ {
+ ok_long(hr, S_OK);
+ pPropBag->Release();
+ }
+}
+
START_TEST(SHPropertyBag)
{
SHPropertyBag_ReadTest();
SHPropertyBag_WriteTest();
+ SHPropertyBag_OnMemory();
}
diff --git a/sdk/include/reactos/shlwapi_undoc.h b/sdk/include/reactos/shlwapi_undoc.h
index a50900a731e..9ed45b2c8d5 100644
--- a/sdk/include/reactos/shlwapi_undoc.h
+++ b/sdk/include/reactos/shlwapi_undoc.h
@@ -124,6 +124,8 @@ HRESULT WINAPI SHPropertyBag_WritePOINTL(IPropertyBag *ppb, LPCWSTR
pszPropName,
HRESULT WINAPI SHPropertyBag_WritePOINTS(IPropertyBag *ppb, LPCWSTR pszPropName, const
POINTS *ppts);
HRESULT WINAPI SHPropertyBag_WriteRECTL(IPropertyBag *ppb, LPCWSTR pszPropName, const
RECTL *prcl);
+HRESULT WINAPI SHCreatePropertyBagOnMemory(_In_ DWORD dwMode, _In_ REFIID riid, _Out_
void **ppvObj);
+
HWND WINAPI SHCreateWorkerWindowA(WNDPROC wndProc, HWND hWndParent, DWORD dwExStyle,
DWORD dwStyle, HMENU hMenu, LONG_PTR wnd_extra);