Entender el Bucle de Mensajes y la estructura de env�o de mensajes es escencial para escribir hasta el programa mas trivial. Ahora que hemos visto un poco sobre manejo de mensajes deber�amos mirar en profundidad todo este proceso porque las cosas se tornar�n mas confusas si no entendemos por qu� las cosas suceden de la forma en que lo hacen.
#define WM_INITDIALOG 0x0110 #define WM_COMMAND 0x0111 #define WM_LBUTTONDOWN 0x0201...y asi siguiendo. Los mensajes son usados para comunicar la mayor�a de las cosas en windows, al menos en los niveles b�sicos. Si quieres que una ventana o control (el cual es una ventana especializada) haga algo, debes enviarle un mensaje. Si otra ventana quiere que vos hagas algo, entoces te env�a un mensaje. Si ocurre un evento, como cuando el usuario mueve el mouse, presiona el teclado, etc... entonces el sistema la env�a un mensaje a la ventana afectada. Dicha ventana recibe el mensaje y act�a adecuadamente.
Cada mensaje en windows puede tener hasta dos par�metros, wparam y lparam. Originalmente wparam ten�a 16 bits y lparam ten�a 32 bits, pero en Win32 ambos son de 32 bits. No todos los mensajes usan estos par�metros y cada mensaje los usa de manera diferente. Por ejemplo, el mensaje WM_CLOSE no usa ninguno de los dos y por lo tanto deberias ignorarlos. El mensaje WM_COMMAND usa ambos, wparam contiene dos valores HIWORD(wparam)
es la notificaci�n del mensaje (si se aplica) y LOWORD(wparam) es el ID del control o men� que envi� el mensaje. lparam es el HWND (Windows Handle) del control que envi� el mensaje o nulo (NULL) si el mensaje no proviene de un control.
HIWORD( ) y LOWORD( ) son macros definidas por windows que simplemente retornan la palabra superior (High Word) y la palabra inferior (Low Word), respectivamente, de un valor de 32 bits. En Win32 una palabra es un valor de 16 bits, haciendo un DWord (palabra doble) un valor de 32 bits.
Para enviar un mensaje puedes usar PostMessage( ) o SendMessage( ). PostMessage( ) pone el mensaje en la Cola de Mensajes y retorna inmediatamente. Esto significa que una vez que la llamada a PostMessage( ) se completa el mensaje puede o no puede haber sido procesado a�n. SendMessage( ) env�a el mensaje directamente a windows y no retorna hasta que windows haya finalizado de procesarlo. Si quisieramos cerrar una ventana, podr�amos enviarle a un mensaje WM_CLOSE
de la siguiente manera: PostMessage(hWnd,WM_CLOSE,0,0). Lo cual podr�a tener el mismo efecto que hacer click en el boton cerrar de la ventana. Observa que wparam y lparam son ambos 0. Esto es debido a que, como mencionamos, no se usan con el mensaje WM_CLOSE.
Una vez que comiences a usar cajas de di�logo necesitar�s enviar mensajes a los controles para poder comunicarte con ellos. Puedes hacer esto primero atrav�s de GetDlgItem( ) (utilizando el ID como par�metro) para obtener el handle al control y luego usar SendMessage( ). O puedes usar SendDlgItemMsg( ), el cual combina ambos pasos. Como par�metros utilizamos el handle de la ventana y un ID de un hijo y SendDlgItemMsg( ) retorna el handle del hijo y le env�a el mensaje. Ambas formas
de enviar mensajes funcionan bien en todas las ventanas, no solamente en cajas de di�logo.
Digamos que te encuentras ocupado manejando el mensaje WM_PAINT y repentinamente el usuario ingresa un gran cantidad de datos por el teclado. �Que deber�a suceder?. �Deber�as interrumpir el procesamiento de WM_PAINT (dejar de dibujar la pantalla) para atender el teclado, o deber�as simplemente descartar lo que el usuario ingres� por el teclado?. Error!, obviamente ninguna de �stas opciones son rasonables. Para esto tenemos la Cola de Mensajes. Cada vez que se recibe un mensaje, dicho mensaje es encolado en la
cola de mensajes y cada vez que se quiere procesar un mensaje dicho mensaje es removido de la cola (valga la redundancia). Esto asegura que no se pierdan mensajes, si est�s procesando uno, entonces los otros ser�n encolados hasta que los recuperes de la cola.
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
TranslateMessage(&Msg);
DispatchMessage(&Msg);
}
GetMessage( ), el cual busca en tu cola de mensajes. Si la cola de mensajes est� vac�a, tu programa se detiene y espera hasta que haya un mensaje (se bloquea).
GetMessage( ) retorna un valor positivo indicando que hay un mensaje para ser procesado y que ha llenado los campos de la estructura MSG que le pasamos.
Msg) y se lo pasamos a TransalateMessage( ), �ste hace un proceso adicional traduciendo mensajes con teclas virtuales, en mensajes con caracteres. Este paso puede ser opcional, pero ciertas cosas no funcionar�n si no lo utilizamos.
DispatchMessage( ). Lo que DispatchMessage( ) hace es tomar el mensaje, chequear a que ventana est� destinado y buscar el Window Procedure de dicha ventana. Luego llama a dicho procedimiento enviando como par�metro el handle de la ventana, el mensaje, wparam y lparam.
DefWindowProc( ) el cual realizar� las acciones "por default" (lo cual significa que a veces no hace nada).
DispatchMessage() retorna y regresamos al comienzo del bucle.
Este es un concepto muy importante para los programas de windows. El Windows Procedure no es m�gicamente llamado por el sistema, de hecho lo llamamos indirectamente atrav�s de DispatchMessage( ). Si deseas, puedes usar GetWindowLong( ) en el handle de la ventana a la cual est� destinado el mensaje para buscar el Window Procedure de la misma y llamarlo directamente!
while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
WNDPROC fWndProc = (WNDPROC)GetWindowLong(Msg.hwnd, GWL_WNDPROC);
fWndProc(Msg.hwnd, Msg.message, Msg.wParam, Msg.lParam);
}
He intentado esto con el c�digo del ejemplo anterior y funciona. Sin embargo hay varios aspectos como la traducci�n UNICODE/ANSI, entre otros, que este m�todo no tendr� en cuenta y probablemente haga fallar hasta la aplicaci�n mas trivial. Por lo tanto hazlo como prueba pero no lo hagas en el c�digo real.
Observa que hemos usado GetWindowLong( ) para recuperar el Window Procedure asociado con la ventana, pero �por que no lo llamamos directamente?. Bien, nuestro bucle de mensajes es responsable de TODAS las ventanas en nuestro programa, esto incluye cosas como botones, listas, etc.. que tienen su propio Window Procedure asociado, por lo tanto necesitamos asegurarnos que llamamos al procedimiento correcto para una cierta ventana. Debido a que m�s de una ventana puede usar el mismo Window Procedure, usamos el primer par�metro
(el handle de la ventana) para decirle al Window Procedure a que ventana est� destinado el mensaje.
Como puedes ver tu aplicaci�n pasa la mayor parte del tiempo dando vueltas y vueltas en el bucle de mensajes donde alegremente env�as mensajes a las felices ventanas que los procesar�n. �Pero que haces cuando quieres que tu programa salga? Debido a que estamos usando un bucle while( ) si GetMessage( ) retornara FALSE (i.e 0), el bucle podr�a finalizar y de esta manera podr�amos alcanzar el final de nuestro WinMain( ), es decir salir del programa. Esto es exactamente
lo que PostQuitMessage() realiza; pone un WM_QUIT en la cola y GetMessage( ) en vez de retornar un valor positivo, llena la estrucura MSG y retorna 0. Hasta este punto el campo wparam de la variable Msg contiene el valor que le pasamos en PostQuitMessage( ) y podemos ignorarlo o retornarlo al WinMain( ) donde se usar� como valor de salida cuando el programa finalice.
IMPORTANTE: GetMessage( ) retornar� -1 cuando encuentre un error. Aseg�rate de recordar esto o te sorprender� en alg�n momento... A�n cuando GetMessage( ) est� definido para retornar un BOOL (Booleano), �ste puede retornar valores distinos de TRUE o FALSE, debido a que BOOL est� definido como UINT (unsigned int).
Los siguientes son ejemplos de c�digo que puede parecer que funciona pero no procesar�n ciertas condiciones correctamente.
while(GetMessage(&Msg, NULL, 0, 0))
while(GetMessage(&Msg, NULL, 0, 0) != 0)
while(GetMessage(&Msg, NULL, 0, 0) == TRUE)
Los segmentos de c�digo anterior est�n mal! Quiz�s has notado que he usado el primero de ellos en lo que va del tutorial, asi como lo mencion�, este funciona bien siempre y cuando GetMessage( ) no falle y cuando tu c�digo es correcto, no lo hace. Sin embargo no he tenido en cuenta que si est�s leyendo este tutorial tu c�digo probablemente no sea correcto en todo momento y GetMessage( ) puede fallar en alg�n punto. Ya lo he corregido pero perd�name si he olvidado algunos puntos.
while(GetMessage(&Msg, NULL, 0, 0) > 0)
Deber�an usarse este o c�digos que tengam siempre el mismo efecto. Espero que ahora tengas un mejor entendimiento del bucle de mensajes, si no es as�, no te asustes, las cosas tendr�n mas sentido despu�s que hayan sido usadas un par de veces.
Versi�n en Espa�ol: Federico Pizarro - 2003