You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# MAUI桌面程序后台捕获按键的实现方案咨询

在MAUI Windows平台实现全局键盘钩子

问题原因

MAUI Windows基于WinUI 3,其消息循环机制和WPF存在差异。原代码中SetWindowsHookEx传入IntPtr.Zero作为模块句柄,在MAUI环境下无法正确完成钩子注册;另外需要确保钩子回调的委托不会被垃圾回收(你的代码已通过类成员_proc保持强引用,这部分无需调整)。

修改现有GlobalKeyboardHook类

以下是适配MAUI Windows的修改版本:

using System;
using System.Runtime.InteropServices;
using System.Diagnostics;

class GlobalKeyboardHook
{
    private const int WH_KEYBOARD_LL = 13;
    private const int WM_KEYUP = 0x0101;

    private delegate int LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern bool UnhookWindowsHookEx(IntPtr hhk);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr GetModuleHandle(string lpModuleName);

    private IntPtr _hookID = IntPtr.Zero;
    private LowLevelKeyboardProc _proc;

    // 用事件替代硬编码逻辑,提升灵活性
    public event Action<int> KeyReleased;

    public GlobalKeyboardHook()
    {
        _proc = HookCallback;
    }

    public void Hook()
    {
        // 获取当前进程主模块句柄,替代原代码的IntPtr.Zero
        using var currentProcess = Process.GetCurrentProcess();
        using var currentModule = currentProcess.MainModule;
        var moduleHandle = GetModuleHandle(currentModule.ModuleName);

        _hookID = SetWindowsHookEx(WH_KEYBOARD_LL, _proc, moduleHandle, 0);

        // 检查钩子是否注册成功,失败则抛出异常
        if (_hookID == IntPtr.Zero)
        {
            throw new System.ComponentModel.Win32Exception(Marshal.GetLastWin32Error());
        }
    }

    public void Unhook()
    {
        if (_hookID != IntPtr.Zero)
        {
            UnhookWindowsHookEx(_hookID);
            _hookID = IntPtr.Zero;
        }
    }

    private int HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0 && wParam == (IntPtr)WM_KEYUP)
        {
            int vkCode = Marshal.ReadInt32(lParam);
            KeyReleased?.Invoke(vkCode);
        }

        return (int)CallNextHookEx(_hookID, nCode, wParam, lParam);
    }
}

使用方式

在MAUI的Windows平台特定代码中注册钩子,比如在App.xaml.cs中:

#if WINDOWS
private GlobalKeyboardHook _keyboardHook;

protected override void OnLaunched(LaunchActivatedEventArgs args)
{
    base.OnLaunched(args);

    _keyboardHook = new GlobalKeyboardHook();
    _keyboardHook.KeyReleased += vkCode =>
    {
        if (vkCode == 9) // 检测Tab键释放
        {
            Global.Initiate_server?.SendMessage(Global.TrepCserver.Takescreenshot);
        }
    };
    
    try
    {
        _keyboardHook.Hook();
    }
    catch (Exception ex)
    {
        Debug.WriteLine($"钩子注册失败: {ex.Message}");
    }
}

// 应用退出时务必取消钩子,避免内存泄漏
protected override void OnClosed(WindowClosedEventArgs args)
{
    _keyboardHook?.Unhook();
    base.OnClosed(args);
}
#endif

第三方库选项

如果不想手动实现钩子逻辑,可以使用NuGet上的GlobalHotkey库。它支持MAUI Windows平台,专门用于全局热键检测,只需安装包后注册热键并订阅事件即可。注意该库仅支持检测特定热键组合(如Ctrl+Tab),若需要捕获所有按键操作,自定义钩子仍是更合适的选择。


内容的提问来源于stack exchange,提问作者Toballz Ibot

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 02:22:05