# VBForums CodeBank > CodeBank - Visual Basic .NET >  Subclass (hook) external programs

## moeur

This is the .NET version of the hooking code I wrote for the VB6 Codebank.
http://www.vbforums.com/showthread.php?t=322261

This code allows you to hook into (subclass) external programs to intercept their windows messages.  This allows you to do things like monitor button presses and keyboard input as well as respond to new menu items you can add to other programs which you don't have the source code for.

There are two parts to the code: a VB class and a C++ dll.  I've attached the class and the compiled dll, the C++ dll source is in the previous thread if you want it.

Here is a description of the class clsHook.vb

*Methods
*GetWindowHandle(Caption, ClassName) - returns a handle to a top-level window
GetChildHandle(Caption, ClassName, TopLevelHandle) - returns a handle to a child window
AddMessage(NewMessage, strMessage) - Adds a windows message to the list of messages we want to monitor
SetHook - Sets the hook into the external program.
RemoveHook - Removes the hook from the external program.*Properties
*isHooked - Returns True if the hook is set.
TargethWnd - Sets or returns the handle of the target window that is to be hooked
*Events
*SentMessage(ByRef m As Message) - Sent messages arrive here
SentRETMessage(ByRef m As Message) - Sent messages after processing arrive here
PostedMessage(ByRef m As Message) - Posted messages arrive here
HookError(Number, LocalMsg, APIMsg) - Raised on certain non-fatal error conditions
UnHook() - Raised when the hook is removed
*Example*

```
Public Class Form1
    'This Program hooks Notepad.exe and monitors WM_CHAR, 
    ' WM_LBUTTONDOWN, WM_LBUTTONUP messages
    Private WithEvents EditHook As New clsHook
    Private Const WM_LBUTTONDOWN = &H201
    Private Const WM_LBUTTONUP = &H202
    Private Const WM_CHAR = &H102

    Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) _
        Handles Button1.Click
        'Set the hook
        Dim hWndMain As Integer
        Dim hwndEdit As Integer
        'Get handle to notepad
        hWndMain = EditHook.GetWindowHandle(vbNullString, "NotePad")
        'Get handle to notepad's Edit window
        hwndEdit = EditHook.GetChildHandle("", "Edit", hWndMain)
        'Make that our target
        EditHook.TargethWnd = hwndEdit

        'Add the message(s) we want to look for
        EditHook.AddMessage(WM_CHAR, "WM_CHAR")
        EditHook.AddMessage(WM_LBUTTONDOWN, "WM_LBUTTONDOWN")
        EditHook.AddMessage(WM_LBUTTONUP, "WM_LBUTTONUP")
        'Set the hook
        EditHook.SetHook()
    End Sub

    Private Sub EditHook_PostedMessage(ByRef m As System.Windows.Forms.Message) _
        Handles EditHook.PostedMessage
        'Here is where Posted messages will arrive
        Dim myPoint As Int32
        Select Case m.Msg
            Case WM_CHAR
                'wParam points to char code
                TextBox1.Text &= EditHook.Messages(m.Msg) & ":  " & Chr(CByte(m.WParam.ToInt32)) _
                    & vbCrLf
            Case WM_LBUTTONUP, WM_LBUTTONDOWN
                'lParam: x-lo word, y-hi word
                myPoint = m.LParam.ToInt32
                TextBox1.Text &= EditHook.Messages(m.Msg) & ":  " & CStr(myPoint And &HFFFF) _
                    & " : " &                   CStr(myPoint \ &H10000) & vbCrLf   
             End Select
    End Sub
End Class
```

Edit: Added source code for C++ dll

----------


## NJKA

The MainHook.Dll appears to have a bug. The Hooks only work if you Install both the WH_GETMESSAGE and the WH_CALLWNDPROC Hooks. If you try and use one hook without the other then no messages appear.

Regards
Neal

----------


## moeur

Using the class, there is only one method to set hooks called SetHook.  This actually sets three types of hooks.

If you are not using the class, but instead directly calling functions in the dll then you should know that the dll and VB communicate via sent messages so at least the WH_CALLWNDPROC hook must be set.

This is not a bug but the way it was designed to function.

----------


## NJKA

