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

时间:2014-01-15 05:26:47

标签: c# winapi bit-manipulation intptr

目前,我正在开发一个简单的类库,以便在各种global hot keysblog posts的帮助下处理SO answers

考虑我放在一起的这个完整的代码段。

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);
}

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

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

1 个答案:

答案 0 :(得分:3)

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

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

LPARAM & 0xffff有效,因为32位LPARAM看起来像:

   V bit 31                              V bit 0
   HIGH WORD           LOW WORD
0y vvvv vvvv vvvv vvvv mmmm mmmm mmmm mmmm where v is the vk, and m is the modifier 
0y 0000 0000 0000 0000 1111 1111 1111 1111 == 0x0000ffff
========================================== Bitwise and
0y 0000 0000 0000 0000 mmmm mmmm mmmm mmmm Just left with the modifier bits

LPARAM >> 16通过将位16位置向右移动

来工作
   V bit 31                              V bit 0
   HIGH WORD           LOW WORD
0y vvvv vvvv vvvv vvvv mmmm mmmm mmmm mmmm where v is the vk, and m is the modifier 
0y 0vvv vvvv vvvv vvvv vmmm mmmm mmmm mmmm right shifted once (>> 1)
0y 00vv vvvv vvvv vvvv vvmm mmmm mmmm mmmm right shifted twice (>> 2)
...
0y 0000 0000 0000 0000 vvvv vvvv vvvv vvvv right shifted sixteen times (>> 16)

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

如果我们不将IntPtr的大小调整为Int32,那么如果应用程序要发送一个由lparam >> 16设置的高位设置的消息,则可能会出现异常。< / p>

IntPtr lparam = /* comes from WndProc */;
Int32 lparam32 = unchecked((int)(long)lparam);

Int16 lowWord = lparam32 & 0xffff;
Int16 highWord = lparam32 >> 16;

话虽如此,这里是C#中热键监听器的完整实现,我不记得我找到了什么。如果有人对此有所归属,请告诉我,或编辑此帖以列出原作者。

using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Windows.Forms;

public sealed class KeyboardHook : IDisposable
{
    private int _currentId;
    private Window _window;

    public event EventHandler<KeyPressedEventArgs> KeyPressed;

    public KeyboardHook()
    {
        EventHandler<KeyPressedEventArgs> handler = null;
        this._window = new Window();
        if (handler == null)
        {
            handler = delegate (object sender, KeyPressedEventArgs args) {
                if (this.KeyPressed != null)
                {
                    this.KeyPressed(this, args);
                }
            };
        }
        this._window.KeyPressed += handler;
    }

    public void Dispose()
    {
        for (int i = this._currentId; i > 0; i--)
        {
            UnregisterHotKey(this._window.Handle, i);
        }
        this._window.Dispose();
    }

    public void RegisterHotKey(ModifierKeys modifier, Keys key)
    {
        this._currentId++;
        if (!RegisterHotKey(this._window.Handle, this._currentId, (uint) modifier, (uint) key))
        {
            throw new InvalidOperationException("Couldn’t register the hot key.");
        }
    }

    [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 class Window : NativeWindow, IDisposable
    {
        private static int WM_HOTKEY = 0x312;

        public event EventHandler<KeyPressedEventArgs> KeyPressed;

        public Window()
        {
            this.CreateHandle(new CreateParams());
        }

        public void Dispose()
        {
            this.DestroyHandle();
        }

        protected override void WndProc(ref Message m)
        {
            base.WndProc(ref m);
            if (m.Msg == WM_HOTKEY)
            {
                Keys key = ((Keys) (((int) m.LParam) >> 0x10)) & Keys.KeyCode;
                ModifierKeys modifier = ((ModifierKeys) ((int) m.LParam)) & ((ModifierKeys) 0xffff);
                if (this.KeyPressed != null)
                {
                    this.KeyPressed(this, new KeyPressedEventArgs(modifier, key));
                }
            }
        }
    }
}