.版本 2 ══════════════════════════════════════════════════════════ 按钮点击call —— 置入代码版(对应 Asm 写法改写) ══════════════════════════════════════════════════════════ 【原 Asm 版本存在的三个问题】 1. mov esi,[esi] 这一行是错的 / 多余的 参数 = 控件对象指针 P [P+0] = 虚表 [P+4] = 父窗口 ← ecx 必须传这个(CE 活体已实读确认) "mov esi,[esi]" 把 esi 换成虚表 V 之后, "mov ecx,[esi+4]" 读的就是 V[1](虚表第2项), 而不是 [P+4](父窗口)→ 调用参数错。 2. pushad / popad 和 ecx 传参互相冲突 popad 会把 ecx 还原成 pushad 时的值, 而 call 要用 ecx 当 this → 必须先 push ecx 再 pop ecx, 或者直接去掉 pushad/popad(eax/ecx/edx 是易语言允许破坏的)。 3. "retn" 的位置 直接放在子程序里顺序执行时不需要 retn; 如果这段是单独被 call 的代码块,才需要补 C3。 【修正后的定义(置入代码版)】 寄存器流向: esi ← 控件对象指针(从控件池按槽号读出) ecx ← [esi+4] = 父窗口 ← 这个才是 thiscall 的 this eax ← [ecx] = 父窗口虚表 call [eax+4] = 调虚表第 2 个方法 栈(从栈顶往下):0x3F4 / 控件ID / 0 .子程序 内挂丸_点控件, 逻辑型, 公开 .参数 槽号, 整数型 .局部变量 池地址, 整数型 .局部变量 b0, 字节型 .局部变量 b1, 字节型 .局部变量 b2, 字节型 .局部变量 b3, 字节型 池地址 = 63133528 + 槽号 × 4 ' 池基址 + 槽号×4 b0 = 池地址 与 255 b1 = (池地址 ÷ 256) 与 255 b2 = (池地址 ÷ 65536) 与 255 b3 = (池地址 ÷ 16777216) 与 255 ' mov esi,[绝对地址] 8B 35 置入代码 ({ 139, 53, b0, b1, b2, b3 }) ' push 0 6A 00 置入代码 ({ 106, 0 }) ' push [esi+0x364] FF B4 24 64 03 00 00 ★控件ID = [控件+868] 置入代码 ({ 255, 180, 36, 100, 3, 0, 0 }) ' push 0x3F4 68 F4 03 00 00 置入代码 ({ 104, 244, 3, 0, 0 }) ' mov ecx,[esi+4] 8B 4C 24 04 ★父窗口 → ecx 置入代码 ({ 139, 76, 36, 4 }) ' mov eax,[ecx] 8B 01 置入代码 ({ 139, 1 }) ' call [eax+4] FF 50 04 置入代码 ({ 255, 80, 4 }) 返回 (真) .子程序 开关内挂丸, 逻辑型, 公开 .参数 状态, 整数型 .如果 (状态 = 31) 返回 (内挂丸_点控件 (4231)) ' 启动内挂丸 ID=49 .否则 返回 (内挂丸_点控件 (4232)) ' 停止内挂丸 ID=50 .如果结束 ══════════════════════════════════════════════════════════ 备查:如果参数是「控件对象指针」而不是槽号 ══════════════════════════════════════════════════════════ .子程序 点控件_按指针, 逻辑型, 公开 .参数 控件, 整数型 .局部变量 c0, 字节型 .局部变量 c1, 字节型 .局部变量 c2, 字节型 .局部变量 c3, 字节型 c0 = 控件 与 255 c1 = (控件 ÷ 256) 与 255 c2 = (控件 ÷ 65536) 与 255 c3 = (控件 ÷ 16777216) 与 255 置入代码 ({ 190, c0, c1, c2, c3 }) ' mov esi, 控件 BE imm32 置入代码 ({ 106, 0 }) ' push 0 置入代码 ({ 255, 180, 36, 100, 3, 0, 0 }) ' push [esi+0x364] 控件ID 置入代码 ({ 104, 244, 3, 0, 0 }) ' push 0x3F4 置入代码 ({ 139, 76, 36, 4 }) ' mov ecx,[esi+4] 父窗口 置入代码 ({ 139, 1 }) ' mov eax,[ecx] 置入代码 ({ 255, 80, 4 }) ' call [eax+4] 置入代码 ({ 195 }) ' retn 返回 (真) ══════════════════════════════════════════════════════════ 机器码逐条对照表(本次核对结果) ══════════════════════════════════════════════════════════ 指令 机器码 备注 ──────────────────────────────────────────────────────── push 0 6A 00 mov esi,[disp32] 8B 35 ★绝对地址编码,不是 8B B0 push [esi+0x364] FF B4 24 64 03 00 00 ★disp32 形式(>127) push 0x3F4 68 F4 03 00 00 mov ecx,[esi+4] 8B 4C 24 04 ★带 SIB,不是 8B 48 04 mov eax,[ecx] 8B 01 call [eax+4] FF 50 04 retn C3 常见写错的三种: 8B B0 xxxxxxxx → 错,这是 mov esi,[eax+disp32] 8B 48 04 → 错,这是 mov ecx,[eax+4] FF B6 xx 03 00 00 → 错,mod=10 不能有 disp8