- 数据模型: ToolItem 新增 IsGroup(bool) + SubToolIds(List<string>) 字段
- 执行逻辑: ProcessExecutionService 改为 ExecuteAsync, 组合卡片遍历子工具逐一启动(500ms延迟), 孤儿ID跳过并打印警告
- 组合编辑: GroupEditViewModel + GroupEditWindow, 复选框列表勾选非组合工具
- 主界面: 标题栏新增 '+添加组合' 按钮(蓝色), 组合卡片右下角显示 📦 角标
- 右键菜单: 区分 '编辑工具' (普通) 和 '编辑组合' (IsGroup=true)
- 快捷键: HotKeyManager 适配 ExecuteAsync 异步调用
- 测试: 82 tests total (ProcessExecution 4->6, GroupEdit 5 new)
175 lines
5.2 KiB
C#
175 lines
5.2 KiB
C#
using System.Runtime.InteropServices;
|
|
using System.Windows.Input;
|
|
using PersonalToolBox.Models;
|
|
using PersonalToolBox.Services;
|
|
|
|
namespace PersonalToolBox.Helpers;
|
|
|
|
/// <summary>
|
|
/// 全局快捷键管理器,使用 Win32 API RegisterHotKey / UnregisterHotKey
|
|
/// </summary>
|
|
public class HotKeyManager
|
|
{
|
|
private IntPtr _hwnd;
|
|
private int _nextId = 1;
|
|
private readonly Dictionary<int, ToolItem> _hotkeyMap = new();
|
|
private readonly ILogService _logService;
|
|
private readonly IProcessExecutionService _processService;
|
|
|
|
// ──────────────── Win32 API ────────────────
|
|
|
|
private const int WM_HOTKEY = 0x0312;
|
|
private const uint MOD_ALT = 0x0001;
|
|
private const uint MOD_CONTROL = 0x0002;
|
|
private const uint MOD_SHIFT = 0x0004;
|
|
private const uint MOD_WIN = 0x0008;
|
|
|
|
[DllImport("user32.dll", SetLastError = true)]
|
|
private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
|
|
|
|
[DllImport("user32.dll", SetLastError = true)]
|
|
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
|
|
|
|
// ──────────────── 构造函数 ────────────────
|
|
|
|
public HotKeyManager(ILogService logService, IProcessExecutionService processService)
|
|
{
|
|
_logService = logService;
|
|
_processService = processService;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 设置窗口句柄(在 MainWindow.OnSourceInitialized 中调用)
|
|
/// </summary>
|
|
public void Initialize(IntPtr hwnd)
|
|
{
|
|
_hwnd = hwnd;
|
|
}
|
|
|
|
// ──────────────── 注册/注销 ────────────────
|
|
|
|
/// <summary>
|
|
/// 批量注册所有工具的快捷键
|
|
/// </summary>
|
|
public void RegisterAll(IEnumerable<ToolItem> tools)
|
|
{
|
|
UnregisterAll();
|
|
foreach (var tool in tools.Where(t => !string.IsNullOrWhiteSpace(t.HotKey)))
|
|
Register(tool);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 为单个工具注册快捷键
|
|
/// </summary>
|
|
public void Register(ToolItem tool)
|
|
{
|
|
if (_hwnd == IntPtr.Zero) return;
|
|
if (string.IsNullOrWhiteSpace(tool.HotKey)) return;
|
|
|
|
if (!TryParseHotKey(tool.HotKey, out uint modifiers, out uint vk))
|
|
{
|
|
_logService.Warning($"快捷键格式无效: {tool.HotKey}");
|
|
return;
|
|
}
|
|
|
|
int id = _nextId++;
|
|
if (RegisterHotKey(_hwnd, id, modifiers, vk))
|
|
{
|
|
_hotkeyMap[id] = tool;
|
|
_logService.Info($"已注册快捷键: {tool.HotKey} → {tool.Name}");
|
|
}
|
|
else
|
|
{
|
|
int err = Marshal.GetLastWin32Error();
|
|
_logService.Error($"快捷键注册失败: {tool.HotKey} (错误码: {err},可能与其他程序冲突)");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 注销所有已注册的快捷键
|
|
/// </summary>
|
|
private void UnregisterAll()
|
|
{
|
|
foreach (var kvp in _hotkeyMap)
|
|
UnregisterHotKey(_hwnd, kvp.Key);
|
|
_hotkeyMap.Clear();
|
|
}
|
|
|
|
// ──────────────── 消息处理 ────────────────
|
|
|
|
/// <summary>
|
|
/// 由 WndProc 钩子调用,处理 WM_HOTKEY 消息
|
|
/// </summary>
|
|
public bool TryHandleMessage(int msg, IntPtr wParam, IntPtr lParam)
|
|
{
|
|
if (msg != WM_HOTKEY) return false;
|
|
|
|
int id = wParam.ToInt32();
|
|
if (_hotkeyMap.TryGetValue(id, out var tool))
|
|
{
|
|
_logService.Info($"通过快捷键启动: {tool.Name}");
|
|
_ = _processService.ExecuteAsync(tool);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// ──────────────── 快捷键解析 ────────────────
|
|
|
|
/// <summary>
|
|
/// 解析快捷键字符串(如 "Ctrl+Alt+T")为修饰键和虚拟键码
|
|
/// </summary>
|
|
public static bool TryParseHotKey(string hotkey, out uint modifiers, out uint vk)
|
|
{
|
|
modifiers = 0;
|
|
vk = 0;
|
|
|
|
if (string.IsNullOrWhiteSpace(hotkey)) return false;
|
|
|
|
var parts = hotkey.Split('+', StringSplitOptions.TrimEntries);
|
|
if (parts.Length < 2) return false;
|
|
|
|
// 解析修饰键(最后一段之前的部分)
|
|
foreach (var part in parts.Take(parts.Length - 1))
|
|
{
|
|
uint mod = part.ToUpperInvariant() switch
|
|
{
|
|
"CTRL" or "CONTROL" => MOD_CONTROL,
|
|
"ALT" => MOD_ALT,
|
|
"SHIFT" => MOD_SHIFT,
|
|
"WIN" or "WINDOWS" => MOD_WIN,
|
|
_ => 0
|
|
};
|
|
|
|
if (mod == 0) return false;
|
|
modifiers |= mod;
|
|
}
|
|
|
|
if (modifiers == 0) return false;
|
|
|
|
// 解析按键(最后一段)
|
|
var keyStr = parts.Last();
|
|
if (keyStr.Length == 1)
|
|
{
|
|
vk = char.ToUpperInvariant(keyStr[0]);
|
|
}
|
|
else if (Enum.TryParse<Key>(keyStr, true, out var key))
|
|
{
|
|
vk = (uint)KeyInterop.VirtualKeyFromKey(key);
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 程序退出时清理所有快捷键
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
UnregisterAll();
|
|
}
|
|
}
|