Global System Hooks in .net

xiaoxiao2021-03-06  88

REL = "stylesheet" type = text / css href = "http://www.codeproject.com/styles/global.css">

Download Demo Project - 105 KB Download Source - 154 KB

Introduction

This Article Discusses The Use of Global System Hooks in .NET Applications.

You may have noticed other articles on using system hooks with P / Invoke on Code Project or other publications (see background section below). This article is similar to those but there is a significant difference. This article covers using global system hooks in .NET .....................

Background

In Case you are not family hooks in Windows, Let Me State A Few Brief Descriptions:

A system hook allows you to insert a callback function which intercepts certain Windows messages (eg mouse related messages). A local system hook is a system hook that is called only when the specified messages are processed by a single thread. A global system hook is .

There are several good articles which introduce the concept of system hooks. Rather than rehashing the introductory information here, I'll simply refer readers to those articles for background information on system hooks. If you're familiar with the concept of system hooks, then You Should Be Able To Get Everything You NEED from this Article.

About hooks in the msdn library. Cutting Edge - Windows Hooks in the .net framework by dino esposito. Using hooks from c # by don kackman.

What we are going to cover in this article is extending this information to create a global system hook which is usable in .NET classes. We will develop a class library in C # and a DLL in unmanaged C which together will accomplish this goal.Using the code

Before we dig into developing this library, let's take a quick look at where we are headed. In this article, we will develop a class library that installs global system hooks and exposes the events processed by the hook as a .NET event of our hook Class. To illustrate the usage of the system hook classes, We Will Create A Mouse Event Hook and keyboard Event Hook in a Windows Forms Application Written in C #.

The class library can be used to create any type of system hook There are two that come pre-built:.. MouseHook and KeyboardHook We have also included specialized versions of these classes called MouseHookExt and KeyboardHookExt respectively Following the model set by those classes,. you can easily build system hooks for any of the 15 hook event types in the Win32 API. Also, the entire class library comes with a compiled HTML help file which documents the classes. Be sure to look at this help file if you decide to use This Library in your application.

.

INTPTR AppInstance = GetApplicationInstance ();

Mousehook = new mousehook (AppInstance); // Mousehook Is a Member Variable

Next, We Wire Up 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 evenet: {0}: ({1}, {2}).",

Mevent.tostring (), x, y);

AddText (msg); // adds the message to the text box.

}

To Start Receiving Mouse Events, Simply Install the hook.

Mousehook.installhook ();

To Stop Receiving Events, Simply Uninstall the hook.

Mousehook.unInstallhook ();

You Can Also Call Dispose Which Will Uninstall The Hook As Well.

It is important that you uninstall the hook when your application exits. Leaving system hooks installed will slow message processing for all applications on the system at best. It could even cause one or more processes to become unstable. To put it in more technical terms that speaks to the developer in all of us: it's really really bad to forget this part So, be sure to remove your system hooks when you are done with them We ensure that we remove the system hooks in our sample application by adding a Dispose.. Call in the form's dispose method.

Protected Override Void Dispose (Bool Disposing)

{

IF (Disposing)

{

IF (Mousehook! = NULL)

{

Mousehook.dispose ();

mousehook = null;

}

// ...

}

}

.

Building the library

There are two major components of the library. The first part is a C # class library which you use directly in your application. That class library, in turn, uses an unmanaged C DLL internally to manage the system hooks directly. We'll first discuss developing the C part. Next, we'll cover how to use this library in C # to build a general hooking class. As we discuss the C / C # interaction, we'll pay particular attention to how the C methods and data types map to .NET methods and data types.You may be wondering why we need two libraries, especially an unmanaged C DLL. You may have noticed also that two of the reference articles mentioned in the background section of this article do not use any unmanaged code. To this I say, "Exactly! that's why I'm writing this article". When you think about how system hooks actually implement their functionality, it makes sense that we need unmanaged code. In order for a global system hook to work, Windows inserts Your DLL INTO The Process Spa ce of every running process. Since most processes are not .NET processes, they can not execute .NET assemblies directly. We need an unmanaged code stub which Windows can insert into all the processes that will be hooked.

The first order of business is to provide a mechanism to pass a .NET delegate into our C library. Thus, we defined the following function (SetUserHookCallback) and function pointer (HookProc) in C .

Int setUserhookCallback (HookProc UserProc, Uint Hookid)

TypeDef void (Callback * HookProc) (Int Code, WPARAM W, LPARAM L)

The second parameter to SetUserHookCallback is the type of hook that this function pointer is intended to be used with Now, we have to define corresponding methods and delegates in C # to use this code Here is how we map this to C #:.. Private static extern SetCallbackResults

