该位掩码如何应用于 LParam (WM_HOTKEY)

Cas*_*roy 1 c# winapi bit-manipulation intptr

目前,我正在开发一个简单的类库,以在各种博客文章SO 答案的帮助下处理全局热键

考虑一下我整理的这段完整的代码。

protected override void WndProc(ref Message m)
{
    const int WM_HOTKEY = 0x0312;

    if (m.Msg == WM_HOTKEY)
    {
        var modifier = (int) m.LParam & 0xFFFF;
        var key = ((int) m.LParam & 0xFFFF);
    }

    base.WndProc(ref m);
}
Run Code Online (Sandbox Code Playgroud)

我真的不明白,想解释一下位掩码在这里是如何工作的。我对如何应用按位运算符有合理的理解,但我不明白为什么在这里应用这个特定的位掩码。

此外,我正在努力理解 的目的IntPtr。为什么是LParam并且IntPtr在这种情况下?谢谢。

Mit*_*tch 5

的文档WM_HOTKEY说包含LPARAM两个 16 位字段。高字(高 16 位)具有按下的按键的 VK,低字(低 16 位)具有指定的修饰符。

\n\n

LPARAM & 0xffff将获取低 16 位,LPARAM >> 16并将获取高 16 位。

\n\n

LPARAM & 0xffff之所以有效,是因为 32 位 LPARAM 如下所示:

\n\n
   V bit 31                              V bit 0\n   HIGH WORD           LOW WORD\n0y vvvv vvvv vvvv vvvv mmmm mmmm mmmm mmmm where v is the vk, and m is the modifier \n0y 0000 0000 0000 0000 1111 1111 1111 1111 == 0x0000ffff\n========================================== Bitwise and\n0y 0000 0000 0000 0000 mmmm mmmm mmmm mmmm Just left with the modifier bits\n
Run Code Online (Sandbox Code Playgroud)\n\n

LPARAM >> 16工作原理是将位向右移动 16 个位置

\n\n
   V bit 31                              V bit 0\n   HIGH WORD           LOW WORD\n0y vvvv vvvv vvvv vvvv mmmm mmmm mmmm mmmm where v is the vk, and m is the modifier \n0y 0vvv vvvv vvvv vvvv vmmm mmmm mmmm mmmm right shifted once (>> 1)\n0y 00vv vvvv vvvv vvvv vvmm mmmm mmmm mmmm right shifted twice (>> 2)\n...\n0y 0000 0000 0000 0000 vvvv vvvv vvvv vvvv right shifted sixteen times (>> 16)\n
Run Code Online (Sandbox Code Playgroud)\n\n

LPARAM被定义为指针的大小,因此它将映射到IntPtr托管代码中。由于我们只使用 LPARAM 的低 32 位,因此我们需要砍掉高 32 位。我们可以强制转换 toint来执行此操作,但如果您运行checked算术,则会产生OverflowException. 相反,我们将 转换IntPtr为 a long(在 32 位机器上将进行零填充),然后转换为 a int(将截断)。

\n\n

如果我们不将 的大小调整IntPtr为 a Int32,如果应用程序要发送设置为 的结果的高位消息,则可能会出现异常lparam >> 16

\n\n
IntPtr lparam = /* comes from WndProc */;\nInt32 lparam32 = unchecked((int)(long)lparam);\n\nInt16 lowWord = lparam32 & 0xffff;\nInt16 highWord = lparam32 >> 16;\n
Run Code Online (Sandbox Code Playgroud)\n\n

话虽如此,这里是 C# 中热键侦听器的完整实现,我不记得在哪里找到的。如果有人对此有出处,请告诉我,或编辑这篇文章以列出原作者。

\n\n
using System;\nusing System.Runtime.CompilerServices;\nusing System.Runtime.InteropServices;\nusing System.Windows.Forms;\n\npublic sealed class KeyboardHook : IDisposable\n{\n    private int _currentId;\n    private Window _window;\n\n    public event EventHandler<KeyPressedEventArgs> KeyPressed;\n\n    public KeyboardHook()\n    {\n        EventHandler<KeyPressedEventArgs> handler = null;\n        this._window = new Window();\n        if (handler == null)\n        {\n            handler = delegate (object sender, KeyPressedEventArgs args) {\n                if (this.KeyPressed != null)\n                {\n                    this.KeyPressed(this, args);\n                }\n            };\n        }\n        this._window.KeyPressed += handler;\n    }\n\n    public void Dispose()\n    {\n        for (int i = this._currentId; i > 0; i--)\n        {\n            UnregisterHotKey(this._window.Handle, i);\n        }\n        this._window.Dispose();\n    }\n\n    public void RegisterHotKey(ModifierKeys modifier, Keys key)\n    {\n        this._currentId++;\n        if (!RegisterHotKey(this._window.Handle, this._currentId, (uint) modifier, (uint) key))\n        {\n            throw new InvalidOperationException("Couldn\xe2\x80\x99t register the hot key.");\n        }\n    }\n\n    [DllImport("user32.dll")]\n    private static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk);\n    [DllImport("user32.dll")]\n    private static extern bool UnregisterHotKey(IntPtr hWnd, int id);\n\n    private class Window : NativeWindow, IDisposable\n    {\n        private static int WM_HOTKEY = 0x312;\n\n        public event EventHandler<KeyPressedEventArgs> KeyPressed;\n\n        public Window()\n        {\n            this.CreateHandle(new CreateParams());\n        }\n\n        public void Dispose()\n        {\n            this.DestroyHandle();\n        }\n\n        protected override void WndProc(ref Message m)\n        {\n            base.WndProc(ref m);\n            if (m.Msg == WM_HOTKEY)\n            {\n                Keys key = ((Keys) (((int) m.LParam) >> 0x10)) & Keys.KeyCode;\n                ModifierKeys modifier = ((ModifierKeys) ((int) m.LParam)) & ((ModifierKeys) 0xffff);\n                if (this.KeyPressed != null)\n                {\n                    this.KeyPressed(this, new KeyPressedEventArgs(modifier, key));\n                }\n            }\n        }\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n