In-depth discussion on Hook Technology in. net

Source: Internet
Author: User
Tags virtual environment

I. Introduction

This article will discuss applications in. netProgramUse of Global System hooks in. To this end, I have developed a reusable class library and created a corresponding sample program.

 
Figure 1

You may have noticed another question about using system hooks.Article. This article is similar but has important differences. This article will discuss the use of Global System hooks in. net, while other articles will only discuss local system hooks. These ideas are similar, but the implementation requirements are different.

Ii. Background

If you are not familiar with the Windows system hook concept, Let me provide a brief description:

◆ A system hook allows you to insert a callback function that intercepts certain Windows messages (for example, messages associated with the mouse ).

◆ A local system hook is a system hook-it is called only when the specified message is processed by a single thread.

◆ A global system hook is a system hook that is called when a specified message is processed by any application on the entire system.

There are several good articles to introduce the concept of system hooks. Here, I am not trying to re-collect these introductory information, but simply ask the reader to refer to the following background documents on system hooks. If you are familiar with the system hook concept, you can get everything you can get from this article.

◆ Knowledge about hooks in the msdn library.

◆ Dino Esposito's cutting edge-Windows hooks in the. NET Framework.

◆ Don kackman's app hook in C #.

In this article, we will discuss how to expand this information to create a global system hook-it can be used by. Net classes. We will use C # And a DLL and a non-hosted C ++ to develop a class library-they will accomplish this goal together.

Iii. UseCode

Before developing this database, let's take a quick look at our goals. In this article, we will develop a class library-It installs global system hooks and exposes these events processed by hooks as A. Net event of our hook class. To demonstrate the usage of this system hook class, we will create a mouse event hook and a keyboard event hook in a Windows form application written in C.

These class libraries can be used to create system hooks of any type, including two pre-compiled hooks-mousehook and keyboardhook. We have also included specific versions of these classes, called mousehookext and keyboardhookext. Based on the models set by these classes, you can easily build system hooks-for any of the 15 hook event types in Win32 APIs. In addition, this complete class library contains a Compiled HTML Help file, which archive these classes. Please be sure you have read this help file-if you decide to use this library in your application.

The usage and lifecycle of the mousehook class are quite simple. First, we create an instance of the mousehook class.

Mousehook = new mousehook (); // mousehook is a member variable.

Next, we bind the mouseevent event to a class-level method.

Mousehook. mouseevent + = new mousehook. mouseeventhandler (mousehook_mouseevent );

//...

Private void mousehook_mouseevent (mouseevents mevent, int X, int y ){

String MSG = string. Format ("mouse event: {0} :( {1}, {2}).", mevent. tostring (), x, y );

Addtext (MSG); // Add a message to the text box

}

To start receiving mouse events, simply install the following hooks.

Mousehook. installhook ();

To stop receiving events, simply uninstall the hook.

Mousehook. uninstallhook ();

You can also call dispose to uninstall the hook.

When your application exits, it is important to uninstall the hook. Keeping System hooks installed will slow down message processing for all applications in the system. It can even make one or more processes unstable. Therefore, make sure that you have to move your system hook when you have used the hook. We are sure that the system hook will be removed in our sample application-by adding a dispose call to the form dispose method.

Protected override void dispose (bool disposing ){

If (disposing ){

If (mousehook! = NULL ){

Mousehook. Dispose ();

Mousehook = NULL;

}

//...

}

}

This is the case when this class library is used. This class library has two system hook classes and is quite easy to expand.

4. Build a database

This library has two main components. The first part is a C # class library-you can use it directly in your application. This class library, in turn, uses an unmanaged C ++ DLL internally to directly manage system hooks. We will first discuss how to develop the C ++ section. Next, we will discuss how to use this library in C # To build a common hook class. Just like discussing C ++/C # interaction, we will pay special attention to how C ++ methods and data types are mapped to. Net methods and data types.

You may want to know why we need two libraries, especially an unmanaged C ++ DLL. You may also notice the two references mentioned in the Background section of this article, which do not use any unmanaged code. To this end, my answer is, "Yes! This is why I wrote this article ". When you think about how system hooks actually implement their functions, we need unmanaged code. To enable a global system hook to work, Windows inserts your DLL into the process space of each running process. Since most processes are not. Net processes, they cannot directly execute. Net Assembly sets. We need an unmanaged code proxy-Windows can insert it into all the processes to be hooked.

