热血江湖 YGOnline.exe —— 槽偏移遍历(写日志输出) 时间:2026-09-28 15:2x 目标:D:\rxjh2025\client\YGOnline.exe(32位,无ASLR,模块基址恒 0x400000) ================================================================================ 【用途】一次性把 IFManager 全部 192 个槽扫一遍,把每个槽是什么窗口、有几个内容面板、 哪些面板正在显示,全部写进挂机日志。游戏更新后用它重新摸清"哪个槽是什么窗口"。 【两个版本】 · 遍历槽偏移_仅显示 —— 只写"此刻在屏幕上"的窗口,输出 3~6 行,日常诊断用 · 遍历槽偏移_全部 —— 每个非空槽写一行,输出约 80 行,更新后摸底用 -------------------------------------------------------------------------------- 一、日志样例(数据取自 2026-09-28 15:01 那次实测) -------------------------------------------------------------------------------- 【遍历槽偏移_仅显示】 15:20:01 [槽扫] 显示中:槽 940 (0x3AC) InterMAIN ID 108 面板 14 可见 4 0x344 0x494 0x4A0 0x4E8 15:20:01 [槽扫] 显示中:槽 972 (0x3CC) InterOption ID 1654 面板 3 可见 1 0x340 15:20:01 [槽扫] 显示中:槽 1236 (0x4D4) InterExchange ID 6695 面板 3 可见 1 0x334 15:20:02 [槽扫] 遍历完成:在显示 3 个窗口 【遍历槽偏移_全部】(节选,实际 80 行左右) 15:20:01 [槽扫] IFManager 对象 1120125408 开始遍历 192 个槽 15:20:01 [槽扫] 槽 840 (0x348) (非接口对象) 对象 1790572988 ID ... 面板 0 可见 0 15:20:01 [槽扫] 槽 940 (0x3AC) InterMAIN 对象 1121117104 ID 108 面板 14 可见 4 0x344 0x494 0x4A0 0x4E8 ← 显示中 15:20:01 [槽扫] 槽 944 (0x3B0) InterChat 对象 1186720832 ID 904 面板 0 可见 0 15:20:01 [槽扫] 槽 948 (0x3B4) InterStatus 对象 1110916024 ID 466 面板 3 可见 0 15:20:01 [槽扫] 槽 952 (0x3B8) InterInventory 对象 933512216 ID 953 面板 2 可见 0 15:20:01 [槽扫] 槽 972 (0x3CC) InterOption 对象 929942064 ID 1654 面板 3 可见 1 0x340 ← 显示中 15:20:01 [槽扫] 槽 1236 (0x4D4) InterExchange 对象 1205504664 ID 6695 面板 3 可见 1 0x334 ← 显示中 ...(中间略)... 15:20:02 [槽扫] 槽 1360 (0x550) ScriptDungeon 对象 1460602512 ID 8771 面板 1 可见 0 15:20:02 [槽扫] 遍历完成:非空槽 80 个,其中在显示 3 个 每列含义: 槽 NNNN 十进制槽偏移,直接当 `窗口是否弹出(NNNN)` 的参数用 (0xXXX) 同一偏移的十六进制 类名 RTTI 实测类名;(非接口对象) = 该位置不是接口窗口(读不出类名) 对象 窗口对象地址 ID 控件ID(会话内递增,会变,只能现读) 面板 N 该窗口名下"类型码24 = InterWindow"的内容面板个数 可见 N 其中 +0x40 == 1 的个数 → **> 0 就是这个窗口正显示在屏幕上** 0x344 ... 正在显示的那些面板的相对偏移 ← 显示中 本行窗口此刻真的在屏幕上 -------------------------------------------------------------------------------- 二、常量(刷进常量表) -------------------------------------------------------------------------------- .版本 2 .常量 IFManager基址, "25740828" .常量 窗口可见标志, "64" .常量 控件类型偏移, "8" .常量 控件ID偏移, "12" .常量 面板类型码, "24" .常量 窗口子槽起点, "768" .常量 窗口子槽个数, "192" -------------------------------------------------------------------------------- 三、子程序 ① 读控件类名(遍历靠它把"槽"翻译成"是什么窗口") -------------------------------------------------------------------------------- .版本 2 .子程序 读控件类名, 文本型 .参数 对象, 整数型 .局部变量 虚表, 整数型 .局部变量 定位器, 整数型 .局部变量 类型描述符, 整数型 .局部变量 名, 文本型 .局部变量 长度, 整数型 .如果真 (对象 ≤ 65536) 返回 (“”) .如果真结束 虚表 = 内存_读整数型 (全局_进程句柄, 对象) .如果真 (虚表 ≤ 65536) 返回 (“”) .如果真结束 定位器 = 内存_读整数型 (全局_进程句柄, 虚表 - 4) .如果真 (定位器 ≤ 65536) 返回 (“”) .如果真结束 类型描述符 = 内存_读整数型 (全局_进程句柄, 定位器 + 12) .如果真 (类型描述符 ≤ 65536) 返回 (“”) .如果真结束 名 = 内存_读文本型 (全局_进程句柄, 类型描述符 + 12) 长度 = 取文本长度 (名) .如果真 (长度 > 2) .如果真 (取文本右边 (名, 2) = “@@”) 名 = 取文本左边 (名, 长度 - 2) .如果真结束 .如果真结束 返回 (名) -------------------------------------------------------------------------------- 四、子程序 ② 遍历槽偏移_仅显示(★ 日常用,只写此刻在屏幕上的) -------------------------------------------------------------------------------- .版本 2 .子程序 遍历槽偏移_仅显示 .局部变量 接口管理, 整数型 .局部变量 i, 整数型 .局部变量 j, 整数型 .局部变量 槽偏移, 整数型 .局部变量 窗口对象, 整数型 .局部变量 子对象, 整数型 .局部变量 可见, 整数型 .局部变量 面板数, 整数型 .局部变量 可见数, 整数型 .局部变量 可见明细, 文本型 .局部变量 窗口名, 文本型 .局部变量 显示数, 整数型 .局部变量 前缀, 文本型 前缀 = 自定义时间 () + “[槽扫] ” 接口管理 = 内存_读整数型 (全局_进程句柄, #IFManager基址) .如果真 (接口管理 ≤ 65536) 挂机信息.加入文本 (前缀 + “IFManager 未读到(” + 到文本 (接口管理) + “),遍历中止” + #换行符 + #换行符) 返回 () .如果真结束 显示数 = 0 .计次循环首 (#窗口子槽个数, i) 槽偏移 = #窗口子槽起点 + (i - 1) × 4 窗口对象 = 内存_读整数型 (全局_进程句柄, 接口管理 + 槽偏移) .如果真 (窗口对象 > 65536) 面板数 = 0 可见数 = 0 可见明细 = “” .计次循环首 (#窗口子槽个数, j) 子对象 = 内存_读整数型 (全局_进程句柄, 窗口对象 + #窗口子槽起点 + (j - 1) × 4) .如果真 (子对象 > 65536) .如果真 (内存_读整数型 (全局_进程句柄, 子对象 + #控件类型偏移) = #面板类型码) 面板数 = 面板数 + 1 可见 = 内存_读整数型 (全局_进程句柄, 子对象 + #窗口可见标志) .如果真 (可见 = 1) 可见数 = 可见数 + 1 可见明细 = 可见明细 + “ 0x” + 取十六进制文本 (#窗口子槽起点 + (j - 1) × 4) .如果真结束 .如果真结束 .如果真结束 .计次循环尾 () .如果真 (可见数 > 0) 窗口名 = 读控件类名 (窗口对象) 显示数 = 显示数 + 1 挂机信息.加入文本 (前缀 + “显示中:槽 ” + 到文本 (槽偏移) + “ (0x” + 取十六进制文本 (槽偏移) + “) ” + 窗口名 + “ ID ” + 到文本 (内存_读整数型 (全局_进程句柄, 窗口对象 + #控件ID偏移)) + “ 面板 ” + 到文本 (面板数) + “ 可见 ” + 到文本 (可见数) + 可见明细 + #换行符) .如果真结束 .如果真结束 .计次循环尾 () .如果真 (显示数 = 0) 挂机信息.加入文本 (前缀 + “当前没有任何接口窗口在显示” + #换行符) .如果真结束 挂机信息.加入文本 (前缀 + “遍历完成:在显示 ” + 到文本 (显示数) + “ 个窗口” + #换行符 + #换行符) 返回 () -------------------------------------------------------------------------------- 五、子程序 ③ 遍历槽偏移_全部(更新后摸底用,约 80 行日志) -------------------------------------------------------------------------------- .版本 2 .子程序 遍历槽偏移_全部 .局部变量 接口管理, 整数型 .局部变量 i, 整数型 .局部变量 j, 整数型 .局部变量 槽偏移, 整数型 .局部变量 窗口对象, 整数型 .局部变量 子对象, 整数型 .局部变量 可见, 整数型 .局部变量 面板数, 整数型 .局部变量 可见数, 整数型 .局部变量 可见明细, 文本型 .局部变量 窗口名, 文本型 .局部变量 非空数, 整数型 .局部变量 显示数, 整数型 .局部变量 行, 文本型 .局部变量 前缀, 文本型 前缀 = 自定义时间 () + “[槽扫] ” 接口管理 = 内存_读整数型 (全局_进程句柄, #IFManager基址) .如果真 (接口管理 ≤ 65536) 挂机信息.加入文本 (前缀 + “IFManager 未读到(” + 到文本 (接口管理) + “),遍历中止” + #换行符 + #换行符) 返回 () .如果真结束 挂机信息.加入文本 (前缀 + “IFManager 对象 ” + 到文本 (接口管理) + “ 开始遍历 ” + 到文本 (#窗口子槽个数) + “ 个槽” + #换行符) 非空数 = 0 显示数 = 0 .计次循环首 (#窗口子槽个数, i) 槽偏移 = #窗口子槽起点 + (i - 1) × 4 窗口对象 = 内存_读整数型 (全局_进程句柄, 接口管理 + 槽偏移) .如果真 (窗口对象 > 65536) 非空数 = 非空数 + 1 窗口名 = 读控件类名 (窗口对象) .如果真 (窗口名 = “”) 窗口名 = “(非接口对象)” .如果真结束 面板数 = 0 可见数 = 0 可见明细 = “” .计次循环首 (#窗口子槽个数, j) 子对象 = 内存_读整数型 (全局_进程句柄, 窗口对象 + #窗口子槽起点 + (j - 1) × 4) .如果真 (子对象 > 65536) .如果真 (内存_读整数型 (全局_进程句柄, 子对象 + #控件类型偏移) = #面板类型码) 面板数 = 面板数 + 1 可见 = 内存_读整数型 (全局_进程句柄, 子对象 + #窗口可见标志) .如果真 (可见 = 1) 可见数 = 可见数 + 1 可见明细 = 可见明细 + “ 0x” + 取十六进制文本 (#窗口子槽起点 + (j - 1) × 4) .如果真结束 .如果真结束 .如果真结束 .计次循环尾 () 行 = 前缀 + “槽 ” + 到文本 (槽偏移) + “ (0x” + 取十六进制文本 (槽偏移) + “) ” + 窗口名 + “ 对象 ” + 到文本 (窗口对象) + “ ID ” + 到文本 (内存_读整数型 (全局_进程句柄, 窗口对象 + #控件ID偏移)) + “ 面板 ” + 到文本 (面板数) + “ 可见 ” + 到文本 (可见数) .如果真 (可见数 > 0) 行 = 行 + 可见明细 + “ ← 显示中” 显示数 = 显示数 + 1 .如果真结束 挂机信息.加入文本 (行 + #换行符) .如果真结束 .计次循环尾 () 挂机信息.加入文本 (前缀 + “遍历完成:非空槽 ” + 到文本 (非空数) + “ 个,其中在显示 ” + 到文本 (显示数) + “ 个” + #换行符 + #换行符) 返回 () -------------------------------------------------------------------------------- 六、性能与注意 -------------------------------------------------------------------------------- · 单次开销:192 次槽读 + 每个非空槽 192 次子槽读 ≈ **1.6 万次读内存**(80 个非空槽)。 易语言里约 50~150ms,日志控件写入是主要耗时(80 行)。 · **别放进高频循环**。推荐:开局跑一次、按热键跑一次、或每 10 秒跑一次。 · 两个子程序都无参数、无返回值,直接 `遍历槽偏移_仅显示 ()` 调用。 · 日志最后一定有一行「遍历完成」,看到它就说明跑完了;没看到就是中途卡住/进程掉了。 -------------------------------------------------------------------------------- 七、日志读法速查 -------------------------------------------------------------------------------- | 日志 | 含义 / 处理 | |---|---| | `IFManager 未读到(0)` | 没进游戏 / 基址失效 / 句柄无效 | | `(非接口对象)` | 该槽放的不是接口窗口,读不出类名,正常,跳过 | | `面板 0 可见 0` | 该接口名下没有类型码24面板(如 InterChat 944),判不出弹出 | | `可见 N` 且 N>0 | ★ 这个窗口此刻在屏幕上,后面列的就是可见面板偏移 | | `遍历完成:非空槽 80 个` | 正常基线。若数字明显变化,说明游戏版本改动了接口表 | | 槽 0x3D0 对象为 0 不出现 | 小地图接口本次未创建,正常 | -------------------------------------------------------------------------------- 八、游戏更新后怎么用它重新定位 -------------------------------------------------------------------------------- 1. 打开目标窗口(比如交易窗),跑一次 `遍历槽偏移_仅显示`; 2. 日志里找 `← 显示中` 且类名符合的那一行 → 该行的**十进制槽号**就是新槽偏移; 3. 关掉窗口再跑一次,同一槽行的 `可见` 应从 N 变成 0 —— 双向验证; 4. 若原来的槽号整行消失、换了新槽号,说明 IFManager 结构体加了成员(此前实测过一次整体 +0x1C)。 ================================================================================