WH_GETMESSAGE nightmare - help needed.

Started by
8 comments, last by george135 15 years, 6 months ago
Ok Guys, Is it possible to monitor an external application eg. notepad, ms word etc for Messages that they receive, i.e WM_DESTROY? I've made several attempts to write a simple C++ application that can monitor a specified application for WM_DESTROY, WM_CLOSE. I did uses the SetWindowsHooksEx and WH_GETMESSAGE function but my GetMsgProc was never called. Can someone provide an example if possible to monitor other application with Hooks? Jr
Advertisement
try with WH_CALLWNDPROC hook.
Here's some sample code (in straight C) that demonstrates a global hook. A global hook (a.k.a. a system wide hook) hooks into every currently running user mode process. The demo consists of two parts, a monitor app and a dll. The dll uses memory mapping to establish shared memory between the dll and the processes it will get loaded into. It sets a WH_GETMESSAGE hook. The MsgHookProc filters out all messages that are not WM_PAINT. When it receives a WM_PAINT message, it bundles together some information about the message and sends it to the monitor app for display. The monitor app loads the dll, invokes the sethook functions exported by the dll, receives WM_COPDATA messages from the hook proc and displays them in a listbox. Try adapting it to capture the messages you're interested in. YMMV. msghook.zip

Do yourself a favor and seek out a couple of useful and free utilities. Google for "ProcessExplorer" by Mark Russinovich. This task manager type program lists all of processes currently running on your machine. It provides details about which dlls are loaded into those processes. This can be very helpful when trying to determine if your hook dll is loaded into the process you're targeting. Also google for "DependencyWalker". This utility lets you examine dlls and exes to see which other dlls they load in. It lets you confirm that the functions your hook dll exports are named the way you want them to be named. Another useful utility is WinSpy from catch22.net. This tool can help you ferret out the actual names of windows that you might be interested in hooking as well as the ancestry of those windows.





"I thought what I'd do was, I'd pretend I was one of those deaf-mutes." - the Laughing Man
See all MSDN samples (PSDK, KB, etc)
All the hooks are demonstrated (wide or low level)
Best code for hooks, as always.
OK, I've done some more reading and have produce/updated the below sample code. I've set a break point on the first line in the GetMsgProc function. The problem is that it is not entering the GetMsgProc function when I open or close notepad. I've also tried changing the message to WH_CALLWNDPROC but it still doesn't work. At the end of the day, all I want is to be able to intercept a WM_CLOSE/WM_QUIT/WM_DESTROY.

Below is my full source code.

#define WIN32_LEAN_AND_MEAN																	// trim the excess fat from Windows#include <stdlib.h>#include <windows.h>#include <Windowsx.h>#include <tchar.h>#include <stdio.h>#include <string.h>LRESULT WINAPI GetMsgProc(UINT, WPARAM, LPARAM);DWORD WINAPI MsgLoop(LPVOID lpParameter);// Global Hook handlestatic HHOOK hIISHook =NULL;static HINSTANCE hinstDLL = NULL; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd){	HANDLE hThread;	DWORD dwThread;	DWORD exThread;	BOOL bOK = FALSE;	HWND nPad = NULL;	DWORD threadId = GetCurrentThreadId();		hIISHook = SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, NULL, threadId);	bOK = (hIISHook != NULL);	if (bOK)	{		nPad = FindWindow(_T("NotePad"), NULL);		SetWindowLongPtr(nPad, GWLP_WNDPROC, GetMsgProc);		MessageBox(NULL,_T("Low Level hook installed properly"), _T("Information"), MB_OK);	}	hThread = CreateThread(NULL,NULL,(LPTHREAD_START_ROUTINE)		MsgLoop, (LPVOID) hInstance, NULL, &dwThread);	if (hThread) {		return WaitForSingleObject(hThread,INFINITE);	} else {		return 1;	}	UnhookWindowsHookEx(hIISHook);	return 0;}DWORD WINAPI MsgLoop(LPVOID lpParameter){	MSG msg;	while (GetMessage(&msg,NULL,0,0)) {		switch(msg.message)		{		case WM_QUIT:		case WM_DESTROY:			break;		}		TranslateMessage( &msg );					DispatchMessage( &msg );	}}LRESULT WINAPI GetMsgProc(UINT nCode, WPARAM wParam, LPARAM lParam){	PMSG msg;	HWND tmpHWND = NULL;	int i = 0;		msg = (PMSG)lParam;	tmpHWND = FindWindow(_T("NotePad"), NULL);	if (tmpHWND == msg->hwnd)	{		i = 1;	}	if (msg->message == WM_CLOSE || msg->message == WM_DESTROY || msg->message == WM_QUIT);	{		i = 2;	}	return CallNextHookEx(hIISHook, nCode, wParam, lParam);}
1. Never ever ever ever ever cast function pointers like that.
2. Always run code with warnings set to maximum, so you can get warnings like this one: warning C4390: ';' : empty controlled statement found; is this the intent?
3. That code doesn't compile for me, because your function signature is wrong, you're not casting the last parameter to SetWindowLongPtr() to a LONG, MsgLoop() doesn't return anything, and UnhookWindowsHookEx is never called (unreachable code):
LRESULT WINAPI GetMsgProc(UINT nCode, WPARAM wParam, LPARAM lParam)
Should be:
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
4. You can't subclass a window in another process using the window proc in your own process. You'll need to put the window proc into a DLL and inject that into the Notepad process.

