mirror of
https://github.com/fofolee/uTools-quickcommand.git
synced 2025-06-29 20:32:44 +08:00
win自动化分类新增监控剪贴板和文件夹,支持等待剪贴板变化和文件夹变化,并返回变化内容
This commit is contained in:
parent
0ff5c4b49f
commit
55be953eef
279
plugin/lib/csharp/monitor.cs
Normal file
279
plugin/lib/csharp/monitor.cs
Normal file
@ -0,0 +1,279 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Windows.Forms;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Drawing;
|
||||
|
||||
class Monitor {
|
||||
// Win32 API
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
private static extern IntPtr SetClipboardViewer(IntPtr hWndNewViewer);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
private static extern bool ChangeClipboardChain(IntPtr hWndRemove, IntPtr hWndNewNext);
|
||||
|
||||
private static FileSystemWatcher fsWatcher;
|
||||
private static IntPtr nextClipboardViewer;
|
||||
private static ClipboardForm clipboardForm;
|
||||
private static bool running = true;
|
||||
private static bool listenOnce = false;
|
||||
private static bool isFirstClipboardEvent = true;
|
||||
|
||||
private class ClipboardForm : Form {
|
||||
public ClipboardForm() {
|
||||
this.ShowInTaskbar = false;
|
||||
this.Visible = false;
|
||||
this.WindowState = FormWindowState.Minimized;
|
||||
this.FormBorderStyle = FormBorderStyle.None;
|
||||
this.Size = new System.Drawing.Size(1, 1);
|
||||
this.Load += (sender, e) => {
|
||||
nextClipboardViewer = SetClipboardViewer(this.Handle);
|
||||
};
|
||||
this.FormClosing += (sender, e) => {
|
||||
ChangeClipboardChain(this.Handle, nextClipboardViewer);
|
||||
};
|
||||
}
|
||||
|
||||
protected override CreateParams CreateParams {
|
||||
get {
|
||||
CreateParams cp = base.CreateParams;
|
||||
cp.ExStyle |= 0x80; // WS_EX_TOOLWINDOW
|
||||
return cp;
|
||||
}
|
||||
}
|
||||
|
||||
protected override void WndProc(ref Message m) {
|
||||
if (m.Msg == 0x308) { // WM_DRAWCLIPBOARD
|
||||
if (isFirstClipboardEvent) {
|
||||
isFirstClipboardEvent = false;
|
||||
} else {
|
||||
HandleClipboardChanged();
|
||||
if (listenOnce) {
|
||||
running = false;
|
||||
this.BeginInvoke(new Action(this.Close));
|
||||
}
|
||||
}
|
||||
SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
|
||||
}
|
||||
else if (m.Msg == 0x30D) { // WM_CHANGECBCHAIN
|
||||
if (m.WParam == nextClipboardViewer)
|
||||
nextClipboardViewer = m.LParam;
|
||||
else if (nextClipboardViewer != IntPtr.Zero)
|
||||
SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
|
||||
}
|
||||
base.WndProc(ref m);
|
||||
}
|
||||
}
|
||||
|
||||
public static void ShowHelp() {
|
||||
Console.WriteLine(@"Windows 监控工具使用说明
|
||||
用法: monitor.exe -type <监控类型> [参数...]
|
||||
|
||||
监控类型:
|
||||
clipboard - 剪贴板监控
|
||||
参数:
|
||||
-once 只监听一次变化就停止(可选)
|
||||
示例:
|
||||
monitor.exe -type clipboard -once
|
||||
|
||||
filesystem - 文件系统监控
|
||||
参数:
|
||||
-path <路径> 要监控的文件夹路径
|
||||
-filter <过滤器> 文件过滤器,如 *.txt(可选,默认监控所有文件)
|
||||
-recursive <bool> 是否监控子文件夹(可选,默认为 true)
|
||||
-once 只监听一次变化就停止(可选)
|
||||
示例:
|
||||
monitor.exe -type filesystem -path C:\MyFolder -filter *.txt -recursive true -once
|
||||
|
||||
返回值:
|
||||
监控到的变化会实时输出 JSON 格式的事件信息:
|
||||
剪贴板事件:
|
||||
{""type"": ""clipboard"", ""format"": ""文本/图片/文件"", ""content"": ""变化内容""}
|
||||
文件系统事件:
|
||||
{""type"": ""filesystem"", ""event"": ""created/changed/deleted/renamed"", ""path"": ""文件路径"", ""oldPath"": ""重命名前的路径""}
|
||||
|
||||
注意事项:
|
||||
1. 按 Ctrl+C 停止监控
|
||||
2. 剪贴板监控支持文本、图片和文件格式
|
||||
3. 文件系统监控支持文件和文件夹的创建、修改、删除和重命名事件
|
||||
4. 使用 -once 参数可以在监听到第一次变化后自动停止");
|
||||
}
|
||||
|
||||
private static string GetArgumentValue(string[] args, string key) {
|
||||
for (int i = 0; i < args.Length - 1; i++) {
|
||||
if (args[i].Equals(key, StringComparison.OrdinalIgnoreCase)) {
|
||||
return args[i + 1];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static string EscapeJsonString(string str) {
|
||||
if (str == null) return "null";
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (char c in str) {
|
||||
switch (c) {
|
||||
case '\"': sb.Append("\\\""); break;
|
||||
case '\\': sb.Append("\\\\"); break;
|
||||
case '\b': sb.Append("\\b"); break;
|
||||
case '\f': sb.Append("\\f"); break;
|
||||
case '\n': sb.Append("\\n"); break;
|
||||
case '\r': sb.Append("\\r"); break;
|
||||
case '\t': sb.Append("\\t"); break;
|
||||
default:
|
||||
if (c < ' ') {
|
||||
sb.Append(string.Format("\\u{0:X4}", (int)c));
|
||||
}
|
||||
else {
|
||||
sb.Append(c);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return string.Format("\"{0}\"", sb.ToString());
|
||||
}
|
||||
|
||||
private static void OutputEvent(string type, string format, string content) {
|
||||
string json = string.Format("{{\"type\": {0}, \"format\": {1}, \"content\": {2}}}",
|
||||
EscapeJsonString(type),
|
||||
EscapeJsonString(format),
|
||||
EscapeJsonString(content));
|
||||
Console.WriteLine(json);
|
||||
}
|
||||
|
||||
private static void OutputFileSystemEvent(string type, string eventType, string path, string oldPath = null) {
|
||||
StringBuilder json = new StringBuilder();
|
||||
json.Append("{\"type\": ").Append(EscapeJsonString(type));
|
||||
json.Append(", \"event\": ").Append(EscapeJsonString(eventType));
|
||||
json.Append(", \"path\": ").Append(EscapeJsonString(path));
|
||||
if (oldPath != null) {
|
||||
json.Append(", \"oldPath\": ").Append(EscapeJsonString(oldPath));
|
||||
}
|
||||
json.Append("}");
|
||||
Console.WriteLine(json.ToString());
|
||||
}
|
||||
|
||||
private static void StartClipboardMonitor() {
|
||||
Thread thread = new Thread(() => {
|
||||
clipboardForm = new ClipboardForm();
|
||||
Application.Run(clipboardForm);
|
||||
});
|
||||
|
||||
thread.SetApartmentState(ApartmentState.STA);
|
||||
thread.Start();
|
||||
}
|
||||
|
||||
[DllImport("user32.dll", CharSet = CharSet.Auto)]
|
||||
private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
private static void HandleClipboardChanged() {
|
||||
if (Clipboard.ContainsText()) {
|
||||
OutputEvent("clipboard", "text", Clipboard.GetText());
|
||||
}
|
||||
else if (Clipboard.ContainsImage()) {
|
||||
OutputEvent("clipboard", "image", "image_data");
|
||||
}
|
||||
else if (Clipboard.ContainsFileDropList()) {
|
||||
OutputEvent("clipboard", "files", string.Join(", ", Clipboard.GetFileDropList().Cast<string>()));
|
||||
}
|
||||
}
|
||||
|
||||
private static void StartFileSystemMonitor(string path, string fileFilter, bool includeSubdirectories) {
|
||||
fsWatcher = new FileSystemWatcher(path);
|
||||
fsWatcher.Filter = fileFilter ?? "*.*";
|
||||
fsWatcher.IncludeSubdirectories = includeSubdirectories;
|
||||
|
||||
FileSystemEventHandler handler = (s, e) => {
|
||||
OutputFileSystemEvent("filesystem", e.ChangeType.ToString().ToLower(), e.FullPath);
|
||||
if (listenOnce) {
|
||||
running = false;
|
||||
fsWatcher.Dispose();
|
||||
}
|
||||
};
|
||||
|
||||
RenamedEventHandler renameHandler = (s, e) => {
|
||||
OutputFileSystemEvent("filesystem", "renamed", e.FullPath, e.OldFullPath);
|
||||
if (listenOnce) {
|
||||
running = false;
|
||||
fsWatcher.Dispose();
|
||||
}
|
||||
};
|
||||
|
||||
fsWatcher.Created += handler;
|
||||
fsWatcher.Changed += handler;
|
||||
fsWatcher.Deleted += handler;
|
||||
fsWatcher.Renamed += renameHandler;
|
||||
|
||||
fsWatcher.EnableRaisingEvents = true;
|
||||
}
|
||||
|
||||
private static bool HasArgument(string[] args, string key) {
|
||||
return Array.Exists(args, arg => arg.Equals(key, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
public static void Main(string[] args) {
|
||||
if (args.Length == 0 || args[0] == "-h" || args[0] == "--help") {
|
||||
ShowHelp();
|
||||
return;
|
||||
}
|
||||
|
||||
string type = GetArgumentValue(args, "-type");
|
||||
if (string.IsNullOrEmpty(type)) {
|
||||
Console.Error.WriteLine("Error: 必须指定监控类型 (-type)");
|
||||
return;
|
||||
}
|
||||
|
||||
listenOnce = HasArgument(args, "-once");
|
||||
|
||||
Console.CancelKeyPress += (s, e) => {
|
||||
running = false;
|
||||
if (fsWatcher != null) {
|
||||
fsWatcher.Dispose();
|
||||
}
|
||||
if (clipboardForm != null) {
|
||||
clipboardForm.Invoke(new Action(() => clipboardForm.Close()));
|
||||
}
|
||||
Environment.Exit(0);
|
||||
};
|
||||
|
||||
try {
|
||||
switch (type.ToLower()) {
|
||||
case "clipboard":
|
||||
StartClipboardMonitor();
|
||||
break;
|
||||
|
||||
case "filesystem":
|
||||
string path = GetArgumentValue(args, "-path");
|
||||
if (string.IsNullOrEmpty(path)) {
|
||||
Console.Error.WriteLine("Error: 必须指定监控路径 (-path)");
|
||||
return;
|
||||
}
|
||||
|
||||
string filter = GetArgumentValue(args, "-filter");
|
||||
bool recursive = true;
|
||||
string recursiveArg = GetArgumentValue(args, "-recursive");
|
||||
if (!string.IsNullOrEmpty(recursiveArg)) {
|
||||
recursive = bool.Parse(recursiveArg);
|
||||
}
|
||||
|
||||
StartFileSystemMonitor(path, filter, recursive);
|
||||
break;
|
||||
|
||||
default:
|
||||
Console.Error.WriteLine(string.Format("Error: 不支持的监控类型: {0}", type));
|
||||
return;
|
||||
}
|
||||
|
||||
while (running) {
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
Console.Error.WriteLine(string.Format("Error: {0}", ex.Message));
|
||||
}
|
||||
}
|
||||
}
|
49
plugin/lib/js/monitor.js
Normal file
49
plugin/lib/js/monitor.js
Normal file
@ -0,0 +1,49 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
const quickcommand = require("../../quickcommand");
|
||||
|
||||
// 读取 monitor.cs 模板
|
||||
const monitorTemplate = fs.readFileSync(
|
||||
path.join(__dirname, "..", "csharp", "monitor.cs"),
|
||||
"utf8"
|
||||
);
|
||||
|
||||
// 监控剪贴板变化
|
||||
const watchClipboard = async function () {
|
||||
const args = ["-type", "clipboard"];
|
||||
const result = await quickcommand.runCsharp(monitorTemplate, args);
|
||||
if (result && result.startsWith("Error:")) {
|
||||
throw new Error(result.substring(7));
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
// 监控文件系统变化
|
||||
const watchFileSystem = async function (options = {}) {
|
||||
const { path: watchPath, filter = "*.*", recursive = true } = options;
|
||||
|
||||
if (!watchPath) {
|
||||
throw new Error("必须指定监控路径");
|
||||
}
|
||||
|
||||
const args = ["-type", "filesystem", "-path", watchPath];
|
||||
|
||||
if (filter !== "*.*") {
|
||||
args.push("-filter", filter);
|
||||
}
|
||||
|
||||
if (!recursive) {
|
||||
args.push("-recursive", "false");
|
||||
}
|
||||
|
||||
const result = await quickcommand.runCsharp(monitorTemplate, args);
|
||||
if (result && result.startsWith("Error:")) {
|
||||
throw new Error(result.substring(7));
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
watchClipboard,
|
||||
watchFileSystem,
|
||||
};
|
@ -1,9 +1,11 @@
|
||||
const window = require("./window");
|
||||
const message = require("./message");
|
||||
const automation = require("./automation");
|
||||
const monitor = require("./monitor");
|
||||
|
||||
module.exports = {
|
||||
window,
|
||||
message,
|
||||
automation,
|
||||
monitor,
|
||||
};
|
||||
|
48
plugin/lib/quickcomposer/windows/monitor.js
Normal file
48
plugin/lib/quickcomposer/windows/monitor.js
Normal file
@ -0,0 +1,48 @@
|
||||
const fs = require("fs");
|
||||
const path = require("path");
|
||||
|
||||
// 读取 monitor.cs 模板
|
||||
const monitorTemplate = fs.readFileSync(
|
||||
path.join(__dirname, "..", "..", "csharp", "monitor.cs"),
|
||||
"utf8"
|
||||
);
|
||||
|
||||
// 监控剪贴板变化
|
||||
const watchClipboard = async function () {
|
||||
const args = ["-type", "clipboard", "-once"];
|
||||
const result = await quickcommand.runCsharp(monitorTemplate, args);
|
||||
if (result && result.startsWith("Error:")) {
|
||||
throw new Error(result.substring(7));
|
||||
}
|
||||
return JSON.parse(result);
|
||||
};
|
||||
|
||||
// 监控文件系统变化
|
||||
const watchFileSystem = async function (watchPath, options = {}) {
|
||||
const { filter = "*.*", recursive = true } = options;
|
||||
|
||||
if (!watchPath) {
|
||||
throw new Error("必须指定监控路径");
|
||||
}
|
||||
|
||||
const args = ["-type", "filesystem", "-path", watchPath, "-once"];
|
||||
|
||||
if (filter !== "*.*") {
|
||||
args.push("-filter", filter);
|
||||
}
|
||||
|
||||
if (!recursive) {
|
||||
args.push("-recursive", "false");
|
||||
}
|
||||
|
||||
const result = await quickcommand.runCsharp(monitorTemplate, args);
|
||||
if (result && result.startsWith("Error:")) {
|
||||
throw new Error(result.substring(7));
|
||||
}
|
||||
return JSON.parse(result);
|
||||
};
|
||||
|
||||
module.exports = {
|
||||
watchClipboard,
|
||||
watchFileSystem,
|
||||
};
|
@ -455,5 +455,70 @@ export const windowsCommands = {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.windows.monitor.watchClipboard",
|
||||
label: "剪贴板/文件监控",
|
||||
desc: "监控系统变化",
|
||||
icon: "monitor_heart",
|
||||
isAsync: true,
|
||||
outputVariable: "monitorEvent",
|
||||
saveOutput: true,
|
||||
showLoading: true,
|
||||
subCommands: [
|
||||
{
|
||||
value: "quickcomposer.windows.monitor.watchClipboard",
|
||||
label: "等待剪贴板变化",
|
||||
icon: "content_paste",
|
||||
},
|
||||
{
|
||||
value: "quickcomposer.windows.monitor.watchFileSystem",
|
||||
label: "等待文件夹变化",
|
||||
icon: "folder",
|
||||
config: [
|
||||
{
|
||||
label: "监控路径",
|
||||
component: "VariableInput",
|
||||
icon: "folder",
|
||||
width: 12,
|
||||
options: {
|
||||
dialog: {
|
||||
type: "open",
|
||||
options: {
|
||||
title: "选择文件夹",
|
||||
properties: ["openDirectory"],
|
||||
},
|
||||
},
|
||||
},
|
||||
placeholder: "要监控的文件夹路径",
|
||||
required: true,
|
||||
},
|
||||
{
|
||||
key: "options",
|
||||
component: "OptionEditor",
|
||||
width: 12,
|
||||
options: {
|
||||
filter: {
|
||||
label: "文件过滤",
|
||||
component: "VariableInput",
|
||||
icon: "filter_alt",
|
||||
width: 6,
|
||||
placeholder: "如: *.txt, *.docx",
|
||||
},
|
||||
recursive: {
|
||||
label: "包含子文件夹",
|
||||
component: "CheckButton",
|
||||
icon: "subdirectory_arrow_right",
|
||||
width: 6,
|
||||
defaultValue: true,
|
||||
},
|
||||
},
|
||||
defaultValue: {
|
||||
recursive: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user