非 ASCII 字符走 char code 构造绕开智能引号解析陷阱

This commit is contained in:
yu 2026-11-04 09:36:45 +08:00
parent 143fcae34a
commit 1ff6f59990
2 changed files with 25 additions and 6 deletions

View File

@ -140,6 +140,16 @@ function sendKeysEscape(ch: string): string {
return "+^%~(){}[]".includes(ch) ? `{${ch}}` : ch;
}
/**
* 把字符包装成 PowerShell 安全字面量。
* ASCII 直接用单引号字符串(撇号内部 '' 转义);非 ASCII 用 [char]0xXXXX 数值构造——
* Windows PowerShell 5.1 会把 ‘ ’ “ ” 等智能引号当作引号变体解析,直接放进字符串字面量会报「字符串缺少终止符」。
*/
function psCharLiteral(text: string): string {
if (/^[\x20-\x7E]*$/.test(text)) return psString(text);
return Array.from(text).map((c) => `[char]0x${c.codePointAt(0)!.toString(16).padStart(4, "0")}`).join("+") || psString("");
}
const keyboardSend: CapabilityHandler = async (_ctx, node, args) => {
// 字段: words=输入内容(键盘输入), keys/key=按键组合(按键系列), interval=输入间隔(V1毫秒/基础版秒), fuctype=输入方式(0后台接口/1人工模拟/2剪贴板粘贴), window_title=输入目标窗口(留空=当前焦点窗口)
const keys = text(args, ["words", "keys", "key", "text", "value"]);
@ -156,18 +166,20 @@ const keyboardSend: CapabilityHandler = async (_ctx, node, args) => {
// 三种输入方式:脚本尽量简单,转义逻辑放 JS 侧,避免 PowerShell 字符串拼接的语法陷阱
let send: string;
if (mode === "2") {
// 剪贴板粘贴:原文进剪贴板 + Ctrl+V,不做 SendKeys 转义
send = `Set-Clipboard -Value ${psString(keys)}; Start-Sleep -Milliseconds 120; [System.Windows.Forms.SendKeys]::SendWait('^v')`;
// 剪贴板粘贴:原文进剪贴板 + Ctrl+V,不做 SendKeys 转义;内容含智能引号等非 ASCII 时同样走 [char] 构造
const contentExpr = /^[\x20-\x7E]*$/.test(keys) ? psString(keys) : `(-join @(${Array.from(keys).map((c) => psCharLiteral(c)).join(",")}))`;
send = `Set-Clipboard -Value ${contentExpr}; Start-Sleep -Milliseconds 120; [System.Windows.Forms.SendKeys]::SendWait('^v')`;
} else if (mode === "1" && isTextSend) {
// 人工模拟:逐字符;JS 侧把每个字符转义成 SendKeys 字面量,PS 侧只做数组循环
// 数组元素必须用 psString 统一包裹转义(先包 '' 再转义内部引号,顺序反了会产生 ''' 未闭合字符串)
// 数组元素必须用 psCharLiteral:ASCII 走引号字符串,非 ASCII 走 [char]0xXXXX(避开 PS5.1 智能引号解析陷阱)
const escapedChars = Array.from(keys).map((ch) => sendKeysEscape(ch));
const arr = escapedChars.map((c) => psString(c)).join(",");
send = `$chars = @(${arr}); foreach ($c in $chars) { [System.Windows.Forms.SendKeys]::SendWait($c); Start-Sleep -Milliseconds ${intervalMs} }`;
const arr = escapedChars.map((c) => psCharLiteral(c)).join(",");
send = `$chars = @(${arr}); foreach ($c in $chars) { [System.Windows.Forms.SendKeys]::SendWait([string]$c); Start-Sleep -Milliseconds ${intervalMs} }`;
} else {
// 后台接口 / 按键组合 / 人工模拟(按键组合组件):整段发送,文本特殊字符转义
const escaped = isTextSend ? Array.from(keys).map(sendKeysEscape).join("") : keys;
send = `[System.Windows.Forms.SendKeys]::SendWait(${psString(escaped)})`;
const contentExpr = /^[\x20-\x7E]*$/.test(escaped) ? psString(escaped) : `(-join @(${Array.from(escaped).map((c) => psCharLiteral(c)).join(",")}))`;
send = `[System.Windows.Forms.SendKeys]::SendWait(${contentExpr})`;
}
await powershell(`${USER32}; ${activate}Add-Type -AssemblyName System.Windows.Forms; ${send}`);
await manualDelay();

View File

@ -38,6 +38,13 @@ describe("键盘输入组件真实冒烟", () => {
console.log("[键盘输入] 人工模拟+撇号+特殊字符 ✓:", out);
}, 60000);
it("人工模拟模式含中文标点(用户 09:32 踩坑场景:】【‘;。/ 智能引号)不抛语法错", async () => {
const words = "jashfjksudfgisa54445哈哈哈哈AAAAHHBHG+t】【‘;。/";
const out = await invoke("Keyboard_Send_V1", { words, interval: "10", fuctype: "1" });
expect(out).toBe(words);
console.log("[键盘输入] 人工模拟+中文标点+智能引号 ✓");
}, 60000);
it("剪切板粘贴模式: 原文进入剪贴板并发送 Ctrl+V", async () => {
const content = `粘贴${Date.now()}`;
const out = await invoke("Keyboard_Send_V1", { words: content, fuctype: "2" });