Link to home
Start Free TrialLog in
Avatar of Wyclef_Joan
Wyclef_Joan

asked on

Windows Hook - PLZ HELP

Here is my souce code in c# for a Windows Hook (WH_KEYBOARD).
The event fires fine every time a key is pressed but how do i find out which key was pressed?

                                [DllImport("user32.dll")]
            public static extern IntPtr SetWindowsHookEx(int HookType,HookProc lpfn,IntPtr hMod,int dwThreadId);

            [DllImport("user32.dll")]
            public static extern int UnhookWindowsHookEx(IntPtr hHook);

            [DllImport("user32.dll")]
            public static extern int CallNextHookEx(IntPtr hHook,int Code,IntPtr wParam,IntPtr lParam);
            
            public const int HookType=2;
            private System.Windows.Forms.Button button1;
            private System.Windows.Forms.Button button2;
            public static IntPtr hHook=IntPtr.Zero;

            public delegate int HookProc(int Code, IntPtr wParam, IntPtr lParam);

            public int Keypress(int Code, IntPtr wParam, IntPtr lParam)
            {
                  if(Code>0)MessageBox.Show("");//here is where i should be able to get the key but how do i do it?
                  return CallNextHookEx(hHook,Code,wParam,lParam);
            }

            public void Install()
            {
                  hHook=SetWindowsHookEx(HookType,new HookProc(this.Keypress),IntPtr.Zero,(int)AppDomain.GetCurrentThreadId());
            }

            public void Uninstall()
            {
                  UnhookWindowsHookEx(hHook);
            }
Avatar of cookre
cookre
Flag of United States of America image

The hook procedure gets the wParam and lParam that are passed to the intended message recipient.

Here's what gets passed to your KeyboardProc:
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/WinUI/WindowsUserInterface/Windowing/Hooks/HookReference/HookFunctions/KeyboardProc.asp


Here's some code that shows several hooks, WH_KEYBOARD among them, and the extraction of key info.
http://www.cam.hi-ho.ne.jp/oishi/source/xkeymacsdll_cpp.html
Avatar of Wyclef_Joan
Wyclef_Joan

ASKER

ok so the hHook is supposed to contain the ascii value... however when i run my app the hHook value is the same no matter which key is pressed and its a huge number like 123456

i read in the first article that if the Code varible is less the zero, the function returns the value returned by the next hook in line. it appears that this is my problem because i am not assigning anything to Code

what should i assign to it?
Actually, I didn't think a call back from the Win32 API back into managed code would work.  I've got to fiddle with it for a while.
it works ok except that i cant get the key back... my ultimate goal is still to put it into a dll and make a global hook but its kind of pointless trying to do that if i cant code a local hook!

cool then plz tell us if u find a way to get the key back

thankz... suma
Ah ha, so you can write normally afterall.
In any case, be aware that EE frowns heavily on dupe accounts, but my lips are sealed.

I'm in the midst of see what really happens when the call back is to managed code...
Your persistence has paid off and convinced this old fart to not hold to firmly to what he believes to be so.

It would appear as if there is a way to get call backs to managed code.  I'm burned out right now, but will continue tomorrow night.

cheers...
hehe i'm sure they dont appreciate it but im prepared to take the risk!

well i've read quite a few articles about using local hooks in c# so it should be able to be done... well my code works so it can be done, its just a question of working out how to get the key back.

does this mean that it might be possible to get a global hook working by creating the dll file in c#

if the only reason that the code needs to be in a dll file is so that the os can insert it into more than 1 running process, then i cant see why this wouldn't work... but im probably missing something obvious as usual...

anywho im not going to worry about that until i get the local hook working

laters...
wassup bro... check out this article. it claims that its possible to use a global hook on a standalone exe file! the source code is written in c++ but so i cant understand it very well.

http://rattlesnake.at.box.sk/newsread.php?newsid=193
do yo think that this is the reason why my above code does not work?

