| |||||||||||||||||||||||||||||||
Formalizando ideas Este capítulo supone que tienes conocimeintos previos en. . . 5.1 Mentalidad del programador en WindowsSin tomar en cuenta el aspecto gráfico de interfaz de usuario, los programas de consola son relativamente más fácil de programar qué los de Windows. En un programa de consola el usuario debe seguir instrucciones específicas donde se le dicta qué hacer, mientras que los programas de Windows las acciones del usuario pueden ocurrir en distinto orden. Por ejemplo, el ejecutable en consola dice "Dame tu nombre" y lo único que se puede hacer es introducir el nombre desde el teclado, si se quiere avanzar para introducir otra información primero debe teclear el nombre. En este caso, el programador solo necesita pensar en una secuencia de preguntas y esperar a que el usuario responda para seguir avanzando. Llenar un formulario con los programas Windows es muy diferente. No hay reglas que el usuario deba seguir. Desde que inicia una aplicación en Windows, puede empezar a mover el puntero del ratón, quizás pulse algunos botones, maximice la ventana, introduzca información desde el teclado para llenar el campo de Dirección y después el Nombre... pero no está obligado a hacerlo de esa manera ni en ese orden. En este caso, el programador debe cambiar su metodología de programación, para responder las acciones del usuario sin importar la seccuencia. ¿Se pueden predecir las acciones del usuario en un programa Windows? No directamente (aunque se pueden restringir). Una aplicación en Windows responde a las acciones impredecibles y no secuenciales del usuario, es decir, espera una acción y emite una reacción. 5.2 Programación orientada a EventosLas acciones del usuario son interpretadas como eventos. Los programas que se escriben para Windows reaccionan a eventos enviados a la ventana del programa. Cada evento que ocurre se almacena automáticamente en una lista llamada Cola de Mensajes creada para cada aplicación. Los eventos pueden ser acciones que realiza el usuario como mover el ratón, presionar un botón, escribir un texto, maximizar la ventana, etc. Otros eventos pueden ser los generados por el sistema como volver a trazar la ventana, mostrarla, quitar el foco, llegada de información por el puerto, etc.
Cada que sucede un evento se crea un mensaje y se pone en la cola del programa al que corresponde. El programa debe consultar la Cola de Mensajes (donde se almacenan estos eventos) y los recibe en forma de mensaje. Si la acción que efectua el usuario es de interés para el programa, éste reacciona con un código; de lo contrario, el programa lo ignora y lo manda a un procedimiento por omisión de Windows para que el sistema decida que hacer con el mensaje.
5.3 Bucle de mensajesPensado en términos de programación, debería existir un bucle que itere constantemente para verificar si existen nuevos mensajes. Si el mensaje significa terminar la ejecución (generado cuando el usuario oprime el botón cerrar por ejemplo) se sale del bucle y finaliza el programa (al salir de Dado que Windows es un sistema multi-tarea, este bucle de mensajes no afectará el rendimiento de otros programas en ejecución (por lo menos no lo hará significativamente) ya que Windows se encarga de repartir equitativamente el uso del procesador a cada una de las aplicaciones. Este bucle de mensajes es un componente básico de todas las aplicaciones de Windows. Después de mostrar la ventana, la aplicación está preparada para llevar a cabo su tarea principal: procesar mensajes. Recuerde que Windows no envía información de entrada directamente desde el teclado o el ratón a una aplicación, sino que la ubica directamente en la cola de mensajes de la aplicación. La cola puede contener mensajes generados por Windows o mensajes enviados por otras aplicaciones. Observe el código siguiente:
//Necesario para obtener los mensajes o eventos de la aplicación MSG mensaje; while(GetMessage(&mensaje, 0, 0, 0) == TRUE) { DispatchMessage(&mensaje); } Una aplicación inicia con la llamada a la función El código anterior es muy utilizado para procesar los mensajes que recibe la vantana (un bucle while), funciona en la gran mayoría de los casos, sin embargo no considera que La función La función Hay dos condiciones que deben finalizar el bucle.
BOOL bRet; while( (bRet = GetMessage(&mensaje, NULL, 0, 0)) != 0) { if (bRet == -1) { // GetMessage() regresó -1, es decir, ocurrió un error. // Lidiar con el error mostrando un mensaje con la // información obtenida usando GetLastError() // y MessageBox(), y terminar la aplicación } else { DispatchMessage(&mensaje); } } La condición de while nunca debiera ser
MSG mensaje; while(GetMessage(&mensaje, 0, 0, 0) > 0) { DispatchMessage(&mensaje); } La función 5.4 Procesando los mensajes de ventanaTodas las aplicaciones de Windows necesitan una función
Como se vio en el capítulo anterior, dentro de la función Para definir la estructura clase de ventana, la aplicación debe definir la variable de la siguiente manera: El campo
//Definición de WNDPROC declarada dentro de windows.h typedef LRESULT (*WNDPROC)(HWND, UINT, WPARAM, LPARAM); //Prototipo de la función de procedimiento de ventana //Ésta será la encargada de recibir los mensajes/eventos generados LRESULT CALLBACK ProcedimientoVentana(HWND hwnd, UINT mensaje, WPARAM wParam, LPARAM lParam); ... WNDCLASSEX ventana; ... ventana.lpfnWndProc = ProcedimientoVentana; ... Windows dispone de cientos de mensajes diferentes que puede enviar a la función procedimiento de ventana. Estos mensajes tienen etiquetas con identificadores que comienzan por "WM_". Por ejemplo
#include <windows.h> /* La función ProcedimientoVentana() es necesaria porque es la LRESULT CALLBACK ProcedimientoVentana(HWND hwnd, UINT mensaje,encargada de recibir los eventos (movimientos del ratón, tecla, clics a un botón, etc). En este caso, solo monitorea el momento el que el usuario decide cerrar la ventana para descargar la aplicación de memoria */ WPARAM wParam, LPARAM lParam) { switch (mensaje) { case WM_DESTROY: PostQuitMessage (0); //Publica el mensaje WM_QUIT en la cola de //mensajes que en la próxima iteracción del //bucle de mensajes hará que GetMessage() //regrese el valor 0 y por lo tanto salga //de WinMain() y finalice el programa return 0; //Sale de la función, no es necesario mandar a procesar //el mensaje con DefWindowProc() } return DefWindowProc (hwnd, mensaje, wParam, lParam); } El primer parámetro es El segundo parámetro del procedimiento de ventana, Errores comúnes en C++. Como ha visto, es común utilizar la sentencia 5.5 De la teoría a la prácticaSabiendo lo anterior, es posible programar una ventana que responda a las acciones del usuario. El siguiente código interpretará los mensajes
#include <windows.h> #include <sstream> LRESULT CALLBACK ProcedimientoVentana(HWND hwnd, UINT mensaje, WPARAM wParam, LPARAM lParam) { switch (mensaje) { case WM_CREATE: { MessageBox(hwnd, "Se creó la ventana", "Ejemplo", MB_ICONINFORMATION | MB_OK); break; } case WM_LBUTTONUP: { int xPos = LOWORD(lParam); int yPos = HIWORD(lParam); std::ostringstream os; os << "Hizo clic con el botón izquierdo"; if (wParam == MK_CONTROL) os << " mientras oprimía la tecla CONTROL"; else if (wParam == MK_SHIFT) os << " mientras oprimía la tecla SHIFT"; os << "\n\nCoordenadas: " << xPos << ", " << yPos; MessageBox(hwnd, os.str().c_str(), "Clic!", MB_ICONINFORMATION | MB_OK); break; } case WM_DESTROY: { MessageBox(hwnd, "¡Adiós mundo cruel!", "Cerrando...", MB_ICONERROR | MB_OK); PostQuitMessage (0); //Manda WM_QUIT a la cola de mensajes que hará //que el programa salga del bucle de mensajes return 0; } } return DefWindowProc (hwnd, mensaje, wParam, lParam); } int WINAPI WinMain(HINSTANCE hInstancia, HINSTANCE hInstanciaPrev, LPSTR lpLineaCmd, int nEstadoVentana) { const char szNombreAplicacion[] = "Programa4"; HWND hwnd; MSG mensaje; WNDCLASSEX ventana; //Configuramos la ventana ventana.cbSize = sizeof(ventana); ventana.style = CS_HREDRAW | CS_VREDRAW; ventana.lpfnWndProc = ProcedimientoVentana; ventana.hInstance = hInstancia; ventana.lpszClassName = szNombreAplicacion; ventana.hIcon = LoadIcon (NULL, IDI_APPLICATION); ventana.hIconSm = LoadIcon (NULL, IDI_APPLICATION); ventana.hCursor = LoadCursor (NULL, IDC_ARROW); ventana.lpszMenuName = NULL; ventana.cbClsExtra = 0; ventana.hbrBackground = (HBRUSH)(COLOR_HIGHLIGHT+1); ventana.cbWndExtra = 0; // Registrar la clase de ventana, si falla, salir del programa if(!RegisterClassEx(&ventana)) return (FALSE); hwnd = CreateWindowEx( 0, szNombreAplicacion, //Nombre de clase de la ventana a la que pertenece szNombreAplicacion, //Título de la ventana WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 200, //Ancho 200, //Alto HWND_DESKTOP, NULL, hInstancia, NULL); ShowWindow(hwnd, SW_SHOWMAXIMIZED); // Bucle de mensajes: while(GetMessage(&mensaje, 0, 0, 0) > 0) { DispatchMessage(&mensaje); } return mensaje.wParam; }
Al mandar llamar a la función
Cada vez que el usuario de un clic izquierdo sobre la ventana, Windows mandará el mensaje
Al cerrar la aplicación, ya sea presionando ALT + F4 o sobre el botón cerrar (X) de la ventana, la aplicación recibirá el mensaje
El resto de los mensajes que pudiera recibir la aplicación por diversas acciones del usuario sobre la ventana de la aplicación, tales como minimizar la ventana o cambiar su tamaño, serán procesados por Windows mediante la función 5.6 ConclusiónComo ha podido apreciar, una aplicación en Windows no escribe directamente a la pantalla ni utilizar interrupciones de hardware. Estas son completamente distintas a las antiguas aplicaciones de consola (DOS o LINUX). En lugar de ello, la aplicación utiliza funciones disponibles en la API de Windows para recibir e enviar mensajes. Es por medio de mensajes que Windows difunde información en un entorno multitarea. Para nosotros, un mensaje es una notificación de que ha ocurrido un evento de interés que puede necesitar una respuesta específica. Una aplicación Windows debe estar totalmente orientada al procesamiento de mensajes. | |||||||||||||||||||||||||||||||
|