Hi Moeur, Yes I'm using the dll directly. As I understand it the C++ dll should still send messages to my window even if I just set the WH_CALLWNDPROC hook rather than all three hooks. However the C++ dll only sends messages to my window with both the WH_CALLWNDPROC and the WH_GETMESSAGE hooks set. When just one hook is set no mesages are sent. Looking at the C++ dll's source, there does not appear to be anything obvious that's stoppng the messages. If I swap the MainHook.dll for the older version that does not use Thead local storage it works fine, so I know it's not my code that's blocking the messages.

Regards
Neal

----------


## moeur

The way this dll is designed to be used is the VB program is supposed to select which messages to monitor by sending the dll UM_ADDMESSAGE notifications.  This cannot be received by the dll unless the WH_CALLWNDPROC hook is set.

----------


## VikingMan

Does this work on windows msgs only? Not packets? If its possible to intercept packets instead of msgs please tell me how..  :Ehh:

----------


## hcadieu

Hi all,

As I see you are comfortable with hooks and VB6 I have a question to you guys. I am trying to hook the  Msgbox which displays in Microsoft Word 2000 or later  when  running the following code from a macro :

dim sig as signature

set sig=activedocument.signatures.add

'here the soft prompts for saving the doc first if the document is not already saved
'then the msgbox is displayed to show the available X.509 certificates available in "My store" on the current computer

I would like to choose programmatically the correct certificate from the list box and valid the button and also if possible I would like to hook the digital signature date which will be used to make the digital signature.

Has any one of you any idea of how to achieve it ?

 :wave:

----------


## inostranka

Hi Moeur/Anyone else,

First of all, thanks so much for this code

I have been struggling with something for weeks now.  I am trying to use this vb.net code to
subclass an external program and extract the tooltip info on a fly.  

I am able to see the actual TTM_UPDATETIPTEXTA messages come back from the dll and am getting the correct m.lparam, m.wparam and so on.

The program i am subclassing is in a different memory space.

I have tried a lot of different things to get to the tooltip text.

I am actually able to correctly send a TTM_GETTOOLCOUNT message and get the correct number of tools back

by sending a SendMessage(EditHook.TargethWnd, TTM_GETTOOLCOUNT, 0, 0) message

but as soon as i try to send either a TTM_GETTEXT,TTM_GETTOOLINFO, TTM_ENUMTOOLS with an index my app as well as my source apps crash

I also tried different variations of sending these messages with allocating memory first in a foreign process by using 
                Dim p As TTTOOLINFOA
                Dim memoryBuffer As IntPtr = System.Runtime.InteropServices.Marshal.AllocHGlobal(System.Runtime.InteropServices.Marshal.SizeOf(p)  )
and then passing memorybuffer along with a TTM_GETText message

I also played with a Marshal.PtrToStructure function

but nothing seems to be working

My TTTOOLINFOA structure is as follows:

    Private Structure TTTOOLINFOA
        Dim cbSize As Integer
        Dim lFlags As Integer
        Dim hwnd As IntPtr
        Dim uId As Integer
        Dim hinst As IntPtr
        Dim rect As Rectangle
        Dim lpszText As IntPtr
    End Structure

And according to SPY my m.lparam should point to a TTTOOLINFOA in a foreign space and lpszText  in that struct should point to a string.
I have several individual tools and I am just trying to get the text and the index of each one

Has anyone accomplished this in VB.NET ?  Am i missing something?

Thanks a million,

Anna

----------


## mityVB2005

