哪些win32调用可用于从windows服务全局检测按键事件(不仅仅是1个窗口,我想每次按下一个键时都收到一条消息)?
5 回答
你想使用 Win32 Hooks。特别是键盘挂钩。
您想要的挂钩类型是 WH_KEYBOARD,您可以通过 Win32 API SetWindowsHookEx 设置它。
基本上,每次在任何应用程序系统范围内按下一个键时,windows 都会调用一个 dll 中的函数。
该钩子将调用您的函数,该函数将具有此接口:
LRESULT CALLBACK KeyboardProc(
int code,
WPARAM wParam,
LPARAM lParam
);
使用 windows 挂钩,您不仅可以跨所有进程跟踪系统范围的事件,还可以过滤它们并完全停止它们。
看看你可以用 SetWindowHookEx 设置的钩子:
http://msdn.microsoft.com/en-us/library/ms644990(VS.85).aspx
但是,除非您正在运行“交互式”服务,否则您将无法访问桌面(并且您不应该运行交互式服务,因为它在较新版本的 Windows 中无法正常工作)。您必须查看会话和桌面管理 API 才能访问桌面/控制台。
查看 MSDN 中的原始输入 API:
http://msdn.microsoft.com/en-us/library/ms645536(VS.85).aspx
它允许您从键盘(除其他外)获取输入,而不会弄乱全局挂钩。全局挂钩只能作为最后的手段使用,因为它们可能会带来意想不到的后果。
使用原始输入的唯一缺点是它可能无法正确接收由软件生成的击键。
只需使用来自 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 为真或 0 为假,那么你可以将它们的返回类型改为bool,但如果你这样做,那么我认为你应该在原生函数定义之上添加另一行代码,即:
[MarshalAs(UnmanagedType.Bool)]
或者,您可以在本机函数定义的中间添加以下行(在 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。如果提到的键被按住,则表达式的返回值不为零,否则为零。根据表达式返回的整数,您可以确定按下或不按下哪个键。请注意,如果您将代码放入带有该表达式的 if 语句中!= 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
}
}
如果您在 .net 应用程序中实现此功能,我会推荐GlobalKeyboardHook,否则我会查看它的源代码并获取您需要的内容,因为它正在实现上述 DLLImport 函数。