[ contenidos | #winprog ]

Manejo de Mensajes

Ejemplo: window click

[images/window_click.gif]

Bien, tenemos una ventana pero esta no hace nada excepto lo que DefWindowProc( ) le permite que haga: cambiar el tama�o, ser maximizada, minimizada etc... Nada realmente excitante.

En la siguiente secci�n voy a mostrar como modificar lo que ya tenemos para que haga algo nuevo. As�, yo solo podr�a decirte "Procesa este mensaje y haz esto en �l..." y tu sabr�s lo que digo y ser�s capaz de hacerlo sin mirar un ejemplo entero. Esto es lo que espero, de todas maneras presta atenci�n.

Ok, para comenzar tomemos el ejemplo de la �ltima ventana en la que trabajamos y asegur�monos que compila y se ejecuta como lo esperamos. Puedes seguir trabajando en este archivo o puedes copiar el c�digo a un nuevo proyecto.

Vamos a agregarle a la ventana la capacidad de mostarle al usuario cu�l es el nombre del programa. ESto se har� cada vez que el usuario realice un click sobre la ventana. Nada excitante, es b�sicamente para ir entendiendo como manejar mensajes. Observemos que tenemos en nuestro WndProc():

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg)
    {
        case WM_CLOSE:
            DestroyWindow(hwnd);
        break;
        case WM_DESTROY:
            PostQuitMessage(0);
        break;
        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

Si queremos manipular clicks del mouse, necesitamos agregar un menejador (o procesador) WM_LBUTTONDOWN (o WM_RBUTTONDOWN, MBUTTONDOWN para clicks con el bot�n derecho y medio, respectivamente). Cuando decimos "procesar un mensaje" significa agregar dicho mensaje dentro del WinProc( ) de nuestra clase ventana, lo hacemos de la siguiente manera:

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
   switch(msg)
   {
      case WM_LBUTTONDOWN:    // <-
                              // <-     agregamos esto
      break;                  // <-
      case WM_CLOSE:
         DestroyWindow(hwnd);
      break;
      case WM_DESTROY:
         PostQuitMessage(0);
      break;
      default:
         return DefWindowProc(hwnd, msg, wParam, lParam);
   }
   return 0;
}

El orden en cual manejamos los mensajes raramente importa. Solo aseg�rate de poner el "break" despues de cada uno. Como puedes ver necesitamos otro case dentro de nuestro switch(). Ahora queremos que algo suceda cuando llegamos a esta nueva parte de nuestro programa.

Primero presentar� el c�digo que queremos agregar (el que le mostrar� al usuario el nombre del archivo del programa) y luego lo integrar� dentro del nuestro programa. Mas adelante, probablemente solo te muestre el c�digo y te dejar� que lo integres tu mismo. Esto es, por su puesto, mejor para mi porque no tengo que andar tipeando demasiado y mejor para vos porque ser�s capaz de agregar el c�digo dentro de CUALQUIER programa, no solamente el que estamos presentando. De todas maneras, si no est�s seguro de como hacerlo puedes mirar el c�digo fuente correspondiente a esta secci�n inclu�do en el archivo zip.

GetModuleFileName(hInstance, szFileName, MAX_PATH);
MessageBox(hwnd, szFileName, "This program is:", MB_OK | MB_ICONINFORMATION);