Delegates are managed objects in .NET. This means that they are garbage collected. A problem occurs when we pass a delegate to the unmanaged code of the user32.dll API. Apparently, the garbage collector doesn't know that the delegate object is in use and after a short interval—roughly 47 seconds in experiments—the delegate is garbage collected. Consequently, when the API method attempts to call the method represented by the delegate back, a null reference exception occurs. To prevent the delegate from getting GC'd, we need to tag a delegate variable with the System.Runtime.InteropServices.MarshalAsAttribute, passing the enumerated value UnmanagedType.FunctionPtr. This tags the delegate argument, preventing it from being GC'd in an untimely fashion.

[MarshalAs(UnmanagedType.FunctionPtr)]
//declare the delegate

plz tell me what u think coz im having trouble declaring the delegate seperatly (it wont let me use the 'this' keyword for some reason) and i dont want to waste time fixing this problem if its not going to pay off
i got that thing to work... it didnt seem to make much difference but i left it in anywayz the modified code is as follows:

            [DllImport("user32.dll")]
            public static extern IntPtr SetWindowsHookEx(int HookType,HookProc lpfn,IntPtr hMod,int dwThreadId);

            [DllImport("user32.dll")]
            public static extern bool UnhookWindowsHookEx(IntPtr hHook);

            [DllImport("user32.dll")]
            public static extern int CallNextHookEx(IntPtr hHook,int Code,IntPtr wParam,IntPtr lParam);
            
            public const int HookType=2;
            private System.Windows.Forms.Button button1;
            private System.Windows.Forms.Button button2;
            public static IntPtr hHook=IntPtr.Zero;
            
            public delegate int HookProc(int Code, IntPtr wParam, IntPtr lParam);

            [MarshalAs(UnmanagedType.FunctionPtr)]
            HookProc address=new HookProc(Form1.Keypress);

            public static int Keypress(int Code, IntPtr wParam, IntPtr lParam)
            {
                  if(Code>0)MessageBox.Show(wParam.ToString());//get the key here somehow
                  return CallNextHookEx(hHook,Code,wParam,lParam);
            }

            public void Install()
            {
                  hHook=SetWindowsHookEx(HookType,address,IntPtr.Zero,(int)AppDomain.GetCurrentThreadId());
            }

            public void Uninstall()
            {
                  bool ok=UnhookWindowsHookEx(hHook);
            }
Well done.
thanks. the lpfn parameter had me stumped for ages... until i realized to declare it as a HookProc, not an int or IntPtr

so what do u think of that article?

also have u found out a way to get the key back?
Didn't see that article, this is the one I found (written by an EE'er):
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncscol/html/csharp09192002.asp

I had just started massaging the code when I got the notif you had it working.


<<also have u found out a way to get the key back?>>
Recall that I thought the DLL was needed because I didn't think the call back could be made into the C# code.  Since you're getting the keystroke info in the C# code, there's no need for a DLL.
well that article (http://rattlesnake.at.box.sk/newsread.php?newsid=193 ) claims that its possible to give the handle of an exe file not a dll file and it wont know the difference.

but i dont know how to get the "handle" of my application (im not totally sure what it is)



my code does this: every time a key is pressed (local) it fires a messagebox

i use this messagebox so find out what wParam is. it changes for each key but the ascii value is not correct

i.e. if i hit 'a' on the keyboard the wParam is 65, in ascii 'a' is 97. so i must be doing something wrong...

im not going to start on the global hook until i get the local 1 working so can u help me on this as i really dont know what is wrong.

ima go read your article now and see if i can find out what is wrong

cheers

If you post ALL of your source, I'll fiddle with it, too.

Although we're not supposed to communicate outside this forum on questions posted here, I think it would be acceptable if you were to mail me a zip of your project directory.  That way I'd be sure to have the same project settings as you and we'd be working with the same base.
what your email address?
or if u prefer not to post your email for every1 to see then send me a mail at: suma_ds@hotmail.com and ill reply wit the zipped folder

laters... suma
i got it to work!!!!!!!! for me this is WEEKS of hard work finally paid off!! its so cool.

my next step it to make it global... u say i dont need to put it in a dll so i assume im supposed to get the handle of the app. ppl use the function GetModuleHandle() but i do not know the parameters (and whenever i try it it does not recognise it)?
I'm sorry, after you asked, I KNOW I posted it, but I see I either didn't, or the post went astray.

cookre (0) yahoo periodot komm
that dosnt matter anymore.

heres what i plan to do:

make a dll (the way i showed u) which has functions to set up the hook, remove the hook, and gets the callback

find out a way to get the handle of it and put that in hMod.

set dwThreadId to 0

then this should be a working global hook, am i wrong?

my problems:

>>activate a method in the exe file every time the dll recives a callback (i.e. get the key info)
>>get the handle of the dll

i cant belive how close i am!!
When you call SetWindowsHookEx, you passed it the address of a chunk of code in your program to call whenever the event is fired.

When you call your DLL entry point, let's call it "InitiateKbdMonitor(...), one of the parameters will be that very same address you're now passing to SetWindowsHookEx().  

"But,"  you ask (reasonably, I might add), "when I call SetWindo... in my DLL, how do I pass along the address that was passed to me?"

I see three possibilities:

1) drop into ASM and build the call to SetWin...() dynamically
2) Something like *FuncPtr might work (i.e., indirect operator)
3) Have a function in the DLL that calls the address you passed to it.  Pass SetWindo...() the address of the function in the DLL (which will then call the address you passed the DLL.

I'd prefer 2). but would have to fiddle a bit to come up with the precise syntax.
would it work if i set hMod as the handle to my app (the exe file) and set dwThreadId to 0?
i think:

1) is way to advanced for me

