https://git.reactos.org/?p=reactos.git;a=commitdiff;h=b4684e9809ba42fa5f5c644f02aa75893f232324

commit b4684e9809ba42fa5f5c644f02aa75893f232324
Author:     Katayama Hirofumi MZ <[email protected]>
AuthorDate: Sun Aug 6 20:54:55 2023 +0900
Commit:     GitHub <[email protected]>
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);
 

Reply via email to