C # + low-level Windows API Hook intercepts keyboard input

Source: Internet
Author: User
Tags passthrough
At home, babies and other animals may hit your computer keyboard, causing unpredictable results. This C # example application in this article Program It will show you how to implement keys based on Windows Hook APIs before capturing them for any harm.
I. Introduction
There are many similarities between cats and babies. They all like to eat home-grown plants and hate to close the door. They also love to play with your keyboard. As a result, the email you are sending to your boss may be sent in half a sentence, your Excel account has been added to some messy content, and you have not noticed that when you open windows Resource Manager, several files have been moved to the recycle bin!
The solution is to develop an application to implement the following functions: as long as the keyboard is in the "threat" status, you can switch and ensure that any keyboard input activity will not cause harm. This article will show you how to use a low-level Windows API hook to "control" the keyboard in a C # application ". Is a running snapshot of the sample program in this article.
Ii. Background
In fact, there are already many Article And examples Code And someone has written almost the same C ++ sample program as this article. However, when I search for the source code of the corresponding C # application, I find very few. Net examples, and no program can provide a convenient self-contained C # class.
The. NET framework allows you to host Keyboard Events that you use most frequently (via keypress, keyup, and keydown ). Unfortunately, none of these events can be used to stop Windows keys (such as ALT + TAB or Windows "start"), allowing users 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. Therefore, 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 a message of this type, the application can selectively transmit the message, perform normal processing, or "Suppress" it so that no other applications (including windows) are available). This article explains how it works.
However, please note that the code in this article is only applicable to Windows (NT, 2000, and XP) based on the NT version, and cannot be used to disable CTRL + ALT + Delete. For more information about how to achieve this, see msdn.
3. Use Code
For ease of use, I have provided two independent ZIP files in this article. One class that only contains keyboardhook is the focus of this article. The other is a complete Microsoft Visual C #2005 express edition application project named "Baby keyboard Bash", which enables displaying the name or color of the strike key to respond to the strike key.
Iv. instantiation class
Keyboard hooks are created and managed through the keyboardhook class in keyboard. CS. This class implements the idisposable interface. Therefore, the simplest way to instantiate it is to use the using keyword in the main () method of the application to encapsulate the application. Run () call. This will ensure that the hook is established as long as the application starts and, more importantly, the hook becomes invalid when the application ends.
This class triggers an event to warn the application that a key has been pressed. Therefore, it is very important for the main form to be able to access the keyboardhook instance created in the main () method; the simplest way is to store this instance in a public member variable.
The keyboardhook provides three constructors to enable or disable some settings:
& #8226; keyboardhook (): captures all the keys without any content being passed to Windows or other applications.
& #8226; keyboardhook (string PARAM): converts the parameter string to one of the values in the parameters enumeration, and then calls the following constructor:
& #8226; keyboardhook (keyboardhook. Parameters Enum): Start the following settings based on the values selected from the parameters enumeration:
O parameters. allowalttab: allows you to use Alt + TAB to switch to another application.
O parameters. allowwindowskey: allows you to use Ctrl + ESC or a Windows key to access the taskbar and Start Menu.
O parameters. allowalttabandwindows: Enable Alt + TAB, CTRL + ESC, and Windows keys.
O parameters. passallkeystonextapp: If this parameter is set to true, all the keys will be passed to any other listening application (including windows ).
When the Keys continue to be captured by keyboard hooks, enable Alt + tab and/or Windows keys to allow users who actually use the computer to switch to another application and interact with them with the mouse. The passallkeystonextapp setting effectively disables key capture. This class also creates a low-level keyboard hook and triggers its keyintercepted event, but it is also responsible for passing the keyboard event to another listener.
Therefore, you can instantiate this class to capture all the keys as follows:
Public static keyboardhook KH;
[Stathread]
Static void main ()
{
// Other code
Using (kh = new keyboardhook ())
{
Application. Run (New form1 ());
}
5. Handle keyintercepted events
When a foreign key is pressed, The keyboardhook class activates a keyintercepted event that contains some keyboardhookeventargs. This is implemented through a keyboardhookeventhandler type method using the following methods:
Kh. keyintercepted + = new keyboardhook. keyboardhookeventhandler (kh_keyintercepted );
The keyboardhookeventargs returns the following information about the pressed key:
& #8226; keyname: key name, which is obtained by forcibly converting the captured key code to system. Windows. Forms. Keys.
& #8226; keycode: the original key code returned by the keyboard hook
& #8226; passthrough: Specifies whether the keyboardhook instance is configured to allow the key to be passed to other applications. If you want to allow a user to use the Alt + TAB or Ctrl + ESC/Windows key to switch to another application, it is very useful to check it.
Then, use a method with an appropriate signature to execute any task called by the key. The following is an example:
Void kh_keyintercepted (keyboardhookeventargs E)
{
// Check whether this key-click event is passed to other applications and topmost is disabled, in case they need to be transferred to the front end
If (E. passthrough)
{
This. topmost = false;
}
DS. Draw (E. keyname );
}
The rest of this article explains how low-level keyboard hooks are implemented in keyboardhook.
6. Implement a low-level Windows API keyboard hook
In user32.dll, Windows API contains three methods to achieve this goal:
& #8226; setwindowshookex, which is used to create a keyboard hook
& #8226; unhookwindowshookex, which is used to remove keyboard hooks
& #8226; callnexthookex, which is used to transmit the key information to the application that listens to the next keyboard event
The key to creating an application that can intercept the keyboard is to implement the first two methods, while "abandon" the third method. The result is that any key can only be passed to this application.
To achieve this goal, the first step is to include the system. runtime. interopservices namespace and import the API method. The first step is 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 );
For the code for importing unhookwindowshookex and callnexthookex, see the following discussion.
The next step is to call setwindowshookex to create a hook. The following four parameters must be passed:
& #8226; idhook:
This number determines the type of the hook to be created. For example, setwindowshookex can be used to hook mouse events (of course there are other events ). In 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.
& #8226; lpfn:
This is a long pointer to a function that handles Keyboard Events. In C #, the "Pointer" is obtained by passing a proxy instance to reference an appropriate method. This is the method we call every time we use a hook.
It is worth noting that this proxy instance needs to be stored in a member variable of this class. This is to prevent the method from being recycled as garbage once the first method is called.
& #8226; hmod:
Create an instance handle for the hook application. The vast majority of instances I found only set it to intptr. Zero, because it is unlikely that multiple instances of the application will exist. However, this part of Code uses the getmodulehandle from kernel32.dll to identify accurate instances, making this class more flexible.
& #8226; dwthreadid:
The ID of the current process. Setting it to 0 can make the hook a global constructor. This is the correct setting for a low-level keyboard hook.
Setwindowshookex returns a hook ID, which is used to decouple from the hook chain when the application ends. Therefore, it needs to be stored in a member variable for future use. The 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 );
}
}
7. Handle Keyboard Events
As mentioned above, setwindowshookex requires a pointer to the callback function used to handle keyboard events. It expects a function with the following signature:
Lresult callback lowlevelkeyboardproc (INT ncode, wparam, lparam );
In fact, the C # method for creating a function pointer uses a proxy. Therefore, the first step to point out the required content to setwindowshookex is to use the correct signature to declare a proxy:
Private delegate intptr hookhandlerdelegate (INT ncode, intptr wparam, ref KBDLLHOOKSTRUCT lparam );
Then, write a callback method using the same signature. This method will contain all the code that actually handles Keyboard Events. In the case of keyboardhook, it checks whether the key should be passed to other applications and then fires the keyintercepted event. The following is a simplified version that does not contain a key to process the Code:
Private const int wm_keydown = 0x0100;
Private const int wm_syskeydown = 0x0104;
Private intptr hookcallback (INT ncode, intptr wparam, ref KBDLLHOOKSTRUCT lparam)
{
// Filter wparam only for keydown events; otherwise, the code will be executed again-for each key (that is, corresponding to keydown and keyup)
// Wm_syskeydown is required to capture alt-related keys.
If (ncode> = 0 & (wparam = (intptr) wm_keydown | wparam = (intptr) wm_syskeydown ))
{
// Trigger event
Onkeyintercepted (New keyboardhookeventargs (lparam. vkcode, allowkey ));
// Returns a "dumb" value to capture the strike key
Return (system. intptr) 1;
}
// The event is not handled. Pass it to the next application.
Return callnexthookex (hookid, ncode, wparam, ref lparam );
}
Next, a reference to hookcallback is assigned to an instance of hookhandlerdelegate and passed to the call of setwindowshookex, as shown in the previous section.
The following parameters are passed to hookcallback whenever a keyboard event occurs:
& #8226; ncode:
According to the msdn document, the callback function should return the callnexthookex result if the value is smaller than zero. A normal keyboard event returns an ncode value greater than or equal to zero.
& #8226; wparam:
This value indicates the type of event: whether the key is pressed or released, and whether the key is a system key (ALT on the left or right ).
& #8226; lparam:
This is a structure that stores precise key information, such as the Code of the key. The structure declared in keyboardhook is as follows:
Private struct KBDLLHOOKSTRUCT
{
Public int vkcode;
Int scancode;
Public int flags;
Int time;
Int dwextrainfo;
}
The two public parameters are the only two parameters used by the callback method in the keyboardhook. Vkcoke returns the virtual key code, which can be forcibly converted to system. windows. forms. keys to get the key name, and flags shows whether this is an extended key (for example, the Windows Start key) or whether the Alt key is simultaneously pressed. The complete code about the hook callback method shows which flags values should be checked in each case.
If the information provided by flags and other elements of KBDLLHOOKSTRUCT are not required, the callback method and code signature can be modified as follows:
Private delegate intptr hookhandlerdelegate (
Int ncode, intptr wparam, intptr lparam );
In this case, lparam returns only vkcode.
8. Pass the keys to the next application
A good keyboard hook callback method should end with calling the callnexthookex function and returning its results. This ensures that other applications have the opportunity to process the keys for them.
However, the main function of the keyboardhook class is to prevent keys from being propagated to any other application. Therefore, the hookcallback will return a dummy value no matter when it processes the key:
Return (system. intptr) 1;
On the other hand, it does call callnexthookex-if it does not handle this event, or if the parameters passed by keyboardhook In the overloaded constructor allow some key combinations to pass through.
Callnexthookex is enabled-import this 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 );
Then, the imported method is called by hookcallmethod, which ensures that all parameters received by the hook are passed to the next application:
Callnexthookex (hookid, ncode, wparam, ref lparam );
As mentioned above, if flags in lparam are irrelevant, you can modify the imported callnexthookex signature to define lparam as system. intptr.
9. Remove the hook
The last step to handle the hook is to use the unhookwindowshookex function imported from user32.dll to remove it (when the keyboardhook class instance is damaged), as shown below:
[Dllimport ("user32.dll", charset = charset. Auto, setlasterror = true)]
[Return: financialas (unmanagedtype. bool)]
Private Static extern bool unhookwindowshookex (intptr HHK );
Since keyboardhook implements idisposable, this can be done in the dispose method.
Public void dispose ()
{
Unhookwindowshookex (hookid );
}
Hookid is the ID returned by the constructor when setwindowshookex is called. This will delete the application from the hook chain.
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.