REPENTOGON 中 DwmWindowAttribute 枚举全解:用 Lua 控制《以撒的结合:忏悔》游戏窗口的 DWM 属性
REPENTOGON 中 DwmWindowAttribute 枚举全解:用 Lua 控制《以撒的结合:忏悔》游戏窗口的 DWM 属性
DwmWindowAttribute 是 REPENTOGON 为《The Binding of Isaac: Repentance》模组脚本新增的一组 Windows DWM(Desktop Window Manager)窗口属性枚举,配合 Isaac.GetDwmWindowAttribute / Isaac.SetDwmWindowAttribute 两个全局 API,模组作者可以直接在 Lua 脚本里读写游戏窗口的标题栏深色模式、圆角、边框颜色、系统背景类型等外观属性。阅读本文后,你将掌握该枚举的完整取值表、底层 C++ 实现原理与安全限制,并能写出可实际运行的窗口自定义脚本。
一、这是什么:DWM 窗口属性与 REPENTOGON 的桥接
DwmWindowAttribute 对应 Windows SDK dwmapi.h 中的 DWMWINDOWATTRIBUTE 枚举。DWM 是 Windows 负责桌面合成与窗口外观的系统组件,其窗口属性(DWMWA_*)控制着每个顶级窗口的标题栏渲染、Aero Peek 行为、深色模式、圆角样式、边框/标题栏/文字颜色、Mica 与 Acrylic 等系统背景材质等细节。
REPENTOGON 将这一整套 Windows 枚举原样暴露给游戏内的 Lua 环境,使模组不再需要依赖外部 DLL 或注入代码,仅靠官方扩展 API 即可调整游戏窗口本身的外观。该功能在 changelog.txt 中有明确记录:
* Isaac:
- Get/SetDwmWindowAttribute (for use with new DwmWindowAttributes enum)
二、枚举完整取值表
依据 docs/docs/enums/DwmWindowAttribute.md 的定义,该枚举的全部成员如下(原文档注释为 blocked 的两项已被 REPENTOGON 在 API 层禁用):
| 值 | 枚举名 | 备注 |
|---|---|---|
| 1 | DWMWA_NCRENDERING_ENABLED | |
| 2 | DWMWA_NCRENDERING_POLICY | |
| 3 | DWMWA_TRANSITIONS_FORCEDISABLED | |
| 4 | DWMWA_ALLOW_NCPAINT | |
| 5 | DWMWA_CAPTION_BUTTON_BOUNDS | |
| 6 | DWMWA_NONCLIENT_RTL_LAYOUT | |
| 7 | DWMWA_FORCE_ICONIC_REPRESENTATION | |
| 8 | DWMWA_FLIP3D_POLICY | |
| 9 | DWMWA_EXTENDED_FRAME_BOUNDS | |
| 10 | DWMWA_HAS_ICONIC_BITMAP | |
| 11 | DWMWA_DISALLOW_PEEK | |
| 12 | DWMWA_EXCLUDED_FROM_PEEK | |
| 13 | DWMWA_CLOAK | blocked |
| 14 | DWMWA_CLOAKED | blocked |
| 15 | DWMWA_FREEZE_REPRESENTATION | |
| 16 | DWMWA_PASSIVE_UPDATE_MODE | |
| 17 | DWMWA_USE_HOSTBACKDROPBRUSH | |
| 20 | DWMWA_USE_IMMERSIVE_DARK_MODE | |
| 33 | DWMWA_WINDOW_CORNER_PREFERENCE | |
| 34 | DWMWA_BORDER_COLOR | |
| 35 | DWMWA_CAPTION_COLOR | |
| 36 | DWMWA_TEXT_COLOR | |
| 37 | DWMWA_VISIBLE_FRAME_BORDER_THICKNESS | |
| 38 | DWMWA_SYSTEMBACKDROP_TYPE | |
| 39 | DWMWA_LAST |
值得注意的细节:
- 取值并不连续:13、14 之后直接跳到 20,再跳到 33 至 39,中间的 18、19、21–32 均未被该枚举收录。这些空缺对应 Windows 不同版本中新增或保留的属性槽位,REPENTOGON 仅暴露了文档表中列出的成员。
- 39 是哨兵值:
DWMWA_LAST在 Windows DWM 定义中用于标识"最后一个有效属性",用于对属性 ID 做范围校验,本身不是一个可用的外观开关。 - 13 / 14 被标记为 blocked:
DWMWA_CLOAK(将窗口隐藏出 DWM 合成输出)与DWMWA_CLOAKED(查询窗口是否被 cloaked)在文档和 Lua 枚举定义中均标注为blocked,调用时会被 API 直接拒绝(详见下文安全机制)。
三、各属性的用途速览
下面按功能类别对各成员做简要说明,帮助你在实际调用时选择合适的属性(这些描述遵循 Windows DWM 的通用定义,供选参参考;具体读写效果以系统版本为准)。
渲染与绘制类
DWMWA_NCRENDERING_ENABLED(1):是否启用非客户区(标题栏与边框)的 DWM 渲染。DWMWA_NCRENDERING_POLICY(2):非客户区渲染策略,属于枚举值型属性。DWMWA_TRANSITIONS_FORCEDISABLED(3):是否强制禁用窗口过渡动画。DWMWA_ALLOW_NCPAINT(4):是否允许非客户区自绘内容生效。DWMWA_EXTENDED_FRAME_BOUNDS(9):查询窗口扩展帧的边界矩形,常用于把内容延伸到标题栏区域。DWMWA_VISIBLE_FRAME_BORDER_THICKNESS(37):可见窗口边框的厚度。
Aero Peek 与 Cloak 类
DWMWA_DISALLOW_PEEK(11):禁止该窗口出现在 Aero Peek 预览中。DWMWA_EXCLUDED_FROM_PEEK(12):从 Aero Peek 预览中排除该窗口。DWMWA_CLOAK(13,blocked):将窗口从 DWM 合成中隐藏。DWMWA_CLOAKED(14,blocked):查询窗口当前是否处于 cloaked 状态。DWMWA_FORCE_ICONIC_REPRESENTATION(7):强制使用"图标化"表示方式。DWMWA_HAS_ICONIC_BITMAP(10):窗口是否提供图标位图供 DWM 使用。DWMWA_FREEZE_REPRESENTATION(15):冻结窗口当前画面表示(快照模式)。DWMWA_PASSIVE_UPDATE_MODE(16):被动更新模式,窗口内容由 DWM 侧按需获取。
现代外观类(深色模式、圆角、颜色)
DWMWA_USE_IMMERSIVE_DARK_MODE(20):布尔值,1 启用深色标题栏,0 恢复浅色——这是最常用于"游戏窗口深色化"的属性。DWMWA_WINDOW_CORNER_PREFERENCE(33):窗口圆角偏好,配合DWMWCP_*(如圆角/直角)数值使用。DWMWA_BORDER_COLOR(34):窗口边框颜色(ARGB)。DWMWA_CAPTION_COLOR(35):标题栏颜色(ARGB)。DWMWA_TEXT_COLOR(36):标题栏文字颜色(ARGB)。DWMWA_SYSTEMBACKDROP_TYPE(38):系统背景材质类型(如 Mica、Acrylic 等),数值对应DWMSBT_*系列常量。
其他
DWMWA_CAPTION_BUTTON_BOUNDS(5):标题栏按钮区域的边界矩形。DWMWA_NONCLIENT_RTL_LAYOUT(6):非客户区是否使用从右到左的 RTL 布局。DWMWA_FLIP3D_POLICY(8):Windows Flip 3D 切换时的策略。DWMWA_USE_HOSTBACKDROPBRUSH(17):是否使用宿主窗口的背景画笔。DWMWA_LAST(39):最后一个有效属性标记。
四、枚举在 Lua 环境中的定义
该枚举随 REPENTOGON 的扩展 Lua 环境一起注入游戏,定义位于 repentogon/resources/scripts/enums_ex.lua,与文档表格逐项对应:
DwmWindowAttribute = {
DWMWA_NCRENDERING_ENABLED=1,
DWMWA_NCRENDERING_POLICY=2,
DWMWA_TRANSITIONS_FORCEDISABLED=3,
DWMWA_ALLOW_NCPAINT=4,
DWMWA_CAPTION_BUTTON_BOUNDS=5,
DWMWA_NONCLIENT_RTL_LAYOUT=6,
DWMWA_FORCE_ICONIC_REPRESENTATION=7,
DWMWA_FLIP3D_POLICY=8,
DWMWA_EXTENDED_FRAME_BOUNDS=9,
DWMWA_HAS_ICONIC_BITMAP=10,
DWMWA_DISALLOW_PEEK=11,
DWMWA_EXCLUDED_FROM_PEEK=12,
DWMWA_CLOAK=13, --blocked
DWMWA_CLOAKED=14, --blocked
DWMWA_FREEZE_REPRESENTATION=15,
DWMWA_PASSIVE_UPDATE_MODE=16,
DWMWA_USE_HOSTBACKDROPBRUSH=17,
DWMWA_USE_IMMERSIVE_DARK_MODE = 20,
DWMWA_WINDOW_CORNER_PREFERENCE = 33,
DWMWA_BORDER_COLOR=34,
DWMWA_CAPTION_COLOR=35,
DWMWA_TEXT_COLOR=36,
DWMWA_VISIBLE_FRAME_BORDER_THICKNESS=37,
DWMWA_SYSTEMBACKDROP_TYPE=38,
DWMWA_LAST=39
}
因此脚本中可以直接用 DwmWindowAttribute.DWMWA_USE_IMMERSIVE_DARK_MODE 这种命名方式引用属性,而不必记忆裸数字。
五、配套 API:Isaac.GetDwmWindowAttribute / Isaac.SetDwmWindowAttribute
该枚举的消费端是 Isaac 全局类下的两个函数,签名记录在 docs/docs/Isaac.md 与 docs/docs/Isaac.md:
DwmWindowAttribute GetDwmWindowAttribute ( )
void SetDwmWindowAttribute ( DwmWindowAttribute Attribute )
5.1 底层实现与真实参数约定
两个函数在 repentogon/LuaInterfaces/LuaIsaac.cpp 中实现,并注册到 Lua 全局类 Isaac(见 repentogon/LuaInterfaces/LuaIsaac.cpp):
LUA_FUNCTION(Lua_SetDWMAttrib)
{
HWND hwnd = GetActiveWindow();
DWORD activeProcessId;
GetWindowThreadProcessId(hwnd, &activeProcessId);
DWORD currentProcessId = GetCurrentProcessId();
int32_t attribid = (int32_t)luaL_optinteger(L, 1, 0);
int32_t attribval = (int32_t)luaL_optinteger(L, 2, 0);
switch (attribid) {
case (DWMWINDOWATTRIBUTE)DWMWA_CLOAK:
return luaL_error(L, "Usage of DWMWA_CLOAK attribute is prohibited!");
break;
case (DWMWINDOWATTRIBUTE)DWMWA_CLOAKED:
return luaL_error(L, "Usage of DWMWA_CLOAKED attribute is prohibited!");
break;
};
if (activeProcessId == currentProcessId) {
DwmSetWindowAttribute(hwnd, attribid, &attribval, sizeof(attribval));
};
return 0;
};
LUA_FUNCTION(Lua_GetDWMAttrib)
{
HWND hwnd = GetActiveWindow();
int32_t attribid = (int32_t)luaL_optinteger(L, 1, 0);
int32_t attribval;
DwmGetWindowAttribute(hwnd, attribid,&attribval,sizeof(attribval));
lua_pushinteger(L, attribval);
return 1;
};
从源码可以归纳出以下几点实际调用约定(文档签名之外的重要信息):
SetDwmWindowAttribute实际读取两个参数:第一个是属性 ID,第二个是属性值(int32_t),缺省值为 0。也就是说,文档中仅列出的Attribute参数是属性 ID,而具体写入的值(如深色模式的 1/0、ARGB 颜色、圆角偏好等)需要作为第二个参数传入。GetDwmWindowAttribute同样需要传入属性 ID:实现中通过luaL_optinteger(L, 1, 0)读取第一个参数,返回值为整数型,可直接与枚举成员比较。- 读写目标固定为活动窗口:两者都通过
GetActiveWindow()取得句柄,不提供自定义 HWND 参数。 - 颜色类属性按 ARGB 整数值处理:写入
DWMWA_BORDER_COLOR/DWMWA_CAPTION_COLOR/DWMWA_TEXT_COLOR时,第二个参数应为 0xRRGGBB 或 0xAARRGGBB 格式的 32 位整数。
5.2 安全机制
源码中内置了两道保护,这是使用该 API 时必须了解的限制:
- 禁止 Cloak 相关属性:当属性 ID 为
DWMWA_CLOAK(13)或DWMWA_CLOAKED(14)时,Lua_SetDWMAttrib会直接调用luaL_error抛出运行时错误,提示"Usage of DWMWA_CLOAK attribute is prohibited!"。这正是文档表格中这两项被标记为blocked的原因——防止脚本把游戏窗口从桌面隐藏掉或查询该状态。 - 仅对当前进程生效:
SetDwmWindowAttribute在执行写入前会比对activeProcessId与currentProcessId,只有游戏自身是当前活动窗口所属进程时才调用DwmSetWindowAttribute。这避免了脚本误改其他应用窗口,也保证了设置始终作用于 Isaac 自己的窗口。
六、实战示例
以下 Lua 脚本展示了如何利用该枚举调整游戏窗口外观。以 MC_POST_GAME_STARTED(或 MC_POST_RENDER 的一次性初始化)等回调中调用为宜:
-- 1) 启用深色标题栏(值 1 开启,0 关闭)
Isaac.SetDwmWindowAttribute(
DwmWindowAttribute.DWMWA_USE_IMMERSIVE_DARK_MODE,
1
)
-- 2) 查询当前深色模式状态并打印
local dark = Isaac.GetDwmWindowAttribute(
DwmWindowAttribute.DWMWA_USE_IMMERSIVE_DARK_MODE
)
print("immersive dark mode = " .. tostring(dark))
-- 3) 设置标题栏与边框颜色(ARGB 整数)
Isaac.SetDwmWindowAttribute(DwmWindowAttribute.DWMWA_CAPTION_COLOR, 0x1E1E1E)
Isaac.SetDwmWindowAttribute(DwmWindowAttribute.DWMWA_BORDER_COLOR, 0x007ACC)
Isaac.SetDwmWindowAttribute(DwmWindowAttribute.DWMWA_TEXT_COLOR, 0xFFFFFF)
关于窗口圆角偏好,DWMWA_WINDOW_CORNER_PREFERENCE(33)的取值对应 Windows 的 DWMWCP_* 常量:0(DWMWCP_DEFAULT)、1(DWMWCP_DONOTROUND,直角)、2(DWMWCP_ROUND,圆角)、3(DWMWCP_ROUNDSMALL,小圆角)。例如让窗口恢复直角:
Isaac.SetDwmWindowAttribute(DwmWindowAttribute.DWMWA_WINDOW_CORNER_PREFERENCE, 1)
调用被禁用的属性则会中断当前 Lua 调用并报错,可借此在开发期验证枚举的受限项:
-- 以下调用会抛出 "Usage of DWMWA_CLOAK attribute is prohibited!" 错误
Isaac.SetDwmWindowAttribute(DwmWindowAttribute.DWMWA_CLOAK, 1)
七、注意事项与适用限制
- 仅适用于 Windows 平台:DWM 是 Windows 专属组件,该枚举与两个 API 在非 Windows 环境下无实际意义;REPENTOGON 本身以 Windows 为目标平台。
- 效果依赖系统版本:
DWMWA_USE_IMMERSIVE_DARK_MODE(20)、DWMWA_WINDOW_CORNER_PREFERENCE(33)等较新的属性需要较新的 Windows 10 / Windows 11 版本才有效,旧系统上写入可能被静默忽略,建议写入后通过GetDwmWindowAttribute回读校验。 - 设置不保证持久化:与 REPENTOGON 多数运行时 API 一样,窗口属性是进程级即时设置,随游戏进程退出而失效;需要每次启动时在回调中重新应用。
- 写入值请使用整数语义:实现按
int32_t传递属性值,布尔类属性传 1/0,颜色类属性传 ARGB 整数,枚举类属性传对应数值。 - 不要在模组中依赖 Cloak 能力:13/14 被 API 层硬性禁止,设计功能时应绕开。
八、参考资源
- 枚举文档本体:docs/docs/enums/DwmWindowAttribute.md
- 配套函数文档:docs/docs/Isaac.md(Get)与 docs/docs/Isaac.md(Set)
- Lua 枚举定义:repentogon/resources/scripts/enums_ex.lua
- C++ 实现与注册:repentogon/LuaInterfaces/LuaIsaac.cpp、repentogon/LuaInterfaces/LuaIsaac.cpp
- 功能引入记录:changelog.txt