Files
isphere-ai-bridge/docs/source-discovery/2026-07-11-a-route-open-real-chat-window.md
2026-07-12 01:22:56 +08:00

178 lines
7.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 需要上传文件,需要另开节点定位真实工具栏按钮或菜单路径。
## 2026-07-12 主分支状态校准
该能力已经随 A-route 分支合并到 `main`。业务含义如下:
- 已完成:真实离线 `frmP2PChat` 打开、UIA 识别、发送框定位、发送按钮定位。
- 已演示:在真实离线窗口内写入文本并点击 `btnSend`,可用于验证数字员工的窗口识别、定位和点击链路。
- 未完成:在线登录态下的真实发送、服务端 ack、接收方送达、发送记录匹配。
- 未完成:真实文件上传/发送。当前真实 UIA dump 没有稳定暴露 `btnSendFile`,文件发送要单独做工具栏/菜单定位。
因此,文档里出现的 helper `sent_message=true` 只能解释为“UI 发送动作已触发”,不能解释为“业务消息已送达”。
## 下一轮建议
1. 把 A-route RPA 的窗口定位目标从 synthetic probe 切到真实 `frmP2PChat`
- root `automation_id=frmP2PChat`
- send box `automation_id=rtbSendMessage`
- send button `automation_id=btnSend`
2. 把真实窗口 HWND 接入 `isphere_send_message``uia_rpa` connector形成可重复的本地 UI 动作 smoke。
3. 在线登录环境中补一次业务发送验证发送前后记录、content hash、ack/发送记录、idempotency 不重复发送。
4. 如果后续需要文件按钮,再专门分析真实工具栏图标按钮、菜单项和 `ToolStrip` 命中路径。