First, we provide a mechanism to pass a. Net proxy to our c ++ library. In this way, we use C ++

Int setuserhookcallback (hookproc userproc, uint hookid)

Typedef void (callback * hookproc) (INT CoDe, wparam W, lparam L)

The second parameter of setuserhookcallback is the hook type-this function pointer will use it. Now, we must use C # To define the corresponding methods and proxies to use this code. Below is how we map it to C #.

Private Static extern setcallbackresults

Setuserhookcallback (hookprocessedhandler hookcallback, hooktypes hooktype)

Protected Delegate void hookprocessedhandler (INT CoDe, uintptr wparam, intptr lparam)

Public Enum hooktypes {

Journalrecord = 0,

Journalplayback = 1,

//...

Keyboardll = 13,

Mousell = 14

};

First, we use dllimpThe ORT attribute is imported into the setuserhookcallback function as a static external method of the abstract base hook class systemhook. Therefore, we must map some external data types. First, we must create a proxy as our function pointer. This is implemented by defining the hookprocesshandler above. We need a function whose C ++ signature is (INT, wparam, lparam ). In the Visual Studio. net c ++ compiler, int and C # are the same. That is to say, in C ++ and C #, Int Is int32. This is not always the case. Some compilers treat c ++ int as int16. We insist on using Visual Studio. net c ++ compiler to implement this project. Therefore, we do not have to worry about additional definitions brought about by compiler differences.

 

Next, we need to use C # To pass the wparam and lparam values. These are indeed pointers. They point to the uint and long values of C ++ respectively. In C #, they are pointers to uint and Int. If you are not sure what wparam is, you can right-click it in the C ++ code and select "go to definition ". This will guide you through the definition in windef. h.

// From windef. h:

Typedef uint_ptr wparam;

Typedef long_ptr lparam;

Therefore, system. uintptr and system. intptr are selected as our variable types-they correspond to the wparam and lparam types respectively when they are used in C.

Now, let's take a look at how the hook base class uses these import methods to pass a call-back function (proxy) to C ++-it allows the C ++ library to directly call your system hook class instance. First, in the constructor, The systemhook class creates a proxy to the private method internalhookcallback-It matches the hookprocessedhandler proxy signature. Then, it passes the proxy and Its hooktype to the c ++ library to use the setuserhookcallback method to register the callback function, as discussed above. The code implementation is as follows:

Public systemhook (hooktypes type ){

_ Type = type;

_ Processhandler = new hookprocessedhandler (internalhookcallback );

Setuserhookcallback (_ processhandler, _ type );

}

The implementation of internalhookcallback is quite simple. Internalhookcallback only calls the abstract method hookcallback while encapsulating it with a catch-all try/Catch Block. This simplifies the implementation in the derived class and protects the C ++ code. Remember, once everything is done properly, this c ++ hook will directly call this method.

[Methodimpl (methodimploptions. noinlining)]

Private void internalhookcallback (INT CoDe, uintptr wparam, intptr lparam ){

Try {hookcallback (CoDe, wparam, lparam );}

Catch {}

}

We have added a method implementation Attribute-which tells the compiler not to inline this method. This is not optional. At least, it is required before I add try/catch. It seems that for some reason, the compiler is trying to inline this method-this will cause various troubles for the proxy that wraps it. Then, the C ++ layer will call back, and the application will crash.

Now, let's see how the next derived class uses a specific hooktype to receive and process hook events. The hookcallback method of the virtual mousehook class is implemented as follows:

Protected override void hookcallback (INT CoDe, uintptr wparam, intptr lparam ){

If (mouseevent = NULL) {return ;}

Int x = 0, y = 0;

Mouseevents mevent = (mouseevents) wparam. touint32 ();

Switch (mevent ){

Case mouseevents. leftbuttondown:

Getmouseposition (wparam, lparam, ref X, ref y );

Break;

//...

}

Mouseevent (mevent, new point (x, y ));

}

First, note that this class defines an event mouseevent-This class fires this event when it receives a hook event. This class converts the data from the wparam and lparam types to meaningful mouse event data in. net before it is triggered. In this way, the consumers of the class can avoid worrying about interpreting the data structure. This class uses the imported getmouseposition function-we defined in C ++ DLL to convert these values. For this reason, see the following sections.