SetuserhookCallback (HookProcessedHandler Hookcallback, hooktypes hooktype)

Protected Delegate Void HookProcessedhandler (int code,

Uintptr WPARAM, INTPTR LPARAM)

Public enum hooktypes

{

JapanRecord = 0,

Journalplayback = 1,

// ...

KeyBoardll = 13,

Mousell = 14

}

First, we import the SetUserHookCallback function as a static external method of our abstract base hook class SystemHook using the DllImport attribute. To accomplish this, we have to map some rather foreign data types. First, we have to create a delegate to serve as our function pointer. This is done by defining the HookProcessHandler as above. We need a function which in C has the signature (int, WPARAM, LPARAM). in the Visual Studio .NET C compiler, int is the same as in C #. That is , int is Int32 in both C and C #. This has not always been the case. Some compilers treat C int as Int16. We're sticking with the Visual Studio .NET C compiler for this project, so we will not worry about other definitions due to compiler differences. Finally, we have defined the HookTypes enumeration by explicitly setting the enumeration values ​​to the same ones defining the C equivalents of the hook types. These C definitions are located in the winuser.h header file.

Next, we need to pass WPARAM and LPARAM values ​​around in C #. These are really pointers to C UINT and LONG values ​​respectively. In C # speak, that's pointers to uint and int. In case you're not sure what a WPARAM is, you CAN SIMPLY LOOK IT UP Where It is defined by Right Clicking in The C Code and Choosing "Go to Definition". That takef.h.// from WINDEF.H:

Typedef uint_ptr wparam;

Typedef long_ptr lparam;

Therefore, we chose system.uintptr and system.intptr As Our Variable Types for the wparam and lparam Types..

Now, let's see how the hook base class uses these imported methods to pass a callback function (delegate) to C which allows the C library to directly call into your system hook class instance. First, in the constructor, the SystemHook class creates a delegate to the private method InternalHookCallback which matches the HookProcessedHandler delegate signature Then it passes this delegate and its HookType to the C library to register the callback using the SetUserHookCallback method as discussed above Here it is in code..:

Public SystemHook (Hooktypes Type, INTPTR AppInstance)

{

THIS.Appinstance = AppInstance;

THIS.TYPE = TYPE;

ProcessHandler = New HookProcessedHandler (InternalHookCallback);

SetuserhookCallback (ProcessHandler, this.Type);

}

The implementation of InternalHookCallback is quite simple. InternalHookCallback just passes the call to the abstract method HookCallback while wrapping it in a catch-all try / catch block. This simplifies the implementation in the derived classes and guards the C code from uncaught .NET exceptions. Remember, overce everything is Wired Up, The C Hook Will Be Calling this method Directly. [MethodImpl (MethodImploptions.noin)]

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 to not inline this method. This is not optional. At least, it was required before I added the try / catch. It seems that for some reason, the compiler was attempting to inline this Method Which Caused All Sorts of Trouble with the delegate That Was Wrapping it. The C Layer Would The Call Back and The Method Would Be gone.

NOW, Let's Look Athow A Derived Class With A Specific HookType Receives And Processes Hook Events. Here Is The Virtual Hookcallback Method Implementation for the MouseHook Class:

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 which it fires whenever it receives a hook event. This class transforms the data from WPARAM and LPARAM types to data meaningful for mouse events in .NET before firing its event. That saves the consumers of the class from having to worry about interpreting these data structures. This class uses the imported GetMousePosition function that we have defined in the C DLL to convert these values. See the discussion a few paragraphs below for more details on this.In this method, we check that someone is actually listening to the event. If not, there is no reason to continue processing the event. Then we convert our WPARAM to a MouseEvents enumeration type. We have carefully constructed the MouseEvents enumeration to exactly match the constants they mirror in C by Value. this allows us to simply cast the pointer's value to the enumerated type. Be Careful Thought, this Cast Will Succeed Even if the value of the wparam does not match an ENUMERATED VALUE. The Value of Mevent Will Simply Be Undefined (NOT NULL). See The Method System.enum.Indefined for Details on this.

NEXT, AFTER DETERMINININININININININED, AND THE CONSUER IS NOTIFIFIED of The Type of Mouse Event and the location of the mouse during.

A final note about converting the 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 the values ​​differently I chose to perform this conversion in C rather.. than trying to mimic complex C structures and pointers in C # For example, the previous class used a C function called GetMousePosition Here's that method from the C DLL:.. bool GetMousePosition (wPARAM wparam, lPARAM lparam, int & x, int & y)

{

MousehookStruct * pmousestruct = (mousehookstruct *) lparam;

x = pmousestruct-> pt.x;

y = pmousestruct-> pt.y;

Return True;

}

