I have been programming in C# for a while now and now am forced to make a C++ dll to set up a global keyboard hook for a clipboard program I am making.
My code never enters DllMain!! Is that ok?
Code:
#include <windows.h>
#include "Global Hook.h"
#include <stdio.h>
#pragma data_seg(".shared")
#pragma data_seg()
//extern "C" tells compiler not to change the name of the
//function so that it can be called directly from another program.
extern "C" _declspec(dllexport) HHOOK SetHook();
extern "C" _declspec(dllexport) void Unhook();
_declspec(dllexport) int _WEP (int bSystemExit);
HHOOK hKeyhook=0;
HINSTANCE hDll = LoadLibrary((LPCTSTR)"WinHook.dll");
//---------------------------
BOOL APIENTRY DllMain (
HINSTANCE hInst /* Library instance handle. */ ,
DWORD reason /* Reason this function is being called. */ ,
LPVOID reserved /* Not used. */ )
{
if ( reason == DLL_PROCESS_ATTACH)
hDll = hInst;
MessageBox(NULL, TEXT("ENTERED DLLMAIN"), TEXT(""), MB_OK);
//return (int)(DisableThreadLibraryCalls (hDll));
/* Returns TRUE on success, FALSE on failure */
return TRUE;
}
//---------------------------
LRESULT CALLBACK KeyBoardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
// This is where you want to decide what to do when receiving keyboard parameters.
MessageBox(NULL, TEXT("In CallBack"), TEXT("KeyPress"), MB_OK);
return CallNextHookEx(hKeyhook, nCode, wParam, lParam); //CallNextHookEx returns long value.
}
//---------------------------
extern "C" _declspec(dllexport) HHOOK SetHook()
{
if(hKeyhook == NULL)
{
hKeyhook = SetWindowsHookEx(WH_KEYBOARD, KeyBoardProc, hDll, 0);
if (hKeyhook == NULL)
MessageBox(NULL, TEXT("Failed"), TEXT("Failed"), MB_OK);
else
MessageBox(NULL, TEXT("Hooked!!!"), TEXT("Success!!!"), MB_OK);
}
return hKeyhook;
}
It works but how do I notify my C# app that the callback was called and get which key was pressed?
Last edited by aewarnick; Jun 10th, 2003 at 11:06 PM.
You get the key by evaluating wParam and lParam (see docs for how).
Actually it's not okay that DllMain isn't called - is it a managed DLL? Maybe that's an effect of it.
For notification: that's quite tricky because of the way managed and unmanaged code work together (or rather, against ).
One option would be to compile the DLL as managed code. The hook procedure would be unmanaged (using some #pragma directive). The SetHook function would be a static method of a class, so that you can access it directly from C#. It would take some notification delegate as parameter and store it somewhere - the tricky thing here is breaching the process border.
Probably the best thing would be to store the process handle of the original process of the DLL in shared memory. Then use this handle to use CreateRemoteThread to create a thread in the original process from whereever the hook procedure was called. This thread then invokes the delegate.
Something like this:
Code:
// The DLL:
// shared memory:
HANDLE hProcess;
// normal memory
__delegate void KeyNotify(int keyCode);
KeyNotify *pNotify;
// Declaration
unsigned long ManagedDo(int key);
#pragma unmanaged
DWORD ThreadProc(void *d);
// hook proc
LRESULT CALLBACK KeyBoardProc(int nCode, WPARAM wParam, LPARAM lParam)
{
int keyCode = DECODE_KEYCODE_SOMEHOW(wParam, lParam);
CreateRemoteThread(hProcess, ThreadProc, (void*)keyCode);
return CallNextHookEx(nCode, wParam, lParam);
}
// ThreadProc
DWORD ThreadProc(void *d)
{
return ManagedDo((int)d);
}
// ManagedDo
#pragma managed
unsigned long ManagedDo(int key)
{
pNotify->Invoke(key); // or something like that
}
// SetHook
namespace You
{
public __sealed __gc class GlobalHook
{
public static System::IntPtr SetHook(KeyNotify *pNot) {
pNotify = pNot;
hProcess = GetProcessHandle();
// Set the hook.
}
};
};
No guarantees at all that it might work, it's just an idea and I can't test it here.
All the buzzt CornedBee
"Writing specifications is like writing a novel. Writing code is like writing poetry."
- Anonymous, published by Raymond Chen
Don't PM me with your problems, I scan most of the forums daily. If you do PM me, I will not answer your question.
Actually it is completely unmanaged, written in pure C++ with Dev. I don't think I can write managed peices of code and unmanaged with C# anyways. With C++.net yes but I am not using that.
Shouldn't dllmain be called first thing? I am going to need a different idea I think, in getting the callback notifications to the C# app from the unmanaged dll with the key codes (wParam lParam).
That might be tricky, I have no idea how to do it.
There was something with conversion between function pointers and delegates in managed/unmanaged bridges, maybe you'll find something in the P/Invoke docs.
All the buzzt CornedBee
"Writing specifications is like writing a novel. Writing code is like writing poetry."
- Anonymous, published by Raymond Chen
Don't PM me with your problems, I scan most of the forums daily. If you do PM me, I will not answer your question.
The site I got that code from said that you would have to use a message hook to let the .net program know about a callback and the key that was pressed. But maybe that was not for .net but for unmanaged programs.
For a WH_KEYBOARD hook the wParam is the VKey of the key that generated the keyboard event and lParam is composed of a number of bit masks indicating the state of the keyboard when the key event occurs.
vis.
Bits 0-15 is an integer being the key repetition count (if the key was held down, for instance)
Bits 16-23 is the keyboard scancode of the key - this is hardware dependent.
Bit 30 is set if the key was already down when the event was triggered (i.e a key repeat or key up event)
Bit 31 is set if the key is being pressed (key down)
You can use the following masks on lParam:
VB Code:
Public Enum KeyboardHookFlags
KF_ALTDOWN = &H2000
KF_DLGMODE = &H800
KF_EXTENDED = &H100
KF_MENUMODE = &H1000
KF_REPEAT = &H4000
KF_UP = &H8000
End Enum
To cancel a keyboard event, return 1 from the hook function.
Are you sure that is not what I want CornedBee? I really had my hopes up...
Merrion, I saw that in the MS docs but it does not solve my problem. I can get the keys but I cannot send them or notify my C# program about the keys that are pressed.
Here is what I have been working on:
// Hooks up the Message hook
hMsghook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, hDll, 0);
Code:
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if(nCode == HC_ACTION)
{
if(lParam.Message == WM_KEYDOWN)
{
MessageBox(NULL, TEXT("WM_KEYDOWN"), TEXT("KeyPress"), MB_OK);
// tagMSG(Ptr(lParam)^).hwnd is the handle of the window
// change 'A' to 'B' system wide
if(wParam == 65)
{
wParam = 66;
}
}
}
return CallNextHookEx(hMsghook, nCode, wParam, lParam);
}
I think if I can send the message about the keys that are pressed to a handle of my app it will work.
If that is so, could you help me get that code working. For one thing lParam.Message does not compile.
Last edited by aewarnick; Jun 11th, 2003 at 05:05 PM.
If you only want to be notified when a given key is pressed, I have just posted a system wide hotkey control (VB.Net - but you should be able to C# it pretty easily) in the code library....
I will try out the hot key. But I was really hoping I could learn how to make the code work above.
I had my hopes set on the code above and was all excited when I found it. I think it might work. I just need some help getting the code to work and sending a message to my C# app from the message Callback in the unmanaged dll.
Would you help me get that working if you think it might work?
I could not figure out how to get a true statement from:
if(nCode == HC_ACTION) so I just left it out??
Also, for some reason, the key I am really looking to grab is the print screen key (44) but it does not enter the block like 65 does and other keys I have had work??
Those two questions and this one I think are all I need to get this working.
How do I send a message to my app with a key? DispatchMessage();?
Last edited by aewarnick; Jun 12th, 2003 at 08:57 AM.
When you install a WH_GETMESSAGE hook you are telling the operating system "please run my code whenever an application takes a message of its message queue before giving it that message".
I'm guessing that when the PrintScreen key is pressed then this is handled by the operating system first and not passed on to any application? After all, no matter what application is the foreground app. PrintScreen always copies a snapshot of the entire desktop.
There are two ways around this that I can see. First you can set PrintScreen as a system wide hotkey (as earlier post) but in this case I don't think you can change the key press.
Al;ternatively if you are using Windows NT, 2000, XP then you can install a WH_KEYBOARD_LL hook. This gets called when a keypress is translated by the keyboard driver and therefore you can intercept and change PrintScreen.
Since wParam will only ever be VK_SNAPSHOT you don't actually need to send wParam - just notify your C# app that it has been spotted.
The way I would do this is to have the C# app create a wait handle and pass that to the dll. The dll will need to store this handle in shared memory (i.e. all instances of the dll have access to the same variable) and when the keypress occurs simply set the waithandle to the signalled state.
Then in your c# program you have a thread that simply waits on that handle beinng set to signalled which resets it to unsignalled and raises the "PrintScreen key pressed" event.
I already have my program all set up to recieve a Message object from the global hook dll. If I used what you are suggesting then I would have to do more coding in C++ if I wanted to watch for another key and then recreate the dll.
But if I just send all the windows messages to my C# program, the dll would be complete and I would not have to mess with it anymore.
Isn't there a way in c++ to send a windows message to a certain handle?
With sufficient patience and time anything is possible - but this is quite difficult.
See - when you install a system wide hook the dll that your hook proc is loaded into the address space of each running application. When the event you are hooking occurs then the control is passed to the instance of the dll that is loaded in the application that would recieve that message - which is not neccesarily the instance loaded in your C# application. This means that to communicate with the C# program you have to do some form of inter process communication.
I'm having trouble getting this into a dll - but my C++ environment is pretty snafu at the moment so it's probably this end.
Could you post the header and .def files for me too?
Noe that I recommend you remove "MessageBox" for debugging pruproses and use "OutputDebugString" instead. The default trace listener in C# will then pick up these messages and display them in the IDE.
aewarnick: Did you manage to solve this? If so, how did you do it? I'm having problems very simular to yours...
Never argue with fools, they will only drag you down to their level, and beat you with experience.
Q: How do you tell an experienced hacker from a novice?
A: The latter thinks there's 1000 bytes in a kilobyte, while the former is sure there's 1024 meters in a kilometer
Never argue with fools, they will only drag you down to their level, and beat you with experience.
Q: How do you tell an experienced hacker from a novice?
A: The latter thinks there's 1000 bytes in a kilobyte, while the former is sure there's 1024 meters in a kilometer