In this method, we check whether someone is listening to this event. If not, you do not have to continue to process this event. Then, we convert wparam into a mouseevents Enumeration type. We have carefully constructed the mouseevents enumeration to accurately match their corresponding constants in c ++. This allows us to simply convert the pointer value to the enumeration type. However, you must note that this conversion will succeed even if the wparam value does not match an enumerated value. The mevent value will only be undefined (not null, but not within the enumerated value range ). Therefore, analyze the system. enum. isdefined method in detail.

Next, after determining the event type we receive, this class activates the event and notifies the consumer of the type of the mouse event and the mouse position during the event.

Finally, note the conversion of wparam and lparam values: for each type of event, the values and meanings of these variables are different. Therefore, in each hook type, we must interpret these values differently. I chose to use C ++ to implement this conversion, instead of using C # To simulate complex C ++ structures and pointers. For example, the previous class uses a C ++ function called getmouseposition. Below is the method in C ++ dll:

Bool getmouseposition (wparam, lparam, Int & X, Int & Y ){

Mousehookstruct * pmousestruct = (mousehookstruct *) lparam;

X = pmousestruct-> pt. X;

Y = pmousestruct-> pt. Y;

Return true;

}

Instead of ing the mousehookstruct structure pointer to C # as much as possible, we will temporarily upload it back to the C ++ layer to extract the value we need. Note: because we need to return some values from this call, we pass our integers as reference variables. This is directly mapped to int * in C *. However, we can reload this behavior and import this method by selecting the correct signature.

Private Static extern bool internalgetmouseposition (uintptr wparam,

Intptr lparam, ref int X, ref int y)

By defining the integer parameter as REF int, we get the value passed to us through the C ++ reference. If we want to, we can also use out Int.

5. Restrictions

Some hook types are not suitable for implementing global hooks. I am currently working on a solution-it will allow the use of restricted hook types. So far, do not add these types back to the database because they will cause application failure (often system-wide catastrophic failure ). The next section will focus on the causes and solutions behind these restrictions.

Hooktypes. callwindowprocedure

Hooktypes. callwindowproret

Hooktypes. computerbasedtraining

Hooktypes. Debug

Hooktypes. foregroundidle

Hooktypes. journalrecord

Hooktypes. journalplayback

Hooktypes. getmessage

Hooktypes. systemmessagefilter

6. Two types of hooks

In this section, I will try to explain why some hook types are limited to a certain category, while others are not limited. If I use a biased term, please forgive me. I have not found any documentation on this part of the topic, so I made up my own vocabulary. In addition, if you think that I am not at all, please let me know.

When Windows calls the callback functions passed to setwindowshookex (), they are called differently for different types of hooks. There are basically two cases: the hook for switching the execution context and the hook for not switching the execution context. In another way, that is, the execution of the hook callback function in the application process space where the hook is stored and the execution of the hook callback function in the hooked application process space.

Hook types, such as mouse and keyboard hooks, are used to switch the context before being called by windows. The process is roughly as follows:

1. Application X has focus and execution.

2. Press the next key.

3. Windows takes the context from application X and switches the execution context to the application with the hook.

4. In Windows, the hook callback function is called by using the key message parameter in the process space of the application program with hooks.

5. Windows uses the hook app to take over the context and returns the response to the context switch.

6. In Windows, messages are placed in the queue of application X.

7. A little later, when application X is executed, it extracts the message from its own message queue and calls its internal buttons (or releases or presses) processor.

8. Application x continues to execute...

 

For example, the CBT hook (window creation, and so on .) The hook type does not switch context. For these types of hooks, the process is roughly as follows:

1. Application X has focus and execution.

2. Application X creates a window.

3. Windows uses the CBT event Message Parameter in application x process space to call the hook callback function.

4. Application x continues to execute...

This should explain why some types of hooks can work with the library structure, but some cannot. Remember, this is exactly what the database is going to do. Insert the following steps after steps 4th and 3rd respectively:

1. Windows calls the hook callback function.

2. The target callback function is executed in an unmanaged DLL.

3. The target callback function searches for the corresponding hosted call proxy.

4. The managed proxy is executed with appropriate parameters.

5. The target callback function returns and executes the hook processing corresponding to the specified message.

