tools: open real offline frmP2PChat window
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
# A-route 离线打开真实 frmP2PChat 会话窗口
|
||||
|
||||
日期:2026-07-11
|
||||
分支:`codex/a-route-open-real-chat-window`
|
||||
目标:不登录、不连服务端,在本机离线样本中打开真实 `IMPP.Client.Business.ChatManager.SingleChat.frmP2PChat` 窗口,供后续 RPA 对真实窗口做 UIA 控制。
|
||||
|
||||
## 结论
|
||||
|
||||
已经在未登录/离线环境下打开真实 `frmP2PChat`。
|
||||
|
||||
成功命令:
|
||||
|
||||
```powershell
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\open-offline-real-frmP2PChat.ps1 -RuntimeMode Deps -ShowWindow -HoldSeconds 1
|
||||
```
|
||||
|
||||
成功产物:
|
||||
|
||||
- 结果 JSON:`E:\coding\codex\isphere-ai-bridge\runs\offline-chat-window\real-frmP2PChat-result-20260711-205312.json`
|
||||
- HWND:`0xC0CB4`
|
||||
- 截图:`E:\coding\codex\isphere-ai-bridge\runs\offline-chat-window\real-frmP2PChat-20260711-205312.png`
|
||||
- UIA dump:`E:\coding\codex\isphere-ai-bridge\runs\offline-chat-window\uia-dump-real-frmP2PChat-20260711-205312.json`
|
||||
- UIA root:
|
||||
- `automation_id = frmP2PChat`
|
||||
- `control_type = Window`
|
||||
- `framework_id = WinForm`
|
||||
- `visible = true`
|
||||
|
||||
UIA dump 已确认可见关键控件:
|
||||
|
||||
- `rtbRecvMessage`
|
||||
- `rtbSendMessage`
|
||||
- `btnSend`
|
||||
|
||||
窗口内显示“您已处于离线状态,无法发送消息,请上线后再次尝试!”。这是预期状态;本轮目标不是发送消息,也不是上传文件,只是打开真实会话窗口。
|
||||
|
||||
## 新增脚本
|
||||
|
||||
### 1. 真实窗口打开脚本
|
||||
|
||||
`scripts/open-offline-real-frmP2PChat.ps1`
|
||||
|
||||
关键能力:
|
||||
|
||||
- 自动切到 32-bit PowerShell,避免 x86 客户端程序集 `BadImageFormatException`。
|
||||
- 只读加载真实客户端目录:
|
||||
`runs/offline-real-client-window/full/zyl/Impp`
|
||||
- 使用 `AssemblyResolve` 从真实客户端目录解析依赖。
|
||||
- 构造真实类型:
|
||||
`IMPP.Client.Business.ChatManager.SingleChat.frmP2PChat`
|
||||
- 支持不 Show 的构造验证,也支持 `-ShowWindow` 真实显示、截图和 UIA dump。
|
||||
- 捕获 WinForms `ThreadException`,输出 IL offset、堆栈和 JSON 证据,避免 .NET 异常弹窗卡死。
|
||||
|
||||
### 2. 构造函数 IL / NullRisk 提取脚本
|
||||
|
||||
`scripts/extract-frmP2PChat-il-risk.ps1`
|
||||
|
||||
输出:
|
||||
|
||||
- 完整构造函数 IL:
|
||||
`runs/offline-chat-window/frmP2PChat-ctor.il.txt`
|
||||
- `callvirt` / `ldfld` NullRisk CSV:
|
||||
`runs/offline-chat-window/frmP2PChat-ctor-null-risk.csv`
|
||||
- 风险摘要:
|
||||
`runs/offline-chat-window/frmP2PChat-ctor-null-risk.md`
|
||||
|
||||
提取结果:
|
||||
|
||||
- 构造函数源 IL 行:`406271-406860`
|
||||
- 构造函数行数:`590`
|
||||
- `callvirt` / `ldfld` 风险项:`150`
|
||||
|
||||
## 关键 IL 证据
|
||||
|
||||
构造函数签名:
|
||||
|
||||
```text
|
||||
frmP2PChat(com.vision.smack.Chat chat, string pluginInfo, string extendTabJson)
|
||||
```
|
||||
|
||||
高信号 NullRisk:
|
||||
|
||||
| IL | 证据 | 需要补的运行态 |
|
||||
|---|---|---|
|
||||
| `IL_00f6` | `XMPPConnection::add_OnConnectError` | `IMPPManager.Instance.Connection` / backing field |
|
||||
| `IL_0111` | `XMPPConnection::add_OnReConnectOk` | 同上 |
|
||||
| `IL_013b` / `IL_0140` | `P2PChat::get_OtherJid()` / `Jid::getBareJid()` | `P2PChat.OtherJid` |
|
||||
| `IL_0156` | `IMPPManager::get_UserInfo()` | `IMPPManager._userInfo` |
|
||||
| `IL_0575` | `MessageCenter::add_OnTcpMessageArrived` | `IMPPManager.MessageCenter` |
|
||||
| `IL_0614` | `RosterManager::UpdateStrangerStatus` | `IMPPManager.RosterManager` |
|
||||
| `IL_0631` | `BaseConfig::VisualPhoneEnable()` | `Config.BaseConfig` |
|
||||
|
||||
Show 阶段新增证据:
|
||||
|
||||
| IL | 证据 | 处理 |
|
||||
|---|---|---|
|
||||
| `frmP2PChat_Load IL_004d/0058` | `Config::get_BaseConfig()` 后 `BaseConfig::RemoteControlVisible()` | 补 `Config.<BaseConfig>k__BackingField` |
|
||||
| `frmChatBase_Activated IL_0000/0005` | `IMPPManager.get_Instance().get_frmMain().BringModalFormToFront()` | 补 `IMPPManager.<frmMain>k__BackingField` |
|
||||
| `frmP2PChat_Load IL_137f` | `UserInfoManager.getUserInfo(otherBareJid,true)` 后使用 `UserInfo::get_CompanyID()` | 构造完成后预热 `UserInfoCache` |
|
||||
|
||||
注意:`UserInfoCache` 不能在 `frmP2PChat` 构造前预热。过早预热会让构造函数进入 `UCUserInformation.LoadUserInfo(UserInfo)`,在 `UCUserInformation::LoadUserInfo IL_0085` 触发新的 NRE。当前脚本是在真实窗体构造完成后、`Show()` 前预热缓存。
|
||||
|
||||
## 最小 fake runtime 清单
|
||||
|
||||
当前成功路径最小补齐如下:
|
||||
|
||||
1. `IMPPManager.Instance`
|
||||
- `<MessageCenter>k__BackingField`
|
||||
- `<frmMain>k__BackingField`
|
||||
- `LogonUser`
|
||||
- `_userInfo`
|
||||
- `_rosterUserInfoList`
|
||||
2. `SmarkManager`
|
||||
- `<SmarkManagerInstance>k__BackingField`
|
||||
3. `XMPPConnection`
|
||||
- `Jid`
|
||||
- `UserName`
|
||||
- `LoginState`
|
||||
- `CompanyID`
|
||||
- `Resource`
|
||||
- `_packetListeners`
|
||||
- `_packetCollectors`
|
||||
4. `IMPPManager` inherited manager fields/properties
|
||||
- `Connection` and `<Connection>k__BackingField`
|
||||
- `IsConnected` and `<IsConnected>k__BackingField`
|
||||
- `PresenceManager` and backing field
|
||||
- `P2PChatManager` and backing field
|
||||
- `RosterManager` and backing field
|
||||
5. Config
|
||||
- `ConfigSystemManager.config`
|
||||
- `Config.<BaseConfig>k__BackingField`
|
||||
- `Config.<UserInfo>k__BackingField`
|
||||
- `Config.<LoginInfo>k__BackingField`
|
||||
6. `UserInfoCache`
|
||||
- 在 `frmP2PChat` 构造后、`Show()` 前加入自己和对端的 JID/ID 映射。
|
||||
7. Chat 对象
|
||||
- 真实 `com.vision.smack.P2PChat`
|
||||
- `OtherJid = codex-probe@offline.local`
|
||||
|
||||
## 验证命令
|
||||
|
||||
```powershell
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\test-extract-frmP2PChat-il-risk.ps1
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\extract-frmP2PChat-il-risk.ps1 -OutDir runs/offline-chat-window
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\test-open-offline-real-frmP2PChat-contract.ps1
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\open-offline-real-frmP2PChat.ps1 -RuntimeMode Deps
|
||||
powershell -NoProfile -ExecutionPolicy Bypass -File scripts\open-offline-real-frmP2PChat.ps1 -RuntimeMode Deps -ShowWindow -HoldSeconds 1
|
||||
```
|
||||
|
||||
## 当前边界
|
||||
|
||||
- 已成功打开真实 `frmP2PChat`。
|
||||
- UIA 可 dump 到真实 `frmP2PChat`、`rtbRecvMessage`、`rtbSendMessage`、`btnSend`。
|
||||
- 离线环境下窗口会提示无法发送消息,这是客户端真实离线状态表现,不影响后续 RPA 窗口定位。
|
||||
- 本轮没有处理真实发送、真实上传、真实登录态。
|
||||
- `btnSendFile` 在这次真实离线 UIA dump 中没有以 `btnSendFile` automation id 暴露;后续如果 RPA 需要上传文件,需要另开节点定位真实工具栏按钮或菜单路径。
|
||||
|
||||
## 下一轮建议
|
||||
|
||||
1. 把 A-route RPA 的窗口定位目标从 synthetic probe 切到真实 `frmP2PChat`:
|
||||
- root `automation_id=frmP2PChat`
|
||||
- send box `automation_id=rtbSendMessage`
|
||||
- send button `automation_id=btnSend`
|
||||
2. 如果弹出“企业信息获取失败”影响自动化,单独加一个非阻塞关闭弹窗步骤。
|
||||
3. 如果后续需要文件按钮,再专门分析真实工具栏图标按钮、菜单项和 `ToolStrip` 命中路径。
|
||||
Reference in New Issue
Block a user