As for the actual problem, to capture messages for another process, you need to set a global hook. To do that, you need to put the hook proc in a DLL, use LoadLibrary() to load the DLL, and pass the HMODULE to SetWindowsHookEx in the 3rd parameter.
Ok, I've knocked up an example application for you: Source Code.

Or, if you just want to read it; the DLL:
//============================================================================// DllMain.cpp - DLL entry point//============================================================================#define WIN32_LEAN_AND_MEAN#include <windows.h>#include <stdio.h>//============================================================================__declspec(dllexport) LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam){	if(nCode == HC_ACTION)	{		// Is this a message for Notepad?		MSG* pMsg = (MSG*)lParam;		char szFilename[256];		GetWindowModuleFileName(pMsg->hwnd, szFilename, sizeof(szFilename));		if(strstr(szFilename, "notepad.exe"))		{			char szBuff[256];			sprintf_s(szBuff, sizeof(szBuff), "Message: %d (%d, %d)\n",				pMsg->message, pMsg->wParam, pMsg->lParam);			OutputDebugString(szBuff);		}	}	return CallNextHookEx(NULL, nCode, wParam, lParam);}//============================================================================
Note that I've used a module definition file to export the function to prevent name mangling (Or just use "_GetMsgProc@12" for the function name in the EXE below).

And the EXE:
//============================================================================// Main.cpp - Program entry point//============================================================================#define WIN32_LEAN_AND_MEAN#include <windows.h>typedef LRESULT (CALLBACK *LPGetMsgProc)(int nCode, WPARAM wParam, LPARAM lParam);int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int){	// Load DLL and get address of GetMsgProc() function	HMODULE hDll = LoadLibrary("TestDll.dll");	if(!hDll)		return -1;	LPGetMsgProc pfnProc = (LPGetMsgProc)GetProcAddress(hDll, "GetMsgProc");	if(!pfnProc)	{		FreeLibrary(hDll);		return -1;	}	// Setup global hook	HHOOK hHook = SetWindowsHookEx(WH_GETMESSAGE, pfnProc, hDll, 0);	if(hHook != NULL)	{		// Wait a bit		Sleep(10000);		// Cleanup hook		UnhookWindowsHookEx(hHook);	}	// Cleanup DLL	FreeLibrary(hDll);	return hHook != NULL;}
The code just moves the hook proc into a DLL, and then the main EXE loads the DLL, sets the hook up, waits a bit and then exits. You can use DebugView to see the debug messages being output.
Steve, many many thanks for this help. It just makes me realized how little I do know about programming in windows.

See, when reading the msdn on hooks, it is not clear that you need to have it in a dll. I just thought I could have one single exe that did it all.

Again, many thanks for this guys.
Quote:Original post by gp343
Steve, many many thanks for this help. It just makes me realized how little I do know about programming in windows.

See, when reading the msdn on hooks, it is not clear that you need to have it in a dll. I just thought I could have one single exe that did it all.

Again, many thanks for this guys.
You need to use a DLL if the hook is global - since you'll be getting the callback in the context of other processes, your code would need to be loaded into the other process, and the only sane way to do that is with a DLL. And yeah, I agree that it's not entirely clear, I had the same issues you're having when I first did some hooking stuff [smile]
Quote:Original post by gp343
See, when reading the msdn on hooks, it is not clear that you need to have it in a dll. I just thought I could have one single exe that did it all.


No, it's very clear.
"
The global hooks are a shared resource, and installing one affects all applications in the same desktop as the calling thread. All global hook functions must be in ***libraries***.
"

And, as I said, see the dozens of MS samples with DLL for ***all the hooks***
It's the only reliable source on the Web. All others ones are just (bad) copies...

This topic is closed to new replies.

Advertisement