Steps 3 and 4 are doomed to fail because they do not switch the hook type. Step 3 fails because the corresponding managed callback function is not set for the application. Remember, this DLL uses global variables to track these managed proxies and the hook DLL is loaded into every process space. However, this value is only set in the application process space where the hook is stored. In other cases, all of them are null.

Tim Sylvester pointed out in his article "Other hook types" that using a shared memory segment will solve this problem. This is true, but, as Tim pointed out, the managed proxy addresses are meaningless for any process except the hook application. This means that they are meaningless and cannot be called during callback function execution. It will be troublesome.

Therefore, in order to use these callback functions for Hook types that do not execute context switching, you need some kind of inter-process communication.

I have already tested this idea-using an out-of-process COM object in the unmanaged DLL hook callback function for IPC. If you can work in this way, I will be glad to understand this. As for my attempt, the results were not satisfactory. The basic reason is that it is difficult to correctly initialize com units for various processes and their threads (coinitialize (null. This is a basic requirement before you can use COM objects.

I don't doubt that there is a way to solve this problem. But I have not tried them yet, because I think they are of limited use. For example, the CBT hook allows you to cancel the creation of a window, if you want. You can imagine what will happen to make this work.

1. The hook callback function starts execution.

2. Call the corresponding hook callback function in the unmanaged hook DLL.

3. The execution must be routed back to the master Hook application.

4. The application must determine whether to allow this creation.

5. The call must be routed back to the still running hook callback function.

6. The hook callback function in the unmanaged hook DLL receives the action to be taken from the master Hook application.

7. The hook callback function in the unmanaged hook DLL takes appropriate actions for the CBT hook call.

8. Complete the execution of the hook callback function.

This is not impossible, but not good. I hope this will eliminate the mysteries in this library around the allowed and restricted hook types.

VII. Others

◆ Library documentation: we have included a complete code document on the managedhooks class library. This is converted to standard help XML via Visual Studio. NET when the configuration is compiled using "documentation. Finally, we have used ndoc to convert it into Compiled HTML Help (CHM ). You can read this help file, simply click the hooks. CHM File in Solution Explorer of the solution or find the downloadable ZIP file related to the file.

◆ Enhanced smart sensing: if you are not familiar with Visual Studio. net, how to use the compiled XML file (pre-ndoc output) to enhance intelligent awareness for the reference library project, so let me briefly introduce it. If you decide to use this class library in your application, you can consider copying a stable build version of the Library to the location where you want to refer to it. At the same time, copy the XML document (systemhooks \ managedhooks \ bin \ debug \ Kennedy. managedhooks. XML) to the same location. When you add a reference to this library, Visual Studio. NET will automatically read the file and use it to add smart sensing documents. This is useful, especially for third-party libraries like this.

◆ Unit test: I believe that all databases should have corresponding unit tests. Since I am a partner and software engineer in a company (mainly responsible for unit testing for. NET environment software), no one will be surprised. Therefore, you will find a unit test project in the solution named managedhookstests. To run this unit test, you need to download and install harnessit-this download is a free trial version of our commercial unit test software. In this unit test, I paid special attention to this-here, invalid parameters of the method may cause exceptions in C ++ memory. Although this library is quite simple, this unit test does help me find some errors in some more subtle situations.

◆ Unmanaged/managed debugging: debugging is one of the most skillful aspects of hybrid solutions (such as the hosted and unmanaged code in this article. If you want to debug the C ++ code in one step or set breakpoints in the C ++ code, you must start unmanaged debugging. This is a project setting in Visual Studio. NET. Note that you can smoothly debug the hosted and unmanaged layers in one step. However, during the debugging process, unmanaged debugging does seriously slow down application loading time and execution speed.

8. Last warning

Obviously, system hooks are quite powerful; however, using such power should be responsible. When a system hook goes wrong, they not only crash your application. They can collapse every application running in your current system. However, the possibility of such a degree is generally very small. However, when using system hooks, you still need to check your code repeatedly.

I found a useful technology that can be used to develop applications-it uses system hooks to install a copy of your favorite development operating system and Visual Studio on Microsoft's Virtual PC.. net. Then, you can develop your applications in this virtual environment. In this way, when an error occurs in your hook application, they will only exit the virtual instance of your operating system rather than your real operating system. I have to restart my real OS-this virtual OS crashes due to a hook error, but this is not always the case.

Note: If you subscribe to an msdn instance online, you can freely use the virtual PC instance throughout the subscription process.

 

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.