SetWindowsHookEx不会对媒体键做出反应

Tom*_*mmi 2 c# winapi keyboard-hook

我想捕获用户键盘输入以对键盘媒体键做出良好反应:播放/暂停,特别是下一个和上一个.我尝试使用低级WH_KEYBOARD_LL参数的SetWindowsHookEx来确保我可以从这个WINAPI函数中获得最大的责任,而我什么都没有.如果我在钩子回调中设置断点,当我按下常规键(例如字母或F修饰符)时,调试器会在任何键盘事件上停止,但是当我按下其中一个媒体键(例如Play/Payse)时,它永远不会停止.我试着下载演示应用程序,它演示了SetWindowsHookEx的用法,但没有一个适用于MM键.我也读过这个相关的问题,但没有答案.

我用这段代码来设置钩子:

    private const int WH_KEYBOARD_LL = 13;
    private const int WM_KEYUP = 0x0101;
    private static LowLevelKeyboardProc _proc = HookCallback;
    private static IntPtr _hookID = IntPtr.Zero;

    public static IntPtr SetHook()
    {
        using (var curProcess = Process.GetCurrentProcess())
        {
            using (var curModule = curProcess.MainModule)
            {
                return SetWindowsHookEx(WH_KEYBOARD_LL, _proc, GetModuleHandle(curModule.ModuleName), 0);
            }
        }
    }

    public static void UnsetHook()
    {
        UnhookWindowsHookEx(_hookID);
    }

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

    private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0 && wParam == (IntPtr)WM_KEYUP)
        {
            int vkCode = Marshal.ReadInt32(lParam);
            Debug.WriteLine((Keys)vkCode);
        }
        return CallNextHookEx(_hookID, nCode, wParam, 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)]
    [return: MarshalAs(UnmanagedType.Bool)]
    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);
Run Code Online (Sandbox Code Playgroud)

我找到了RegisterHotKey应该对我的任务有帮助的信息,但由于我也希望对常规键做出反应,因此对于所有常规和MM键使用一个API成员会更加舒适.这是死的方法还是我错过了什么?

Han*_*ant 6

那是因为这些键不会生成键盘消息.它们生成WM_APPCOMMAND消息.因此,按"播放"按钮会生成APPCOMMAND_MEDIA_PLAY.

该消息被发送到拥有焦点的任何窗口.这样的窗口几乎从不对消息起作用并将其传递给默认的窗口过程.拿起它并将它传递给shell.在技​​术上可能挂钩,你需要一个WH_SHELL钩子. 回调获取HSHELL_APPCOMMAND通知.

但这通常是好消息结束的地方,你不能用托管语言编写全局钩子.因为它们需要一个可以在创建窗口的每个进程中注入的DLL.这样的DLL不能是托管DLL,该进程不会加载CLR. 这是一个实现这样一个DLL 的项目,并展示了如何挂钩shell通知的例子,不知道它的工作原理.这些项目往往不适用于需要32位和64位挂钩的现代Windows版本,并且需要为UAC升级过程做一些合理的事情.