问题
What win32 calls can be used to detect key press events globally (not just for 1 window, I'd like to get a message EVERY time a key is pressed), from a windows service?
回答1:
You want to use Win32 Hooks. In particular a keyboard hook.
You can read more about it here
The type of hook you want is WH_KEYBOARD and you can set it via the Win32 API SetWindowsHookEx.
Basically windows will call a function in a dll that you create everytime a key is pressed in any application system wide.
The hook will call your function which will have this interface:
LRESULT CALLBACK KeyboardProc(
int code,
WPARAM wParam,
LPARAM lParam
);
More information about this callback here.
With windows hooks you can not only track system wide events across all processes, but you can also filter them and stop them altogether.
回答2:
Take a look at the hooks you can set with SetWindowHookEx:
http://msdn.microsoft.com/en-us/library/ms644990(VS.85).aspx
However, unless you are running an "interactive" service, you won't have access to the desktop (and you shouldn't be running an interactive service because it won't work right in newer versions of Windows). You will have to look into the session and desktop management APIs in order to access the desktop/console.
回答3:
Just use the native function GetKeyState, or GetAsyncKeyState, or GetKeyboardState from user32 DLL.
You can read about each function I mentioned above in the msdn site:
h ttp://msdn.microsoft.com/en-us/library/windows/desktop/ms646301(v=vs.85).aspx
for the GetKeyState, and
h ttp://msdn.microsoft.com/en-us/library/windows/desktop/ms646293(v=vs.85).aspx
for the GetAsyncKeyState, and
h ttp://msdn.microsoft.com/en-us/library/windows/desktop/ms646299(v=vs.85).aspx
for the GetKeyboardState.
If you program in C# then:
Your code must include the following line out of any class, struct or enum declaration:
using System.Runtime.InteropServices;
Then in the class that has the method where you want to detect the keys, define one of three functions above for your choice, or what works and what not.
[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!
If you dislike uint as return type, you can also change it to int.
If the native functions always return 1 as true or 0 as false, then you can change their return type to bool instead, but if you do so, then I think you should add another line of code above the native function definition, which is:
[MarshalAs(UnmanagedType.Bool)]
OR you can add the following line in the middle of the native function definition (below the line where the DllImport word is, and above the line, where the 'extern' word is):
[return: MarshalAs(UnmanagedType.Bool)]
Pinvoke site offers better definition for these functions, so you don't have to use integers to mention a particular key of your keyboard, but more understandable enum.
The following link leads to the definition of the GetKeyState in C#:
h ttp://www.pinvoke.net/default.aspx/user32.getkeystate
The following link leads to the definition of the GetAsyncKeyState in C#
h ttp://www.pinvoke.net/default.aspx/user32.getasynckeystate
The following link leads to the definition of the GetKeyboardState in C#
h ttp://www.pinvoke.net/default.aspx/user32.getkeyboardstate
To detect which key is held down (at any time, anywhere) then call GetKeyState or GetAsyncKeyState function, in the single parameter, mention the key you want to detect and then add: & 0x8000, or & 0x80 after the call. If the mentioned key is held down, then the return value of the expression is not zero, but otherwise it is zero. According to the returned integer of the expression, you can determine which key held down or not. Note that if you put code inside if statement with that expression != 0 as enter condition, which is inside a loop, the code in that if statement will be executed more than once, actually many times, when you held down that key. If you want some code to be executed once When you press a key and then release it, then the following code is a trick that achieves this goal:
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
}
}
回答4:
Check out the Raw Input API in MSDN:
http://msdn.microsoft.com/en-us/library/ms645536(VS.85).aspx
It allows you to get input from keyboards (among other things) without messing around with global hooks. Global hooks should only be used as a last resort as they may introduce unintended consequences.
The only drawback to using Raw Input is that it may not properly receive keystrokes which are generated by software.
回答5:
If you are implementing this in a .net application i'd recommend GlobalKeyboardHook, Otherwise i'd look at it's source and take what you need since it is implementing the DLLImport functions mentioned above.
来源:https://stackoverflow.com/questions/310576/low-level-keyboard-input-from-windows