2) seems to be the best option

3) good but complicated

this problem would be avoided if i could do as without the dll as mentioned above

if however a dll is necessary (as microsoft says) then i think my best bet would be to create a method/function in the dll which calls a method/function in the exe

do u know how to do this?
Hmm, just saw this:
http://support.microsoft.com/?kbid=318804

At the bottom:

Global Hook Is Not Supported in .NET Framework

You cannot implement global hooks in Microsoft .NET Framework. To install a global hook, a hook must have a native dynamic-link library (DLL) export to inject itself in another process that requires a valid, consistent function to call into. This requires a DLL export, which .NET Framework does not support. Managed code has no concept of a consistent value for a function pointer because these function pointers are proxies that are built dynamically.

I was playing with 3), and this works:

int  *FncPtr;


int sbr2()
{
printf("HI!\n");
return 2;
}


int sbr1(int x())
{
x();
return 1;
}


int main(int argc, char* argv[])
{
FncPtr=(int *)&sbr2;
sbr1((int (__cdecl *)(void))FncPtr);
return 0;
}

In otherwords, the DLL would catch the event then make a call into the EXE.
hmmm ill try it out but its sorta hard to follow... could u comment it so i can see whats going on?

<Global Hook Is Not Supported in .NET Framework>

yea this is what i expected

cheers


what type of project is this code for?
That was just a simple C program to test out address passing as mentioned in 3).

What 3) looks like is:

EXE calls DLL which calls SetWindo... (et al).

EXE has routine Wigit that get's control from DLL, so the address of Wigit is passed to the DLL using the same c# syntax as you used when passing the address of the hook routine to SetWindo...

The DLL will lok something like:
(I'm just going to use int for all the function types.  You'll have to change them to whatever is appropriate)



// This is where we save the address in the EXE to call
int * FncPtr;

// This is where we call the EXE
int DoTheCallBackToTheEXE(int x())
{
x();
}

// This gets the call back from the hook
int GetEventFromSWHE()
{
DoTheCallBackToTheEXE((int (__cdecl *)(void))FncPtr);
}

// This is the entry point to the DLL called by the EXE
int DLLEntryPoint(int WhoToCall())
{
FncPtr=(int *)&WhoToCall; // Save EXE call back address
SetWindo...(..,GetEventFromSWHE,..);
}
lolz... let me make sure it got it right:

>>when the exe wants to set a hook it activates a function in the dll, giving one of its own methods as a parmeter

>>the activated function (in the dll) sets the hook so that the callback will activate one of its own methods

>>when the method in the dll is activated, it calls a method in the exe passing the key info

>>the dll can NOT be created using  c#, but CAN be created using certain types of c++ projects

once u confirm that the above is correct i will try to:

1) make a non-managed dll
2) call its entry point from an exe
3) make a function (in the dll) that passes a value to the exe
That's it.

The only things I worry about are:

1) That comment from MS: "Managed code has no concept of a consistent value for a function pointer because these function pointers are proxies that are built dynamically."

