758f6772c6
2.切换项目名称为Hua.Todo
190 lines
6.3 KiB
C#
190 lines
6.3 KiB
C#
#if WINDOWS
|
|
using System.Runtime.InteropServices;
|
|
using WinRT.Interop;
|
|
using MauiWindow = Microsoft.Maui.Controls.Window;
|
|
|
|
namespace Hua.Todo.Maui.Services.Platforms
|
|
{
|
|
/// <summary>
|
|
/// Windows 平台全局热键服务实现类
|
|
/// 使用 Windows API 实现全局热键功能
|
|
/// </summary>
|
|
public class WindowsGlobalHotKeyService : IGlobalHotKeyService
|
|
{
|
|
private const int HOTKEY_ID = 9000;
|
|
private const int WM_HOTKEY = 0x0312;
|
|
private const int GWL_WNDPROC = -4;
|
|
|
|
public const uint MOD_ALT = 0x0001;
|
|
public const uint MOD_CONTROL = 0x0002;
|
|
public const uint MOD_SHIFT = 0x0004;
|
|
public const uint MOD_WIN = 0x0008;
|
|
|
|
[DllImport("user32.dll")]
|
|
private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);
|
|
|
|
[DllImport("user32.dll")]
|
|
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
|
|
|
|
private IntPtr _windowHandle;
|
|
private MauiWindow? _window;
|
|
private Action? _callback;
|
|
private bool _isRegistered;
|
|
private uint _currentModifiers;
|
|
private uint _currentKey;
|
|
private IntPtr _originalWndProc;
|
|
private WndProcDelegate? _wndProc;
|
|
|
|
/// <summary>
|
|
/// Windows 平台支持全局热键
|
|
/// </summary>
|
|
public bool IsSupported => true;
|
|
|
|
/// <summary>
|
|
/// 注册全局热键
|
|
/// </summary>
|
|
public void RegisterHotKey(string modifiers, string key, Action callback)
|
|
{
|
|
if (_window == null)
|
|
{
|
|
_window = Microsoft.Maui.Controls.Application.Current?.Windows.FirstOrDefault();
|
|
if (_window == null) return;
|
|
}
|
|
|
|
if (_window.Handler?.PlatformView is not Microsoft.UI.Xaml.Window platformWindow) return;
|
|
_windowHandle = WindowNative.GetWindowHandle(platformWindow);
|
|
if (_windowHandle == IntPtr.Zero) return;
|
|
|
|
_callback = callback;
|
|
_currentModifiers = ParseModifiers(modifiers);
|
|
_currentKey = ParseKey(key);
|
|
|
|
if (_isRegistered)
|
|
{
|
|
UnregisterHotKey();
|
|
}
|
|
|
|
if (RegisterHotKey(_windowHandle, HOTKEY_ID, _currentModifiers, _currentKey))
|
|
{
|
|
_isRegistered = true;
|
|
EnsureWndProcHook();
|
|
}
|
|
else
|
|
{
|
|
System.Diagnostics.Debug.WriteLine("Failed to register hotkey");
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 注销全局热键
|
|
/// </summary>
|
|
public void UnregisterHotKey()
|
|
{
|
|
if (_isRegistered)
|
|
{
|
|
UnregisterHotKey(_windowHandle, HOTKEY_ID);
|
|
_isRegistered = false;
|
|
}
|
|
|
|
if (_originalWndProc != IntPtr.Zero && _windowHandle != IntPtr.Zero)
|
|
{
|
|
SetWindowProc(_windowHandle, GWL_WNDPROC, _originalWndProc);
|
|
_originalWndProc = IntPtr.Zero;
|
|
_wndProc = null;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 更新热键配置
|
|
/// </summary>
|
|
public void UpdateHotKey(string modifiers, string key)
|
|
{
|
|
if (_callback != null)
|
|
{
|
|
RegisterHotKey(modifiers, key, _callback);
|
|
}
|
|
}
|
|
|
|
private void EnsureWndProcHook()
|
|
{
|
|
if (_originalWndProc != IntPtr.Zero) return;
|
|
if (_windowHandle == IntPtr.Zero) return;
|
|
|
|
_wndProc = WndProc;
|
|
var newWndProcPtr = Marshal.GetFunctionPointerForDelegate(_wndProc);
|
|
_originalWndProc = SetWindowProc(_windowHandle, GWL_WNDPROC, newWndProcPtr);
|
|
}
|
|
|
|
private IntPtr WndProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam)
|
|
{
|
|
if (msg == WM_HOTKEY && wParam == (IntPtr)HOTKEY_ID)
|
|
{
|
|
_callback?.Invoke();
|
|
}
|
|
|
|
if (_originalWndProc != IntPtr.Zero)
|
|
{
|
|
return CallWindowProc(_originalWndProc, hWnd, msg, wParam, lParam);
|
|
}
|
|
|
|
return DefWindowProc(hWnd, msg, wParam, lParam);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 解析修饰键字符串
|
|
/// </summary>
|
|
private uint ParseModifiers(string modifiers)
|
|
{
|
|
uint mod = 0;
|
|
if (string.IsNullOrEmpty(modifiers)) return mod;
|
|
|
|
var parts = modifiers.Split(',');
|
|
foreach (var part in parts)
|
|
{
|
|
var p = part.Trim();
|
|
if (p.Equals("Control", StringComparison.OrdinalIgnoreCase)) mod |= MOD_CONTROL;
|
|
if (p.Equals("Alt", StringComparison.OrdinalIgnoreCase)) mod |= MOD_ALT;
|
|
if (p.Equals("Shift", StringComparison.OrdinalIgnoreCase)) mod |= MOD_SHIFT;
|
|
if (p.Equals("Windows", StringComparison.OrdinalIgnoreCase)) mod |= MOD_WIN;
|
|
}
|
|
return mod;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 解析主键
|
|
/// </summary>
|
|
private uint ParseKey(string key)
|
|
{
|
|
if (key.Length == 1)
|
|
{
|
|
char c = char.ToUpper(key[0]);
|
|
if (c >= 'A' && c <= 'Z') return (uint)c;
|
|
if (c >= '0' && c <= '9') return (uint)c;
|
|
}
|
|
return 0x58; // Default 'X'
|
|
}
|
|
|
|
private delegate IntPtr WndProcDelegate(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
|
|
|
|
[DllImport("user32.dll", EntryPoint = "SetWindowLongPtrW")]
|
|
private static extern IntPtr SetWindowLongPtr64(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
|
|
|
|
[DllImport("user32.dll", EntryPoint = "SetWindowLongW")]
|
|
private static extern IntPtr SetWindowLong32(IntPtr hWnd, int nIndex, IntPtr dwNewLong);
|
|
|
|
private static IntPtr SetWindowProc(IntPtr hWnd, int nIndex, IntPtr newProc)
|
|
{
|
|
return IntPtr.Size == 8
|
|
? SetWindowLongPtr64(hWnd, nIndex, newProc)
|
|
: SetWindowLong32(hWnd, nIndex, newProc);
|
|
}
|
|
|
|
[DllImport("user32.dll")]
|
|
private static extern IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
|
|
|
|
[DllImport("user32.dll")]
|
|
private static extern IntPtr DefWindowProc(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
|
|
}
|
|
}
|
|
#endif
|