diff --git a/dll/win32/shlwapi/CMakeLists.txt b/dll/win32/shlwapi/CMakeLists.txt index caed6103141..295565e4155 100644 --- a/dll/win32/shlwapi/CMakeLists.txt +++ b/dll/win32/shlwapi/CMakeLists.txt @@ -25,6 +25,7 @@ list(APPEND SOURCE list(APPEND PCH_SKIP_SOURCE assoc.c + policy.cpp propbag.cpp utils.cpp wsprintf.c diff --git a/dll/win32/shlwapi/policy.cpp b/dll/win32/shlwapi/policy.cpp new file mode 100644 index 00000000000..7a9fe99ee59 --- /dev/null +++ b/dll/win32/shlwapi/policy.cpp @@ -0,0 +1,311 @@ +/* + * PROJECT: ReactOS shlwapi + * LICENSE: LGPL-2.0-or-later (https://spdx.org/licenses/LGPL-2.0-or-later) + * PURPOSE: Implement SHWindowsPolicyGetValue + * COPYRIGHT: Copyright 2026 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com) + */ + +#define _ATL_NO_EXCEPTIONS +#include +#include +#include +#include +#include +#include +#include +#include + +WINE_DEFAULT_DEBUG_CHANNEL(policy); + +typedef enum tagPOLICY_STATE +{ + POLICY_STATE_UNCACHED = 0, // Uncached + POLICY_STATE_NOT_FOUND = 1, // Not found + POLICY_STATE_CACHED = 2, // Cached +} POLICY_STATE; + +// Result +typedef struct tagSHPOLICY_RESULT +{ + POLICY_STATE state; + DWORD dwValue; +} SHPOLICY_RESULT, *PSHPOLICY_RESULT; + +// Constraints +typedef struct tagSHPOLICY_CONSTRAINT +{ + WORD wFlags; + WORD wMinSize; + DWORD dwMin; + DWORD dwMax; +} SHPOLICY_CONSTRAINT, *PSHPOLICY_CONSTRAINT; + +static const SHPOLICY_CONSTRAINT c_Bool = { SRRF_RT_DWORD, sizeof(DWORD), 0, 1 }; +static const SHPOLICY_CONSTRAINT c_String = { SRRF_RT_REG_SZ, sizeof(WCHAR), 0, 0 }; +static const SHPOLICY_CONSTRAINT c_TriValue = { SRRF_RT_DWORD, sizeof(DWORD), 1, 3 }; +static const SHPOLICY_CONSTRAINT c_Special = { SRRF_RT_DWORD, sizeof(DWORD), 0x1806, 0x1808 }; + +// Items +typedef struct tagSHPOLICY_ITEM +{ + REFGUID rpolid; // POLID (policy descriptor) + PCWSTR pszKeyName; + PCWSTR pszValueName; + const SHPOLICY_CONSTRAINT *pConstraint; +} SHPOLICY_ITEM, *PSHPOLICY_ITEM; + +static const SHPOLICY_ITEM g_PolicyItems[] = +{ + { POLID_UsePathEnvVarForCommandTemplates, L"Explorer", L"UsePathEnvVarForCommandTemplates", + &c_Bool }, + { POLID_ScanWithAntiVirus, L"Attachments", L"ScanWithAntiVirus", &c_TriValue }, + { POLID_SaveZoneInformation, L"Attachments", L"SaveZoneInformation", &c_TriValue }, + { POLID_UseTrustedHandlers, L"Attachments", L"UseTrustedHandlers", &c_TriValue }, + { POLID_HideZoneInfoOnProperties, L"Attachments", L"HideZoneInfoOnProperties", &c_Bool }, + { POLID_DefaultFileTypeRisk, L"Associations", L"DefaultFileTypeRisk", &c_Special }, + { POLID_HighRiskFileTypes, L"Associations", L"HighRiskFileTypes", &c_String }, + { POLID_ModRiskFileTypes, L"Associations", L"ModRiskFileTypes", &c_String }, + { POLID_LowRiskFileTypes, L"Associations", L"LowRiskFileTypes", &c_String }, + { POLID_PreXPSP2ShellProtocolBehavior, L"Explorer", L"PreXPSP2ShellProtocolBehavior", &c_Bool }, + { POLID_CompareJunctionness, L"Explorer", L"CompareJunctionness", &c_Bool }, +}; + +/************************************************************************** + * CPolicyCache + */ +class CPolicyCache +{ +public: + ~CPolicyCache() + { + LocalFree(m_pResults); + + if (m_hGlobalCounter) + CloseHandle(m_hGlobalCounter); + } + + static void* operator new(size_t size) + { + // Returns NULL on failure; caller must check before use. + // NOTE: C++ UB if constructor runs on NULL, but ReactOS convention. + return LocalAlloc(LPTR, size); + } + static void operator delete(void *ptr) + { + LocalFree(ptr); + } + + BOOL Initialize(const SHPOLICY_ITEM *pItems, UINT cItems) + { + m_nCounterValue = MINLONG; + m_pszRootKey = L"Software\\Microsoft\\Windows\\CurrentVersion\\Policies"; + m_pItems = pItems; + m_cItems = cItems; + m_pResults = (PSHPOLICY_RESULT)LocalAlloc(LPTR, cItems * sizeof(SHPOLICY_RESULT)); + if (!m_pResults) + return FALSE; + m_hGlobalCounter = SHGlobalCounterCreate(GUID_Restrictions); + if (!m_hGlobalCounter) + { + LocalFree(m_pResults); + m_pResults = NULL; + return FALSE; + } + return TRUE; + } + + HRESULT GetValue(_In_ REFGUID rpolid, _Out_opt_ PVOID pvValue, _Out_opt_ PDWORD pcbValue); + +protected: + LPCWSTR m_pszRootKey; + HANDLE m_hGlobalCounter; + LONG m_nCounterValue; + DWORD m_cItems; + const SHPOLICY_ITEM *m_pItems; + PSHPOLICY_RESULT m_pResults; + + void _ValidateCachedResults() + { + LONG Value = SHGlobalCounterGetValue(m_hGlobalCounter); + if (m_nCounterValue != Value) + { + m_nCounterValue = Value; + ZeroMemory(m_pResults, m_cItems * sizeof(SHPOLICY_RESULT)); + } + } + + HRESULT _GetValue( + _In_ LPCWSTR pszSubKey, + _In_ LPCWSTR pszValueName, + _In_ const SHPOLICY_CONSTRAINT* pConstraint, + _Out_opt_ PDWORD pdwType, + _Out_opt_ PVOID pvData, + _Inout_opt_ PDWORD pcbData); + + static void _CacheResult( + const SHPOLICY_CONSTRAINT *pConstraint, + PVOID pvValue, + PDWORD pcbValue, + PSHPOLICY_RESULT pResult); +}; + +HRESULT +CPolicyCache::GetValue(_In_ REFGUID rpolid, _Out_opt_ PVOID pvValue, _Out_opt_ PDWORD pcbValue) +{ + _ValidateCachedResults(); + + if (m_cItems == 0) + return E_UNEXPECTED; + + UINT iItem; + for (iItem = 0; iItem < m_cItems; ++iItem) + { + if (IsEqualGUID(m_pItems[iItem].rpolid, rpolid)) + break; + } + + if (iItem >= m_cItems) + return E_UNEXPECTED; + + const SHPOLICY_ITEM *pItem = &m_pItems[iItem]; + PSHPOLICY_RESULT pResult = &m_pResults[iItem]; + + if (pResult->state == POLICY_STATE_NOT_FOUND) + return HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); + + // Use caching only when dealing with DWORD value + if (pvValue && pcbValue && *pcbValue == sizeof(DWORD) && pResult->state == POLICY_STATE_CACHED) + { + *(PDWORD)pvValue = pResult->dwValue; + return S_OK; + } + + HRESULT hr = _GetValue(pItem->pszKeyName, pItem->pszValueName, pItem->pConstraint, NULL, + pvValue, pcbValue); + if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) + pResult->state = POLICY_STATE_NOT_FOUND; + else if (SUCCEEDED(hr) && pvValue) + _CacheResult(pItem->pConstraint, pvValue, pcbValue, pResult); + + return hr; +} + +HRESULT CPolicyCache::_GetValue( + _In_ LPCWSTR pszSubKey, + _In_ LPCWSTR pszValueName, + _In_ const SHPOLICY_CONSTRAINT* pConstraint, + _Out_opt_ PDWORD pdwType, + _Out_opt_ PVOID pvData, + _Inout_opt_ PDWORD pcbData) +{ + CStringW szFullKey = CStringW(m_pszRootKey) + L"\\" + pszSubKey; + + DWORD cbDataSaved = pcbData ? *pcbData : 0; + WORD wFlags = pConstraint->wFlags; + + LSTATUS error = RegGetValueW(HKEY_LOCAL_MACHINE, szFullKey, pszValueName, wFlags, pdwType, + pvData, pcbData); + if (error == ERROR_FILE_NOT_FOUND) + { + if (pcbData) + *pcbData = cbDataSaved; + error = RegGetValueW(HKEY_CURRENT_USER, szFullKey, pszValueName, wFlags, pdwType, + pvData, pcbData); + } + + if (error) + return HRESULT_FROM_WIN32(error); + + if (!pvData) + return S_OK; + + if (pConstraint->wFlags == SRRF_RT_DWORD && pConstraint->wMinSize == sizeof(DWORD)) + { + DWORD dwValue = *(PDWORD)pvData; + if (dwValue < pConstraint->dwMin || pConstraint->dwMax < dwValue) + return E_DATATYPE_MISMATCH; + } + + return S_OK; +} + +void +CPolicyCache::_CacheResult( + const SHPOLICY_CONSTRAINT *pConstraint, + PVOID pvValue, + PDWORD pcbValue, + PSHPOLICY_RESULT pResult) +{ + UNREFERENCED_PARAMETER(pcbValue); // Reserved for future use + + // Use caching only when dealing with DWORD value + if (pConstraint->wFlags == SRRF_RT_DWORD) + { + pResult->dwValue = *(PDWORD)pvValue; + pResult->state = POLICY_STATE_CACHED; + } +} + +/***************************************************************************/ + +CPolicyCache* g_pPolicyCache = NULL; +CRITICAL_SECTION g_csPolicyLock; + +// This function must be called under g_csPolicyLock +static BOOL SHPolicyCache_Create(VOID) +{ + if (g_pPolicyCache) + return TRUE; + + CPolicyCache *pCache = new CPolicyCache; + if (!pCache) + return FALSE; + + if (!pCache->Initialize(g_PolicyItems, _countof(g_PolicyItems))) + { + delete pCache; + return FALSE; + } + + g_pPolicyCache = pCache; + return TRUE; +} + +EXTERN_C VOID SHPolicyCache_DllProcessAttach(VOID) +{ + InitializeCriticalSection(&g_csPolicyLock); +} + +EXTERN_C VOID SHPolicyCache_DllProcessDetach(VOID) +{ + CPolicyCache* pCache; + + EnterCriticalSection(&g_csPolicyLock); + pCache = g_pPolicyCache; + g_pPolicyCache = NULL; + LeaveCriticalSection(&g_csPolicyLock); + + delete pCache; + DeleteCriticalSection(&g_csPolicyLock); +} + +/************************************************************************** + * SHWindowsPolicyGetValue (SHLWAPI.560) + * + * https://www.geoffchappell.com/studies/windows/shell/shlwapi/api/winpolicy/getvalue.htm + */ +EXTERN_C +HRESULT WINAPI +SHWindowsPolicyGetValue( + _In_ REFGUID rpolid, + _Out_opt_ PVOID pvValue, + _Out_opt_ PDWORD pcbValue) +{ + HRESULT hr = E_FAIL; + + EnterCriticalSection(&g_csPolicyLock); + if (SHPolicyCache_Create()) + hr = g_pPolicyCache->GetValue(rpolid, pvValue, pcbValue); + LeaveCriticalSection(&g_csPolicyLock); + return hr; +} diff --git a/dll/win32/shlwapi/shlwapi.spec b/dll/win32/shlwapi/shlwapi.spec index d5946393494..5baf5519dc3 100644 --- a/dll/win32/shlwapi/shlwapi.spec +++ b/dll/win32/shlwapi/shlwapi.spec @@ -557,7 +557,7 @@ 557 stub -noname SHCoCreateExtension 558 stub -noname SHCoExtensionCollectStats 559 stub -noname SHGetSignatureInfo -560 stdcall -stub -noname SHWindowsPolicyGetValue(ptr ptr ptr) +560 stdcall -noname SHWindowsPolicyGetValue(ptr ptr ptr) 561 stub -noname AssocGetUrlAction 562 stub -noname SHGetPrivateProfileInt 563 stdcall -stub -noname SHGetPrivateProfileSection(wstr ptr long ptr) diff --git a/dll/win32/shlwapi/shlwapi_main.c b/dll/win32/shlwapi/shlwapi_main.c index 71e45a8d1f8..97385915c6a 100644 --- a/dll/win32/shlwapi/shlwapi_main.c +++ b/dll/win32/shlwapi/shlwapi_main.c @@ -38,6 +38,8 @@ extern CRITICAL_SECTION g_csZoneMgrLock; extern CRITICAL_SECTION g_csBagCacheLock; VOID FreeViewStatePropertyBagCache(VOID); VOID SHLWAPI_DeleteCachedZonesManager(VOID); +VOID SHPolicyCache_DllProcessAttach(VOID); +VOID SHPolicyCache_DllProcessDetach(VOID); #endif /************************************************************************* @@ -72,6 +74,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID fImpLoad) #ifdef __REACTOS__ InitializeCriticalSection(&g_csZoneMgrLock); InitializeCriticalSection(&g_csBagCacheLock); + SHPolicyCache_DllProcessAttach(); #endif break; case DLL_PROCESS_DETACH: @@ -81,6 +84,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID fImpLoad) SHLWAPI_DeleteCachedZonesManager(); DeleteCriticalSection(&g_csBagCacheLock); DeleteCriticalSection(&g_csZoneMgrLock); + SHPolicyCache_DllProcessDetach(); #endif if (SHLWAPI_ThreadRef_index != TLS_OUT_OF_INDEXES) TlsFree(SHLWAPI_ThreadRef_index); break; diff --git a/sdk/include/reactos/shlguid_undoc.h b/sdk/include/reactos/shlguid_undoc.h index 12d3b5ae8b1..79d90d60b81 100644 --- a/sdk/include/reactos/shlguid_undoc.h +++ b/sdk/include/reactos/shlguid_undoc.h @@ -203,6 +203,19 @@ DEFINE_GUID(IID_IDriveFolderExt, 0x3EC36F3E, 0x5BA3, 0x4C3D, 0xBF, 0x3 DEFINE_GUID(IID_IPinnedListOld, 0xC3C6EB6D, 0xC837, 0x4EAE, 0xB1, 0x72, 0x5F, 0xEC, 0x52, 0xA2, 0xA4, 0xFD); /*version 6.0*/ DEFINE_GUID(IID_IPinnedList, 0xBBD20037, 0xBC0E, 0x42F1, 0x91, 0x3F, 0xE2, 0x93, 0x6B, 0xB0, 0xEA, 0x0C); /*version 6.1*/ +DEFINE_GUID(GUID_Restrictions, 0xA48F1A32, 0xA340, 0x11D1, 0xBC, 0x6B, 0x00, 0xA0, 0xC9, 0x03, 0x12, 0xE1); +DEFINE_GUID(POLID_UsePathEnvVarForCommandTemplates, 0x1FF8F603, 0x056A, 0x4F52, 0x89, 0xCC, 0x82, 0xAD, 0x1C, 0xA1, 0xC1, 0x9A); +DEFINE_GUID(POLID_ScanWithAntiVirus, 0xECF90404, 0xF38C, 0x44FE, 0x9F, 0x1B, 0x02, 0xA7, 0x2B, 0xAF, 0x60, 0x3C); +DEFINE_GUID(POLID_SaveZoneInformation, 0xECF90405, 0xF38C, 0x44FE, 0x9F, 0x1B, 0x02, 0xA7, 0x2B, 0xAF, 0x60, 0x3C); +DEFINE_GUID(POLID_UseTrustedHandlers, 0xECF90406, 0xF38C, 0x44FE, 0x9F, 0x1B, 0x02, 0xA7, 0x2B, 0xAF, 0x60, 0x3C); +DEFINE_GUID(POLID_HideZoneInfoOnProperties, 0xECF90408, 0xF38C, 0x44FE, 0x9F, 0x1B, 0x02, 0xA7, 0x2B, 0xAF, 0x60, 0x3C); +DEFINE_GUID(POLID_DefaultFileTypeRisk, 0xECF90409, 0xF38C, 0x44FE, 0x9F, 0x1B, 0x02, 0xA7, 0x2B, 0xAF, 0x60, 0x3C); +DEFINE_GUID(POLID_HighRiskFileTypes, 0xECF9040A, 0xF38C, 0x44FE, 0x9F, 0x1B, 0x02, 0xA7, 0x2B, 0xAF, 0x60, 0x3C); +DEFINE_GUID(POLID_ModRiskFileTypes, 0xECF9040B, 0xF38C, 0x44FE, 0x9F, 0x1B, 0x02, 0xA7, 0x2B, 0xAF, 0x60, 0x3C); +DEFINE_GUID(POLID_LowRiskFileTypes, 0xECF9040C, 0xF38C, 0x44FE, 0x9F, 0x1B, 0x02, 0xA7, 0x2B, 0xAF, 0x60, 0x3C); +DEFINE_GUID(POLID_PreXPSP2ShellProtocolBehavior, 0x4FC60822, 0x47AF, 0x4BEE, 0xA7, 0xDA, 0xC9, 0x9A, 0x6E, 0x8E, 0x5D, 0x8D); +DEFINE_GUID(POLID_CompareJunctionness, 0xB3AB7D3B, 0x698F, 0x401E, 0xBF, 0x89, 0x30, 0x02, 0x33, 0x35, 0xA2, 0x67); + #define CGID_IExplorerToolbar IID_IExplorerToolbar #define SID_IExplorerToolbar IID_IExplorerToolbar #define SID_ITargetFrame2 IID_ITargetFrame2 diff --git a/sdk/include/reactos/shlwapi_undoc.h b/sdk/include/reactos/shlwapi_undoc.h index b86de689c6d..30f752a6a8d 100644 --- a/sdk/include/reactos/shlwapi_undoc.h +++ b/sdk/include/reactos/shlwapi_undoc.h @@ -497,6 +497,14 @@ PWSTR WINAPI CharLowerNoDBCSW(_Inout_ PWSTR lpString); PSTR WINAPI CharUpperNoDBCSA(_Inout_ PSTR lpString); PWSTR WINAPI CharUpperNoDBCSW(_Inout_ PWSTR lpString); +HRESULT WINAPI +SHWindowsPolicyGetValue( + _In_ REFGUID rpolid, + _Out_opt_ PVOID pvValue, + _Out_opt_ PDWORD pcbValue); + +#define E_DATATYPE_MISMATCH HRESULT_FROM_WIN32(ERROR_DATATYPE_MISMATCH) + /***************************************************************************** * IAssociationElementOld interface *