2) Timing.  If the box is heavily loaded and a lot of key events occur in rapid succession, either key handler (DLL and/or EXE may get called before it's finished processing the previous event.  There are two ways to deal with this:

a) when the DLL gets an event, disable the hook.  When event processing is complete, re-enable the hook.

b) when either the DLL or EXE gets an event, check an ImBusy flag.  If it's set, ignore the event (yes, thereby missing the event):

in EXE:
CallDLL();
EXEBusy=false;

top of handler in EXE:
if (EXEBusy) return;
EXEBusy=true;
...
EXEBusy=false;
return;

in DLL:
SetWindowsHookEx();
DLLBusy=false;

top of handler in DLL:
if (DLLBusy) return;
DLLBusy=true;
...
DLLBusy=false;
return;




so it should be cool if i use a win32 project (c++) to make the dll

unfortunatly i dont know c++ so ima have to ask my dad for a book about it and it will take me ages until i know enuf to make the dll

or could u post/mail me the source code to make the dll and then it would be pretty easy for me to figure out what is going on without me having to learn another language...i know this is a really big ask but im pretty desperate here. let us know either way...

thankz...
suma

And here I thought it was supposed to be us old folks who are forgetful.

The second link above is code for such a DLL:
http://www.cam.hi-ho.ne.jp/oishi/source/xkeymacsdll_cpp.html 
lolz lolz i forgot about that :)

where exactly do i put the code? heres what im doing:

new c++ win32 project
application settings
set application type to DLL
finish

now i got:

Souce Files(Folder)
        stdafx.cpp
        <projectname>.cpp

Header Files(Folder)
        stdafx.h

Resouce Files(Folder)
        readme.txt

exactly where do i add the code?

That's the CPP file
<project name> or stdafx ?
Projectname
k i got:

// avvc.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"
BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved)
{
//1 do i put the code here?
    return TRUE;
}

//2 or here?
1 is where you put the code that's called when the dll is loaded, i.e., what the EXE calls.

2 is where other routines would go, e.g., the dll's call back routine from SetWind...
k thanks bro... i know enuf to have a crack at it now

ill post here if i have any problems

later
ive havnt done a lot of work on this stuff (coz i went to scary movie 3 and rented friday after next) but i have a problem already:

heres my code:


#include "stdafx.h"
BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                               )
{
    DLLMIN();
    return TRUE;
}

void DLLMIN()
{
}
i get these errors:

error C2065: 'DLLMIN' : undeclared identifier
error C2365: 'DLLMIN' : redefinition; previous definition was a 'formerly unknown identifier'
There was no declaration of DLLMIN.

If a function is referenced before it's defined, youi need a declaration.  

Some people put the code for internal routines above the point they're first referenced, but with lots of internal routines, that can get a bit messy - not to mention a little more difficult to follow.

The common way to deal with this it to put function declarations after globals but before the first function definition.

So, in this case, add:

void DLLMIN();

above BOOL APIENTRY...

That will get you a little further.
ok that works... thanks

ive actually changed my mind and think that this will be very easy to make... i just need to work out how to transfer back values and callback to the exe.

now im trying to return a value into the exe:

heres my function that returns a value

int DLLMIN()
{
          return 1;
}

i can set it up so that when the entry point in the dll is called, DLLMIN() will be called... but how do i get the value of 1 back to the exe?

