proof 脚本原先硬编码 D 盘绝对路径,换机器就失效;仓库内已有同约定 (build-android.ps1 用 \ 推导),此处对齐。 text_check 的报错文案说「为空」,实际触发条件是 --units 缺失,改正。
91 lines
3.4 KiB
Rust
91 lines
3.4 KiB
Rust
//! 协议 v3 文本注入链路的桌面真边界验证工具(loop-2b,不依赖 Android)。
|
||
//!
|
||
//! 证明的事:`Session::send_text` 经线格式到被控端后,`agent` 的
|
||
//! `KEYEVENTF_UNICODE` 注入把**同一段 UTF-16 code unit** 送进了前台应用。
|
||
//! 判据由外部读取目标窗口的实际文本内容(调用方负责),本工具只负责发送。
|
||
//!
|
||
//! 用法:
|
||
//! 终端1: target\release\agent.exe --port 47800 --code t
|
||
//! 终端2: 把目标应用(记事本)切到前台,然后
|
||
//! cargo run -p viewer-core --example text_check -- \
|
||
//! --port 47800 --code t --units 20320,22909,55357,56832
|
||
//!
|
||
//! `--units` 是十进制的 UTF-16 code unit 列表(逗号分隔):
|
||
//! 「你好😀」= 20320,22909,55357,56832(😀 是代理对)。
|
||
//! 刻意不收自然语言字符串参数:命令行到 Rust 的编码往返会引入与本轮无关的变量,
|
||
//! 由调用方(PowerShell)从 .NET 字符串取 char[] 更忠实。
|
||
//!
|
||
//! `--delay`:连接后等待毫秒再注入。给调用方把目标窗口切到前台的时间——
|
||
//! SendInput 注入的是**前台**应用,窗口没在前台等于测了个寂寞。
|
||
//! `--hold`:注入后保持会话的毫秒数,等消息在远端消化完再关连接。
|
||
|
||
use anyhow::{bail, Result};
|
||
use std::time::Duration;
|
||
use viewer_core::{ConnectConfig, Session};
|
||
|
||
fn arg(name: &str) -> Option<String> {
|
||
let a: Vec<String> = std::env::args().collect();
|
||
a.iter()
|
||
.position(|x| x == name)
|
||
.and_then(|i| a.get(i + 1))
|
||
.cloned()
|
||
}
|
||
|
||
fn parse_units(spec: &str) -> Result<Vec<u16>> {
|
||
if spec.trim().is_empty() {
|
||
bail!(
|
||
"--units 缺失或为空:本工具要验证的是「有文本送出并被注入」,\
|
||
空输入没有验证价值(空 Text 在产品路径上由 Session::send_text 拦掉)"
|
||
);
|
||
}
|
||
spec.split(',')
|
||
.map(|s| s.trim())
|
||
.filter(|s| !s.is_empty())
|
||
.map(|s| {
|
||
s.parse::<u32>()
|
||
.ok()
|
||
.and_then(|v| u16::try_from(v).ok())
|
||
.ok_or_else(|| anyhow::anyhow!("--units 必须是 0..=65535 的十进制数,got {s:?}"))
|
||
})
|
||
.collect()
|
||
}
|
||
|
||
#[tokio::main]
|
||
async fn main() -> Result<()> {
|
||
let port: u16 = arg("--port")
|
||
.map(|s| s.parse())
|
||
.transpose()?
|
||
.unwrap_or(47800);
|
||
let code = arg("--code").unwrap_or_else(|| "123456".into());
|
||
let host = arg("--host").unwrap_or_else(|| "127.0.0.1".into());
|
||
let delay = arg("--delay")
|
||
.map(|s| s.parse())
|
||
.transpose()?
|
||
.unwrap_or(1500u64);
|
||
let hold = arg("--hold")
|
||
.map(|s| s.parse())
|
||
.transpose()?
|
||
.unwrap_or(800u64);
|
||
let units = parse_units(&arg("--units").unwrap_or_default())?;
|
||
|
||
let session = Session::connect(ConnectConfig::direct(&host, port, &code)).await?;
|
||
println!(
|
||
"[text_check] connected peer={} units={}",
|
||
session.peer_addr(),
|
||
units.len()
|
||
);
|
||
|
||
tokio::time::sleep(Duration::from_millis(delay)).await;
|
||
session.send_text(&units);
|
||
println!("[text_check] sent (只报长度,不打印内容)");
|
||
|
||
tokio::time::sleep(Duration::from_millis(hold)).await;
|
||
let st = session.stats();
|
||
println!(
|
||
"[text_check] frames={} rtt={}ms — 会话活着,注入已发给前台应用",
|
||
st.frames_in, st.rtt_ms
|
||
);
|
||
session.close().await;
|
||
Ok(())
|
||
}
|