class CModelessDialogHook

	by Shawn A. Van Ness, with thunking code inspired by Andrew Nosenko's 
	excellent AtlAux library (http://www.geocities.com/~andien/atlaux.htm)

Motivation:  Ever try to encapsulate a dialog behind an in-proc COM object?  
If it's a modal dialog, easy.  If it's a modeless dialog... not so easy!  Your COM 
server code probably doesn't own the message pump -- the client does, in all 
likelyhood.  And Win32 dictates that message pumps call IsDialogMessage() 
whenever modeless dialogs are visible/active, else keyboard accelerators 
don't work.  Bummer!

Even if you have access to your client's code, if it's written in MFC or VB, 
the message pump code will be buried under 6 feet of boilerplate macros and 
header files.

But never fear -- with a little low level thunking code, we can hook 
into the message pump, and call IsDialogMessage() ourselves!


The code is intended for use in ATL projects, but should play well with any 
Win32 C++ code.

	void ShowModelessDialogsWhilePumpingMessagesImproperly()
		// Create a few modeless dialogs
		CTestDialog dlg1;
		CTestDialog dlg2;
		CTestDialog dlg3;


		// Hook onto them
		CModelessDialogHook hook1(dlg1);
		CModelessDialogHook hook2(dlg2);
		CModelessDialogHook hook3(dlg3);

		// Pump messages, while waiting for the dlgs to go away...
		// Note that this message pump doesn't call IsDialogMessage(), 
		// to properly dispatch keyboard/accelerator messages intended 
		// for our modeless dialogs!
		MSG msg;
		while (GetMessage(&msg,0,0,0))

#pragma once 

#ifndef _M_IX86 
 #error "This code is implemented for x86 only."

#pragma pack(push, 1) 

template <class TDerived>
class CThiscallThunk
	BYTE    m_mov;     // opcode: mov ecx, [dword]
	DWORD   m_this;    // dword value
	BYTE    m_jmp;     // opcode: jmp
	DWORD   m_relproc; // relative jmp distance


	typedef void (TDerived::*TMFP)();

	void InitThunk(TMFP method, const TDerived* pThis)
		union { DWORD func; TMFP method; } addr;
		addr.method = (TMFP)method;
		m_mov = 0xB9;
		m_this = (DWORD)pThis;
		m_jmp = 0xE9;
		m_relproc = addr.func - (DWORD)(this+1);
		FlushInstructionCache(GetCurrentProcess(), this, sizeof(*this));

	FARPROC GetThunk() const 
		_ASSERTE(m_mov == 0xB9);
		return (FARPROC)this; 

#pragma pack(pop) 

class CModelessDialogHook : public CThiscallThunk<CModelessDialogHook>

	HHOOK m_hHook;
	HWND m_hWnd;


	CModelessDialogHook() : m_hHook(0), m_hWnd(0)
	{ }

	CModelessDialogHook(HWND h)
	{ Hook(h); }

	{ Unhook(); }

	void Hook(HWND h)
		// Initialize the thunk

		// Install the hook
		m_hHook = ::SetWindowsHookEx(WH_GETMESSAGE,(HOOKPROC)GetThunk(),0,GetCurrentThreadId());

		// Remember the window handle
		m_hWnd = h;

	void Unhook()
		if (m_hHook)

		m_hHook = 0;
		m_hWnd = 0;

	LRESULT HookProc(int iHookCode, WPARAM wParam, LPARAM lParam) // note: not static! sweet!
		MSG* pMsg = reinterpret_cast<MSG*>(lParam);

		// Intercept keystroke messages, and process them
		if ((iHookCode >= 0) &&
			(wParam == PM_REMOVE) &&
			(pMsg->message >= WM_KEYFIRST) &&
			(pMsg->message <= WM_KEYLAST) &&
			pMsg->message = WM_NULL;
			pMsg->lParam = 0;
			pMsg->wParam = 0;
			return 0; // note: we are not calling the next hook

		// Pass on everything else
		return ::CallNextHookEx(m_hHook,iHookCode,wParam,lParam);


w e b c p p
web c plus plus