https://git.reactos.org/?p=reactos.git;a=commitdiff;h=b4684e9809ba42fa5f5c6…
commit b4684e9809ba42fa5f5c644f02aa75893f232324
Author: Katayama Hirofumi MZ <katayama.hirofumi.mz(a)gmail.com>
AuthorDate: Sun Aug 6 20:54:55 2023 +0900
Commit: GitHub <noreply(a)github.com>
CommitDate: Sun Aug 6 20:54:55 2023 +0900
[SHLWAPI][SHLWAPI_APITEST][SDK] SHCreatePropertyBagOnRegKey (#5531)
Follow-up to #5511.
- Add CRegPropertyBag class.
- Implement SHCreatePropertyBagOnRegKey function by using CRegPropertyBag.
- Strengthen SHPropertyBag testcase in shlwapi_apitest.
CORE-9283
---
dll/win32/shlwapi/ordinal.c | 2 +
dll/win32/shlwapi/precomp.h | 1 -
dll/win32/shlwapi/propbag.cpp | 333 ++++++++++++++++++++-
dll/win32/shlwapi/shlwapi.spec | 2 +-
.../rostests/apitests/shlwapi/SHPropertyBag.cpp | 187 +++++++++++-
sdk/include/reactos/shlwapi_undoc.h | 9 +
6 files changed, 528 insertions(+), 6 deletions(-)
diff --git a/dll/win32/shlwapi/ordinal.c b/dll/win32/shlwapi/ordinal.c
index 9053c33b956..f43408d9479 100644
--- a/dll/win32/shlwapi/ordinal.c
+++ b/dll/win32/shlwapi/ordinal.c
@@ -5068,6 +5068,7 @@ free_sids:
return psd;
}
+#ifndef __REACTOS__ /* See propbag.cpp */
/***********************************************************************
* SHCreatePropertyBagOnRegKey [SHLWAPI.471]
*
@@ -5093,6 +5094,7 @@ HRESULT WINAPI SHCreatePropertyBagOnRegKey (HKEY hKey, LPCWSTR
subkey,
return E_NOTIMPL;
}
+#endif
/***********************************************************************
* SHGetViewStatePropertyBag [SHLWAPI.515]
diff --git a/dll/win32/shlwapi/precomp.h b/dll/win32/shlwapi/precomp.h
index 86f3ea401fd..2113a5dd967 100644
--- a/dll/win32/shlwapi/precomp.h
+++ b/dll/win32/shlwapi/precomp.h
@@ -20,7 +20,6 @@
#include <wingdi.h>
#include <wincon.h>
#include <winternl.h>
-#define NO_SHLWAPI_STREAM
#define NO_SHLWAPI_USER
#include <shlwapi.h>
#include <shlobj.h>
diff --git a/dll/win32/shlwapi/propbag.cpp b/dll/win32/shlwapi/propbag.cpp
index 27434091fc3..f9951324d0c 100644
--- a/dll/win32/shlwapi/propbag.cpp
+++ b/dll/win32/shlwapi/propbag.cpp
@@ -51,6 +51,8 @@ public:
*ppvObject = static_cast<IPropertyBag*>(this);
return S_OK;
}
+
+ ERR("%p: %s: E_NOTIMPL\n", this, debugstr_guid(&riid));
return E_NOTIMPL;
}
STDMETHODIMP_(ULONG) AddRef() override
@@ -245,9 +247,336 @@ SHCreatePropertyBagOnMemory(_In_ DWORD dwMode, _In_ REFIID riid,
_Out_ void **pp
CComPtr<CMemPropertyBag> pMemBag(new CMemPropertyBag(dwMode));
- HRESULT hr = pMemBag->QueryInterface(riid, ppvObj);
+ return pMemBag->QueryInterface(riid, ppvObj);
+}
+
+class CRegPropertyBag : public CBasePropertyBag
+{
+protected:
+ HKEY m_hKey;
+
+ HRESULT _ReadDword(LPCWSTR pszPropName, VARIANT *pvari);
+ HRESULT _ReadString(LPCWSTR pszPropName, VARIANTARG *pvarg, UINT len);
+ HRESULT _ReadBinary(LPCWSTR pszPropName, VARIANT *pvari, VARTYPE vt, DWORD uBytes);
+ HRESULT _ReadStream(VARIANT *pvari, BYTE *pInit, UINT cbInit);
+ HRESULT _CopyStreamIntoBuff(IStream *pStream, void *pv, ULONG cb);
+ HRESULT _GetStreamSize(IStream *pStream, LPDWORD pcbSize);
+ HRESULT _WriteStream(LPCWSTR pszPropName, IStream *pStream);
+
+public:
+ CRegPropertyBag(DWORD dwMode)
+ : CBasePropertyBag(dwMode)
+ , m_hKey(NULL)
+ {
+ }
+
+ ~CRegPropertyBag() override
+ {
+ if (m_hKey)
+ ::RegCloseKey(m_hKey);
+ }
+
+ HRESULT Init(HKEY hKey, LPCWSTR lpSubKey);
+
+ STDMETHODIMP Read(_In_z_ LPCWSTR pszPropName, _Inout_ VARIANT *pvari,
+ _Inout_opt_ IErrorLog *pErrorLog) override;
+ STDMETHODIMP Write(_In_z_ LPCWSTR pszPropName, _In_ VARIANT *pvari) override;
+};
+
+HRESULT CRegPropertyBag::Init(HKEY hKey, LPCWSTR lpSubKey)
+{
+ REGSAM nAccess = 0;
+ if ((m_dwMode & (STGM_READ | STGM_WRITE | STGM_READWRITE)) != STGM_WRITE)
+ nAccess |= KEY_READ;
+ if ((m_dwMode & (STGM_READ | STGM_WRITE | STGM_READWRITE)) != STGM_READ)
+ nAccess |= KEY_WRITE;
+
+ LONG error;
+ if (m_dwMode & STGM_CREATE)
+ error = ::RegCreateKeyExW(hKey, lpSubKey, 0, NULL, 0, nAccess, NULL, &m_hKey,
NULL);
+ else
+ error = ::RegOpenKeyExW(hKey, lpSubKey, 0, nAccess, &m_hKey);
+
+ if (error != ERROR_SUCCESS)
+ {
+ ERR("%p %s 0x%08X\n", hKey, debugstr_w(lpSubKey), error);
+ return HRESULT_FROM_WIN32(error);
+ }
+
+ return S_OK;
+}
+
+HRESULT CRegPropertyBag::_ReadDword(LPCWSTR pszPropName, VARIANT *pvari)
+{
+ DWORD cbData = sizeof(DWORD);
+ LONG error = SHGetValueW(m_hKey, NULL, pszPropName, NULL, &V_UI4(pvari),
&cbData);
+ if (error)
+ return E_FAIL;
+
+ V_VT(pvari) = VT_UI4;
+ return S_OK;
+}
+
+HRESULT CRegPropertyBag::_ReadString(LPCWSTR pszPropName, VARIANTARG *pvarg, UINT len)
+{
+ BSTR bstr = ::SysAllocStringByteLen(NULL, len);
+ V_BSTR(pvarg) = bstr;
+ if (!bstr)
+ return E_OUTOFMEMORY;
+
+ V_VT(pvarg) = VT_BSTR;
+ LONG error = SHGetValueW(m_hKey, NULL, pszPropName, NULL, bstr, (LPDWORD)&len);
+ if (error)
+ {
+ ::VariantClear(pvarg);
+ return E_FAIL;
+ }
+
+ return S_OK;
+}
+
+HRESULT CRegPropertyBag::_ReadStream(VARIANT *pvari, BYTE *pInit, UINT cbInit)
+{
+ IStream *pStream = SHCreateMemStream(pInit, cbInit);
+ V_UNKNOWN(pvari) = pStream;
+ if (!pStream)
+ return E_OUTOFMEMORY;
+ V_VT(pvari) = VT_UNKNOWN;
+ return S_OK;
+}
+
+HRESULT
+CRegPropertyBag::_ReadBinary(
+ LPCWSTR pszPropName,
+ VARIANT *pvari,
+ VARTYPE vt,
+ DWORD uBytes)
+{
+ HRESULT hr = E_FAIL;
+ if (vt != VT_UNKNOWN || uBytes < sizeof(GUID))
+ return hr;
+
+ LPBYTE pbData = (LPBYTE)::LocalAlloc(LMEM_ZEROINIT, uBytes);
+ if (!pbData)
+ return hr;
+
+ if (!SHGetValueW(m_hKey, NULL, pszPropName, NULL, pbData, &uBytes) &&
+ memcmp(&GUID_NULL, pbData, sizeof(GUID)) == 0)
+ {
+ hr = _ReadStream(pvari, pbData + sizeof(GUID), uBytes - sizeof(GUID));
+ }
+
+ ::LocalFree(pbData);
+
+ return hr;
+}
+
+HRESULT CRegPropertyBag::_CopyStreamIntoBuff(IStream *pStream, void *pv, ULONG cb)
+{
+ LARGE_INTEGER li;
+ li.QuadPart = 0;
+ HRESULT hr = pStream->Seek(li, 0, NULL);
+ if (FAILED(hr))
+ return hr;
+ return pStream->Read(pv, cb, NULL);
+}
+
+HRESULT CRegPropertyBag::_GetStreamSize(IStream *pStream, LPDWORD pcbSize)
+{
+ *pcbSize = 0;
+
+ ULARGE_INTEGER ui;
+ HRESULT hr = IStream_Size(pStream, &ui);
+ if (FAILED(hr))
+ return hr;
+
+ if (ui.DUMMYSTRUCTNAME.HighPart)
+ return E_FAIL; /* 64-bit value is not supported */
+
+ *pcbSize = ui.DUMMYSTRUCTNAME.LowPart;
+ return hr;
+}
+
+STDMETHODIMP
+CRegPropertyBag::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);
+
+ if ((m_dwMode & (STGM_READ | STGM_WRITE | STGM_READWRITE)) == STGM_WRITE)
+ {
+ ERR("%p: 0x%X\n", this, m_dwMode);
+ ::VariantInit(pvari);
+ return E_ACCESSDENIED;
+ }
+
+ VARTYPE vt = V_VT(pvari);
+ VariantInit(pvari);
+
+ HRESULT hr;
+ DWORD dwType, cbValue;
+ LONG error = SHGetValueW(m_hKey, NULL, pszPropName, &dwType, NULL,
&cbValue);
+ if (error != ERROR_SUCCESS)
+ hr = E_FAIL;
+ else if (dwType == REG_SZ)
+ hr = _ReadString(pszPropName, pvari, cbValue);
+ else if (dwType == REG_BINARY)
+ hr = _ReadBinary(pszPropName, pvari, vt, cbValue);
+ else if (dwType == REG_DWORD)
+ hr = _ReadDword(pszPropName, pvari);
+ else
+ hr = E_FAIL;
+
+ if (FAILED(hr))
+ {
+ ERR("%p: 0x%08X %ld: %s %p\n", this, hr, dwType,
debugstr_w(pszPropName), pvari);
+ ::VariantInit(pvari);
+ return hr;
+ }
+
+ hr = ::VariantChangeTypeForRead(pvari, vt);
if (FAILED(hr))
- ERR("0x%08X %s\n", hr, debugstr_guid(&riid));
+ {
+ ERR("%p: 0x%08X %ld: %s %p\n", this, hr, dwType,
debugstr_w(pszPropName), pvari);
+ ::VariantInit(pvari);
+ }
+
+ return hr;
+}
+
+HRESULT
+CRegPropertyBag::_WriteStream(LPCWSTR pszPropName, IStream *pStream)
+{
+ DWORD cbData;
+ HRESULT hr = _GetStreamSize(pStream, &cbData);
+ if (FAILED(hr) || !cbData)
+ return hr;
+
+ DWORD cbBinary = cbData + sizeof(GUID);
+ LPBYTE pbBinary = (LPBYTE)::LocalAlloc(LMEM_ZEROINIT, cbBinary);
+ if (!pbBinary)
+ return E_OUTOFMEMORY;
+
+ hr = _CopyStreamIntoBuff(pStream, pbBinary + sizeof(GUID), cbData);
+ if (SUCCEEDED(hr))
+ {
+ if (SHSetValueW(m_hKey, NULL, pszPropName, REG_BINARY, pbBinary, cbBinary))
+ hr = E_FAIL;
+ }
+
+ ::LocalFree(pbBinary);
+ return hr;
+}
+
+STDMETHODIMP
+CRegPropertyBag::Write(_In_z_ LPCWSTR pszPropName, _In_ VARIANT *pvari)
+{
+ TRACE("%p: %s %p\n", this, debugstr_w(pszPropName), pvari);
+
+ if ((m_dwMode & (STGM_READ | STGM_WRITE | STGM_READWRITE)) == STGM_READ)
+ {
+ ERR("%p: 0x%X\n", this, m_dwMode);
+ return E_ACCESSDENIED;
+ }
+
+ HRESULT hr;
+ LONG error;
+ VARIANTARG vargTemp = { 0 };
+ switch (V_VT(pvari))
+ {
+ case VT_EMPTY:
+ SHDeleteValueW(m_hKey, NULL, pszPropName);
+ hr = S_OK;
+ break;
+
+ case VT_BOOL:
+ case VT_I1:
+ case VT_I2:
+ case VT_I4:
+ case VT_UI1:
+ case VT_UI2:
+ case VT_UI4:
+ case VT_INT:
+ case VT_UINT:
+ {
+ hr = ::VariantChangeType(&vargTemp, pvari, 0, VT_UI4);
+ if (FAILED(hr))
+ return hr;
+
+ error = SHSetValueW(m_hKey, NULL, pszPropName, REG_DWORD,
&V_UI4(&vargTemp), sizeof(DWORD));
+ if (error)
+ hr = E_FAIL;
+
+ ::VariantClear(&vargTemp);
+ break;
+ }
+
+ case VT_UNKNOWN:
+ {
+ CComPtr<IStream> pStream;
+ hr = V_UNKNOWN(pvari)->QueryInterface(IID_IStream, (void
**)&pStream);
+ if (FAILED(hr))
+ return hr;
+
+ hr = _WriteStream(pszPropName, pStream);
+ break;
+ }
+
+ default:
+ {
+ hr = ::VariantChangeType(&vargTemp, pvari, 0, VT_BSTR);
+ if (FAILED(hr))
+ return hr;
+
+ int cch = lstrlenW(V_BSTR(&vargTemp));
+ DWORD cb = (cch + 1) * sizeof(WCHAR);
+ error = SHSetValueW(m_hKey, NULL, pszPropName, REG_SZ, V_BSTR(&vargTemp),
cb);
+ if (error)
+ hr = E_FAIL;
+
+ ::VariantClear(&vargTemp);
+ break;
+ }
+ }
return hr;
}
+
+/**************************************************************************
+ * SHCreatePropertyBagOnRegKey (SHLWAPI.471)
+ *
+ * Creates a property bag object on registry key.
+ *
+ * @param hKey The registry key.
+ * @param pszSubKey The path of the sub-key.
+ * @param dwMode The combination of STGM_READ, STGM_WRITE, STGM_READWRITE, and
STGM_CREATE.
+ * @param riid Specifies either IID_IUnknown, IID_IPropertyBag or
IID_IPropertyBag2.
+ * @param ppvObj Receives an IPropertyBag pointer.
+ * @return An HRESULT value. S_OK on success, non-zero on failure.
+ * @see
https://source.winehq.org/WineAPI/SHCreatePropertyBagOnRegKey.html
+ */
+EXTERN_C HRESULT WINAPI
+SHCreatePropertyBagOnRegKey(
+ _In_ HKEY hKey,
+ _In_z_ LPCWSTR pszSubKey,
+ _In_ DWORD dwMode,
+ _In_ REFIID riid,
+ _Out_ void **ppvObj)
+{
+ TRACE("%p, %s, 0x%08X, %s, %p\n", hKey, debugstr_w(pszSubKey), dwMode,
+ debugstr_guid(&riid), ppvObj);
+
+ *ppvObj = NULL;
+
+ CComPtr<CRegPropertyBag> pRegBag(new CRegPropertyBag(dwMode));
+
+ HRESULT hr = pRegBag->Init(hKey, pszSubKey);
+ if (FAILED(hr))
+ return hr;
+
+ return pRegBag->QueryInterface(riid, ppvObj);
+}
diff --git a/dll/win32/shlwapi/shlwapi.spec b/dll/win32/shlwapi/shlwapi.spec
index 0844af03d5d..13cac9b8c7a 100644
--- a/dll/win32/shlwapi/shlwapi.spec
+++ b/dll/win32/shlwapi/shlwapi.spec
@@ -468,7 +468,7 @@
468 stub -noname RunIndirectRegCommand
469 stub -noname RunRegCommand
470 stub -noname IUnknown_ProfferServiceOld
-471 stdcall -noname SHCreatePropertyBagOnRegKey(long wstr long ptr ptr)
+471 stdcall -noname SHCreatePropertyBagOnRegKey(ptr wstr long ptr ptr)
472 stub -noname SHCreatePropertyBagOnProfileSelection
473 stub -noname SHGetIniStringUTF7W
474 stub -noname SHSetIniStringUTF7W
diff --git a/modules/rostests/apitests/shlwapi/SHPropertyBag.cpp
b/modules/rostests/apitests/shlwapi/SHPropertyBag.cpp
index 2114ceff231..365c4478f19 100644
--- a/modules/rostests/apitests/shlwapi/SHPropertyBag.cpp
+++ b/modules/rostests/apitests/shlwapi/SHPropertyBag.cpp
@@ -96,7 +96,7 @@ public:
if (lstrcmpiW(pszPropName, L"GUID1") == 0)
{
V_VT(pvari) = (VT_UI1 | VT_ARRAY);
- V_ARRAY(pvari) = CreateByteArray(&IID_IShellLink, sizeof(IID));
+ V_ARRAY(pvari) = CreateByteArray(&IID_IShellLinkW, sizeof(IID));
return S_OK;
}
@@ -225,7 +225,7 @@ static void SHPropertyBag_ReadTest(void)
ok_long(hr, S_OK);
ok_int(s_cRead, 1);
ok_int(s_cWrite, 0);
- ok_int(IsEqualGUID(guid, IID_IShellLink), TRUE);
+ ok_int(IsEqualGUID(guid, IID_IShellLinkW), TRUE);
ResetTest(VT_EMPTY, L"GUID2");
hr = SHPropertyBag_ReadGUID(&dummy, L"GUID2", &guid);
@@ -456,9 +456,192 @@ static void SHPropertyBag_OnMemory(void)
}
}
+static void SHPropertyBag_OnRegKey(void)
+{
+ HKEY hKey, hSubKey;
+ LONG error;
+ VARIANT vari;
+ WCHAR szText[MAX_PATH];
+ IStream *pStream;
+ GUID guid;
+ BYTE guid_and_extra[sizeof(GUID) + sizeof(GUID)];
+
+ // Create HKCU\Software\ReactOS registry key
+ error = RegCreateKeyW(HKEY_CURRENT_USER, L"Software\\ReactOS", &hKey);
+ if (error)
+ {
+ skip("FAILED to create HKCU\\Software\\ReactOS\n");
+ return;
+ }
+
+ IPropertyBag *pPropBag;
+ HRESULT hr;
+
+ // Try to create new registry key
+ RegDeleteKeyW(hKey, L"PropBagTest");
+ hr = SHCreatePropertyBagOnRegKey(hKey, L"PropBagTest", 0,
+ IID_IPropertyBag, (void **)&pPropBag);
+ ok_long(hr, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
+
+ // Try to create new registry key
+ RegDeleteKeyW(hKey, L"PropBagTest");
+ hr = SHCreatePropertyBagOnRegKey(hKey, L"PropBagTest", STGM_READWRITE,
+ IID_IPropertyBag, (void **)&pPropBag);
+ ok_long(hr, HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
+
+ // Create new registry key
+ RegDeleteKeyW(hKey, L"PropBagTest");
+ hr = SHCreatePropertyBagOnRegKey(hKey, L"PropBagTest", STGM_CREATE |
STGM_READWRITE,
+ IID_IPropertyBag, (void **)&pPropBag);
+ if (FAILED(hr))
+ {
+ skip("SHCreatePropertyBagOnRegKey FAILED\n");
+ RegCloseKey(hKey);
+ return;
+ }
+
+ // Write UI4
+ VariantInit(&vari);
+ V_VT(&vari) = VT_UI4;
+ V_UI4(&vari) = 0xDEADFACE;
+ hr = pPropBag->Write(L"Name1", &vari);
+ ok_long(hr, S_OK);
+ VariantClear(&vari);
+
+ // Read UI4
+ 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);
+
+ // Write BSTR
+ VariantInit(&vari);
+ V_VT(&vari) = VT_BSTR;
+ V_BSTR(&vari) = SysAllocString(L"StrValue");
+ hr = pPropBag->Write(L"Name2", &vari);
+ ok_long(hr, S_OK);
+ VariantClear(&vari);
+
+ // Read BSTR
+ VariantInit(&vari);
+ V_VT(&vari) = VT_BSTR;
+ hr = pPropBag->Read(L"Name2", &vari, NULL);
+ ok_long(hr, S_OK);
+ ok_long(V_VT(&vari), VT_BSTR);
+ ok_wstr(V_BSTR(&vari), L"StrValue");
+ VariantClear(&vari);
+
+ // Write GUID
+ VariantInit(&vari);
+ V_VT(&vari) = VT_UNKNOWN;
+ V_UNKNOWN(&vari) = SHCreateMemStream((BYTE*)&IID_IShellLinkW,
sizeof(IID_IShellLinkW));
+ hr = pPropBag->Write(L"Name4", &vari);
+ ok_long(hr, S_OK);
+ VariantClear(&vari);
+
+ // Read GUID
+ VariantInit(&vari);
+ V_VT(&vari) = VT_EMPTY;
+ hr = pPropBag->Read(L"Name4", &vari, NULL);
+ if (IsWindowsVistaOrGreater())
+ {
+ ok_long(hr, S_OK);
+ ok_long(V_VT(&vari), VT_UNKNOWN);
+ pStream = (IStream*)V_UNKNOWN(&vari);
+ FillMemory(&guid, sizeof(guid), 0xEE);
+ hr = pStream->Read(&guid, sizeof(guid), NULL);
+ ok_long(hr, S_OK);
+ ok_int(::IsEqualGUID(guid, IID_IShellLinkW), TRUE);
+ }
+ else // XP/2k3 Read is buggy
+ {
+ ok_long(hr, E_FAIL);
+ ok_long(V_VT(&vari), VT_EMPTY);
+ }
+ VariantClear(&vari);
+
+ pPropBag->Release();
+
+ // Check registry
+ error = RegOpenKeyExW(hKey, L"PropBagTest", 0, KEY_READ, &hSubKey);
+ ok_long(error, ERROR_SUCCESS);
+ DWORD dwType, dwValue, cbValue = sizeof(dwValue);
+ error = RegQueryValueExW(hSubKey, L"Name1", NULL, &dwType,
(BYTE*)&dwValue, &cbValue);
+ ok_long(error, ERROR_SUCCESS);
+ ok_long(dwType, REG_DWORD);
+ ok_long(dwValue, 0xDEADFACE);
+ ok_long(cbValue, sizeof(DWORD));
+ cbValue = sizeof(szText);
+ error = RegQueryValueExW(hSubKey, L"Name2", NULL, &dwType,
(BYTE*)szText, &cbValue);
+ ok_long(error, ERROR_SUCCESS);
+ ok_long(dwType, REG_SZ);
+ ok_wstr(szText, L"StrValue");
+ cbValue = sizeof(guid_and_extra);
+ error = RegQueryValueExW(hSubKey, L"Name4", NULL, &dwType,
(BYTE*)&guid_and_extra, &cbValue);
+ ok_long(error, ERROR_SUCCESS);
+ ok_long(dwType, REG_BINARY);
+ ok_int(memcmp(&guid_and_extra, &GUID_NULL, sizeof(GUID)), 0);
+ ok_int(memcmp(&guid_and_extra[sizeof(GUID)], &IID_IShellLinkW, sizeof(GUID)),
0);
+ RegCloseKey(hSubKey);
+
+ // Create as read-only
+ hr = SHCreatePropertyBagOnRegKey(hKey, L"PropBagTest", STGM_READ,
+ IID_IPropertyBag, (void **)&pPropBag);
+ ok_long(hr, S_OK);
+
+ // Read UI4
+ 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);
+
+ // Write UI4
+ VariantInit(&vari);
+ V_VT(&vari) = VT_UI4;
+ V_UI4(&vari) = 0xDEADFACE;
+ hr = pPropBag->Write(L"Name1", &vari);
+ ok_long(hr, E_ACCESSDENIED);
+ VariantClear(&vari);
+
+ pPropBag->Release();
+
+ // Create as write-only IPropertyBag2
+ hr = SHCreatePropertyBagOnRegKey(hKey, L"PropBagTest", STGM_WRITE,
+ IID_IPropertyBag2, (void **)&pPropBag);
+ ok_long(hr, S_OK);
+
+ // Write UI4
+ VariantInit(&vari);
+ V_VT(&vari) = VT_UI4;
+ V_UI4(&vari) = 0xDEADFACE;
+ hr = pPropBag->Write(L"Name3", &vari);
+ ok_long(hr, E_NOTIMPL);
+ VariantClear(&vari);
+
+ // Read UI4
+ VariantInit(&vari);
+ V_UI4(&vari) = 0xFEEDF00D;
+ hr = pPropBag->Read(L"Name3", &vari, NULL);
+ ok_long(hr, E_NOTIMPL);
+ ok_int(V_VT(&vari), VT_EMPTY);
+ ok_long(V_UI4(&vari), 0xFEEDF00D);
+ VariantClear(&vari);
+
+ pPropBag->Release();
+
+ // Clean up
+ RegDeleteKeyW(hKey, L"PropBagTest");
+ RegCloseKey(hKey);
+}
+
START_TEST(SHPropertyBag)
{
SHPropertyBag_ReadTest();
SHPropertyBag_WriteTest();
SHPropertyBag_OnMemory();
+ SHPropertyBag_OnRegKey();
}
diff --git a/sdk/include/reactos/shlwapi_undoc.h b/sdk/include/reactos/shlwapi_undoc.h
index 9ed45b2c8d5..29aae083047 100644
--- a/sdk/include/reactos/shlwapi_undoc.h
+++ b/sdk/include/reactos/shlwapi_undoc.h
@@ -91,6 +91,7 @@ BOOL WINAPI SHExpandEnvironmentStringsForUserW(HANDLE, LPCWSTR, LPWSTR,
DWORD);
BOOL WINAPI SHIsEmptyStream(IStream*);
+HRESULT WINAPI IStream_Size(IStream *lpStream, ULARGE_INTEGER* lpulSize);
HRESULT WINAPI SHInvokeDefaultCommand(HWND,IShellFolder*,LPCITEMIDLIST);
HRESULT WINAPI SHPropertyBag_ReadType(IPropertyBag *ppb, LPCWSTR pszPropName, VARIANTARG
*pvarg, VARTYPE vt);
HRESULT WINAPI SHPropertyBag_ReadBOOL(IPropertyBag *ppb, LPCWSTR pszPropName, BOOL
*pbValue);
@@ -126,6 +127,14 @@ HRESULT WINAPI SHPropertyBag_WriteRECTL(IPropertyBag *ppb, LPCWSTR
pszPropName,
HRESULT WINAPI SHCreatePropertyBagOnMemory(_In_ DWORD dwMode, _In_ REFIID riid, _Out_
void **ppvObj);
+HRESULT WINAPI
+SHCreatePropertyBagOnRegKey(
+ _In_ HKEY hKey,
+ _In_z_ LPCWSTR pszSubKey,
+ _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);