C++: Capturar Windows Messages de una aplicación MFC sin usar MFC
Supongamos que necesitamos que nuestra dll C++ se comunique con una biblioteca externa que use MFC y Windows Messages para enviar datos y notificaciones. La forma más sencilla de establecer esa comunicación sería escribiendo una aplicación MFC con un diálogo (una clase que herede de CDialog) que maneje los mensajes generados por la biblioteca externa. Pero supongamos que nuestra dll C++ está en una capa de bajo nivel y no queremos meter una interfaz de usuario ahí. Al menos, no abiertamente.
No hay forma de evitar los Windows Messages, así que tendremos que usar, mínimamente, la API Win32 nativa. Entonces, ¿Cómo capturamos Windows Messages sin mostrar una ventana?
Antes de seguir, debemos saber lo siguiente:
- Para capturar Windows Messages, hay que crear una ventana (asociada a un HWND) y usar su message loop
- Hay uno y sólo un message loop por thread. Esto implica que la ventana que atrape los mensajes y su message loop deben vivir en el mismo thread que el código que genera mensajes.
- Una ventana puede ser creada como Message-Only window, en la jerga MSDN. Y eso es lo que haremos
Una vez que se sabe lo anterior, es fácil resolver el problema inicial. El código:
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