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 #include +#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 + */ + +#define _ATL_NO_EXCEPTIONS +#include "precomp.h" +#include +#include +#include // for CStringW +#include // for CSimpleMap +#include // 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(this); + return S_OK; + } +#endif + if (::IsEqualGUID(riid, IID_IUnknown) || ::IsEqualGUID(riid, IID_IPropertyBag)) + { + AddRef(); + *ppvObject = static_cast(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 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 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);