C++: Capture Windows Messages from an MFC app without using MFC
Let's say you need your C++ dll to communicate with a third party dll which uses MFC and Windows Messages to send data and notifications. The easiest way out would be to write an MFC application with a dialog which handled those messages. But let's say your C++ dll is at a very low layer and you don't want GUI code there. At least not openly.
Well, there is no avoiding the Windows Messages, so we'll be using, at least, the Win32 unmanaged API. So how do we capture Windows Messages without showing a window?
Some basic things you should know first:
- To capture Windows Messages, you must create a window (associated with a HWND) and use its message loop
- There is one message loop per thread. This implies that the message-handling window must live in the same thread as the message-generating code
- A window can be created as a Message-Only window, in MSDN jargon. And that's what we'll do
Once I learned all of the above, it was easy to do. Here's some code:
void CSomeDllWrapper::StartMessageLoop(){
m_hMessageLoopThread = ::CreateThread(
NULL, 0, CSomeDllWrapper::MessageLoopThread,
this, 0 , &m_MessageLoopThreadID
);
}
DWORD WINAPI CSomeDllWrapper::MessageLoopThread( void * pParams ){
HWND hwnd;
MSG msg;
WNDCLASSEX wincl;
const string windowClass = "SOME_DLL_WRAPPER_MESSAGE_WINDOW";
// Window class
wincl.hInstance = GetModuleHandle(NULL);
wincl.lpszClassName = windowClass.c_str();
wincl.lpfnWndProc = ::DefWindowProc;
wincl.style = CS_DBLCLKS;
wincl.cbSize = sizeof (WNDCLASSEX);
wincl.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wincl.hIconSm = LoadIcon (NULL, IDI_APPLICATION);
wincl.hCursor = LoadCursor (NULL, IDC_ARROW);
wincl.lpszMenuName = NULL;
wincl.cbClsExtra = 0;
wincl.cbWndExtra = 0;
wincl.hbrBackground = GetSysColorBrush(COLOR_BACKGROUND);
if(!RegisterClassEx(&wincl)){
HandleRegistratioFailure();
}
hwnd = CreateWindowEx(
0, //Default ExStyle
windowClass.c_str(), //Window class
"SomeDllWrapper", //Window Title
WS_OVERLAPPEDWINDOW, //Default Style
CW_USEDEFAULT, //Let Windows decide position
CW_USEDEFAULT,
10, //Width
10, //Height
HWND_MESSAGE, //Message-only window
NULL, //No Menu
GetModuleHandle(NULL), //Handle to application
NULL //Window creation data
);
CSomeDllWrapper* pThis = reinterpret_cast<CSomeDllWrapper*>( pParams );
pThis->InitSomeLibrary(); //This makes the library start firing messages
//Start message loop
while(TRUE == GetMessage(&msg, NULL, 0, 0)){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
void CSomeDllWrapper::InitSomeLibrary(){
//... Library-specific init code
//This is how my dll associates its Windows messages, yours might differ
void *customerData = this;
::RegisterCallbackFunc( CSomeDllWrapper::MessageProc, customerData );
//... More Library-specific init code
}
void __stdcall CSomeDllWrapper::MessageProc(int msgType, LPSTR pMsg, void* pCustomerData ){
CSomeDllWrapper* pSomeDllObject = reinterpret_cast<CSomeDllWrapper*>( pCustomerData );
switch (msgType) {
case IDC_SOME_MSG:
//Do stuff
break;
//You get the rest
}
}
}
Comments
Post a Comment