Rather than attempting to map the MOUSEHOOKSTRUCT structure pointer to C #, we simply pass it back to the C layer temporarily to extract the values ​​we need. Note that because we need to return several values ​​from this call, we passed our integers as reference variables. THIS DIRECTLY MAPS TO INT * IN C #. But we can override this behavior by Selecting The Right Signature To Import this Method.

Private Static Extern Bool InternalGetMousePosition (uintptr wparam,

INTPTR LPARAM, REF INT X, REF INT Y

By Defining The Integer Parameters As Ref Int, We get Our Values ​​Passed by Reference To C . We Could Have Also Used Out Int IF We Wanted.

Restrictions

Some hook types are not suited to this implementation of global hooks. I am currently considering work-arounds that will allow use of the restricted hook types. For now, do not add these types back into the library as they will result in application failures (OFTEN System-Wide Catastrophic Failures).

HookTypes.callWindowProcedure

Hooktypes.callWindowproret

Hooktypes.computerbasedtrainingHooktypes.debug

Hooktypes.ForegroundIdle

Hooktypes.journalrRecord

Hooktypes.journalplayback

Hooktypes.systemMessagefilter

EXTRAS

Library Documentation:. We have included fairly thorough code documentation with the ManagedHooks class library This is converted to standard help XML via Visual Studio .NET when compiling in the "Documentation"

build configuration. Finally, we have used NDoc to convert this to Compiled HTML Help (CHM). This help file is available simply by clicking the Managed Hooks.chm file in the Solution Explorer of the solution or by looking in the downloadable ZIP files associated with this article Enhanced Intellisense:. In case you are not familiar with how Visual Studio .NET uses the compiled XML file (pre-NDoc output) for enhancing intellisense for projects that reference libraries, let me say something about that here If you decide. to use this class library in your applications, you might consider copying a stable build of the library to a location where you will reference it. Then also, copy the XML documentation file (SystemHooks / ManagedHooks / bin / Debug / Kennedy.ManagedHooks.xml ) to the same location. When you add a reference to the library, Visual Studio .NET will automatically read that file and use it to add intellisense documentation. This is very helpful, especially for third party libraries s uch as this one Unit Tests:. I believe all libraries should have unit tests associated with them Since I am a partner and software engineer in a company which makes unit testing software for .NET, this should come as no surprise to anyone Thus.. you will find a unit test project in the solution entitled ManagedHooksTests. to run the unit tests, you will need to download and install HarnessIt from here. This download is a free trial version of our commercial unit testing software. in the unit tests, I PAID SPECIAL ATTENTION TO this Where Invalid Arguments to Methods Could End Up Causeing C

memory exceptions Although this library is fairly simple, the unit tests did help me discover a few bugs in the more subtle situations Unmanaged / Managed Debugging:.. One of the things that is tricky about mixed solutions such as this one (managed and unmanaged code ) is debugging. If you want to be able to step into the C code or set break points in the C code, you must enable unmanaged debugging. This is a project setting in Visual Studio .NET. Note that you can step between the managed And Unmanaged Layers Very Nicely, But Unmanaged Debugging Does Significantly Slow The Load Time and Execution Speed ​​of The Application While in The debugger.a final warning

Let me Paraphrase Dr. Seuss in The Famous CHildren's Book Fox in Sox in providing this final warning.

Take It Slowly. These classes are dangerous.

System hooks are powerful. And, with that power comes responsibility. When something goes wrong with system hooks, they do not just break your application. They can break every application running on your system. It is unlikely that it would actually come to that Extreme. Nonetheless, You NEED To Double And Triple Check Your Code When Using System Hooks.

One technique that I have found useful for developing applications that use system hooks is to install a copy of your favorite development operating system and Visual Studio .NET in Microsoft Virtual PC ^. Then develop your application in the virtual environment. That way, when your hook applications go wrong they only take out the virtual instance of your operating system rather than your real one. I have had to restart my real OS when the virtual one crashed due to a hook error, but it is much less common.

Note That if you have a msdn subscription ^, THEN Virtual PC is Freely Available Through Your Subscription.history

May 12, 2004: (Library Version 1.0.0.2)

Removed hook types from library that always cause errors See the "Restrictions" section above for details Added discussion of developing hook applications in Virtual PC See the "A Final Warning" section above for details March 5, 2004:.... (Library Version 1.0.0.0)

This is the initial release of the article. No change.

About Michael Kennedy

Michael Kennedy is a founding partner and software engineer at United Binary, LLC (http://www.unitedbinary.com [^]). He has been developing software for over 9 years. The last 3 of those years have been solidly focused on . Net Development.in A Previous Life, Michael Was Pursuing a fairly success

转载请注明原文地址:https://www.9cbs.com/read-95291.html

New Post(0)