[C con Clase] Parámetros de WM_QUIT y PostQuitMessage() en WinAPI

Salvador Pozo salvapozo en gmail.com
Mie Jul 16 09:46:56 CEST 2008


El día 16 de julio de 2008 6:53, David Reza escribió:
> Hola, me estoy iniciando en el curos de WIN API 32 de Windows y a pesar de
> que usa tecnicismos que desconozco, o mejor dicho, desconocía, he ido
> aprendiendo mucho; pero tengo una duda respecto a lo siguiente:

Hola:

Primero, me alegra saber que estas aprendiendo tanto.

Segundo, si hay palabras que necesitan definiciones o aclaraciones, te
agradecería que me lo indicases, de modo que pueda completar el curso.
Al final hay un capítulo de "glosario" donde he incluido algunas
definiciones, pero obviamente, este capítulo se puede mejorar.

Y tercero, intentaré aclararte esta duda.

> En la definición del mensaje WM_QUIT viene ésto:
> ...

Windows es un sistema multitarea, y esto complica un poco la gestión
de los programas que se ejecutan en su entorno. El control de cada
programa está orientado a eventos, y no a procedimientos. Un evento es
casi cualquier cosa que puede suceder y que pueda requerir una acción
por parte de nuestro programa. Mientras no ocurra ninguno de esos
eventos, nuestro programa permanecerá en un estado de espera, sin
consumir tiempo de CPU.

Un modo de notificar eventos es usar mensajes. Estos mensajes se
pueden enviar entre la aplicación y el sistema operativo, entre
distintas aplicaciones o desde una aplicación a ella misma. Además,
estos mensajes no siempre se podrán atender tan pronto como se
reciben, de modo que el sistema mantiene una cola en la que se
almacenan estos mensajes y se envían uno a uno a cada aplicación, a
medida que su bucle de mensajes los pide y procesa.

Por lo tanto, aunque los mensajes pueden tener parámetros, no se
procesan del mismo modo que los parámetros de las funciones, ya que
forman parte del sistema operativo, no del lenguaje de programación.

Para enviar mensajes se usan funciones del API, como SendMessage,
PostMessage, etc. Hay varias funciones, dependiendo de la urgencia del
mensaje o del tipo de mensaje. Algunos mensajes tienen incluso una
función específica para enviarlos, como el que nos ocupa: WM_QUIT, que
se envía usando la función PostQuitMessage.

Como curiosidad, ya que esto aún no se ha mencionado en el curso,
enviar un mensaje mediante Post es más rápido que hacerlo mediante
Send. En el primer caso no se almacena el mensaje en la cola, sino que
se envía directamente a la aplicación.

Por lo tanto, cuando usamos la función PostQuitMessage, estamos
intentando cerrar la aplicación. En el parámetro indicaremos el valor
de salida que queremos que tenga la aplicación al cerrarse, esto es
análogo al valor de retorno de la función "main" en los programas de
consola corrientes. El valor de retorno puede ser usado por otras
aplicaciones para saber el motivo por el que la aplicación ha
terminado.

Si nos fijamos en el bucle de mensajes de la aplicación:
----8<------
   /* Bucle de mensajes: */
   while(TRUE == GetMessage(&mensaje, 0, 0, 0))
   {
      TranslateMessage(&mensaje);
      DispatchMessage(&mensaje);
   }

   return mensaje.wParam;
----8<------

Cuando el mensaje es WM_QUIT, el valor retornado por GetMessage es
cero, (como se explica en la documentación del mensaje), es decir,
false. El mensaje no se envía al procedimiento de ventana, y se
termina la aplicación con el valor de retorno contenido en wParam.

La idea es que, cuando queremos que nuestra aplicación termine, le
enviamos un mensaje mediante la función PostQuitMessage. El bucle de
mensajes no procesará este mensaje, y terminará la aplicación con el
valor que hayamos indicado.

