Windhawk模组编译报错:Expected unqualified-id及括号匹配问题
编译错误日志
JSON格式诊断信息
[{ "resource": "/c:/ProgramData/Windhawk/EditorWorkspace/mod.wh.cpp", "owner": "_generated_diagnostic_collection_name_#0", "code": "expected_unqualified_id", "severity": 8, "message": "Expected unqualified-id", "source": "clang", "startLineNumber": 23, "startColumn": 1, "endLineNumber": 23, "endColumn": 2 }]
详细编译错误输出
c:\ProgramData\Windhawk\EditorWorkspace\mod.wh.cpp:24:3: error: expected unqualified-id 24 | "closeButtonHoverColor": { | ^ c:\ProgramData\Windhawk\EditorWorkspace\mod.wh.cpp:81:16: error: expected ')' 81 | Wh_Log(buffer); // 确保宏展开正确 | ^ c:\ProgramData\Windhawk\EditorWorkspace\mod.wh.cpp:81:9: note: 匹配此处的'(' 81 | Wh_Log(buffer); // 确保宏展开正确 | ^ c:/Program Files/Windhawk/Compiler/include/windhawk_api.h:71:35: note: 从宏'Wh_Log'展开 71 | InternalWh_Log_Wrapper(L"[%d:%S]: " message, __LINE__, \ | ^ 生成2个错误。
完整模组代码
// ==WindhawkMod== // @id close-button-hover-color-changer // @name 关闭按钮悬停颜色修改器 // @description 修改所有应用程序中关闭按钮的悬停颜色 // @version 1.0 // @author AssistantModder // @github https://github.com/AssistantModder // @include * // @compilerOptions -ldwmapi -luser32 -lgdi32 // ==/WindhawkMod== // ==WindhawkModReadme== /* 此模组可自定义所有使用Windows DWM API的应用程序的关闭按钮悬停颜色。 特性: - 修改关闭按钮的悬停颜色。 - 基础错误处理与日志记录,便于排查问题。 */ // ==/WindhawkModReadme== // 定义Windhawk模组设置 // ==WindhawkModSettings== { "closeButtonHoverColor": { "type": "string", "default": "#FF6666", "title": "悬停颜色", "description": "鼠标悬停在关闭按钮上时的颜色。" }, "enableMod": { "type": "boolean", "default": true, "title": "启用模组", "description": "开启或关闭此模组。" }, "logLevel": { "type": "integer", "default": 1, "minimum": 0, "maximum": 4, "title": "日志级别", "description": "设置日志级别:0-无,1-错误,2-警告,3-信息,4-调试" } } // ==/WindhawkModSettings== #include <windows.h> #include <dwmapi.h> #include <windhawk_utils.h> // 存储模组设置的结构体 struct ModSettings { COLORREF hoverColor; BOOL enableMod; int logLevel; } settings; // DWM API调用的函数原型 typedef HRESULT (WINAPI *DwmSetWindowAttributeFptr)(HWND, DWORD, LPCVOID, DWORD); // 原始函数指针 DwmSetWindowAttributeFptr OriginalDwmSetWindowAttribute = nullptr; // 将十六进制字符串转换为COLORREF COLORREF HexStringToCOLORREF(const wchar_t* hexColor) { unsigned int r, g, b; if (wcslen(hexColor) == 7 && swscanf_s(hexColor, L"#%02x%02x%02x", &r, &g, &b) == 3) { return RGB(r, g, b); } Wh_Log(L"错误:无效的十六进制颜色格式'%s',使用默认红色。", hexColor); return RGB(255, 0, 0); // 转换失败时默认使用红色 } // 基于日志级别的自定义日志函数 void CustomLog(int level, const wchar_t* format, ...) { if (level <= settings.logLevel) { va_list args; va_start(args, format); wchar_t buffer[256]; vswprintf_s(buffer, sizeof(buffer) / sizeof(buffer[0]), format, args); Wh_Log(buffer); // 确保宏展开正确 va_end(args); } } // 用于修改关闭按钮悬停颜色的DwmSetWindowAttribute钩子函数 HRESULT WINAPI HookDwmSetWindowAttribute(HWND hwnd, DWORD dwAttribute, LPCVOID pvAttribute, DWORD cbAttribute) { if (settings.enableMod && dwAttribute == 35) { // 35是未公开的DWMWA_CAPTION_COLOR_HOVER *(COLORREF*)pvAttribute = settings.hoverColor; CustomLog(4, L"将标题悬停颜色设置为0x%06X", settings.hoverColor); } return OriginalDwmSetWindowAttribute(hwnd, dwAttribute, pvAttribute, cbAttribute); } // 从Windhawk模组设置加载配置 void LoadSettings() { settings.hoverColor = HexStringToCOLORREF(Wh_GetStringSetting(L"closeButtonHoverColor")); settings.enableMod = Wh_GetIntSetting(L"enableMod") != 0; // 转换为布尔值 settings.logLevel = Wh_GetIntSetting(L"logLevel"); } // 模组初始化函数 BOOL Wh_ModInit() { LoadSettings(); HMODULE dwmapiModule = GetModuleHandle(L"dwmapi.dll"); if (!dwmapiModule) { CustomLog(1, L"获取dwmapi.dll模块句柄失败"); return FALSE; } OriginalDwmSetWindowAttribute = (DwmSetWindowAttributeFptr)GetProcAddress(dwmapiModule, "DwmSetWindowAttribute"); if (!OriginalDwmSetWindowAttribute) { CustomLog(1, L"获取DwmSetWindowAttribute函数地址失败"); return FALSE; } // 挂钩DwmSetWindowAttribute函数 Wh_SetFunctionHook((void*)OriginalDwmSetWindowAttribute, (void*)HookDwmSetWindowAttribute, (void**)&OriginalDwmSetWindowAttribute); CustomLog(3, L"模组初始化成功"); return TRUE; } // 模组设置更改时调用的函数 void Wh_ModSettingsChanged() { LoadSettings(); CustomLog(3, L"配置已重新加载"); } // 模组卸载函数 void Wh_ModUninit() { CustomLog(3, L"模组已卸载"); }
已尝试的修复措施
- 修正注释位置:将Windhawk模组设置移到注释块外,修复模组设置未在注释块外正确定义的错误。
- 调整日志宏:确保Wh_Log宏的正确使用,验证日志函数通过vswprintf_s正确处理格式化字符串。
- 修复保留关键字使用:避免使用char等保留关键字作为变量名。
- 修正函数指针处理:确保DwmSetWindowAttribute的函数指针定义和使用正确。
- HexStringToCOLORREF中的错误处理:添加无效十六进制颜色格式的错误处理。
- 布尔设置获取:使用Wh_GetBoolSetting获取布尔类型设置。
错误修复方案
第一个错误:expected unqualified-id(第24行)
该错误是因为Windhawk模组设置块的JSON内容未被正确注释,编译器将其当作C++代码解析。Windhawk要求设置块的JSON必须被多行注释包裹,修改后的设置块如下:
// ==WindhawkModSettings== /* { "closeButtonHoverColor": { "type": "string", "default": "#FF6666", "title": "悬停颜色", "description": "鼠标悬停在关闭按钮上时的颜色。" }, "enableMod": { "type": "boolean", "default": true, "title": "启用模组", "description": "开启或关闭此模组。" }, "logLevel": { "type": "integer", "default": 1, "minimum": 0, "maximum": 4, "title": "日志级别", "description": "设置日志级别:0-无,1-错误,2-警告,3-信息,4-调试" } } */ // ==/WindhawkModSettings==
第二个错误:expected ')'(第81行)
该错误是因为Wh_Log是预处理宏,要求传入的格式化字符串必须是字面量,无法直接传入变量。修改自定义日志函数的调用方式即可解决:
void CustomLog(int level, const wchar_t* format, ...) { if (level <= settings.logLevel) { va_list args; va_start(args, format); wchar_t buffer[256]; vswprintf_s(buffer, sizeof(buffer) / sizeof(buffer[0]), format, args); Wh_Log(L"%s", buffer); // 将buffer作为格式化参数传入 va_end(args); } }
额外优化:布尔设置获取
将LoadSettings中的布尔值获取改为Wh_GetBoolSetting,更符合API设计:
void LoadSettings() { settings.hoverColor = HexStringToCOLORREF(Wh_GetStringSetting(L"closeButtonHoverColor")); settings.enableMod = Wh_GetBoolSetting(L"enableMod"); settings.logLevel = Wh_GetIntSetting(L"logLevel"); }
内容的提问来源于stack exchange,提问作者Kai Piper




