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

Reply via email to