(also how exactly do i call the entry point from c#???)

cheers!

suma
ignore my question of how to get a value back to the exe... i realized that the important thing is how to call the entry point from c#

===suma===
i tried:

putting the dll in the system32 folder
[DllImport("cbf.dll")//everything works so far
bool e=DllMain();//error: no entery point in cbf.dll called DllMain()

if it aint this then i dunno what it is
oi this question is getting so hard to find now... i created a new question by the author curryfast in the programming section called EXE <--> DLL, visual studio .net

post there aiite... ill accept 1 of your comments once u get this message

laters
incase u were wondering... curryfast IS me
Let's give mlmcc a chance - he knows his stuff.

Also, you need to stop using duplicate IDs. It's contrary to EE policy and the administration has started a campaign to find them.  No small number of folks have been banned for their continued use of duplicate accounts.
u get 125 free points... and even if they do find out, they caint do anything coz my isp is telstra and they can hardly block every1 from telstra

aiite cool cool well give that mlmcc dude a chance... we can still use this post to talk about whatever he says so i wont accept 1 of yo answers yet...

laters
yo man it dont look like hes going to reply... must have forgot where its at

does the entry point have to be declared as public?? (coz i cant complie it when i declare anything as public)
I think it's time to post ALL of the code so we can see what's going on.
i compile this into a non-managed win32 dll (no problems here):

// SUMA.cpp : Defines the entry point for the DLL application.
//

#include "stdafx.h"

void MethodTest()
{
}

BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved)
                              
{
    MethodTest();
    return TRUE;
}

i then move the dll to c:\windows\system32\

and then call it from a c# windows application:


using System.Runtime.InteropServices;

[DllImport("SUMA.dll")]
public static extern bool DllMain();

Form1_Load
{
       DllMain();
}


and it says:

Additional information: Unable to find an entry point named DllMain in DLL SUMA.dll.

heres a thought... maybe i should define the c# function with the same parameters as in the dll:  

HANDLE hModule, //no idea what this is
DWORD  ul_reason_for_call, //a DWORD is the same an int but i dont know what value to assign to it
LPVOID lpReserved //no idea

im getting confused...
 
>>im getting confused...
Aren't we all - that's why we nee all the code - the c#,too.
i already gave u the c# code

using System.Runtime.InteropServices;
public static extern bool DllMain();

Form1_Load
{
  DllMain();
}
here is the ENTIRE dll with no changes made so there cant be any problems:

#include "stdafx.h"

BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved)
                         
{
       return TRUE;
}

can u tell me how to call that from c#?
Did somebody forget the DllIMport?


[DllImport("dll file name")]
public static extern bool DllMain();
i was using DllImport, but visual studio gives me an annoying error:

Error: Cannot Find Entry Point in DllFileName called DllMain()

the only reason i can think of why i might be getting this error is that DllMain() does not take zero parameters

BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved)


what do u think?
well that mlmcc dude has given up... lets move the the other post
I'm going to be busy for a few days (new project at work).  I won't be able to look at this until Sunday.
i sucessfully called functions from a managed c# dll but i cant do it with non-managed c++ dlls yet for some reason

seeingz as the local hook worked in managed code, i really cant see why the global hook wouldnt work in a managed dll

maybe that crap that microsoft said was just to deter ppl from making spy applications

do u think it would be worth trying?

If you were able to pass the address of a c# routine to a standard c dll, and got the standard c dll to call that passed address, I would say it's worth a try.

Sorry I haven't been able to do any test coding on this - I started a new project at work last week that has to be deployed to in 3 weeks.  I doubt I'll make much more than quickie comments on EE for a while.
SCORE!!!!!!!!

i can now make a non-managed c++ dll and call functions in it from c#!

now the only things i have to wory about it making the callback function (that passes data from the dll to the exe) and finding out what value should be put in hMod to make a global hook

as with all the other problems ive had so far, ill keep trying until i either fix them or find out that it cant be done

good luk wit your project :)
hey bro... i just had a idea

why am i going to all this trouble to pass the data back to the exe, why dont i just do what ever i want wit in the dll??

im going to experiment using the win32 api messagebox function... if it works ill just find a way to save the characters to a file

what do u say... is this the best idea ive had in my life or what :)
That would certainly get around several problems.  I just thought you wanted a general purpose DLL that could be used by in other projects.  If this is all for a single projerct, that would simplify matters greatly.
cookre... ive solved all problems except this one:

HOOK hhk = SetWindowsHookEx(int idHook, HookProc lfpn, HINSTANCE hMod, DWORD dwThreadId)

HHOOK hhk = SetWindowsHookEx(2,lfpn,?,0);

to make a global hook, what value should be given to hMod and where/how do i get this value

i CAN NOT belive how close i am to making this hook!!!!

thanks... suma
ASKER CERTIFIED SOLUTION
Avatar of cookre
cookre
Flag of United States of America image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
well i guess its the end of the line for this post

i got a global hook working (finally) and all there really is left to do is polish the program adding extra features (such as saving to a text file ect ect)

thanks very much 4 all tha help with everything

cya... suma