Este c�digo no puede ser copiado en cualquier lugar dentro de nuestro programa. Especificamente queremos que �ste se ejecute cuando el usuario clickea el mouse, por lo tanto veremos como poner dicho c�digo dentro de nuestro programa:

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg)
    {
        case WM_LBUTTONDOWN:
// Comienzo del nuevo c�digo
        {
            char szFileName[MAX_PATH];
            HINSTANCE hInstance = GetModuleHandle(NULL);

            GetModuleFileName(hInstance, szFileName, MAX_PATH);
            MessageBox(hwnd, szFileName, "This program is:", MB_OK | MB_ICONINFORMATION);
        }
// Fin del nuevo c�digo
        break;
        case WM_CLOSE:
            DestroyWindow(hwnd);
        break;
        case WM_DESTROY:
            PostQuitMessage(0);
        break;
        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

Observa el nuevo conjunto de llaves { }. Estas son necesarias cuando declaramos una variable dentro de una sentencia switch( ). Esto es conocimiento b�sico de C pero pienso que es bueno recordarlo para aquellos que est�n haciendo las cosas por el camino dif�cil.

Una vez que has agregado el c�digo, comp�lalo. Si funciona, has click dentro de la ventana y ver�s desplegado un cuadro con el nombre del archivo .exe .

Habr�s notado que hemos agregado dos variables, hInstance y szFileName. Observa la funci�n GetModuleFileName y ver�s que el primer par�metro es un HINSTANCE refiri�ndose al m�dulo ejecutable (nuestro programa,el archivo .exe). �Donde obtenemos eso? GetModuleHandle( ) es la respuesta. Las referencias de GetModuleHandle( ) indican que si pasamos NULL como par�metro, nos retornar� un "Handle al archivo usado para crear el proceso de llamada" que es exactamente lo que necesitamos: el HINSTANCE anteriormente mencionado. Poniendo todo esto, finalizamos con la siguiente declaraci�n:

HINSTANCE hInstance = GetModuleHandle(NULL);

Ahora, con el segundo par�metro, nuevamente usamos nuestro confiable manual de referencia. Este dice: "un puntero al buffer que recibe la ruta y el nombre de archivo del modulo especificado" y el tipo de dato es LPTSTR (o LPSTR si tu referencia es vieja). Debido a que LPSTR es equivalente a char* podemos declarar un arreglo de caracteres como el siguiente:

char szFileName[MAX_PATH];

MAX_PATH es una macro inclu�da v�a <windows.h> definida como la m�xima longitud de un buffer necesario para almacenar el nombre de un archivo bajo Win32. Pasamos MAX_PATH a la funci�n GetModuleFileName( ) para que �sta conozca el tama�o del buffer.

Luego de que GetModuleFileName( ) es llamado, el b�ffer szFileName ser� llenado con un string terminado en cero conteniendo el nombre de nuestro archivo exe. Pasamos este valor a MessageBox() porque es una forma f�cil de mostrarlo en pantalla.

Por lo tanto, si has agregado esto en el c�digo, comp�lalo. Si funciona haz click en la ventana y deber�s ver desplegado un mensaje con el nombre del archivo.

Si esto no funciona, aqu� esta el c�digo completo. Comp�ralo con el tuyo para encontrar que errores has cometido.

#include <windows.h>

const char g_szClassName[] = "myWindowClass";

LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch(msg)
    {
        case WM_LBUTTONDOWN:
        {
            char szFileName[MAX_PATH];
            HINSTANCE hInstance = GetModuleHandle(NULL);

            GetModuleFileName(hInstance, szFileName, MAX_PATH);
            MessageBox(hwnd, szFileName, "This program is:", MB_OK | MB_ICONINFORMATION);
        }
        break;
        case WM_CLOSE:
            DestroyWindow(hwnd);
        break;
        case WM_DESTROY:
            PostQuitMessage(0);
        break;
        default:
            return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpCmdLine, int nCmdShow)
{
    WNDCLASSEX wc;
    HWND hwnd;
    MSG Msg;

    wc.cbSize        = sizeof(WNDCLASSEX);
    wc.style         = 0;
    wc.lpfnWndProc   = WndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
    wc.lpszMenuName  = NULL;
    wc.lpszClassName = g_szClassName;
    wc.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);

    if(!RegisterClassEx(&wc))
    {
        MessageBox(NULL, "Window Registration Failed!", "Error!",
            MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    hwnd = CreateWindowEx(
        WS_EX_CLIENTEDGE,
        g_szClassName,
        "The title of my window",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 240, 120,
        NULL, NULL, hInstance, NULL);

    if(hwnd == NULL)
    {
        MessageBox(NULL, "Window Creation Failed!", "Error!",
            MB_ICONEXCLAMATION | MB_OK);
        return 0;
    }

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    while(GetMessage(&Msg, NULL, 0, 0) > 0)
    {
        TranslateMessage(&Msg);
        DispatchMessage(&Msg);
    }
    return Msg.wParam;
}

Copyright © 1998-2003, Brook Miles (theForger). All rights reserved.

Version en Espa�ol: Federico Pizarro - 2003.