Ok, I wrote up this code as an example. I tried to comment the important parts, but if something is unclear, let me know. I compiled this under MSVC++, and there is a chance that some small changes will be needed for it to work with other compilers.
There is two parts here. First is the hook dll which gets loaded into the memory space of all running threads. This DLL contains the hook procedure (I called it KeyboardProc). The other part is a test application which is used to load the hook, and blurt out a message every time a key is pressed.
The hook DLL notifys the test application every time a key is pressed or released by sending the window a globaly registered message.
Here is the code for the DLL:
Code:
#include <windows.h>
/*
* Start of shared data section. All these variable will be shared among all
* DLL instances. The way to declare a shared data section differs from compiler
* to compiler. The following is the MSVC++ method.
*/
#pragma data_seg(".SHARDAT")
static HHOOK g_hkb=NULL;
static HWND g_notifyWnd = NULL;
UINT uKeypressID = ::RegisterWindowMessage("WM_GLOBAL_KEYPRESS");
#pragma data_seg()
#pragma comment(linker, "-section:.SHARDAT,rws")
HINSTANCE g_hInst = NULL;
/*
* Our hook procedure. This will get called whenever a key is pressed
*/
LRESULT __declspec(dllexport)__stdcall CALLBACK KeyboardProc(
int nCode,
WPARAM wParam,
LPARAM lParam)
{
if(nCode == HC_ACTION && g_notifyWnd)
{
SendMessage(g_notifyWnd,uKeypressID,wParam,lParam);
}
return CallNextHookEx( g_hkb, nCode, wParam, lParam );
}
extern "C" BOOL __declspec(dllexport) installhook(HWND notifyWnd)
{
if(!g_hInst) return FALSE;
g_notifyWnd = notifyWnd;
g_hkb = SetWindowsHookEx(WH_KEYBOARD,(HOOKPROC)KeyboardProc,g_hInst,0);
return (BOOL)g_hkb;
}
extern "C" BOOL __declspec(dllexport) unhook()
{
return UnhookWindowsHookEx(g_hkb);
}
/*
* DLL entry point
*/
BOOL APIENTRY DllMain( HINSTANCE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
if(ul_reason_for_call == DLL_PROCESS_ATTACH)
{
g_hInst = hModule;
}
return TRUE;
}
Here is the code for the test application.
Code:
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
/* Global variables */
const UINT uKeypressID = RegisterWindowMessage(_T("WM_GLOBAL_KEYPRESS"));
HWND g_hwnd;
HMODULE g_hMod;
HINSTANCE hInst;
char szWindowClass[] = _T("WinClass");
/* Function prototypes */
ATOM RegisterClassFn(HINSTANCE hInstance);
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow);
/*
* Install the keyboard hook
*/
BOOL LoadHook()
{
typedef BOOL (*FUNCTION_TYPE)(HWND);
//BOOL (CALLBACK *installhook)(HWND);
FUNCTION_TYPE installhook;
/* Load the dll into memory */
g_hMod = LoadLibrary("keyhook.dll");
if(!g_hMod)
return FALSE;
/* Get the address of the installhook() function */
installhook = (FUNCTION_TYPE)GetProcAddress(g_hMod,"installhook");
if(!installhook) return FALSE;
return installhook(g_hwnd);
}
/*
* Uninstall the keyboard hook
*/
BOOL UnloadHook()
{
BOOL (*unhook)();
BOOL result;
if(!g_hMod) return FALSE;
/* Get the address of the unhook() function */
unhook = (BOOL (*)())GetProcAddress(g_hMod,"unhook");
if(!unhook) return FALSE;
result = unhook();
FreeLibrary(g_hMod);
return result;
}
/*
* This gets called when a key is pressed. To see what the wParam and
* lParam values indicate, look at KeyboardProc() under MSDN.
*/
void keypressed(WPARAM wParam, LPARAM lParam)
{
int mask = 0x80000000;
char msg[128];
/* we see if bit 31 is set. If it is, then the button
* was pressed
*/
if(mask & lParam)
{
/* Key was pressed */
sprintf(msg,_T("The %c Key was pressed"),wParam);
MessageBox(g_hwnd,msg,_T("KEYPRESS"),MB_OK | MB_SYSTEMMODAL);
}
}
/*
* Application entry point
*/
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
MSG msg;
/* Register the window */
RegisterClassFn(hInstance);
/* Initialize the application */
if (!InitInstance (hInstance, nCmdShow))
{
return FALSE;
}
/* Main message loop */
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
/*
* Create the window, and display it
*/
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd;
hInst = hInstance;
/* Create the window */
hWnd = CreateWindow(szWindowClass, _T("Test Hook"), WS_OVERLAPPEDWINDOW,
100, 100, 100, 100, NULL, NULL, hInstance, NULL);
if (!hWnd)
{
return FALSE;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
return TRUE;
}
/*
* This is our main window procedure
*/
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
/* First check and see if the message matches our global ID */
if(message == uKeypressID)
{
/* Call the keypressed method to handle a key press */
keypressed(wParam,lParam);
return 0;
}
else
{
switch(message)
{
case WM_CLOSE:
/* Uninstall the hook */
if(!UnloadHook())
MessageBox(g_hwnd,_T("Unable to unload hook"),_T("ERROR"),MB_OK);
return DefWindowProc(hWnd, message, wParam, lParam);
case WM_CREATE:
g_hwnd = hWnd;
/* Install the hook */
if(!LoadHook())
MessageBox(g_hwnd,_T("Unable to install hook"),_T("ERROR"),MB_OK);
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
return 0;
}
ATOM RegisterClassFn(HINSTANCE hInstance)
{
WNDCLASSEX wcex;
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = (WNDPROC)WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = NULL;
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = szWindowClass;
wcex.hIconSm = NULL;
return RegisterClassEx(&wcex);
}
Let me know if you have questions about anything.