Windows的低级键盘输入

时间:2008-11-21 23:52:07

标签: winapi keyboard low-level

从Windows服务中,可以使用哪些win32调用来全局检测按键事件(不只是针对1个窗口,我希望每按一次键都会收到一条消息)?

5 个答案:

答案 0 :(得分:4)

您想使用Win32 Hooks。特别是键盘钩。

You can read more about it here

您想要的钩子类型是WH_KEYBOARD,您可以通过Win32 API SetWindowsHookEx进行设置。

基本上,windows会调用每次在任何系统应用程序中按下某个键时创建的dll中的函数。

钩子会调用你的函数,它将具有这个界面:

LRESULT CALLBACK KeyboardProc(      
    int code,
    WPARAM wParam,
    LPARAM lParam
);

More information about this callback here

使用Windows挂钩,您不仅可以跨所有进程跟踪系统范围的事件,还可以过滤它们并完全停止它们。

答案 1 :(得分:2)

看看你可以用SetWindowHookEx设置的钩子:

http://msdn.microsoft.com/en-us/library/ms644990(VS.85).aspx

但是,除非您正在运行“交互式”服务,否则您将无法访问桌面(并且您不应该运行交互式服务,因为它在较新版本的Windows中无法正常运行)。您必须查看会话和桌面管理API才能访问桌面/控制台。

答案 2 :(得分:1)

只需使用user32 DLL中的本机函数GetKeyState或GetAsyncKeyState或GetKeyboardState。

您可以在msdn网站上阅读我上面提到的每个功能:

h ttp://msdn.microsoft.com/en-us/library/windows/desktop/ms646301(v = vs.85).aspx

用于 GetKeyState

h ttp://msdn.microsoft.com/en-us/library/windows/desktop/ms646293(v = vs.85).aspx

表示 GetAsyncKeyState

h ttp://msdn.microsoft.com/en-us/library/windows/desktop/ms646299(v = vs.85).aspx

表示 GetKeyboardState

如果你用C#编程,那么:

您的代码必须包含以下任何类,结构或枚举声明中的行:

using System.Runtime.InteropServices;

然后在具有您想要检测键的方法的类中,定义上述三个函数中的一个供您选择,或者哪些有效,哪些无效。

[DllImport("user32.dll")]
static uint GetKeyState(byte virtualKeyCode);

//or

[DllImport("user32.dll")]
static uint GetAsyncKeyState(byte virtualKeyCode);

//or

[DllImport("user32.dll")]
static uint GetKeyboardState(byte[] virtualKeyCodes);
//NOTE: The length of the byte array parameter must be always 256 bytes!

如果您不喜欢 uint 作为返回类型,您也可以将其更改为 int

如果原生函数总是将1返回为true或0返回false,那么您可以将其返回类型更改为 bool ,但如果您这样做,那么我认为您应该添加另一行本机函数定义之上的代码,即:

[MarshalAs(UnmanagedType.Bool)]

OR 您可以在原生函数定义的中间添加以下行(在DllImport单词所在的行下方,在行的上方,' extern'是的):

[return: MarshalAs(UnmanagedType.Bool)]

Pinvoke网站为这些功能提供了更好的定义,因此您不必使用整数来提及键盘的特定键,而是更易理解的枚举。

以下链接指向C#中 GetKeyState 的定义:

h ttp://www.pinvoke.net/default.aspx/user32.getkeystate

以下链接指向C#中 GetAsyncKeyState 的定义

h ttp://www.pinvoke.net/default.aspx/user32.getasynckeystate

以下链接指向C#中 GetKeyboardState 的定义

h ttp://www.pinvoke.net/default.aspx/user32.getkeyboardstate

要检测按下哪个键(随时随地),然后调用GetKeyState或GetAsyncKeyState函数,在单个参数中,提及要检测的键,然后添加:& 0x8000 ,或&调用后,0x80 。 如果按下提到的键,则表达式的返回值不为零,否则为零。 根据表达式的返回整数,您可以确定按下哪个键。 请注意,如果您将代码置于带有该表达式的语句!= 0作为输入条件(位于循环内),则该if语句中的代码将被执行多次,实际上多次,当你按下那个键。如果你想要执行一些代码一次当你按一个键然后释放它时,下面的代码就是技巧来实现这个目标:

while (boolean exression that will return false when you want to quit this loop)
{
    if (boolean expression that will return true when particular key is **held down**) //This boolean expression calls either GetKeyState or GetAsyncKeyState with the & 0x8000 or & 0x80 after the call, and then != 0 to check for **held down** key
       while (true) //It's purpose is to wait for the same key that was held down to be released. After code execution, it will encounter the break keyword that will finish him, even though that his enter condition is true.
           if (boolean expression that will return true when the **same** particular key is **NOT** held down! (NOT held down, means that at that time, that key was released). You can copy the same condition from the first if statement, and just change **!=**, which is next to 0, to **==**, or instead add brackets to the condition '(' and ')', and in left of '(' add '!').
           {
               //Put here the code that you want to execute once, when paticular key was pressed and then released.
               break; //the while (true), which is the only way to get out of it
           }
}

答案 3 :(得分:0)

查看MSDN中的Raw Input API:

http://msdn.microsoft.com/en-us/library/ms645536(VS.85).aspx

它允许您从键盘(以及其他东西)获取输入,而不会搞乱全局钩子。全局钩子只应作为最后的手段使用,因为它们可能会引入意想不到的后果。

使用原始输入的唯一缺点是它可能无法正确接收由软件生成的击键。

答案 4 :(得分:0)

如果你在.net应用程序中实现它,我建议使用GlobalKeyboardHook,否则我会查看它的源代码并获取你需要的东西,因为它正在实现上面提到的DLLImport函数。