目前,我正在开发一个简单的类库,以便在各种global hot keys和blog 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
的目的。在这种情况下,为什么LParam
和IntPtr
?谢谢。
答案 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));
}
}
}
}
}