280 lines
10 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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));
}
}
}