mirror of
https://github.com/ApfelTeeSaft/reactos.git
synced 2026-08-26 19:33:31 +00:00
[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 <shlwapi_undoc.h>. - Modify shlwapi.spec. - Add QuerySourceCreateFromKey testcase.
This commit is contained in:
@@ -27,6 +27,7 @@ list(APPEND PCH_SKIP_SOURCE
|
||||
assoc.c
|
||||
policy.cpp
|
||||
propbag.cpp
|
||||
querysrc.cpp
|
||||
utils.cpp
|
||||
wsprintf.c
|
||||
zonechk.c
|
||||
|
||||
@@ -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 <[email protected]>
|
||||
*/
|
||||
|
||||
#include "precomp.h"
|
||||
#include <shlwapi.h>
|
||||
#include <shlwapi_undoc.h>
|
||||
#include <shlobj_undoc.h>
|
||||
#include <shlguid_undoc.h>
|
||||
|
||||
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<IEnumString*>(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<IQuerySourceOld*>(this);
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
if (riid == IID_IObjectWithRegistryKeyOld)
|
||||
{
|
||||
*ppvObject = static_cast<IObjectWithRegistryKeyOld*>(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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -17,6 +17,7 @@ list(APPEND SOURCE
|
||||
PathIsUNCServerShare.c
|
||||
PathUnExpandEnvStrings.c
|
||||
PathUnExpandEnvStringsForUser.c
|
||||
QuerySourceCreateFromKey.cpp
|
||||
SHAreIconsEqual.c
|
||||
SHGetRestriction.c
|
||||
SHInvokeCommandsOnContextMenu.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 <[email protected]>
|
||||
*/
|
||||
|
||||
#include <apitest.h>
|
||||
#include <shlobj.h>
|
||||
#include <shlwapi.h>
|
||||
#include <shlguid_undoc.h>
|
||||
#include <shlobj_undoc.h>
|
||||
#include <shlwapi_undoc.h>
|
||||
#include <versionhelpers.h>
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -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 },
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user