/*
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!


Usage:

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;

		dlg1.Create(GetDesktopWindow());
		dlg2.Create(GetDesktopWindow());
		dlg3.Create(GetDesktopWindow());

		// 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))
			DispatchMessage(&msg);
	}
*/

#pragma once 

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

#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

public:

	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>
{
private:

	HHOOK m_hHook;
	HWND m_hWnd;

public:

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

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

	~CModelessDialogHook()
	{ Unhook(); }

	void Hook(HWND h)
	{
		// Initialize the thunk
		InitThunk((TMFP)HookProc,this);

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

		// Remember the window handle
		m_hWnd = h;
	}

	void Unhook()
	{
		if (m_hHook)
			::UnhookWindowsHookEx(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) &&
			(::IsDialogMessage(m_hWnd,pMsg)))
		{
			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