当窗口未处于焦点状态时,我如何检测按键操作?

22
我试图在表单不是当前活动应用程序时检测Print Screen按钮的按下。如何实现这一点,如果可能的话?

看起来这可能会有所帮助:http://social.msdn.microsoft.com/Forums/en-US/94b1598f-79ce-4c70-8851-86aff6d9b12f/capturing-the-print-screen-key - Surfbutler
3个回答

30

如果您遇到了系统钩子的问题,这里有一个现成的解决方案(基于http://www.dreamincode.net/forums/topic/180436-global-hotkeys/):

在您的项目中定义静态类:

public static class Constants
{
    //windows message id for hotkey
    public const int WM_HOTKEY_MSG_ID = 0x0312;
}

在您的项目中定义类:

public class KeyHandler
{
    [DllImport("user32.dll")]
    private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vk);

    [DllImport("user32.dll")]
    private static extern bool UnregisterHotKey(IntPtr hWnd, int id);

    private int key;
    private IntPtr hWnd;
    private int id;

    public KeyHandler(Keys key, Form form)
    {
        this.key = (int)key;
        this.hWnd = form.Handle;
        id = this.GetHashCode();
    }

    public override int GetHashCode()
    {
        return key ^ hWnd.ToInt32();
    }

    public bool Register()
    {
        return RegisterHotKey(hWnd, id, 0, key);
    }

    public bool Unregiser()
    {
        return UnregisterHotKey(hWnd, id);
    }
}

添加 using:

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

现在,在您的表单中添加字段:

private KeyHandler ghk;

并且在 Form 构造函数中:

ghk = new KeyHandler(Keys.PrintScreen, this);
ghk.Register();

将这两种方法添加到你的表单中:

private void HandleHotkey()
{
        // Do stuff...
}

protected override void WndProc(ref Message m)
{
    if (m.Msg == Constants.WM_HOTKEY_MSG_ID)
        HandleHotkey();
    base.WndProc(ref m);
}

HandleHotkey是您的按钮按下处理程序。您可以通过在此处传递不同的参数来更改按钮:ghk = new KeyHandler(Keys.PrintScreen, this);

现在,即使没有焦点,您的程序也会对按钮输入作出反应。


1
如何编辑这段代码,如果我想要两个不同的热键分别具有两种不同的功能?我能在“WndProc”或“HandleHotkey”中以某种方式检查按下了哪个键吗? - user1696947
1
这个能否适应处理键盘组合键?比如CTRL-N。 - MrVimes
你可以将 message.wParam 与 KeyHandler 的 GetHashCode 进行比较,它们会匹配。 - Hugo Scott-Slade

16

13
API GetAsyncKeyState() 可以作为设置 Windows Hook 的完全可接受的替代方式。
这取决于您希望如何接收输入。如果您喜欢事件驱动通知,则需要使用 hook;但是,如果您喜欢轮询键盘以获取状态更改,则可以使用上述 API。
以下是如何使用 GetAsyncKeyState 的简单演示:
来源于 Pinvoke.NET
[DllImport("User32.dll")]
private static extern short GetAsyncKeyState(int vKey);

private static readonly int VK_SNAPSHOT = 0x2C; //This is the print-screen key.

//Assume the timer is setup with Interval = 16 (corresponds to ~60FPS).
private System.Windows.Forms.Timer timer1 = new System.Windows.Forms.Timer();

private void timer1_Tick(object sender, EventArgs e)
{
    short keyState = GetAsyncKeyState(VK_SNAPSHOT);

    //Check if the MSB is set. If so, then the key is pressed.
    bool prntScrnIsPressed = ((keyState >> 15) & 0x0001) == 0x0001;

    //Check if the LSB is set. If so, then the key was pressed since
    //the last call to GetAsyncKeyState
    bool unprocessedPress = ((keyState >> 0)  & 0x0001) == 0x0001;

    if (prntScrnIsPressed)
    {
        //TODO Execute client code...
    }

    if (unprocessedPress)
    {
        //TODO Execute client code...
    }
}

“if (prntScrnIs-->p<--ressed)” 应该改为 “if (prntScrnIs-->P<--ressed)” 吧? - Michael
3
我简直不敢相信我是第一个点赞的人。如果你想要更可靠的绑定热键功能,那么这个方法更好。在我的情况下,F12键被绑定到Skype,但是Skype却没有任何反应。监控原始输入是解决的办法。非常感谢你,Nicholas! - Gaspa79
1
是的,从我的角度来看,这是最好的解决方案,因为您可以使用它来检测关键组合,甚至区分左右、Shift/Alt等。 - arana

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接