Capítulo 4 El procedimiento de ventana

Cada ventana tiene una función asociada, esta función se conoce como procedimiento de ventana, y es la encargada de procesar adecuadamente todos los mensajes enviados a una determinada clase de ventana. Es la responsable de todo lo relativo al aspecto y al comportamiento de una ventana.

Normalmente, estas funciones están basadas en una estructura "switch" donde cada "case" corresponde aun determinado tipo de mensaje.

Sintaxis

LRESULT CALLBACK WindowProcedure(
   HWND hwnd,     // Manipulador de ventana
   UINT msg,      // Mensaje
   WPARAM wParam, // Parámetro palabra, varía 
   LPARAM lParam  // Parámetro doble palabra, varía
  );
  • hwnd es el manipulador de la ventana a la que está destinado el mensaje.
  • msg es el código del mensaje.
  • wParam es el parámetro de tipo palabra asociado al mensaje.
  • lParam es el parámetro de tipo doble palabra asociado al mensaje.

Podemos considerar este prototipo como una plantilla para crear nuestros propios procedimientos de ventana. El nombre de la función puede cambiar, pero el valor de retorno y los parámetros deben ser los mismos. El miembro lpfnWndProc de la estructura WNDCLASS es un puntero a una función de este tipo, esa función es la que se encargará de procesar todos los mensajes para esa clase de ventana. Cuando registremos nuestra clase de ventana, tendremos que asignar a ese miembro el puntero a nuestro procedimiento de ventana.

Para más detalles sobre la función de procedimiento de ventana, consultar WindowProc.

Prototipo de procedimiento de ventana

LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM);

Implementación de procedimiento de ventana simple

/* Esta función es llamada por la función del API DispatchMessage() */
LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)                  /* manipulador del mensaje */
    {
        case WM_DESTROY:
           PostQuitMessage(0);    /* envía un mensaje WM_QUIT a la cola de mensajes */
           break;
        default:                  /* para los mensajes de los que no nos ocupamos */
           return DefWindowProc(hwnd, msg, wParam, lParam);
    }
    return 0;
}

En general, habrá tantos procedimientos de ventana como programas diferentes y todos serán distintos, pero también tendrán algo en común: todos ellos procesarán los mensajes que lleguen a una clase de ventana.

En este ejemplo sólo procesamos un tipo de mensaje, se trata de WM_DESTROY que es el mensaje que se envía a una ventana cuando se recibe un comando de cerrar, ya sea por menú o mediante el icono de aspa en la esquina superior derecha de la ventana.

Este mensaje sólo sirve para informar a la aplicación de que el usuario tiene la intención de abandonar la aplicación, y le da una oportunidad de dejar las cosas en su sitio: cerrar ficheros, liberar memoria, guardar variables, etc. Incluso, la aplicación puede decidir que aún no es el momento adecuado para abandonar la aplicación. En el caso del ejemplo, efectivamente cierra la aplicación, y lo hace enviándole un mensaje WM_QUIT, mediante la función PostQuitMessage.

El resto de los mensajes se procesan en el caso "default", y simplemente se cede su tratamiento a la función del API que hace el proceso por defecto para cada mensaje, DefWindowProc.

Este es el camino que sigue el mensaje WM_QUIT cuando llega, ya que el proceso por defecto para este mensaje es cerrar la aplicación.

En posteriores capítulos veremos como se complica paulatinamente esta función, añadiendo más y más mensajes.

Primer ejemplo de programa Windows

Ya estamos en condiciones de crear nuestro primer programa Windows, que sólo mostrará una ventana en pantalla.


  Nombre Fichero Fecha Tamaño Contador Descarga
D Ejemplo 1 win001.zip 2004-01-18 1937 bytes 5961

Comentarios de los usuarios (6)

Guillermo Escalona
2012-10-06 20:27:21

Hola que tal una duda porque no corre el programa de la ventanita en visual studio c++ 2010 Express

Steven R. Davidson
2012-10-06 21:25:33

Hola Guillermo,

Hemos probado el ejemplo en VC++ 2010 Express y no hemos tenido graves problemas. Sí hay unos errores pero no son graves. Al usar las cadenas literales, debemos usar la macro 'TEXT()' para que se pueda adaptar a una cadena de caracteres ASCII o Unicode. Reemplaza las sentencias por éstas:

wincl.lpszClassName = TEXT("NUESTRA_CLASE");
...
hwnd = CreateWindowEx(
           0,
           TEXT("NUESTRA_CLASE"),
           TEXT("Ejemplo 001"), 
           ...
);

Comprueba que no tienes errores de compilación ni de enlazado. Recuerda que debes crear un proyecto para "Win32" y recomendamos que el proyecto esté vacío al comenzar.

Espero que esto te ayude.

Steven

JDC
2012-10-11 20:08:26

Hola quisiera que el color de fondo de la ventana sea gris en vez de azul, probe cambiando la linea 31

wincl.hbrBackground = GetSysColorBrush(COLOR_BACKGROUND);

por

wincl.hbrBackground = GetSysColorBrush(RGB(127,127,127));

pero no sucede nada, quiero hacer esto porque en windows7 el color de fondo por defecto es negro y no azul como en windows xp.

saludos

Steven R. Davidson
2012-10-11 23:00:44

Hola JDC,

La función 'GetSysColorBrush()' ( http://winapi.conclase.net/curso/?winfun=GetSysColorBrush#inicio ) sirve para obtener un pincel ya creado por el sistema operativo a partir de los colores del sistema que quedan configurados. De hecho, puedes cambiar estos colores como usuario yendo al panel de control para personalizar la apariencia de las ventanas y el escritorio. Esto significa que el entero que pasamos a esta 'GetSysColorBrush()' es para buscar un color específico y ya creado.

En general, puedes crear tu propio pincel, usando 'CreateSolidBrush()', para lo que quieres. Por ejemplo,

wincl.hbrBackground = CreateSolidBrush( RGB(127,127,127) );

Eso sí, debes recordar destruir este pincel al no necesitarlo más, invocando 'DeleteObject()'. Por ejemplo,

int WINAPI WinMain( HINSTANCE hInstance, 
                   HINSTANCE hPrevInstance,
                   LPSTR lpszCmdParam, 
                   int nCmdShow )
{
  wincl.hbrBackground = CreateSolidBrush( RGB(127,127,127) );
  ...
  DeleteObject( wincl.hbrBackground );
  return Message.wParam;
}

Sin embargo, para el caso específico de un pincel gris, puedes usar el pincel gris - previamente creado por el sistema - para tal color, invocando 'GetStockObject()'. Esto sería simplemente,

wincl.hbrBackground = (HBRUSH)GetStockObject( GRAY_BRUSH );

Hablamos de este tema de los pinceles en el capítulo 20 ( http://winapi.conclase.net/curso/index.php?cap=020#inicio ).

Espero que esto te oriente.

Steven

JDC
2012-10-12 03:09:57

Muy buena respuesta funciono muy bien, gracias por compartir tus conocimientos con todos nosotros

Jared Diaz
2012-10-15 00:32:23

Hola buen dia, me parece excelente el tutorial, estoy aprendiendo a programar con winapi sin embargo como lepuedo hacer para que los programas ejemplos los pueda compilar y ejecutar en dev c++?,saludosy gracias.