Window
I. Introduction
Cats and babies have a lot in common. They all like to eat the plant in the family, all very hate to close the door. And they all love to play with your keyboard, as a result, the email you are sending to your boss may have been sent in half a sentence, your Excel account has been added to the clutter, and you haven't noticed that when you open Windows Explorer, several files have been moved to the Recycle Bin!
The solution is to develop an application that enables you to switch as long as the keyboard is in a "threat state" and to ensure that no keyboard input activity is harmful. This article wants to show you how to use a low-level Windows API hook to implement keyboard "control" in a C # application. The following illustration is a running snapshot of the sample program in this article.
two. Background
In fact, there are already many articles and sample code about Windows hooks, and someone has written a C + + sample program almost the same as this one. however, when I searched for the source code of the corresponding C # application, I found very few. NET example, and none of the programs can provide a convenient self-contained C # class.
. NET Framework enables you to access your most frequently used keyboard events (via Keypress,keyup and KeyDown) in a managed manner. Unfortunately, none of these events can be used to stop Windows key combinations such as ALT + TAB or the Windows Start key, allowing the user to "stay away" from an application.
The idea of this article is to capture keyboard events at the operating system level rather than at the framework level. To do this, the application needs to use Windows API functions to add itself to the application "hook chain" to listen for keyboard messages from the operating system. When it receives this type of message, the application is able to selectively pass messages, either properly or "suppress" it so that no other applications (including Windows) can affect it. This article is to explain its realization mechanism.
Note, however, that the code in this article applies only to Windows (nt,2000 and XP) based on NT versions, and that you cannot use this method to deactivate Ctrl+alt+delete. You can refer to MSDN for information on how to do this.
three. Using Code
For ease of use, I have provided two separate zip files in this article. One contains only the Keyboardhook class, which is the focus of this article. The other is a complete Microsoft Visual C # Edition application project called Baby Keyboard Bash, which implements the name of the keystroke or color shape in response to the keystroke.
Four. Instantiate class
The keyboard hooks are established and managed through the Keyboardhook class in Keyboard.cs. This class implements the IDisposable interface, so the easiest way to instantiate it is by using the Using keyword in the application's main () method to encapsulate the Application.Run () call. This ensures that the hook is established as soon as the application starts and, more importantly, invalidates the hook as soon as the application ends.
This class raises an event to warn that the application already has a key pressed, so it is important that the primary form can access the Keyboardhook instance created in the main () method; The simplest way is to store the instance in a public member variable.
Keyboardhook provides three constructors to enable or disable certain settings:
€€keyboardhook (): Captures all keystrokes and no content is passed to Windows or another application.
€€keyboardhook (string param): Converts a parameter string to one of the values in the parameters enumeration, and then calls the following constructor:
€€keyboardhook (keyboardhook.parameters enum): Starts the following settings, depending on the value selected from the Parameters enumeration:
O Parameters.allowalttab: Allows the user to switch to another application using ALT + TAB.
O Parameters.allowwindowskey: Allows the user to access the taskbar and Start menu using CTRL+ESC or a Windows key.
O Parameters.allowalttabandwindows: Enable Alt+tab,ctrl+esc and Windows keys.
O Parameters.passallkeystonextapp: If this argument is true, all keystrokes will be passed to any other listening application (including Windows).
When the keystroke continues to be captured by the keyboard hook, enabling ALT + TAB and/or Windows keys allows the person actually using the computer to switch to another application and interact with it using the mouse. The Passallkeystonextapp setting effectively disables keystroke capture, which also creates a low-level keyboard hook and raises its keyintercepted event, but it is also responsible for passing keyboard events to another listener.
Therefore, the method that instantiates the class to capture all keystrokes is as follows:
public static Keyboardhook kh;
[STAThread]
static void Main ()
{
Other code
using (kh = new Keyboardhook ())
{
Application.Run (New Form1 ());
}
five. Handling keyintercepted Events
When a foreign key is pressed, the Keyboardhook class activates a keyintercepted event that contains some Keyboardhookeventargs. This is accomplished by using a Keyboardhookeventhandler type of method that uses the following methods:
Kh. keyintercepted + = new Keyboardhook.keyboardhookeventhandler (kh_keyintercepted);
This Keyboardhookeventargs returns the following information about the key being pressed:
€€keyname: Key name, obtained by casting the captured key code to System.Windows.Forms.Keys.
€€keycode: The original key code returned by the keyboard hook
€€passthrough: Indicates whether this Keyboardhook instance is configured to allow the keystroke to be passed to other applications. It is useful to check if you want to allow a user to switch to another application using ALT + TAB or the Ctrl+esc/windows key.
Then, use a method with the appropriate signature to perform any task that is invoked by the keystroke. Here is a sample fragment:
void kh_keyintercepted (Keyboardhookeventargs e)
{
Check if this keystroke event is passed to other applications and deactivate topmost in case they need to be tuned to the front
if (E.passthrough)
{
This. topmost = false;
}
Ds. Draw (E.keyname);
}
The remainder of this article explains how low-level keyboard hooks are implemented in Keyboardhook.
Six. Implement a low-level Windows API keyboard Hook
In User32.dll, the Windows API contains three methods for this purpose:
€€setwindowshookex, it's responsible for setting up the keyboard hooks
€€unhookwindowshookex, it's responsible for removing the keyboard hooks.
€€callnexthookex, which is responsible for passing keystroke information to the next application that listens for keyboard events
The key to creating an application that can intercept a keyboard is to implement the first two methods and "discard" the third. As a result, any keystroke can only be passed into the application.
To achieve this, the first step is to include the System.Runtime.InteropServices namespace and import API methods, first SetWindowsHookEx:
Using System.Runtime.InteropServices
...
Inside the class:
[DllImport ("user32.dll", CharSet = CharSet.Auto, SetLastError = True)]
private static extern IntPtr SetWindowsHookEx (int idhook,
LowLevelKeyboardProc LPFN, IntPtr hmod, uint dwthreadid);
See the following discussion for the code to import UnhookWindowsHookEx and CallNextHookEx.
The next step is to call SetWindowsHookEx to establish the hook, which requires passing the following four parameters:
€€idhook:
This number determines the type of hook to create. For example, SetWindowsHookEx can be used to hook up mouse events (and, of course, other events). In the case of this article, we are only interested in 13, which is the ID of the keyboard hook. To make the code easier to read, we assign it to a constant wh_keyboard_ll.
€€LPFN:
This is a long pointer to a function that is responsible for handling keyboard events. In C #, "Pointer" is obtained by passing an instance of a proxy type, so that it references an appropriate method. This is the method that we call each time we use the hook.
It is noteworthy here that this proxy instance needs to be stored in a member variable of this class. This is to prevent it from being garbage collected once the first method call ends.
€€hmod:
An instance handle of the application that establishes the hook. The vast majority of the instances I've found only set it to IntPtr.Zero, on the grounds that multiple instances of the application are not likely to exist. However, this section of code uses the GetModuleHandle from Kernel32.dll to identify an exact instance to make this class more flexible.
€€dwthreadid:
The ID of the current process. Setting it to 0 allows the hook to become a global construct, which is the correct setting for a lower-level keyboard hook.
SetWindowsHookEx Returns a hook ID that will be used to decouple from the hook chain at the end of the application, so it needs to be stored in a member variable for future use. The relevant code in the Keyboardhook class is as follows:
Private Hookhandlerdelegate proc;
Private IntPtr hookid = IntPtr.Zero;
Private Const int WH_KEYBOARD_LL = 13;
Public Keyboardhook ()
{
proc = new Hookhandlerdelegate (hookcallback);
using (Process curprocess = process.getcurrentprocess ())
using (processmodule curmodule = curprocess.mainmodule)
{
Hookid = SetWindowsHookEx (Wh_keyboard_ll, Proc,getmodulehandle (Curmodule.modulename), 0);
}
}
Seven. Handling Keyboard Events
As mentioned earlier, SetWindowsHookEx requires a pointer to a callback function that is used to handle keyboard events. It expects to have a function that uses the following signature:
LRESULT CALLBACK lowlevelkeyboardproc (int ncode,wparam wparam,lparam LPARAM);
In fact, the C # method that establishes a function pointer uses a proxy, so the first step in pointing out what it needs to SetWindowsHookEx is to declare an agent with the correct signature:
Private delegate IntPtr hookhandlerdelegate (int ncode, IntPtr wParam, ref kbdllhookstruct LParam);
Then, write a callback method with the same signature, which will contain all the code that actually handles the keyboard event. In the case of Keyboardhook, it checks that keystrokes should be passed to other applications and then fires the Keyintercepted event. Here is a simplified version of the case with no keystroke handling code:
Private Const int WM_KEYDOWN = 0X0100;
Private Const int wm_syskeydown = 0x0104;
Private INTPTR hookcallback (int ncode, IntPtr wParam, ref kbdllhookstruct LParam)
{
WPARAM is filtered only for KeyDown events, otherwise the code will be executed again-for each keystroke (i.e., corresponding to KeyDown and KeyUp)
Wm_syskeydown is required to capture alt-related key combinations
if (ncode >= 0 && (WParam = = (IntPtr) wm_keydown | | wParam = (INTPTR) wm_syskeydown))
{
Firing events
onkeyintercepted (New Keyboardhookeventargs (Lparam.vkcode, Allowkey));
Returns a "dummy" value to capture the keystroke
Return (SYSTEM.INTPTR) 1;
}
Event is not processed, passing it to the next application
Return CallNextHookEx (Hookid, ncode, WParam, ref LParam);
}
Next, a reference to the Hookcallback is assigned to an instance of Hookhandlerdelegate and is passed to the SetWindowsHookEx call, as shown in the previous section.
Whenever a keyboard event occurs, the following parameters are passed to Hookcallback:
€€ncode:
According to the MSDN documentation, the callback function should return the result of CallNextHookEx if the value is less than 0. A normal keyboard event returns a Ncode value that is greater than or equal to zero.
€€wparam:
This value indicates what type of event occurred: The key was pressed or released, and whether the pressed key was a system key (the ALT key on the left or right).
€€lparam:
This is a structure that stores the exact keystroke information, such as the code being keyed. The structure declared in Keyboardhook is as follows:
private struct KBDLLHOOKSTRUCT
{
public int vkcode;
int scancode;
public int flags;
int time;
int dwextrainfo;
}
These two common parameters are the only two parameters used by the callback method in Keyboardhook. Vkcoke returns the virtual key code, which can be cast to System.Windows.Forms.Keys to obtain the key name, and the flags show whether this is an extension key (for example, the Windows start key) or whether the ALT key is pressed at the same time. The complete code for the hook callback method shows which flags are checked in each case.
If the information provided by the flags and other constituent elements of the kbdllhookstruct are not required, the signature of the callback method and code can be modified as follows:
Private Delegate IntPtr Hookhandlerdelegate (
int ncode, IntPtr wParam, IntPtr lParam);
In this case, LPARAM will return only Vkcode.
Eight. Pass the keystroke to the next application
A good keyboard hook callback method should end with the call to the CallNextHookEx function and return its result. This ensures that other applications have the opportunity to handle keystrokes against them.
However, the main function of the Keyboardhook class is to prevent the keystrokes from being propagated to any other application. As a result, Hookcallback will return a dummy value whenever the keystroke is processed at any time:
Return (SYSTEM.INTPTR) 1;
On the other hand, it does call callnexthookex-if it does not handle the event, or if the arguments passed in the overloaded constructor allow some key combinations to pass.
CallNextHookEx is enabled-by importing the function from User32.dll, as shown in the following code:
[DllImport ("user32.dll", CharSet = CharSet.Auto, SetLastError = True)]
private static extern IntPtr CallNextHookEx (IntPtr hhk, int ncode,
IntPtr WParam, ref keyinfostruct LParam);
The imported method is then invoked by Hookcallmethod to ensure that all parameters received through the hook are continued to be passed to the next application:
CallNextHookEx (Hookid, ncode, WParam, ref LParam);
As mentioned earlier, if the flags in lparam are irrelevant, you can modify the signature of the imported CallNextHookEx to define lparam as System.IntPtr.
Nine. Remove the hook
The final step in handling the hook is to remove it (when you destroy an instance of the Keyboardhook Class) by using the UnhookWindowsHookEx function imported from User32.dll, as follows:
[DllImport ("user32.dll", CharSet = CharSet.Auto, SetLastError = True)]
[Return:marshalas (Unmanagedtype.bool)]
private static extern bool UnhookWindowsHookEx (IntPtr HHK);
Since Keyboardhook implements IDisposable, this can be done in the Dispose method.
public void Dispose ()