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

7.6 KiB
Raw Blame History

A-route 离线打开真实 frmP2PChat 会话窗口

日期2026-07-11 分支:codex/a-route-open-real-chat-window 目标:不登录、不连服务端,在本机离线样本中打开真实 IMPP.Client.Business.ChatManager.SingleChat.frmP2PChat 窗口,供后续 RPA 对真实窗口做 UIA 控制。

结论

已经在未登录/离线环境下打开真实 frmP2PChat

成功命令:

powershell -NoProfile -ExecutionPolicy Bypass -File scripts\open-offline-real-frmP2PChat.ps1 -RuntimeMode Deps -ShowWindow -HoldSeconds 1

成功产物:

  • 结果 JSONE:\coding\codex\isphere-ai-bridge\runs\offline-chat-window\real-frmP2PChat-result-20260711-205312.json
  • HWND0xC0CB4
  • 截图:E:\coding\codex\isphere-ai-bridge\runs\offline-chat-window\real-frmP2PChat-20260711-205312.png
  • UIA dumpE:\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 证据

构造函数签名:

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 -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 到真实 frmP2PChatrtbRecvMessagertbSendMessagebtnSend
  • 离线环境下窗口会提示无法发送消息,这是客户端真实离线状态表现,不影响后续 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_messageuia_rpa connector形成可重复的本地 UI 动作 smoke。
  3. 在线登录环境中补一次业务发送验证发送前后记录、content hash、ack/发送记录、idempotency 不重复发送。
  4. 如果后续需要文件按钮,再专门分析真实工具栏图标按钮、菜单项和 ToolStrip 命中路径。