> Yo no sé muy bien que es un mensaje concretamente, o cuál debe ser su
> estructura, pero por lo que leo y a mi entender, los mensajes también tienen
> parámetros, pero en el texto de la definición de WinMain dice:

Los parámetros de los mensajes se pueden indicar de muchos modos.
Cuando los mensajes los envía la propia aplicación de forma explícita,
se usan las funciones SendMessage y PostMessage (u otras variantes,
como SendDlgItemMessage, etc).

Esta función tendrá uno o varios parámetros que nos permiten
identificar a qué ventana va dirigido el mensaje, un parámetro será el
código del mensaje, otro el parámetro wParam y otro el parámetro
lParam. Por ejemplo:

LRESULT SendMessage(
    HWND hwnd,     // manipulador de la ventana de destino
    UINT uMsg,     // mensaje a enviar
    WPARAM wParam, // primer parámetro del mensaje
    LPARAM lParam  // segundo parámetro del mensaje
   );

En el bucle de mensajes se usa la función GetMessage para recuperar el
siguiente mensaje de la cola, y se obtiene una estructura MSG con toda
la información necesaria para procesarlo:

typedef struct tagMSG { // msg
   HWND hwnd;
   UINT message;
   WPARAM wParam;
   LPARAM lParam;
   DWORD time;
   POINT pt;
} MSG;

>>
>> Si la función tiene éxito, terminando cuando recibe el mensaje WM_QUIT,
>> debe retornar con el valor de salida contenido en el parámetro wParamit del
>> mensaje. Si la función termina antes de entrar en el bucle de mensajes, debe
>> retornar con 0.
>
> pero supongo que lo de wParamit fue un error de dedo.

Efectivamente, es un error al teclear. Culpa mía. ;-)

> Entonces me gustaría que me aclararan de quién es el parámetro wParam y de
> quién es el parámetro nExitCode.

Recapitulando, la secuencia es: primero se llama a la función
PostQuitMessage, indicando qué valor queremos para wParam. El bucle de
mensajes recupera el mensaje WM_QUIT, y con él, sus parámetros. El
bucle no procesa este mensaje, y usa el valor de wParam como valor de
retorno de la función WinMain.

> Y aprovechando el correo, me gustaría preguntar cómo es la sintaxis de un
> parámetro de un mensaje. En la definición de WM_QUIT como ya mencioné, dice:
>
>>
>> WM_QUIT nExitCode = (int) wParam; // código de salida
>
>
> suponiendo que lo de nExitCode está mal y debería ser wParam, entonces los
> parámetros se declaran así nada mas, después del identificador del mensaje?
> ¿No se utilizan paréntesis o algo parecido?

La sintaxis de los mensajes no debe tomarse "al pié de la letra". Se
usan identificadores para documentar el significado de los parámetros.

Hay que tener en cuenta que a veces, en un mismo parámetro se
empaquetan varios. Por ejemplo:
WM_MOUSEMOVE
fwKeys = wParam;        // banderas de tecla
xPos = LOWORD(lParam);  // posición horizontal del cursor
yPos = HIWORD(lParam);  // posición vertical del cursor

lParam tiene el tamaño de dos palabras, y se usa la palabra de menor
peso para almacenar la coordenada x de la posición del ratón, y la de
mayor peso para la coordenada y.

En el caso de WM_QUIT, el valor de wParam debe interpretarse como un
"int", y el significado es el de un código de salida (nExitCode). La
"n" indica que se trata de un número entero, según la notación
húngara.

El parámetro wParam siempre tiene el tamaño de una palabra (word), y
el parámetro lParam el de una palabra larga o doble. Pero dependiendo
del mensaje, pueden ser enteros, banderines (campos de bits),
punteros, etc. En la sintaxis de los mensajes se explica cómo
interpretar cada parámetro.

Espero haber dejado claro algo, ya sé que tiendo a enrollarme...

Hasta pronto.
-- 
Salvador Pozo Coronado
http://www.conclase.net
mailto:salvapozo en gmail.com




Más información sobre la lista de distribución Cconclase