From 87a2ac3133ba05b290edd8ea0297c0dc9cd1ca5d Mon Sep 17 00:00:00 2001 From: fofolee Date: Fri, 10 Jan 2025 20:54:25 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9Ewindows=E5=88=86=E7=B1=BB?= =?UTF-8?q?=EF=BC=8C=E6=B7=BB=E5=8A=A0=E7=AA=97=E5=8F=A3=E7=BD=AE=E9=A1=B6?= =?UTF-8?q?=E3=80=81=E4=BF=AE=E6=94=B9=E9=80=8F=E6=98=8E=E5=BA=A6=E3=80=81?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=E4=BD=8D=E7=BD=AE=E3=80=81=E8=81=9A=E7=84=A6?= =?UTF-8?q?=E7=AA=97=E5=8F=A3=E3=80=81=E9=9A=90=E8=97=8F=E7=AA=97=E5=8F=A3?= =?UTF-8?q?=E3=80=81=E5=85=B3=E9=97=AD=E7=AA=97=E5=8F=A3=E3=80=81=E4=BF=AE?= =?UTF-8?q?=E6=94=B9=E7=AA=97=E5=8F=A3=E8=BE=B9=E6=A1=86=E3=80=81=E7=AA=97?= =?UTF-8?q?=E5=8F=A3=E7=A9=BF=E9=80=8F=E3=80=81=E8=8E=B7=E5=8F=96=E7=AA=97?= =?UTF-8?q?=E5=8F=A3=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- plugin/lib/quickcomposer.js | 1 + plugin/lib/quickcomposer/windows/index.js | 5 + plugin/lib/quickcomposer/windows/window.js | 559 ++++++++++++++++++++ src/js/composer/commands/index.js | 12 +- src/js/composer/commands/windowsCommands.js | 207 ++++++++ 5 files changed, 782 insertions(+), 2 deletions(-) create mode 100644 plugin/lib/quickcomposer/windows/index.js create mode 100644 plugin/lib/quickcomposer/windows/window.js create mode 100644 src/js/composer/commands/windowsCommands.js diff --git a/plugin/lib/quickcomposer.js b/plugin/lib/quickcomposer.js index d9c379c..edd9f0b 100644 --- a/plugin/lib/quickcomposer.js +++ b/plugin/lib/quickcomposer.js @@ -8,6 +8,7 @@ const quickcomposer = { math: require("./quickcomposer/math"), audio: require("./quickcomposer/audio"), image: require("./quickcomposer/image"), + windows: require("./quickcomposer/windows"), }; module.exports = quickcomposer; diff --git a/plugin/lib/quickcomposer/windows/index.js b/plugin/lib/quickcomposer/windows/index.js new file mode 100644 index 0000000..eec62be --- /dev/null +++ b/plugin/lib/quickcomposer/windows/index.js @@ -0,0 +1,5 @@ +const window = require("./window"); + +module.exports = { + window, +}; diff --git a/plugin/lib/quickcomposer/windows/window.js b/plugin/lib/quickcomposer/windows/window.js new file mode 100644 index 0000000..af16097 --- /dev/null +++ b/plugin/lib/quickcomposer/windows/window.js @@ -0,0 +1,559 @@ +/** + * 窗口置顶 + * @param {string} method 查找方式:title/handle/active + * @param {string} value 查找值(handle时为数字) + * @param {boolean} isTopMost 是否置顶 + */ +async function setTopMost(method, value, isTopMost) { + const script = ` + using System; + using System.Runtime.InteropServices; + using System.Text; + using System.Diagnostics; + using System.Management; + + public class Program { + [DllImport("user32.dll")] + static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags); + + [DllImport("user32.dll")] + static extern IntPtr FindWindow(string lpClassName, string lpWindowName); + + [DllImport("user32.dll")] + static extern IntPtr GetForegroundWindow(); + + static readonly IntPtr HWND_TOPMOST = new IntPtr(-1); + static readonly IntPtr HWND_NOTOPMOST = new IntPtr(-2); + const uint SWP_NOMOVE = 0x0002; + const uint SWP_NOSIZE = 0x0001; + + public static void Main() { + ${findWindowByMethod(method, value)} + if (hwnd != IntPtr.Zero) { + SetWindowPos(hwnd, ${ + isTopMost ? "HWND_TOPMOST" : "HWND_NOTOPMOST" + }, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); + } + } + } + `; + + await quickcommand.runCsharp(script); +} + +/** + * 设置窗口透明度 + * @param {string} method 查找方式:title/handle/active + * @param {string} value 查找值(handle时为数字) + * @param {number} opacity 透明度 0-100 + */ +async function setOpacity(method, value, opacity) { + const script = ` + using System; + using System.Runtime.InteropServices; + using System.Text; + using System.Diagnostics; + using System.Management; + + public class Program { + [DllImport("user32.dll")] + static extern bool SetLayeredWindowAttributes(IntPtr hwnd, uint crKey, byte bAlpha, uint dwFlags); + + [DllImport("user32.dll")] + static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); + + [DllImport("user32.dll")] + static extern IntPtr FindWindow(string lpClassName, string lpWindowName); + + const int GWL_EXSTYLE = -20; + const int WS_EX_LAYERED = 0x80000; + const uint LWA_ALPHA = 0x2; + + public static void Main() { + ${findWindowByMethod(method, value)} + if (hwnd != IntPtr.Zero) { + SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_LAYERED); + SetLayeredWindowAttributes(hwnd, 0, (byte)(${opacity} * 2.55), LWA_ALPHA); + } + } + } + `; + + await quickcommand.runCsharp(script); +} + +/** + * 设置窗口位置和大小 + * @param {string} method 查找方式:title/handle/active + * @param {string} value 查找值(handle时为数字) + * @param {number} x X坐标 + * @param {number} y Y坐标 + * @param {number} width 宽度 + * @param {number} height 高度 + */ +async function setWindowRect(method, value, x, y, width, height) { + const script = ` + using System; + using System.Runtime.InteropServices; + using System.Text; + using System.Diagnostics; + using System.Management; + + public class Program { + [DllImport("user32.dll")] + static extern bool MoveWindow(IntPtr hWnd, int X, int Y, int nWidth, int nHeight, bool bRepaint); + + [DllImport("user32.dll")] + static extern IntPtr FindWindow(string lpClassName, string lpWindowName); + + public static void Main() { + ${findWindowByMethod(method, value)} + if (hwnd != IntPtr.Zero) { + MoveWindow(hwnd, ${x}, ${y}, ${width}, ${height}, true); + } + } + } + `; + + await quickcommand.runCsharp(script); +} + +/** + * 设置窗口状态 + * @param {string} method 查找方式:title/handle/active + * @param {string} value 查找值(handle时为数字) + * @param {string} state 状态:normal/maximize/minimize + */ +async function setWindowState(method, value, state) { + const script = ` + using System; + using System.Runtime.InteropServices; + using System.Text; + using System.Diagnostics; + using System.Management; + + public class Program { + [DllImport("user32.dll")] + static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); + + [DllImport("user32.dll")] + static extern IntPtr FindWindow(string lpClassName, string lpWindowName); + + const int SW_NORMAL = 1; + const int SW_MAXIMIZE = 3; + const int SW_MINIMIZE = 6; + + public static void Main() { + ${findWindowByMethod(method, value)} + if (hwnd != IntPtr.Zero) { + ShowWindow(hwnd, ${ + state === "maximize" + ? "SW_MAXIMIZE" + : state === "minimize" + ? "SW_MINIMIZE" + : "SW_NORMAL" + }); + } + } + } + `; + + await quickcommand.runCsharp(script); +} + +/** + * 关闭窗口 + * @param {string} method 查找方式:title/handle/active + * @param {string} value 查找值(handle时为数字) + */ +async function closeWindow(method, value) { + const script = ` + using System; + using System.Runtime.InteropServices; + using System.Text; + using System.Diagnostics; + using System.Management; + + public class Program { + [DllImport("user32.dll")] + static extern bool PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); + + [DllImport("user32.dll")] + static extern IntPtr FindWindow(string lpClassName, string lpWindowName); + + const uint WM_CLOSE = 0x0010; + + public static void Main() { + ${findWindowByMethod(method, value)} + if (hwnd != IntPtr.Zero) { + PostMessage(hwnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero); + } + } + } + `; + + await quickcommand.runCsharp(script); +} + +/** + * 设置窗口焦点 + * @param {string} method 查找方式:title/handle/active + * @param {string} value 查找值(handle时为数字) + */ +async function setFocus(method, value) { + const script = ` + using System; + using System.Runtime.InteropServices; + using System.Text; + using System.Diagnostics; + using System.Management; + + public class Program { + [DllImport("user32.dll")] + static extern bool SetForegroundWindow(IntPtr hWnd); + + [DllImport("user32.dll")] + static extern IntPtr FindWindow(string lpClassName, string lpWindowName); + + public static void Main() { + ${findWindowByMethod(method, value)} + if (hwnd != IntPtr.Zero) { + SetForegroundWindow(hwnd); + } + } + } + `; + + await quickcommand.runCsharp(script); +} + +/** + * 设置窗口边框 + * @param {string} method 查找方式:title/handle/active + * @param {string} value 查找值(handle时为数字) + * @param {boolean} hasBorder 是否显示边框 + */ +async function setBorder(method, value, hasBorder) { + const script = ` + using System; + using System.Runtime.InteropServices; + using System.Text; + using System.Diagnostics; + using System.Management; + + public class Program { + [DllImport("user32.dll")] + static extern int GetWindowLong(IntPtr hWnd, int nIndex); + + [DllImport("user32.dll")] + static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); + + [DllImport("user32.dll")] + static extern IntPtr FindWindow(string lpClassName, string lpWindowName); + + const int GWL_STYLE = -16; + const int WS_BORDER = 0x00800000; + const int WS_DLGFRAME = 0x00400000; + const int WS_CAPTION = WS_BORDER | WS_DLGFRAME; + + public static void Main() { + ${findWindowByMethod(method, value)} + if (hwnd != IntPtr.Zero) { + int style = GetWindowLong(hwnd, GWL_STYLE); + if (${hasBorder}) { + style |= WS_CAPTION; + } else { + style &= ~WS_CAPTION; + } + SetWindowLong(hwnd, GWL_STYLE, style); + } + } + } + `; + + await quickcommand.runCsharp(script); +} + +/** + * 设置窗口点击穿透 + * @param {string} method 查找方式:title/handle/active + * @param {string} value 查找值(handle时为数字) + * @param {boolean} isTransparent 是否点击穿透 + */ +async function setClickThrough(method, value, isTransparent) { + const script = ` + using System; + using System.Runtime.InteropServices; + using System.Text; + using System.Diagnostics; + using System.Management; + + public class Program { + [DllImport("user32.dll")] + static extern int GetWindowLong(IntPtr hWnd, int nIndex); + + [DllImport("user32.dll")] + static extern int SetWindowLong(IntPtr hWnd, int nIndex, int dwNewLong); + + [DllImport("user32.dll")] + static extern IntPtr FindWindow(string lpClassName, string lpWindowName); + + const int GWL_EXSTYLE = -20; + const int WS_EX_TRANSPARENT = 0x00000020; + const int WS_EX_LAYERED = 0x80000; + + public static void Main() { + ${findWindowByMethod(method, value)} + if (hwnd != IntPtr.Zero) { + int exStyle = GetWindowLong(hwnd, GWL_EXSTYLE); + exStyle |= WS_EX_LAYERED; + if (${isTransparent}) { + exStyle |= WS_EX_TRANSPARENT; + } else { + exStyle &= ~WS_EX_TRANSPARENT; + } + SetWindowLong(hwnd, GWL_EXSTYLE, exStyle); + } + } + } + `; + + await quickcommand.runCsharp(script); +} + +/** + * 获取窗口信息 + * @param {string} method 查找方式:title/handle/active + * @param {string} value 查找值(handle时为数字) + * @returns {Object} 窗口信息 + */ +async function getWindowInfo(method, value) { + const script = ` + using System; + using System.Runtime.InteropServices; + using System.Text; + using System.Diagnostics; + using System.Web.Script.Serialization; + + public class Program { + [DllImport("user32.dll")] + static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect); + + [DllImport("user32.dll")] + static extern bool GetClientRect(IntPtr hWnd, out RECT lpRect); + + [DllImport("user32.dll")] + static extern IntPtr FindWindow(string lpClassName, string lpWindowName); + + [DllImport("user32.dll")] + static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count); + + [DllImport("user32.dll")] + static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount); + + [DllImport("user32.dll")] + static extern bool IsWindowVisible(IntPtr hWnd); + + [DllImport("user32.dll")] + static extern bool IsIconic(IntPtr hWnd); + + [DllImport("user32.dll")] + static extern bool IsZoomed(IntPtr hWnd); + + [DllImport("user32.dll")] + static extern IntPtr GetForegroundWindow(); + + [DllImport("user32.dll")] + static extern int GetWindowLong(IntPtr hWnd, int nIndex); + + [DllImport("user32.dll")] + static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); + + [StructLayout(LayoutKind.Sequential)] + public struct RECT { + public int Left; + public int Top; + public int Right; + public int Bottom; + } + + const int GWL_STYLE = -16; + const int GWL_EXSTYLE = -20; + const int WS_BORDER = 0x00800000; + const int WS_CAPTION = 0x00C00000; + const int WS_CHILD = 0x40000000; + const int WS_POPUP = unchecked((int)0x80000000); + const int WS_SYSMENU = 0x00080000; + const int WS_MINIMIZEBOX = 0x00020000; + const int WS_MAXIMIZEBOX = 0x00010000; + const int WS_EX_TOPMOST = 0x00000008; + const int WS_EX_TRANSPARENT = 0x00000020; + const int WS_EX_TOOLWINDOW = 0x00000080; + const int WS_EX_LAYERED = 0x00080000; + + public static void Main() { + ${findWindowByMethod(method, value)} + if (hwnd != IntPtr.Zero) { + var windowRect = new RECT(); + GetWindowRect(hwnd, out windowRect); + + var clientRect = new RECT(); + GetClientRect(hwnd, out clientRect); + + var titleText = new StringBuilder(256); + GetWindowText(hwnd, titleText, 256); + + var className = new StringBuilder(256); + GetClassName(hwnd, className, 256); + + uint processId = 0; + GetWindowThreadProcessId(hwnd, out processId); + + string processName = ""; + string processPath = ""; + try { + Process process = Process.GetProcessById((int)processId); + processName = process.ProcessName; + + var startInfo = new ProcessStartInfo { + FileName = "wmic", + Arguments = string.Format("process where ProcessId={0} get ExecutablePath /value", processId), + UseShellExecute = false, + RedirectStandardOutput = true, + CreateNoWindow = true + }; + using (var proc = Process.Start(startInfo)) { + string output = proc.StandardOutput.ReadToEnd(); + if (!string.IsNullOrEmpty(output)) { + string[] lines = output.Split(new[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries); + foreach (string line in lines) { + if (line.StartsWith("ExecutablePath=")) { + processPath = line.Substring("ExecutablePath=".Length); + break; + } + } + } + } + } catch {} + + int style = GetWindowLong(hwnd, GWL_STYLE); + int exStyle = GetWindowLong(hwnd, GWL_EXSTYLE); + + var data = new { + handle = hwnd.ToInt64(), + processId = processId, + process = new { + name = processName, + path = processPath + }, + title = titleText.ToString(), + className = className.ToString(), + window = new { + x = windowRect.Left, + y = windowRect.Top, + width = windowRect.Right - windowRect.Left, + height = windowRect.Bottom - windowRect.Top + }, + client = new { + width = clientRect.Right - clientRect.Left, + height = clientRect.Bottom - clientRect.Top + }, + state = new { + visible = IsWindowVisible(hwnd), + minimized = IsIconic(hwnd), + maximized = IsZoomed(hwnd), + focused = GetForegroundWindow() == hwnd + }, + style = new { + border = (style & WS_BORDER) != 0, + caption = (style & WS_CAPTION) != 0, + child = (style & WS_CHILD) != 0, + popup = (style & WS_POPUP) != 0, + sysmenu = (style & WS_SYSMENU) != 0, + minimizeBox = (style & WS_MINIMIZEBOX) != 0, + maximizeBox = (style & WS_MAXIMIZEBOX) != 0 + }, + exStyle = new { + topmost = (exStyle & WS_EX_TOPMOST) != 0, + transparent = (exStyle & WS_EX_TRANSPARENT) != 0, + toolWindow = (exStyle & WS_EX_TOOLWINDOW) != 0, + layered = (exStyle & WS_EX_LAYERED) != 0 + } + }; + + var serializer = new JavaScriptSerializer(); + Console.WriteLine(serializer.Serialize(data)); + } + } + } + `; + + const result = await quickcommand.runCsharp(script); + return JSON.parse(result); +} + +/** + * 设置窗口可见性 + * @param {string} method 查找方式:title/handle/active + * @param {string} value 查找值(handle时为数字) + * @param {boolean} visible 是否可见 + */ +async function setVisible(method, value, visible) { + const script = ` + using System; + using System.Runtime.InteropServices; + using System.Text; + using System.Diagnostics; + using System.Management; + + public class Program { + [DllImport("user32.dll")] + static extern bool ShowWindow(IntPtr hWnd, int nCmdShow); + + [DllImport("user32.dll")] + static extern IntPtr FindWindow(string lpClassName, string lpWindowName); + + const int SW_HIDE = 0; + const int SW_SHOW = 5; + + public static void Main() { + ${findWindowByMethod(method, value)} + if (hwnd != IntPtr.Zero) { + ShowWindow(hwnd, ${visible ? "SW_SHOW" : "SW_HIDE"}); + } + } + } + `; + + await quickcommand.runCsharp(script); +} + +/** + * 根据不同方式查找窗口 + * @param {string} method 查找方式:title/handle/active + * @param {string} value 查找值(handle时为数字) + * @returns {string} C#代码片段 + */ +function findWindowByMethod(method, value) { + switch (method) { + case "handle": + return `IntPtr hwnd = new IntPtr(${value});`; + case "active": + return `IntPtr hwnd = GetForegroundWindow();`; + default: // title + return `IntPtr hwnd = FindWindow(null, "${value}");`; + } +} + +module.exports = { + setTopMost, + setOpacity, + setWindowRect, + setWindowState, + setVisible, + closeWindow, + setFocus, + setBorder, + setClickThrough, + getWindowInfo, +}; diff --git a/src/js/composer/commands/index.js b/src/js/composer/commands/index.js index 5c67119..4d6329b 100644 --- a/src/js/composer/commands/index.js +++ b/src/js/composer/commands/index.js @@ -14,8 +14,9 @@ import { utoolsCommands } from "./utoolsCommand"; import { screenCommands } from "./screenCommands"; import { audioCommands } from "./audioCommands"; import { imageCommands } from "./imageCommands"; +import { windowsCommands } from "./windowsCommands"; -export const commandCategories = [ +let commands = [ fileCommands, networkCommands, systemCommands, @@ -31,5 +32,12 @@ export const commandCategories = [ mathCommands, userdataCommands, screenCommands, - otherCommands, ]; + +if (window.utools.isWindows()) { + commands.push(windowsCommands); +} + +commands.push(otherCommands); + +export const commandCategories = commands; diff --git a/src/js/composer/commands/windowsCommands.js b/src/js/composer/commands/windowsCommands.js new file mode 100644 index 0000000..0f0eed5 --- /dev/null +++ b/src/js/composer/commands/windowsCommands.js @@ -0,0 +1,207 @@ +import { newVarInputVal } from "js/composer/varInputValManager"; + +export const windowsCommands = { + label: "Windows工具", + icon: "window", + defaultOpened: false, + commands: [ + { + value: "quickcomposer.windows.window.setTopMost", + label: "窗口控制", + desc: "Windows窗口操作", + icon: "window", + config: [ + { + key: "method", + label: "查找方式", + component: "q-select", + icon: "search", + width: 3, + options: [ + { label: "标题", value: "title" }, + { label: "句柄", value: "handle" }, + { label: "活动窗口", value: "active" }, + ], + defaultValue: "title", + }, + { + key: "value", + label: "窗口标题/句柄", + component: "VariableInput", + icon: "title", + width: 9, + placeholder: "标题支持模糊匹配,选择活动窗口无需输入", + }, + ], + subCommands: [ + { + value: "quickcomposer.windows.window.setTopMost", + label: "窗口置顶", + icon: "vertical_align_top", + config: [ + { + key: "isTopMost", + component: "ButtonGroup", + icon: "push_pin", + width: 12, + options: [ + { label: "置顶", value: true }, + { label: "取消置顶", value: false }, + ], + defaultValue: true, + }, + ], + }, + { + value: "quickcomposer.windows.window.setOpacity", + label: "窗口透明度", + icon: "opacity", + config: [ + { + key: "opacity", + label: "透明度", + component: "NumberInput", + icon: "opacity", + width: 12, + min: 0, + max: 100, + defaultValue: 80, + }, + ], + }, + { + value: "quickcomposer.windows.window.setWindowRect", + label: "窗口位置大小", + icon: "aspect_ratio", + config: [ + { + key: "x", + label: "X坐标", + component: "NumberInput", + icon: "arrow_right", + width: 6, + defaultValue: 0, + }, + { + key: "y", + label: "Y坐标", + component: "NumberInput", + icon: "arrow_drop_down", + width: 6, + defaultValue: 0, + }, + { + key: "width", + label: "宽度", + component: "NumberInput", + icon: "swap_horiz", + width: 6, + min: 0, + defaultValue: 800, + }, + { + key: "height", + label: "高度", + component: "NumberInput", + icon: "height", + width: 6, + min: 0, + defaultValue: 600, + }, + ], + }, + { + value: "quickcomposer.windows.window.setWindowState", + label: "窗口状态", + icon: "open_in_full", + config: [ + { + key: "state", + component: "ButtonGroup", + icon: "aspect_ratio", + width: 12, + options: [ + { label: "最大化", value: "maximize" }, + { label: "最小化", value: "minimize" }, + { label: "还原", value: "normal" }, + ], + defaultValue: "maximize", + }, + ], + }, + { + value: "quickcomposer.windows.window.setVisible", + label: "窗口可见性", + icon: "visibility", + config: [ + { + key: "visible", + component: "ButtonGroup", + icon: "visibility", + width: 12, + options: [ + { label: "显示", value: true }, + { label: "隐藏", value: false }, + ], + defaultValue: true, + }, + ], + }, + { + value: "quickcomposer.windows.window.closeWindow", + label: "关闭窗口", + icon: "close", + }, + { + value: "quickcomposer.windows.window.setFocus", + label: "聚焦窗口", + icon: "front_hand", + }, + { + value: "quickcomposer.windows.window.setBorder", + label: "窗口边框", + icon: "border_style", + config: [ + { + key: "hasBorder", + component: "ButtonGroup", + icon: "border_style", + width: 12, + options: [ + { label: "显示边框", value: true }, + { label: "隐藏边框", value: false }, + ], + defaultValue: true, + }, + ], + }, + { + value: "quickcomposer.windows.window.setClickThrough", + label: "点击穿透", + icon: "touch_app", + config: [ + { + key: "isTransparent", + component: "ButtonGroup", + icon: "touch_app", + width: 12, + options: [ + { label: "开启穿透", value: true }, + { label: "关闭穿透", value: false }, + ], + defaultValue: false, + }, + ], + }, + { + value: "quickcomposer.windows.window.getWindowInfo", + label: "窗口信息", + icon: "info", + outputVariable: "windowInfo", + saveOutput: true, + }, + ], + isAsync: true, + }, + ], +};