From fc1207cddd78c24625d35ef2892e48db8d6b2395 Mon Sep 17 00:00:00 2001 From: yu <191972707@qq.com> Date: Thu, 29 Oct 2026 10:01:32 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20P2=20=E6=8E=A5=E5=85=A5=E2=80=94?= =?UTF-8?q?=E2=80=94=E5=B1=8F=E5=B9=95=E9=94=81/=E5=B1=8F=E4=BF=9D/?= =?UTF-8?q?=E5=87=AD=E6=8D=AE/=E8=BF=9C=E7=A8=8B=E6=A1=8C=E9=9D=A2/?= =?UTF-8?q?=E8=BE=93=E5=85=A5=E6=B3=95/=E6=96=87=E4=BB=B6=E5=AF=B9?= =?UTF-8?q?=E8=AF=9D=E6=A1=86=EF=BC=8819=20id=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/capabilities/desktop.ts | 50 ++++++++++++++ src/main/capabilities/index.ts | 1 + src/main/capabilities/windows-system.ts | 92 +++++++++++++++++++++++++ 3 files changed, 143 insertions(+) create mode 100644 src/main/capabilities/windows-system.ts diff --git a/src/main/capabilities/desktop.ts b/src/main/capabilities/desktop.ts index e571d20..794d6cd 100644 --- a/src/main/capabilities/desktop.ts +++ b/src/main/capabilities/desktop.ts @@ -160,6 +160,56 @@ register(["Window_CloseWindow", "Window_Close"], closeWindow); register(["App_Run", "App_Start"], runApp); register("Clipboard_GetText", clipboardGet); register("Clipboard_SetText", clipboardSet); +register("Dialog_Notice_V1", async (_ctx, node, args) => { + const msg = text(args, ["text", "message"]); + const title = text(args, ["title"], "通知"); + const timeout = Math.max(1, number(args, ["timeout", "time_out"], 10)); + return powershell("(New-Object -ComObject WScript.Shell).Popup(" + psString(msg) + ", " + timeout + ", " + psString(title) + ", 0) | Out-Null; $true"); +}); + +register("Dialog_Notice_V2", async (ctx, node, args) => { + const msg = text(args, ["text", "message"]); + const title = text(args, ["title"], "通知"); + const timeout = Math.max(1, number(args, ["time_out", "timeout"], 10)); + return powershell("(New-Object -ComObject WScript.Shell).Popup(" + psString(msg) + ", " + timeout + ", " + psString(title) + ", 0) | Out-Null; $true"); +}); + +register("Dialog_CsMsgBox", async (_ctx, _node, args) => { + const msg = text(args, ["text", "message"]); + const title = text(args, ["title"], "提示"); + const buttons = Number(args.msg_button ?? 0); + const autoClose = Number(args.autoClose ?? 0); + const time = Math.max(1, Number(args.time ?? 10)); + const popup = "(New-Object -ComObject WScript.Shell).Popup(" + psString(msg) + ", " + (autoClose ? time : -1) + ", " + psString(title) + ", " + buttons + ")"; + const choice = await powershell(popup + " | Out-Null; if (" + (autoClose ? "$true" : "$false") + ") {}"); + return powershell(popup); +}); + +register("Dialog_OpenFile", async (_ctx, _node, args) => { + const title = text(args, ["title"], "选择文件"); + const wildcard = text(args, ["wildcard"], "所有文件|*.*"); + return powershell("Add-Type -AssemblyName System.Windows.Forms | Out-Null; $dlg = New-Object System.Windows.Forms.OpenFileDialog; $dlg.Filter = " + psString(wildcard) + "; $dlg.Title = " + psString(title) + "; if ($dlg.ShowDialog() -eq 'OK') { $dlg.FileName } else { '' }"); +}); + +register("Dialog_OpenFiles", async (_ctx, _node, args) => { + const title = text(args, ["title"], "选择文件"); + const wildcard = text(args, ["wildcard"], "所有文件|*.*"); + return powershell("Add-Type -AssemblyName System.Windows.Forms | Out-Null; $dlg = New-Object System.Windows.Forms.OpenFileDialog; $dlg.Filter = " + psString(wildcard) + "; $dlg.Title = " + psString(title) + "; $dlg.Multiselect = $true; if ($dlg.ShowDialog() -eq 'OK') { $dlg.FileNames -join ';' } else { '' }"); +}); + +register("Dialog_SaveFile", async (_ctx, _node, args) => { + const title = text(args, ["title"], "保存文件"); + const wildcard = text(args, ["wildcard"], "所有文件|*.*"); + return powershell("Add-Type -AssemblyName System.Windows.Forms | Out-Null; $dlg = New-Object System.Windows.Forms.SaveFileDialog; $dlg.Filter = " + psString(wildcard) + "; $dlg.Title = " + psString(title) + "; if ($dlg.ShowDialog() -eq 'OK') { $dlg.FileName } else { '' }"); +}); + +register(["Dialog_InputBox", "Dialog_CsInputBox"], async (_ctx, _node, args) => { + const title = text(args, ["title", "input_title"], "请输入"); + const description = text(args, ["description", "txt_content"], ""); + const defaultValue = text(args, ["text"], ""); + return powershell("Add-Type -AssemblyName Microsoft.VisualBasic | Out-Null; [Microsoft.VisualBasic.Interaction]::InputBox(" + psString(description) + ", " + psString(title) + ", " + psString(defaultValue) + ")"); +}); + register("Dialog_Notice", notice); register("Dialog_MsgBox", notice); register("System_ExecuteCommand", executeCommand); diff --git a/src/main/capabilities/index.ts b/src/main/capabilities/index.ts index d8c9cc7..fd1e084 100644 --- a/src/main/capabilities/index.ts +++ b/src/main/capabilities/index.ts @@ -14,6 +14,7 @@ import "./web-batch2"; import "./web-crawl"; import "./human-dialog"; import "./time-caps"; +import "./windows-system"; import "./pdf"; import "./word"; import "./word-extended"; diff --git a/src/main/capabilities/windows-system.ts b/src/main/capabilities/windows-system.ts new file mode 100644 index 0000000..0760bb5 --- /dev/null +++ b/src/main/capabilities/windows-system.ts @@ -0,0 +1,92 @@ +import { register } from "../engine/registry"; +import { RuntimeError } from "../engine/types"; +import { execFile } from "node:child_process"; +import { promisify } from "node:util"; + +const run = promisify(execFile); +const ps = (script: string) => run("powershell.exe", ["-NoProfile", "-NonInteractive", "-ExecutionPolicy", "Bypass", "-Command", script], { windowsHide: true, timeout: 60000 }); + +function reqText(args: Record, keys: string[], label: string, nodeId: string): string { + for (const key of keys) { + const v = String(args[key] ?? "").trim(); + if (v) return v; + } + throw new RuntimeError("validation_error", label + "为空", { nodeId, componentId: nodeId }); +} + +register("ScreenControl_Screenlock", async () => { + await run("rundll32.exe", ["user32.dll,LockWorkStation"], { windowsHide: true }); + return { locked: true }; +}); + +register("ScreenControl_ScreenUnlock", async () => { + // 仅唤醒屏幕/解除屏保;系统锁屏需人工输入密码,无法通用自动解锁 + await ps("Add-Type -MemberDefinition '[DllImport(\"user32.dll\")] public static extern void mouse_event(uint f, uint dx, uint dy, uint d, UIntPtr e);' -Name W32U -Namespace WinRpa | Out-Null; [WinRpa.W32U]::mouse_event(0x0001, 1, 1, 0, [UIntPtr]::Zero); Start-Sleep -Milliseconds 200; [WinRpa.W32U]::mouse_event(0x0001, 0xFFFFFFFF, 0, 0, [UIntPtr]::Zero)"); + return { awakened: true, note: "已唤醒屏幕;锁屏需人工输入密码" }; +}); + +const SC_SCREENSAVE = 0xF140; +const WM_SYSCOMMAND = 0x0112; +register(["System_ScreenSaverShow", "System_ScreenSaverShowV1"], async () => { + await ps("Add-Type -MemberDefinition '[DllImport(\"user32.dll\")] public static extern IntPtr FindWindow(string cls, string win); [DllImport(\"user32.dll\")] public static extern IntPtr SendMessage(IntPtr h, uint m, IntPtr w, IntPtr l);' -Name W32S -Namespace WinRpa | Out-Null; [WinRpa.W32S]::SendMessage([IntPtr]0xFFFF, [UIntPtr]0x0112, [IntPtr]0xF140, [IntPtr]::Zero) | Out-Null"); + return { screensaver: true }; +}); + +register("System_ScreenSaverStop", async () => { + await ps("Add-Type -MemberDefinition '[DllImport(\"user32.dll\")] public static extern IntPtr FindWindow(string cls, string win); [DllImport(\"user32.dll\")] public static extern bool PostMessage(IntPtr h, uint m, IntPtr w, IntPtr l);' -Name W32P -Namespace WinRpa | Out-Null; $h = [WinRpa.W32P]::FindWindow('WindowsScreenSaverClass', $null); if ($h -ne [IntPtr]::Zero) { [WinRpa.W32P]::PostMessage($h, 0x0010, [IntPtr]::Zero, [IntPtr]::Zero) | Out-Null }"); + return { stopped: true }; +}); + +register("System_ReadCredential", async (ctx, node, args) => { + const app = reqText(args, ["application"], "凭据目标", node.id); + const { stdout } = await run("cmdkey", ["/list"], { windowsHide: true }); + const exists = stdout.includes(app); + const userLine = stdout.split(/\r?\n/).find(l => l.includes(app)); + return { target: app, exists, detail: exists ? (userLine ?? "").trim() : "" }; +}); + +register("System_AddCredential", async (ctx, node, args) => { + const app = reqText(args, ["application"], "凭据目标", node.id); + const user = reqText(args, ["name"], "用户名", node.id); + const pwd = String(args.password ?? ""); + const psArgs = pwd ? ["/generic:" + app, "/user:" + user, "/pass:" + pwd] : ["/generic:" + app, "/user:" + user]; + await run("cmdkey", psArgs, { windowsHide: true }); + return { added: true, target: app }; +}); + +register("System_DeleteCredential", async (ctx, node, args) => { + const app = reqText(args, ["application"], "凭据目标", node.id); + await run("cmdkey", ["/delete:" + app], { windowsHide: true }); + return { deleted: true, target: app }; +}); + +register("System_ConnectRemoteDesktop", async (ctx, node, args) => { + const ip = reqText(args, ["ip"], "远程地址", node.id); + const port = String(args.port ?? "").trim(); + const target = port ? ip + ":" + port : ip; + await run("mstsc.exe", ["/v:" + target], { windowsHide: true }); + return { connected: true, target }; +}); + +register("System_CloseRemoteDesktop", async () => { + await run("tsdiscon.exe", [], { windowsHide: true }); + return { disconnected: true }; +}); + +register("System_GetInputMethodState", async () => { + const { stdout } = await ps("Add-Type -AssemblyName System.Windows.Forms | Out-Null; $cur = [System.Windows.Forms.InputLanguage]::CurrentInputLanguage; Write-Output ($cur.Culture.Name + '|' + $cur.LayoutName)"); + const [culture, layout] = stdout.split("|"); + return { culture, layout }; +}); + +register("System_SetInputMethodState", async (ctx, node, args) => { + const mode = reqText(args, ["mode"], "输入法文化名", node.id); + await ps("Add-Type -AssemblyName System.Windows.Forms | Out-Null; $lang = [System.Windows.Forms.InputLanguage]::FromCulture('" + mode.replace(/'/g, "''") + "'); if ($lang) { [System.Windows.Forms.InputLanguage]::CurrentInputLanguage = $lang }"); + return { set: true, mode }; +}); + +register("File_OpenFile", async (ctx, node, args) => { + const p = reqText(args, ["path"], "文件夹路径", node.id); + await run("explorer.exe", [p], { windowsHide: true }); + return { opened: true, path: p }; +});