From 3b3e1ff997f3c407e2e66802301cfd5a398ff23b Mon Sep 17 00:00:00 2001 From: Katayama Hirofumi MZ Date: Sun, 31 May 2026 17:33:17 +0900 Subject: [PATCH] [SHLWAPI][SHLWAPI_APITEST][SDK] Support QuerySourceCreateFromKey (#9093) Implementing missing features... JIRA issue: CORE-20467 - Add querysrc.cpp. - Add CRegistryEnumValues, CRegistryEnumKeys, and CRegistrySource classes. - Implement QuerySourceCreateFromKey function. - Add prototype into . - Modify shlwapi.spec. - Add QuerySourceCreateFromKey testcase. --- dll/win32/shlwapi/CMakeLists.txt | 1 + dll/win32/shlwapi/querysrc.cpp | 511 ++++++++++++++++++ dll/win32/shlwapi/shlwapi.spec | 2 +- .../rostests/apitests/shlwapi/CMakeLists.txt | 1 + .../shlwapi/QuerySourceCreateFromKey.cpp | 258 +++++++++ modules/rostests/apitests/shlwapi/testlist.c | 2 + sdk/include/reactos/shlobj_undoc.h | 2 +- sdk/include/reactos/shlwapi_undoc.h | 8 + 8 files changed, 783 insertions(+), 2 deletions(-) create mode 100644 dll/win32/shlwapi/querysrc.cpp create mode 100644 modules/rostests/apitests/shlwapi/QuerySourceCreateFromKey.cpp diff --git a/dll/win32/shlwapi/CMakeLists.txt b/dll/win32/shlwapi/CMakeLists.txt index 295565e4155..87301df7aa4 100644 --- a/dll/win32/shlwapi/CMakeLists.txt +++ b/dll/win32/shlwapi/CMakeLists.txt @@ -27,6 +27,7 @@ list(APPEND PCH_SKIP_SOURCE assoc.c policy.cpp propbag.cpp + querysrc.cpp utils.cpp wsprintf.c zonechk.c diff --git a/dll/win32/shlwapi/querysrc.cpp b/dll/win32/shlwapi/querysrc.cpp new file mode 100644 index 00000000000..b6872c116cc --- /dev/null +++ b/dll/win32/shlwapi/querysrc.cpp @@ -0,0 +1,511 @@ +/* + * PROJECT: ReactOS Shell + * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) + * PURPOSE: Implement QuerySourceCreateFromKey + * COPYRIGHT: Copyright 2026 Katayama Hirofumi MZ + */ + +#include "precomp.h" +#include +#include +#include +#include + +WINE_DEFAULT_DEBUG_CHANNEL(shell); + +static HRESULT SHAllocBlob(ULONG cbData, const BYTE *pbData, FLAGGED_BYTE_BLOB** ppBlob) +{ + FLAGGED_BYTE_BLOB* pBlob = + (FLAGGED_BYTE_BLOB*)CoTaskMemAlloc(sizeof(FLAGGED_BYTE_BLOB) + cbData); + if (!pBlob) + return E_OUTOFMEMORY; + + pBlob->clSize = cbData; + if (pbData) + CopyMemory(pBlob->abData, pbData, cbData); + + *ppBlob = pBlob; + return S_OK; +} + +class CRegistrySource; + +/****************************************************************************** + * CRegistryEnumBase + */ +class CRegistryEnumBase : public IEnumString +{ +protected: + LONG m_cRefs = 1; + DWORD m_dwIndex = 0; + CRegistrySource *m_pSource = NULL; + HKEY m_hKey = NULL; + PWSTR m_pszName = NULL; + WCHAR m_szBuf[64] = {}; + DWORD m_cchNameMax = 0; + + BOOL _Next(PWSTR *ppwsz); + virtual BOOL _RegNext(DWORD dwIndex) = 0; + virtual DWORD _MaxLen() = 0; + +public: + virtual ~CRegistryEnumBase(); + + HRESULT Init(HKEY hKey, CRegistrySource *pSource); + + // IUnknown methods + STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) override; + STDMETHODIMP_(ULONG) AddRef() override; + STDMETHODIMP_(ULONG) Release() override; + // IEnumString methods + STDMETHODIMP Next(ULONG celt, LPWSTR* rgelt, ULONG* pceltFetched) override; + STDMETHODIMP Skip(ULONG celt) override; + STDMETHODIMP Reset() override; + STDMETHODIMP Clone(IEnumString ** ppenum) override; +}; + +/****************************************************************************** + * CRegistryEnumValues + */ +class CRegistryEnumValues : public CRegistryEnumBase +{ +public: + BOOL _RegNext(DWORD dwIndex) override; + DWORD _MaxLen() override; +}; + +/****************************************************************************** + * CRegistryEnumKeys + */ +class CRegistryEnumKeys : public CRegistryEnumBase +{ +public: + BOOL _RegNext(DWORD dwIndex) override; + DWORD _MaxLen() override; +}; + +/****************************************************************************** + * CRegistrySource + */ +class CRegistrySource + : public IQuerySourceOld + , public IObjectWithRegistryKeyOld +{ + LONG m_cRefs = 1; + HKEY m_hKey = NULL; + +public: + virtual ~CRegistrySource(); + + HRESULT Init(HKEY hKey, PCWSTR pszSubKey, BOOL bCreate); + + // IUnknown methods + STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) override; + STDMETHODIMP_(ULONG) AddRef() override; + STDMETHODIMP_(ULONG) Release() override; + // IQuerySourceOld methods + STDMETHODIMP EnumValues(IEnumString **ppEnum) override; + STDMETHODIMP EnumSources(IEnumString **ppEnum) override; + STDMETHODIMP QueryValueString(PCWSTR keyName, PCWSTR valueName, PWSTR *ppszValue) override; + STDMETHODIMP QueryValueDword(PCWSTR keyName, PCWSTR valueName, DWORD *pdwValue) override; + STDMETHODIMP QueryValueExists(PCWSTR keyName, PCWSTR valueName) override; + STDMETHODIMP QueryValueDirect(PCWSTR keyName, PCWSTR valueName, + FLAGGED_BYTE_BLOB **ppBlob) override; + STDMETHODIMP OpenSource(PCWSTR keyName, BOOL bCreate, IQuerySourceOld **ppSource) override; + STDMETHODIMP SetValueDirect(PCWSTR keyName, PCWSTR valueName, DWORD dwType, DWORD cbData, + LPCVOID pvData) override; + // IObjectWithRegistryKeyOld methods + STDMETHODIMP SetKey(HKEY hKey) override; + STDMETHODIMP GetKey(HKEY *phKey) override; +}; + +/******************************************************************************/ + +CRegistryEnumBase::~CRegistryEnumBase() +{ + if (m_pszName && m_pszName != m_szBuf) + LocalFree(m_pszName); + if (m_pSource) + m_pSource->Release(); +} + +HRESULT CRegistryEnumBase::Init(HKEY hKey, CRegistrySource *pSource) +{ + m_hKey = hKey; + m_pSource = pSource; + m_pSource->AddRef(); + m_cchNameMax = _MaxLen(); + if (m_cchNameMax > _countof(m_szBuf)) + { + m_pszName = (PWSTR)LocalAlloc(LPTR, m_cchNameMax * sizeof(WCHAR)); + } + else + { + m_cchNameMax = _countof(m_szBuf); + m_pszName = m_szBuf; + } + return m_pszName ? S_OK : E_OUTOFMEMORY; +} + +BOOL CRegistryEnumBase::_Next(PWSTR *ppwsz) +{ + return _RegNext(m_dwIndex) && SUCCEEDED(SHStrDupW(m_pszName, ppwsz)); +} + +HRESULT CRegistryEnumBase::QueryInterface(REFIID riid, PVOID* ppv) +{ + if (!ppv) + return E_POINTER; + + if (riid == IID_IEnumString) + { + *ppv = static_cast(this); + AddRef(); + return S_OK; + } + return E_NOINTERFACE; +} + +STDMETHODIMP_(ULONG) CRegistryEnumBase::AddRef() +{ + return ::InterlockedIncrement(&m_cRefs); +} + +STDMETHODIMP_(ULONG) CRegistryEnumBase::Release() +{ + LONG refs = ::InterlockedDecrement(&m_cRefs); + if (!refs) + delete this; + return refs; +} + +STDMETHODIMP CRegistryEnumBase::Next(ULONG celt, LPWSTR* rgelt, ULONG* pceltFetched) +{ + if (!rgelt || (celt > 1 && !pceltFetched)) + return E_INVALIDARG; + + ULONG cFetched = 0; + + while (cFetched < celt) + { + if (!_Next(&rgelt[cFetched])) + break; + ++m_dwIndex; + ++cFetched; + } + + if (pceltFetched) + *pceltFetched = cFetched; + + return (cFetched == celt) ? S_OK : S_FALSE; +} + +STDMETHODIMP CRegistryEnumBase::Skip(ULONG celt) +{ + return E_NOTIMPL; +} + +STDMETHODIMP CRegistryEnumBase::Reset() +{ + m_dwIndex = 0; + return S_OK; +} + +STDMETHODIMP CRegistryEnumBase::Clone(IEnumString ** ppenum) +{ + return E_NOTIMPL; +} + +/******************************************************************************/ + +BOOL CRegistryEnumKeys::_RegNext(DWORD dwIndex) +{ + return RegEnumKeyW(m_hKey, dwIndex, m_pszName, m_cchNameMax) == ERROR_SUCCESS; +} + +DWORD CRegistryEnumKeys::_MaxLen() +{ + DWORD cchKeyNameMax = 0; + RegQueryInfoKeyW(m_hKey, NULL, NULL, NULL, NULL, &cchKeyNameMax, NULL, NULL, NULL, NULL, + NULL, NULL); + return cchKeyNameMax + 1; // Including NUL +} + +/******************************************************************************/ + +BOOL CRegistryEnumValues::_RegNext(DWORD dwIndex) +{ + DWORD cchValueNameMax = m_cchNameMax; + return RegEnumValueW(m_hKey, dwIndex, m_pszName, &cchValueNameMax, NULL, + NULL, NULL, NULL) == ERROR_SUCCESS; +} + +DWORD CRegistryEnumValues::_MaxLen() +{ + DWORD cchValueNameMax = 0; + RegQueryInfoKeyW(m_hKey, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &cchValueNameMax, + NULL, NULL, NULL); + return cchValueNameMax + 1; // Including NUL +} + +/******************************************************************************/ + +CRegistrySource::~CRegistrySource() +{ + if (m_hKey) + RegCloseKey(m_hKey); +} + +HRESULT CRegistrySource::Init(HKEY hKey, PCWSTR pszSubKey, BOOL bCreate) +{ + LSTATUS error; + if (bCreate) + error = RegCreateKeyExW(hKey, pszSubKey, 0, NULL, 0, MAXIMUM_ALLOWED, NULL, &m_hKey, NULL); + else + error = RegOpenKeyExW(hKey, pszSubKey, 0, MAXIMUM_ALLOWED, &m_hKey); + + return error ? HRESULT_FROM_WIN32(error) : S_OK; +} + +STDMETHODIMP CRegistrySource::QueryInterface(REFIID riid, void **ppvObject) +{ + if (!ppvObject) + return E_POINTER; + + if (riid == IID_IQuerySourceOld) + { + *ppvObject = static_cast(this); + AddRef(); + return S_OK; + } + + if (riid == IID_IObjectWithRegistryKeyOld) + { + *ppvObject = static_cast(this); + AddRef(); + return S_OK; + } + + *ppvObject = NULL; + return E_NOINTERFACE; +} + +STDMETHODIMP_(ULONG) CRegistrySource::AddRef() +{ + return ::InterlockedIncrement(&m_cRefs); +} + +STDMETHODIMP_(ULONG) CRegistrySource::Release() +{ + LONG refs = ::InterlockedDecrement(&m_cRefs); + if (!refs) + delete this; + return refs; +} + +STDMETHODIMP CRegistrySource::EnumValues(IEnumString **ppEnum) +{ + CRegistryEnumValues* pEnum = new CRegistryEnumValues(); + HRESULT hr = pEnum->Init(m_hKey, this); + if (FAILED(hr)) + { + pEnum->Release(); + pEnum = NULL; + } + *ppEnum = pEnum; + return pEnum ? S_OK : E_OUTOFMEMORY; +} + +STDMETHODIMP CRegistrySource::EnumSources(IEnumString **ppEnum) +{ + CRegistryEnumKeys* pEnum = new CRegistryEnumKeys(); + HRESULT hr = pEnum->Init(m_hKey, this); + if (FAILED(hr)) + { + pEnum->Release(); + pEnum = NULL; + } + *ppEnum = pEnum; + return pEnum ? S_OK : E_OUTOFMEMORY; +} + +STDMETHODIMP CRegistrySource::QueryValueString( + PCWSTR keyName, + PCWSTR valueName, + PWSTR *ppszValue) +{ + *ppszValue = NULL; + + WCHAR szData[128]; + DWORD dwType = REG_NONE, cbData = sizeof(szData); + LSTATUS error = SHGetValueW(m_hKey, keyName, valueName, &dwType, szData, &cbData); + if (error == ERROR_SUCCESS) + { + if (dwType != REG_SZ) + return E_DATATYPE_MISMATCH; + if (!valueName && !szData[0]) + return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + return SHStrDupW(szData, ppszValue); + } + + if (error != ERROR_MORE_DATA) + return error ? HRESULT_FROM_WIN32(error) : S_OK; + + *ppszValue = (PWSTR)CoTaskMemAlloc(cbData); + if (!*ppszValue) + return E_OUTOFMEMORY; + + HRESULT hr = S_OK; + error = SHGetValueW(m_hKey, keyName, valueName, &dwType, *ppszValue, &cbData); + if (error) + { + CoTaskMemFree(*ppszValue); + *ppszValue = NULL; + hr = HRESULT_FROM_WIN32(error); + } + + if (SUCCEEDED(hr) && dwType != REG_SZ) + { + CoTaskMemFree(*ppszValue); + *ppszValue = NULL; + hr = E_DATATYPE_MISMATCH; + } + + return hr; +} + +STDMETHODIMP CRegistrySource::QueryValueDword(PCWSTR keyName, PCWSTR valueName, DWORD *pdwValue) +{ + DWORD dwType, cbValue = sizeof(*pdwValue); + LSTATUS error = SHGetValueW(m_hKey, keyName, valueName, &dwType, pdwValue, &cbValue); + if (error) + return HRESULT_FROM_WIN32(error); + if (dwType != REG_DWORD) + return E_DATATYPE_MISMATCH; + return S_OK; +} + +STDMETHODIMP CRegistrySource::QueryValueExists(PCWSTR keyName, PCWSTR valueName) +{ + LSTATUS error = SHGetValueW(m_hKey, keyName, valueName, NULL, NULL, NULL); + if (error) + return HRESULT_FROM_WIN32(error); + return S_OK; +} + +STDMETHODIMP CRegistrySource::QueryValueDirect( + PCWSTR keyName, + PCWSTR valueName, + FLAGGED_BYTE_BLOB **ppBlob) +{ + HRESULT hr = E_FAIL; + HKEY hKey = m_hKey; + DWORD dwType = REG_NONE; + DWORD cbData = 256; + BYTE abData[256]; + + *ppBlob = NULL; + + if (keyName && *keyName && + RegOpenKeyExW(m_hKey, keyName, 0, KEY_READ, &hKey) != ERROR_SUCCESS) + { + return E_FAIL; + } + + LSTATUS error = RegQueryValueExW(hKey, valueName, NULL, &dwType, abData, &cbData); + if (error == ERROR_SUCCESS) + { + hr = SHAllocBlob(cbData, abData, ppBlob); + } + else if (error == ERROR_MORE_DATA) + { + hr = SHAllocBlob(cbData, NULL, ppBlob); + if (SUCCEEDED(hr)) + { + error = RegQueryValueExW(hKey, valueName, NULL, &dwType, (*ppBlob)->abData, &cbData); + if (error != ERROR_SUCCESS) + { + CoTaskMemFree(*ppBlob); + *ppBlob = NULL; + hr = error ? HRESULT_FROM_WIN32(error) : S_OK; + } + } + } + else + { + hr = HRESULT_FROM_WIN32(error); + } + + if (hKey != m_hKey) + RegCloseKey(hKey); + + if (SUCCEEDED(hr)) + (*ppBlob)->fFlags = dwType; + + return hr; +} + +STDMETHODIMP CRegistrySource::OpenSource(PCWSTR keyName, BOOL bCreate, IQuerySourceOld **ppSource) +{ + return QuerySourceCreateFromKey(m_hKey, keyName, bCreate, IID_IQuerySourceOld, (PVOID*)ppSource); +} + +STDMETHODIMP CRegistrySource::SetValueDirect( + PCWSTR keyName, + PCWSTR valueName, + DWORD dwType, + DWORD cbData, + LPCVOID pvData) +{ + LSTATUS error = SHSetValueW(m_hKey, keyName, valueName, dwType, pvData, cbData); + if (error) + return HRESULT_FROM_WIN32(error); + return S_OK; +} + +STDMETHODIMP CRegistrySource::SetKey(HKEY hKey) +{ + if (m_hKey) + return E_UNEXPECTED; + + m_hKey = SHRegDuplicateHKey(hKey); + return m_hKey ? S_OK : E_UNEXPECTED; +} + +STDMETHODIMP CRegistrySource::GetKey(HKEY *phKey) +{ + if (!m_hKey) + return E_UNEXPECTED; + + *phKey = SHRegDuplicateHKey(m_hKey); + if (!*phKey) + return E_UNEXPECTED; + + return S_OK; +} + +/************************************************************************** + * QuerySourceCreateFromKey (SHLWAPI.544) + * + * @see https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/regsrc/createfromkey.htm + */ +EXTERN_C +HRESULT WINAPI +QuerySourceCreateFromKey( + _In_ HKEY hKey, + _In_opt_ PCWSTR lpSubKey, + _In_ BOOL bCreate, + _In_ REFIID riid, + _Outptr_ PVOID *ppv) +{ + *ppv = NULL; + + CRegistrySource* pRS = new CRegistrySource(); + + HRESULT hr = pRS->Init(hKey, lpSubKey, bCreate); + if (SUCCEEDED(hr)) + hr = pRS->QueryInterface(riid, ppv); + + pRS->Release(); + return hr; +} diff --git a/dll/win32/shlwapi/shlwapi.spec b/dll/win32/shlwapi/shlwapi.spec index 5baf5519dc3..d25033f7bf3 100644 --- a/dll/win32/shlwapi/shlwapi.spec +++ b/dll/win32/shlwapi/shlwapi.spec @@ -541,7 +541,7 @@ 541 stdcall -noname SHInvokeCommandsOnContextMenu(ptr ptr ptr long ptr long) 542 stdcall -noname GetUIVersion() 543 stdcall -noname CreateColorSpaceWrapW(ptr) gdi32.CreateColorSpaceW -544 stub -noname QuerySourceCreateFromKey +544 stdcall -noname QuerySourceCreateFromKey(ptr wstr long ptr ptr) 545 stdcall -noname SHForwardContextMenuMsg(ptr long long long ptr long) 546 stub -noname IUnknown_DoContextMenuPopup 547 stdcall DelayLoadFailureHook(str str) kernel32.DelayLoadFailureHook diff --git a/modules/rostests/apitests/shlwapi/CMakeLists.txt b/modules/rostests/apitests/shlwapi/CMakeLists.txt index a6c5aea4c1d..f258f1a8a0f 100644 --- a/modules/rostests/apitests/shlwapi/CMakeLists.txt +++ b/modules/rostests/apitests/shlwapi/CMakeLists.txt @@ -17,6 +17,7 @@ list(APPEND SOURCE PathIsUNCServerShare.c PathUnExpandEnvStrings.c PathUnExpandEnvStringsForUser.c + QuerySourceCreateFromKey.cpp SHAreIconsEqual.c SHGetRestriction.c SHInvokeCommandsOnContextMenu.cpp diff --git a/modules/rostests/apitests/shlwapi/QuerySourceCreateFromKey.cpp b/modules/rostests/apitests/shlwapi/QuerySourceCreateFromKey.cpp new file mode 100644 index 00000000000..e78080b6696 --- /dev/null +++ b/modules/rostests/apitests/shlwapi/QuerySourceCreateFromKey.cpp @@ -0,0 +1,258 @@ +/* + * PROJECT: ReactOS api tests + * LICENSE: GPL-2.0-or-later (https://spdx.org/licenses/GPL-2.0-or-later) + * PURPOSE: Tests for QuerySourceCreateFromKey + * COPYRIGHT: Copyright 2026 Katayama Hirofumi MZ + */ + +#include +#include +#include +#include +#include +#include +#include + +typedef HRESULT (WINAPI *FN_QuerySourceCreateFromKey)(HKEY, PCWSTR, BOOL, REFIID, PVOID*); +static FN_QuerySourceCreateFromKey g_pQuerySourceCreateFromKey = NULL; + +static const WCHAR k_Root[] = L"Software\\QuerySrcTest"; +static const WCHAR k_SubKeyA[] = L"SubKeyA"; +static const WCHAR k_SubKeyB[] = L"SubKeyB"; + +static void SetupRegistry(void) +{ + HKEY hRoot; + RegCreateKeyExW(HKEY_CURRENT_USER, k_Root, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hRoot, NULL); + + static const WCHAR valA[] = L"hello"; + RegSetValueExW(hRoot, L"ValueA", 0, REG_SZ, (const BYTE*)valA, (DWORD)sizeof(valA)); + + DWORD dwValue = 0xBEEFCAFE; + RegSetValueExW(hRoot, L"ValueB", 0, REG_DWORD, (const BYTE*)&dwValue, (DWORD)sizeof(dwValue)); + + HKEY hSub; + RegCreateKeyExW(hRoot, k_SubKeyA, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hSub, NULL); + RegCloseKey(hSub); + RegCreateKeyExW(hRoot, k_SubKeyB, 0, NULL, 0, KEY_ALL_ACCESS, NULL, &hSub, NULL); + RegCloseKey(hSub); + + RegCloseKey(hRoot); +} + +static void CleanupRegistry(void) +{ + SHDeleteKeyW(HKEY_CURRENT_USER, k_Root); +} + +static void Test_EnumValues(void) +{ + IQuerySourceOld *pSrc = NULL; + HRESULT hr = g_pQuerySourceCreateFromKey(HKEY_CURRENT_USER, k_Root, FALSE, + IID_IQuerySourceOld, (PVOID*)&pSrc); + ok_hr(hr, S_OK); + ok(pSrc != NULL, "pSrc was NULL\n"); + + IEnumString *pEnum = NULL; + hr = 0xDEADFACE; + if (pSrc) + hr = pSrc->EnumValues(&pEnum); + ok(pSrc && hr == S_OK, "EnumValues failed: 0x%08X\n", hr); + + LPWSTR psz = NULL; + ULONG fetched = 0; + hr = 0xDEADFACE; + + if (pEnum) + hr = pEnum->Next(1, &psz, &fetched); + ok_hr(hr, S_OK); + ok(lstrcmpiW(psz, L"ValueA") == 0, "psz was %s\n", wine_dbgstr_w(psz)); + ok_int(fetched, 1); + CoTaskMemFree(psz); + + psz = NULL; + fetched = 0; + hr = 0xDEADFACE; + + if (pEnum) + hr = pEnum->Next(1, &psz, &fetched); + ok_hr(hr, S_OK); + ok(lstrcmpiW(psz, L"ValueB") == 0, "psz was %s\n", wine_dbgstr_w(psz)); + ok_int(fetched, 1); + CoTaskMemFree(psz); + + psz = NULL; + fetched = 0; + hr = 0xDEADFACE; + + if (pEnum) + hr = pEnum->Next(1, &psz, &fetched); + ok_hr(hr, S_FALSE); + ok(psz == NULL, "psz was %s\n", wine_dbgstr_w(psz)); + ok_int(fetched, 0); + CoTaskMemFree(psz); + + if (pEnum) + pEnum->Release(); + if (pSrc) + pSrc->Release(); +} + +static void Test_EnumSources(void) +{ + IQuerySourceOld *pSrc = NULL; + HRESULT hr = g_pQuerySourceCreateFromKey(HKEY_CURRENT_USER, k_Root, FALSE, + IID_IQuerySourceOld, (PVOID*)&pSrc); + ok_hr(hr, S_OK); + ok(pSrc != NULL, "pSrc was NULL\n"); + + IEnumString *pEnum = NULL; + hr = pSrc->EnumSources(&pEnum); + ok_hr(hr, S_OK); + ok(pEnum != NULL, "pEnum was NULL\n"); + + LPWSTR psz = NULL; + ULONG fetched = 0; + hr = 0xDEADFACE; + + if (pEnum) + hr = pEnum->Next(1, &psz, &fetched); + ok_hr(hr, S_OK); + ok(lstrcmpiW(psz, k_SubKeyA) == 0, "psz was %s\n", wine_dbgstr_w(psz)); + ok_int(fetched, 1); + CoTaskMemFree(psz); + + psz = NULL; + fetched = 0; + hr = 0xDEADFACE; + + if (pEnum) + hr = pEnum->Next(1, &psz, &fetched); + ok_hr(hr, S_OK); + ok(lstrcmpiW(psz, k_SubKeyB) == 0, "psz was %s\n", wine_dbgstr_w(psz)); + ok_int(fetched, 1); + CoTaskMemFree(psz); + + psz = NULL; + fetched = 0; + hr = 0xDEADFACE; + + if (pEnum) + hr = pEnum->Next(1, &psz, &fetched); + ok_hr(hr, S_FALSE); + ok(psz == NULL, "psz was %s\n", wine_dbgstr_w(psz)); + ok_int(fetched, 0); + CoTaskMemFree(psz); + + if (pSrc) + pSrc->Release(); +} + +static void Test_CheckValues(void) +{ + IQuerySourceOld *pSrc = NULL; + HRESULT hr = g_pQuerySourceCreateFromKey(HKEY_CURRENT_USER, k_Root, FALSE, + IID_IQuerySourceOld, (PVOID*)&pSrc); + ok_hr(hr, S_OK); + ok(pSrc != NULL, "pSrc was NULL\n"); + + PWSTR pszValue; + + // QueryValueString + hr = 0xDEADFACE; + pszValue = NULL; + if (pSrc) + hr = pSrc->QueryValueString(NULL, L"ValueA", &pszValue); + ok_hr(hr, S_OK); + ok(lstrcmpiW(pszValue, L"hello") == 0, "pszValue was %s\n", wine_dbgstr_w(pszValue)); + + hr = 0xDEADFACE; + pszValue = NULL; + if (pSrc) + hr = pSrc->QueryValueString(NULL, L"ValueB", &pszValue); + ok_hr(hr, E_DATATYPE_MISMATCH); + ok(pszValue == NULL, "pszValue was %s\n", wine_dbgstr_w(pszValue)); + + // QueryValueExists + hr = 0xDEADFACE; + if (pSrc) + hr = pSrc->QueryValueExists(NULL, L"ValueA"); + ok_hr(hr, S_OK); + if (pSrc) + hr = pSrc->QueryValueExists(NULL, L"ValueB"); + ok_hr(hr, S_OK); + + DWORD dwValue; + + // QueryValueDword + dwValue = 0xDEADFACE; + hr = 0xDEADFACE; + if (pSrc) + hr = pSrc->QueryValueDword(NULL, L"ValueA", &dwValue); + ok_hr(hr, HRESULT_FROM_WIN32(ERROR_MORE_DATA)); + ok_long(dwValue, 0xDEADFACE); + + dwValue = 0xDEADFACE; + hr = 0xDEADFACE; + if (pSrc) + hr = pSrc->QueryValueDword(NULL, L"ValueB", &dwValue); + ok_hr(hr, S_OK); + ok_long(dwValue, 0xBEEFCAFE); + + FLAGGED_BYTE_BLOB *pBlob; + + // QueryValueDirect + hr = 0xDEADFACE; + pBlob = NULL; + if (pSrc) + hr = pSrc->QueryValueDirect(NULL, L"ValueA", &pBlob); + ok_hr(hr, S_OK); + ok(pBlob != NULL, "pBlob was %p\n", pBlob); + ok(pBlob && pBlob->clSize == 12, "pBlob->clSize was %ld\n", pBlob->clSize); + ok(pBlob && !memcmp(pBlob->abData, L"hello", 12), "pBlob->abData mismatch\n"); + CoTaskMemFree(pBlob); + + hr = 0xDEADFACE; + pBlob = NULL; + if (pSrc) + hr = pSrc->QueryValueDirect(NULL, L"ValueB", &pBlob); + ok_hr(hr, S_OK); + ok(pBlob != NULL, "pBlob was %p\n", pBlob); + ok(pBlob && pBlob->clSize == 4, "pBlob->clSize was %ld\n", pBlob->clSize); + dwValue = 0xBEEFCAFE; + ok(pBlob && !memcmp(pBlob->abData, &dwValue, sizeof(dwValue)), "pBlob->abData mismatch\n"); + CoTaskMemFree(pBlob); + + if (pSrc) + pSrc->Release(); +} + +START_TEST(QuerySourceCreateFromKey) +{ + if (IsWindowsVistaOrGreater()) + { + skip("Vista+ is not tested well\n"); + return; + } + + g_pQuerySourceCreateFromKey = (FN_QuerySourceCreateFromKey) + GetProcAddress(GetModuleHandleA("shlwapi"), MAKEINTRESOURCEA(544)); + if (!g_pQuerySourceCreateFromKey) + { + skip("QuerySourceCreateFromKey not found\n"); + return; + } + + HRESULT hrCoInit = CoInitialize(NULL); + + SetupRegistry(); + + Test_EnumValues(); + Test_EnumSources(); + Test_CheckValues(); + + CleanupRegistry(); + + if (SUCCEEDED(hrCoInit)) + CoUninitialize(); +} diff --git a/modules/rostests/apitests/shlwapi/testlist.c b/modules/rostests/apitests/shlwapi/testlist.c index d6f9f24f61f..33f6089ac4f 100644 --- a/modules/rostests/apitests/shlwapi/testlist.c +++ b/modules/rostests/apitests/shlwapi/testlist.c @@ -13,6 +13,7 @@ extern void func_PathIsUNCServer(void); extern void func_PathIsUNCServerShare(void); extern void func_PathUnExpandEnvStrings(void); extern void func_PathUnExpandEnvStringsForUser(void); +extern void func_QuerySourceCreateFromKey(void); extern void func_SHAreIconsEqual(void); extern void func_SHGetRestriction(void); extern void func_SHInvokeCommandsOnContextMenu(void); @@ -37,6 +38,7 @@ const struct test winetest_testlist[] = { "PathIsUNCServerShare", func_PathIsUNCServerShare }, { "PathUnExpandEnvStrings", func_PathUnExpandEnvStrings }, { "PathUnExpandEnvStringsForUser", func_PathUnExpandEnvStringsForUser }, + { "QuerySourceCreateFromKey", func_QuerySourceCreateFromKey }, { "SHAreIconsEqual", func_SHAreIconsEqual }, { "SHGetRestriction", func_SHGetRestriction }, { "SHInvokeCommandsOnContextMenu", func_SHInvokeCommandsOnContextMenu }, diff --git a/sdk/include/reactos/shlobj_undoc.h b/sdk/include/reactos/shlobj_undoc.h index 0e0e5f2f0d2..947bfc1f98f 100644 --- a/sdk/include/reactos/shlobj_undoc.h +++ b/sdk/include/reactos/shlobj_undoc.h @@ -888,7 +888,7 @@ DECLARE_INTERFACE_(IQuerySourceOld, IUnknown) // {C7478486-7583-49E7-A6C2-FAF8F0 STDMETHOD(QueryValueExists)(THIS_ PCWSTR keyName, PCWSTR valueName) PURE; STDMETHOD(QueryValueDirect)(THIS_ PCWSTR keyName, PCWSTR valueName, FLAGGED_BYTE_BLOB **ppBlob) PURE; STDMETHOD(OpenSource)(THIS_ PCWSTR keyName, BOOL bCreate, IQuerySourceOld **ppSource) PURE; - STDMETHOD(SetValueDirect)(THIS_ PCWSTR keyName, PCWSTR valueName, DWORD, DWORD, PBYTE) PURE; + STDMETHOD(SetValueDirect)(THIS_ PCWSTR keyName, PCWSTR valueName, DWORD dwType, DWORD cbData, LPCVOID pbData) PURE; }; #undef INTERFACE diff --git a/sdk/include/reactos/shlwapi_undoc.h b/sdk/include/reactos/shlwapi_undoc.h index 84653a17b27..d760fa08973 100644 --- a/sdk/include/reactos/shlwapi_undoc.h +++ b/sdk/include/reactos/shlwapi_undoc.h @@ -126,6 +126,14 @@ EXTERN_C BOOL WINAPI SHBoolSystemParametersInfo(UINT uiAction, PVOID pvParam); HRESULT WINAPI SHRegGetCLSIDKeyW(REFGUID guid, LPCWSTR lpszValue, BOOL bUseHKCU, BOOL bCreate, PHKEY phKey); +HRESULT WINAPI +QuerySourceCreateFromKey( + _In_ HKEY hKey, + _In_opt_ PCWSTR lpSubKey, + _In_ BOOL bCreate, + _In_ REFIID riid, + _Outptr_ PVOID *ppv); + BOOL WINAPI SHAddDataBlock(LPDBLIST* lppList, const DATABLOCK_HEADER *lpNewItem); BOOL WINAPI SHRemoveDataBlock(LPDBLIST* lppList, DWORD dwSignature); DATABLOCK_HEADER* WINAPI SHFindDataBlock(LPDBLIST lpList, DWORD dwSignature);