https://git.reactos.org/?p=reactos.git;a=commitdiff;h=1b5f6c2dc0bb62228a7e329510ec392563380932
commit 1b5f6c2dc0bb62228a7e329510ec392563380932 Author: Katayama Hirofumi MZ <katayama.hirofumi...@gmail.com> AuthorDate: Tue Jan 28 21:05:40 2025 +0900 Commit: GitHub <nore...@github.com> CommitDate: Tue Jan 28 21:05:40 2025 +0900 [UXTHEME][UXTHEME_APITEST][SDK] GetThemeParseErrorInfo (#7662) Implementing missing features... JIRA issue: CORE-12805 - Add dll/win32/uxtheme/errinfo.c. - Implement GetThemeParseErrorInfo function in errinfo.c. - Modify uxtheme.spec. - Add GetThemeParseErrorInfo prototype to <uxundoc.h>. - Adapt <uxundoc.h> to C++. - Add global variable gdwErrorInfoTlsIndex. - Add UXTHEME_UnInitSystem function. --- dll/win32/uxtheme/CMakeLists.txt | 1 + dll/win32/uxtheme/errinfo.c | 150 +++++++++++++++++++++ dll/win32/uxtheme/main.c | 7 +- dll/win32/uxtheme/system.c | 15 +++ dll/win32/uxtheme/uxtheme.spec | 2 +- dll/win32/uxtheme/uxthemep.h | 40 ++++++ modules/rostests/apitests/uxtheme/CMakeLists.txt | 1 + .../apitests/uxtheme/GetThemeParseErrorInfo.c | 92 +++++++++++++ modules/rostests/apitests/uxtheme/testlist.c | 2 + sdk/include/reactos/uxundoc.h | 19 +++ 10 files changed, 327 insertions(+), 2 deletions(-) diff --git a/dll/win32/uxtheme/CMakeLists.txt b/dll/win32/uxtheme/CMakeLists.txt index 92a6f0cb292..44ac0cd0eb4 100644 --- a/dll/win32/uxtheme/CMakeLists.txt +++ b/dll/win32/uxtheme/CMakeLists.txt @@ -6,6 +6,7 @@ spec2def(uxtheme.dll uxtheme.spec ADD_IMPORTLIB) list(APPEND SOURCE buffer.c draw.c + errinfo.c main.c metric.c msstyles.c diff --git a/dll/win32/uxtheme/errinfo.c b/dll/win32/uxtheme/errinfo.c new file mode 100644 index 00000000000..40abe56e802 --- /dev/null +++ b/dll/win32/uxtheme/errinfo.c @@ -0,0 +1,150 @@ +/* + * PROJECT: ReactOS uxtheme.dll + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: UXTHEME error reporting helpers + * COPYRIGHT: Copyright 2025 Katayama Hirofumi MZ <katayama.hirofumi...@gmail.com> + */ + +#include "uxthemep.h" +#include <stdlib.h> +#include <strsafe.h> + +PTMERRINFO +UXTHEME_GetParseErrorInfo(_In_ BOOL bCreate) +{ + PTMERRINFO pErrInfo; + + if (gdwErrorInfoTlsIndex == TLS_OUT_OF_INDEXES) + return NULL; + + pErrInfo = TlsGetValue(gdwErrorInfoTlsIndex); + if (pErrInfo) + return pErrInfo; + + if (bCreate) + { + pErrInfo = LocalAlloc(LPTR, sizeof(*pErrInfo)); + TlsSetValue(gdwErrorInfoTlsIndex, pErrInfo); + } + + return pErrInfo; +} + +VOID +UXTHEME_DeleteParseErrorInfo(VOID) +{ + PTMERRINFO pErrInfo = UXTHEME_GetParseErrorInfo(FALSE); + if (!pErrInfo) + return; + + TlsSetValue(gdwErrorInfoTlsIndex, NULL); + LocalFree(pErrInfo); +} + +static BOOL +UXTHEME_FormatLocalMsg( + _In_ HINSTANCE hInstance, + _In_ UINT uID, + _Out_ LPWSTR pszDest, + _In_ SIZE_T cchDest, + _In_ LPCWSTR pszDrive, + _In_ PTMERRINFO pErrInfo) +{ + WCHAR szFormat[MAX_PATH]; + LPCWSTR args[2] = { pErrInfo->szParam1, pErrInfo->szParam2 }; + + if (!LoadStringW(hInstance, uID, szFormat, _countof(szFormat)) || !szFormat[0]) + return FALSE; + + return FormatMessageW(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY, + szFormat, 0, 0, pszDest, cchDest, (va_list *)args) != 0; +} + +static HRESULT +UXTHEME_FormatParseMessage( + _In_ PTMERRINFO pErrInfo, + _Out_ LPWSTR pszDest, + _In_ SIZE_T cchDest) +{ + DWORD nID; + HMODULE hMod, hUxTheme; + WCHAR szFullPath[_MAX_PATH]; + WCHAR szDrive[_MAX_DRIVE + 1], szDir[_MAX_DIR], szFileName[_MAX_FNAME], szExt[_MAX_EXT]; + BOOL ret; + HRESULT hr; + + nID = LOWORD(pErrInfo->nID); + if (!GetModuleFileNameW(NULL, szFullPath, _countof(szFullPath))) + return S_OK; + + _wsplitpath(szFullPath, szDrive, szDir, szFileName, szExt); + if (lstrcmpiW(szFileName, L"packthem") == 0) + { + hMod = GetModuleHandleW(NULL); + if (UXTHEME_FormatLocalMsg(hMod, nID, pszDest, cchDest, szDrive, pErrInfo)) + return S_OK; + } + + hUxTheme = LoadLibraryW(L"uxtheme.dll"); + if (!hUxTheme) + return E_FAIL; + + ret = UXTHEME_FormatLocalMsg(hUxTheme, nID, pszDest, cchDest, szDrive, pErrInfo); + hr = (ret ? S_OK : UXTHEME_MakeLastError()); + FreeLibrary(hUxTheme); + + return hr; +} + +// Parser should use this function on failure +HRESULT +UXTHEME_MakeParseError( + _In_ UINT nID, + _In_ LPCWSTR pszParam1, + _In_ LPCWSTR pszParam2, + _In_ LPCWSTR pszFile, + _In_ LPCWSTR pszLine, + _In_ INT nLineNo) +{ + PTMERRINFO pErrInfo = UXTHEME_GetParseErrorInfo(TRUE); + if (pErrInfo) + { + pErrInfo->nID = nID; + pErrInfo->nLineNo = nLineNo; + StringCchCopyW(pErrInfo->szParam1, _countof(pErrInfo->szParam1), pszParam1); + StringCchCopyW(pErrInfo->szParam2, _countof(pErrInfo->szParam2), pszParam2); + StringCchCopyW(pErrInfo->szFile, _countof(pErrInfo->szFile), pszFile); + StringCchCopyW(pErrInfo->szLine, _countof(pErrInfo->szLine), pszLine); + } + return HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY); +} + +/************************************************************************* + * GetThemeParseErrorInfo (UXTHEME.48) + */ +HRESULT WINAPI +GetThemeParseErrorInfo(_Inout_ PPARSE_ERROR_INFO pInfo) +{ + PTMERRINFO pErrInfo; + HRESULT hr; + + if (!pInfo) + return E_POINTER; + + if (pInfo->cbSize != sizeof(*pInfo)) + return E_INVALIDARG; + + pErrInfo = UXTHEME_GetParseErrorInfo(TRUE); + if (!pErrInfo) + return E_OUTOFMEMORY; + + hr = UXTHEME_FormatParseMessage(pErrInfo, pInfo->szDescription, _countof(pInfo->szDescription)); + if (FAILED(hr)) + return hr; + + pInfo->nID = pErrInfo->nID; + pInfo->nLineNo = pErrInfo->nLineNo; + StringCchCopyW(pInfo->szFile, _countof(pInfo->szFile), pErrInfo->szFile); + StringCchCopyW(pInfo->szLine, _countof(pInfo->szLine), pErrInfo->szLine); + return hr; +} diff --git a/dll/win32/uxtheme/main.c b/dll/win32/uxtheme/main.c index 4eb6eb33761..9c86347e2bc 100644 --- a/dll/win32/uxtheme/main.c +++ b/dll/win32/uxtheme/main.c @@ -22,7 +22,6 @@ /***********************************************************************/ -/* For the moment, do nothing here. */ BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpv) { TRACE("%p 0x%x %p: stub\n", hInstDLL, fdwReason, lpv); @@ -32,6 +31,12 @@ BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpv) UXTHEME_InitSystem(hInstDLL); break; case DLL_PROCESS_DETACH: + UXTHEME_UnInitSystem(hInstDLL); + break; + case DLL_THREAD_ATTACH: + break; + case DLL_THREAD_DETACH: + UXTHEME_DeleteParseErrorInfo(); break; } return TRUE; diff --git a/dll/win32/uxtheme/system.c b/dll/win32/uxtheme/system.c index eaf8faf924c..c998be4f120 100644 --- a/dll/win32/uxtheme/system.c +++ b/dll/win32/uxtheme/system.c @@ -24,6 +24,8 @@ #include <winreg.h> #include <uxundoc.h> +DWORD gdwErrorInfoTlsIndex = TLS_OUT_OF_INDEXES; + /*********************************************************************** * Defines and global variables */ @@ -588,6 +590,19 @@ void UXTHEME_InitSystem(HINSTANCE hInst) RtlInitializeHandleTable(0xFFF, sizeof(UXTHEME_HANDLE), &g_UxThemeHandleTable); g_cHandles = 0; + + gdwErrorInfoTlsIndex = TlsAlloc(); +} + +/*********************************************************************** + * UXTHEME_UnInitSystem + */ +void UXTHEME_UnInitSystem(HINSTANCE hInst) +{ + UXTHEME_DeleteParseErrorInfo(); + + TlsFree(gdwErrorInfoTlsIndex); + gdwErrorInfoTlsIndex = TLS_OUT_OF_INDEXES; } /*********************************************************************** diff --git a/dll/win32/uxtheme/uxtheme.spec b/dll/win32/uxtheme/uxtheme.spec index 632cfe84a00..17d90eef273 100644 --- a/dll/win32/uxtheme/uxtheme.spec +++ b/dll/win32/uxtheme/uxtheme.spec @@ -45,7 +45,7 @@ 45 stdcall -noname ClassicSystemParametersInfoW(long long ptr long) 46 stdcall -noname ClassicAdjustWindowRectEx(ptr long long long) 47 stdcall DrawThemeBackgroundEx(ptr ptr long long ptr ptr) -48 stub -noname GetThemeParseErrorInfo +48 stdcall -noname GetThemeParseErrorInfo(ptr) 49 stdcall GetThemeAppProperties() 50 stdcall GetThemeBackgroundContentRect(ptr ptr long long ptr ptr) 51 stdcall GetThemeBackgroundExtent(ptr ptr long long ptr ptr) diff --git a/dll/win32/uxtheme/uxthemep.h b/dll/win32/uxtheme/uxthemep.h index 5a047d7fcdd..71c4da68aaa 100644 --- a/dll/win32/uxtheme/uxthemep.h +++ b/dll/win32/uxtheme/uxthemep.h @@ -90,6 +90,16 @@ typedef struct _THEME_FILE { PTHEME_IMAGE images; } THEME_FILE, *PTHEME_FILE; +typedef struct tagTMERRINFO +{ + UINT nID; + WCHAR szParam1[MAX_PATH]; + WCHAR szParam2[MAX_PATH]; + WCHAR szFile[MAX_PATH]; + WCHAR szLine[MAX_PATH]; + INT nLineNo; +} TMERRINFO, *PTMERRINFO; + typedef struct _UXINI_FILE *PUXINI_FILE; typedef struct _UXTHEME_HANDLE @@ -276,6 +286,7 @@ extern ATOM atWndContext; extern BOOL g_bThemeHooksActive; void UXTHEME_InitSystem(HINSTANCE hInst); +void UXTHEME_UnInitSystem(HINSTANCE hInst); void UXTHEME_LoadTheme(BOOL bLoad); BOOL CALLBACK UXTHEME_broadcast_theme_changed (HWND hWnd, LPARAM enable); @@ -286,4 +297,33 @@ BOOL CALLBACK UXTHEME_broadcast_theme_changed (HWND hWnd, LPARAM enable); /* Full alpha blending */ #define ALPHABLEND_FULL 2 +extern DWORD gdwErrorInfoTlsIndex; + +VOID UXTHEME_DeleteParseErrorInfo(VOID); + +static inline +HRESULT +UXTHEME_MakeError(_In_ LONG error) +{ + if (error < 0) + return (HRESULT)error; + return HRESULT_FROM_WIN32(error); +} + +static inline +HRESULT +UXTHEME_MakeLastError(VOID) +{ + return UXTHEME_MakeError(GetLastError()); +} + +HRESULT +UXTHEME_MakeParseError( + _In_ UINT nID, + _In_ LPCWSTR pszParam1, + _In_ LPCWSTR pszParam2, + _In_ LPCWSTR pszFile, + _In_ LPCWSTR pszLine, + _In_ INT nLineNo); + #endif /* _UXTHEME_PCH_ */ diff --git a/modules/rostests/apitests/uxtheme/CMakeLists.txt b/modules/rostests/apitests/uxtheme/CMakeLists.txt index ccb67069779..0af75394767 100644 --- a/modules/rostests/apitests/uxtheme/CMakeLists.txt +++ b/modules/rostests/apitests/uxtheme/CMakeLists.txt @@ -2,6 +2,7 @@ list(APPEND SOURCE CloseThemeData.c DrawThemeParentBackground.c + GetThemeParseErrorInfo.c SetWindowTheme.c SetThemeAppProperties.c ../include/msgtrace.c diff --git a/modules/rostests/apitests/uxtheme/GetThemeParseErrorInfo.c b/modules/rostests/apitests/uxtheme/GetThemeParseErrorInfo.c new file mode 100644 index 00000000000..4d54c226ea7 --- /dev/null +++ b/modules/rostests/apitests/uxtheme/GetThemeParseErrorInfo.c @@ -0,0 +1,92 @@ +/* + * PROJECT: ReactOS API tests + * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later) + * PURPOSE: Tests for GetThemeParseErrorInfo + * COPYRIGHT: Copyright 2025 Katayama Hirofumi MZ <katayama.hirofumi...@gmail.com> + */ + +#include <apitest.h> +#include <stdlib.h> +#include <stdio.h> +#include <windows.h> +#include <uxtheme.h> +#include <uxundoc.h> + +typedef HRESULT (WINAPI *FN_GetThemeParseErrorInfo)(PPARSE_ERROR_INFO); +typedef HRESULT (WINAPI *FN_ParseThemeIniFile)(LPCWSTR, LPWSTR, PARSETHEMEINIFILEPROC, LPVOID); + +static BOOL CALLBACK +ParseThemeIniFileProc( + DWORD dwType, + LPWSTR pszParam1, + LPWSTR pszParam2, + LPWSTR pszParam3, + DWORD dwParam, + LPVOID lpData) +{ + return TRUE; +} + +START_TEST(GetThemeParseErrorInfo) +{ + HRESULT hr; + PARSE_ERROR_INFO Info; + WCHAR szPath[MAX_PATH]; + + FN_GetThemeParseErrorInfo GetThemeParseErrorInfo = + (FN_GetThemeParseErrorInfo)GetProcAddress(GetModuleHandleW(L"uxtheme.dll"), MAKEINTRESOURCEA(48)); + if (!GetThemeParseErrorInfo) + { + skip("No GetThemeParseErrorInfo found\n"); + return; + } + + hr = GetThemeParseErrorInfo(NULL); + ok_hex(hr, E_POINTER); + + ZeroMemory(&Info, sizeof(Info)); + hr = GetThemeParseErrorInfo(&Info); + ok_hex(hr, E_INVALIDARG); + + ZeroMemory(&Info, sizeof(Info)); + Info.cbSize = sizeof(Info); + hr = GetThemeParseErrorInfo(&Info); + ok_hex(hr, HRESULT_FROM_WIN32(ERROR_RESOURCE_NAME_NOT_FOUND)); + + FN_ParseThemeIniFile ParseThemeIniFile = + (FN_ParseThemeIniFile)GetProcAddress(GetModuleHandleW(L"uxtheme.dll"), MAKEINTRESOURCEA(11)); + if (!ParseThemeIniFile) + { + skip("No ParseThemeIniFile found\n"); + return; + } + + FILE *fout = _wfopen(L"invalid.ini", L"wb"); + fprintf(fout, "[InvalidKey]\n"); + fprintf(fout, "InvalidValueName=InvalidValue\n"); + fclose(fout); + + hr = ParseThemeIniFile(L"invalid.ini", szPath, ParseThemeIniFileProc, NULL); + ok_hex(hr, HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY)); + + _wremove(L"invalid.ini"); + + ZeroMemory(&Info, sizeof(Info)); + Info.cbSize = sizeof(Info); + Info.szDescription[0] = L'@'; + Info.szDescription[MAX_PATH] = L'@'; + Info.szFile[0] = L'@'; + Info.szLine[0] = L'@'; + hr = GetThemeParseErrorInfo(&Info); + ok_hex(hr, S_OK); + ok_int(Info.nID, 160); + + ok(Info.szDescription[0] != UNICODE_NULL, "Info.szDescription was empty\n"); + ok(Info.szDescription[0] != L'@', "Info.szDescription had no change\n"); + trace("szDescription: %s\n", wine_dbgstr_w(Info.szDescription)); // "Must be Primitive, enum, or type: InvalidValueName" + + ok_int(Info.szDescription[MAX_PATH], L'@'); + ok_wstr(Info.szFile, L"invalid.ini"); + ok_wstr(Info.szLine, L"InvalidValueName=InvalidValue"); + ok_int(Info.nLineNo, 2); +} diff --git a/modules/rostests/apitests/uxtheme/testlist.c b/modules/rostests/apitests/uxtheme/testlist.c index 0ecc2b98943..c3260d8499e 100644 --- a/modules/rostests/apitests/uxtheme/testlist.c +++ b/modules/rostests/apitests/uxtheme/testlist.c @@ -5,6 +5,7 @@ extern void func_CloseThemeData(void); extern void func_DrawThemeParentBackground(void); +extern void func_GetThemeParseErrorInfo(void); extern void func_SetThemeAppProperties(void); extern void func_SetWindowTheme(void); @@ -12,6 +13,7 @@ const struct test winetest_testlist[] = { { "CloseThemeData", func_CloseThemeData }, { "DrawThemeParentBackground", func_DrawThemeParentBackground }, + { "GetThemeParseErrorInfo", func_GetThemeParseErrorInfo }, { "SetWindowTheme", func_SetWindowTheme }, { "SetThemeAppProperties", func_SetThemeAppProperties }, { 0, 0 } diff --git a/sdk/include/reactos/uxundoc.h b/sdk/include/reactos/uxundoc.h index ed3ed4368ff..2c6e23cb22b 100644 --- a/sdk/include/reactos/uxundoc.h +++ b/sdk/include/reactos/uxundoc.h @@ -1,7 +1,23 @@ #pragma once +#ifdef __cplusplus +extern "C" { +#endif + typedef HANDLE HTHEMEFILE; +typedef struct tagPARSE_ERROR_INFO +{ + DWORD cbSize; + UINT nID; + WCHAR szDescription[2 * MAX_PATH]; + WCHAR szFile[MAX_PATH]; + WCHAR szLine[MAX_PATH]; + INT nLineNo; +} PARSE_ERROR_INFO, *PPARSE_ERROR_INFO; + +HRESULT WINAPI GetThemeParseErrorInfo(_Inout_ PPARSE_ERROR_INFO pInfo); + /********************************************************************** * ENUMTHEMEPROC * @@ -118,3 +134,6 @@ BOOL WINAPI ThemeHooksInstall(VOID); BOOL WINAPI ThemeHooksRemove(VOID); +#ifdef __cplusplus +} // extern "C" +#endif