Hi, i am writing an app which will be automaiting another app (stock client) and will probably use your dll for capturng messages (thank's for a great job). 
Have you ever considered making communication betwin dll and a caller through tcp protocol.

1. you install tcp listener on specific port
2. pass port and parameters to dll throug a procedur call
3. wait for incoming connecton and start receiving messages
4. connection closed - hook removed, simple as that some king of .net gabage stuff.

with that approach u don't need to have a window to receive messages, you don't have to install WH_CALLWNDPROC hook, and probably it will be easer to handle multi threaded enviroment (can you install multiple instances of mainhook.dll?).

I don't know c++ at all.But if wanted to learn it i can't find dll code any way, is it private property or you can share it? Or may be YOU find it interesting to addjust the code yourself   :Wink:  , i saw you wrote an exampe about tcp server-clent communication... 

Dimitry

----------


## Megalith

Great article, this will help me a lot with a tricky application i have been developing. 

The application i am hooking onto creates 2 events i need to monitor called User +10 and User +11. i have done this successfully but i am unable to retrieve the messages using Wparam, is there another place it could be located?  :Eek Boom:

----------


## moeur

mityVB2005,

I don't see much advantage to using tcp/ip for communications, but if you want to the source code for the dll is included in the zip file for the VB 6.0 version.  See the vb6 codebank.

----------


## moeur

Megalith,

You'll have to give me more information on what you are trying to do for me to help.

----------


## Megalith

hey moeur, thanks for getting back on this.

The application I'm working on is intended to monitor a logbox in an external application (the external application is a program that comes with a motor i own and provides various controls to adjust settings relating to the motor and provides feedback for the user in a logbox), when it updates to copy the updated text and if the keyword Alarm: is contained in the copied text, to create a visual alart in my application.

Ideally i want the application to allow the user to perform other tasks whilst this processing is going on. 

Winspector informed me that the 2 events triggered when the logbox is updated are USER + 110 or USER + 111 (USER + 111 is the one that contains ALERT messages and USER + 110 is other data releted to motor speed and voltages) it also supplies me with the lparam and wparam values that your method produces when i hook to these message streams. ( these values btw are the same whichever User event is triggered and each time the routine is called, i believe they could be a pointer to a location where the actual text is?)

your method has solved one of the 2 problems ive come across namely notification of the logbox update but i still am unable to actually read the contents that are displayed in the logbox.

The logbox btw isnt a conventional textbox or richtextbox as single clicking on the box selects the entire line and selection of individual parts of the line is not possible. the logbox is contained in a frame within a window which inturn is the child of the main application window.

I can obtain the text by selecting a line by clicking it and doing a Ctrl+C but this method takes away focus from another application i may be working on i.e. Word or an IM

hope this is enough information  :Smilie:

----------


## Daemon23

I have been using the VB.net client and the included MainHook.dll binary from clsHook.zip without any problems. However I now need to do a minor change to the DLL code and started with trying to build the MainHook project (included in HookControl.zip from the original forum thread) in Visual studio 2005 without making any source code change. 
When opening the MainHook.dsw file Visual Studio 2005 had to convert it to it's newer project format. The project then builds without any errors. However when I then try to use the resulting MainHook.dll file using the clsHook.vb class the third filter installation call in SetHook(): InstallFilterDLL(WH_CALLWNDPROCRET, m_ThreadID, m_Targethwnd) is causing an exception to occurr: Error Setting DLL Filter. 12 (&HC): The access code is invalid.

Do you have any idea of what could be wrong and what I need to do in order to fix it?

Thank you

----------


## Daemon23

I just found the solution to my problem. The DLL source code in HookControl.zip must be older than the what originally built the MainHook.dll in clsHook.zip since it does not support the WH_CALLWNDPROCRET when sent as first argument to InstallFilterDLL.

----------


## moeur

Thanks Daemon23,

I have uploaded the proper source code to the first post

----------


## Juggz

Hello!

I'm trying to use this in VB .Net 2003, however as soon as I attach clsHook to a project it starts to read out a few minor error's like intptr's trying to be converted to integer, which is easily fixed by setting the intptr to convert to int32.

But what I am confused by are the following lines:



```
Public Messages As New Dictionary(Of Integer, String)

Dim message As System.Collections.Generic.KeyValuePair(Of Integer, String)
```

Could someone help me get this working in .Net 1.1? Thanks much!  :Smilie:

----------


## quacka

I am trying to make a program which looks for the visibility of a button in an external application as fast as possible. I was wondering if i can use this to hook into the application and then receive notification when the state of the button is changed.

----------


## quacka

I managed to get the code to work with:
EditHook.AddMessage(70, "WM_WINDOWPOSCHANGING")
I wanted to use:
 124 WM_STYLECHANGING 
 125 WM_STYLECHANGED 
However when I hook these two messages I get no notifications of events.

I was wondering if there is a way to hook the same message to 5 different window handle on the same thread? I basically want to monitor 5 different button on the same window application

----------


## RobDog888

Hey Moeur, great as usual but I was wondering if its possible to make it self contained and all in the .NET unmanaged code instead of still relying upon the C++ dll?

I know with VB 6 its required as VB 6 cant acess external windows message streams but seeing how .NET is way more advanced with true multi-threading that there may be a way.

----------


## quacka

Someone told me it is possible using PInvoke. Again according to this article there are limitation of the Visual Basic language to that of their C# and C++ counter part which can prevent this from being programmed in 100% VB.NET
http://www.codeproject.com/dotnet/PInvoke.asp

----------


## RobDog888

Yes, I always use dllimport attributes for my APIs but the point about the pointers and unsafe markings can be handled with C#. 

So if C# can replace the C++ dll then it would be concievable that you could have it all in one probject (vb and C# code classes) with no external dll for the subclassing?

----------


## jmcilhinney

Hi moeur.  Thanks for the code.  I've been trying to put it to use but I'm having an issue.  Here's a bit of background on my situation:

I'm creating a dialogue with a Opacity of 0.  It then creates an instance of your class, finds the handle to a dialogue displayed by another application and hooks that dialogue's WM_SHOWWINDOW message.  When my dialogue receives notification of the message it removes the hook and closes.  This essentially makes the other application's dialogue behave like it's a modal dialogue of my app.

The UnHook method appears to succeed but when I test this setup by repeatedly displaying the dialogue and dismissing it I eventually end up getting the following error message:


> CallbackOnCollectedDelegate was detected
> Message: A callback was made on a garbage collected delegate of type 'System.Windows.Forms!System.Windows.Forms.NativeMethods+WndProc::Invoke'. This may cause application crashes, corruption and data loss. When passing delegates to unmanaged code, they must be kept alive by the managed application until it is guaranteed that they will never be called.


This indicates to me that the hooks are not being properly removed and each of my objects' WndProc methods are still referenced in unmanaged code.  When the garbage collector gets around to cleaning up those objects those references become invalid and the error occurs.  Any thoughts on this?

Here's a bit more information too.  The dialogue I'm hooking is a single instance that the application reuses.  I'm considering reusing the same instance of my managed form too, so there's only ever one hook created and it's not removed until the applications are closed.  The drawback with this is that I'm going to want to do this with numerous dialogues and I'd rather not have to identify which ones are reused and which aren't.  It's not the end of the world if I have to do that but I'd rather figure this out if possible.

----------


## IceM

Hello,

In my search on the internet for hooking in .net I found this thread. Im a c# .net user, but that should not matter much. I converted the vb.net classes to c# .net and it works like it should.
However, when I try to compile the c++ dll file I get link errors. When I open the project, I need to convert it to the new format (I use visual c++ express 2005). Then, when I (try to) compile the dll, the following error accures:



```
1>MainHook.obj : error LNK2019: unresolved external symbol __imp__SetWindowsHookExA@16 referenced in function "int __stdcall InstallFilterDLL(unsigned long,unsigned long,struct HWND__ *)" (?InstallFilterDLL@@YGHKKPAUHWND__@@@Z)
1>MainHook.obj : error LNK2019: unresolved external symbol __imp__SendMessageA@16 referenced in function "int __stdcall UnInstallFilterDLL(unsigned long,struct HWND__ *,struct HWND__ *)" (?UnInstallFilterDLL@@YGHKPAUHWND__@@0@Z)
1>MainHook.obj : error LNK2019: unresolved external symbol __imp__UnhookWindowsHookEx@4 referenced in function "int __stdcall UnInstallFilterDLL(unsigned long,struct HWND__ *,struct HWND__ *)" (?UnInstallFilterDLL@@YGHKPAUHWND__@@0@Z)
1>MainHook.obj : error LNK2019: unresolved external symbol __imp__CallNextHookEx@16 referenced in function "long __stdcall CallWndProc(int,unsigned int,long)" (?CallWndProc@@YGJHIJ@Z)
1>MainHook.dll : fatal error LNK1120: 4 unresolved externals
```

I want to use the hook libary to catch WM_PAINT messages of external forms. The application im trying to monitor has a random amount of open windows. When I hook the dll into 1 or 2 windows, it works fine. However, if you hook into a 3rd window, the external application just closes/crashes (no error messages or whatever). Can this be caused by exceeding memory that is allocated for/by the external application? And if so, can this be fixed somehow? (im not in control of the 3rd party application).

Thanks in advance.

----------


## jmcilhinney

> Hello,
> 
> In my search on the internet for hooking in .net I found this thread. Im a c# .net user, but that should not matter much. I converted the vb.net classes to c# .net and it works like it should.
> However, when I try to compile the c++ dll file I get link errors. When I open the project, I need to convert it to the new format (I use visual c++ express 2005). Then, when I (try to) compile the dll, the following error accures:
> 
> 
> 
> ```
> 1>MainHook.obj : error LNK2019: unresolved external symbol __imp__SetWindowsHookExA@16 referenced in function "int __stdcall InstallFilterDLL(unsigned long,unsigned long,struct HWND__ *)" (?InstallFilterDLL@@YGHKKPAUHWND__@@@Z)
> ...


Don't bother compiling the C++ library if you don't intend to change it.  Just use the compiled version moeur has provided.

----------


## IceM

Then please look at my 2nd question, caus I might need to change something since the 3rd party application keeps crashing.

----------


## MikkyThomeon

Hi 

I need to validate the external programs input and then do some validation on this. If the validation proves false, then my program (using your code!) must cancel the keystroke sent to the external program. In the wndproc procedure I have tried to set all the parameters to zero in the setshareddata call but the charater is not being cancelled. 

Any ideas on how to do this ?? 

Thanks in advance!
Mike

----------


## jmcilhinney

> Hi 
> 
> I need to validate the external programs input and then do some validation on this. If the validation proves false, then my program (using your code!) must cancel the keystroke sent to the external program. In the wndproc procedure I have tried to set all the parameters to zero in the setshareddata call but the charater is not being cancelled. 
> 
> Any ideas on how to do this ?? 
> 
> Thanks in advance!
> Mike


I would think that if you don't want the input processed you simply don't base on the message.

----------


## MikkyThomeon

Thanks for your reply jm!

YAY! I followed the comments and set the values of the message parameter passed into the wndproc procedure to 0 eg:


```
m.Msg = 0
m.WParam = 0
m.LParam = 0
```

This worked like a treat  :big yellow:   :big yellow:   :big yellow:

----------


## gollkla

Hello!

I have some problems as well with unsing the code. As Mikky I want to use the dll to capture keystrokes in an external programm. Since i also need to capure keys like "CTRL" or "ALT" I use the message WM_KEYDOWN combined with the "PostedMessage" Event. 

My problem is: Every keystroke is received twice in my hook application. I'am sure that my code is correct, it must be something in the dll. 

Anybody who can help me? I've attached a simple working example.

Regards,
Klaus

TestMainHookDll.zip

----------


## PT Exorcist

Hello. Hooking this way allows us to receive WM_COMMAND and EN_UPDATE messages? I do not seem to be able to do it.

----------


## McBluffin

I can't seem to get this to work on Vista 64.  I posted details in a new thread:
http://www.vbforums.com/showthread.php?t=547825

----------


## K-C-S

Hi, I know it's been a while since this post, but I wondered if you could post your C# hook code. I converted to C# and it seems to run without any errors, but I don't get the hook events firing. Can you help. Thanks.

----------


## K-C-S

[QUOTE=IceM;3020715]Hello,

In my search on the internet for hooking in .net I found this thread. Im a c# .net user, but that should not matter much. I converted the vb.net classes to c# .net and it works like it should.....


Hi IceM, I know it's been a while since this post, but I wondered if you could post your C# hook code. I converted to C# and it seems to run without any errors, but I don't get the hook events firing. Can you help? Thanks.

----------


## K-C-S

[QUOTE=moeur;2383351]This is the .NET version of the hooking code I wrote for the VB6 Codebank.
http://www.vbforums.com/showthread.php?t=322261

This code allows you to hook into (subclass) external programs to intercept their windows messages.  

Hi mouer, I know it's been a while since this post. I converted clsHook to C# and it seems to run without any errors, but I don't get the hook events firing. Do you have a C# version? Thanks.

----------


## K-C-S

Hi. the Hook works on a Win32-only machine, but not on a Win64 machine when targeted to x86. Can you advise? Thanks.

----------

