jueves, 22 de abril de 2010

Manual: WM_DESTROY VS WM_CLOSE

 

Con motivo del arreglo del Bug de “Open with…” vamos a intentar explicar qué es esto del WM_DESTROY y del WM_CLOSE.

Lo básico: Todas las ventanas de Windows/ReactOS funcionan a través de mensajes.

windowsmessagesPor ejemplo, cuando hacemos click con el ratón en una ventana, se le está mandando un mensaje a la ventana indicándole que hemos clickeado sobre ella. Entonces la ventana recibe el mensaje y actúa en consecuencia. Para ello tenemos que programar cómo debe actuar al recibir al recibir un determinado mensaje.

Mensajes hay unos cuantos: Mensaje de que estamos haciendo click con el botón izquierdo, otro cuando lo hacemos con el derecho, otro cuando hacemos scroll,etc..( AQUÍ podéis ver una lista de ellos.)

Y por supuesto “mensajes más especiales”. Vamos esta vez a centrarnos en estos dos mensajes especiales: WM_DESTROY y WM_CLOSE 

Al clickear sobre una región “especial” como el botón cerrar, minimizar,etc.…la ventana recibe mensajes “especiales” y la ventana reacciona según lo que le hayamos programado.

Por ejemplo, al recibir un mensaje “especial” de cerrado podríamos hacer que suene la canción de “Paquito el chocolatero” o hacer que la ventana se cierre. Es decir: El mensaje en sí mismo no provoca el cierre de la ventana, sino que hace reaccionar a la ventana según lo que hayamos programado.

 

WM_DESTROY vs WM_CLOSE

 

(Por cierto: Al igual que la ventana recibe mensajes, la ventana también manda mensajes al Sistema Operativo.Es una comunicación de dos direcciones)

Para ello vamos a analizar 2 tipos de mensajes “especiales” que son bastante cabroncetes… WM_CLOSE, WM_DESTROY

Parecen ser lo mismo, pero tienen sutiles diferencias.

 

WM_CLOSE: Es un mensaje que se manda a la ventana indicándole que el usuario ha realizado una petición para cerrar una ventana,como por ejemplo pinchar sobre el botón X o hacer Alt+F4. Ambas acciones(pinchar en el botón cerrar o hacer Alt+F4) mandan un mensaje WM_CLOSE a la ventana.

 

WM_DESTROY: Indica que una ventana está siendo destruida. Es decir, ya ha comenzado la destrucción de la misma. Este mensaje es enviado después de que la Ventana haya sido eliminada de la pantalla.

 

¿Entonces para que sirve WM_DESTROY?

 

Tanto WM_CLOSE como WM_DESTROY funcionan de manera coordinada. Pero antes de llamar a WM_DESTROY se tiene que haber llamado a WM_CLOSE. Veamos un ejemplo:

El usuario decide cerrar una ventana cualquiera, para ello pincha sobre el botón de cerrar.

 

El Sistema Operativo manda a la ventana un mensaje especial llamado WM_CLOSE, la ventana comprueba como debe reaccionar ante ese mensaje, lo lógico es que mande “destruir la ventana física”,la aplicación sigue corriendo de fondo pero la ventana ha sido destruida y por tanto no la muestra.Efectivamente: Solo hemos matado la interfaz con el usuario, pero el Administrador de Tareas nos mostraría el proceso corriendo de fondo.

Pero inmediatamente la aplicación recibe un WM_DESTROY y entonces el proceso es Destruido.El Administrador de Tareas dejaría de mostrarnos el proceso. Ahora si que está todo bien cerrado.

 

 

Firefox y WM_DESTROY

 

firefox-ejecutandose Un ejemplo de esto ocurre con Firefox( o al menos a mi).

¿Os habéis dado cuenta que a veces al cerrar FF y al intentar lanzarlo de nuevo rápidamente os lanza un mensaje de "Firefox ya se está ejecutando, pero no responde. Para abrir una ventana nueva, debe cerrar antes el proceso Firefox, o reiniciar su sistema” .?

Lo que podría estar ocurriendo es que si bien se ha cerrado la ventana física con un WM_CLOSE, el proceso firefox.exe no ha recibido aún el WM_DESTROY y tenemos el proceso corriendo de fondo.

Esto puede ser debido a que tiene que ejecutar muchas acciones  al recibir el WM_CLOSE (que si guardar las Pestañas, almacenar los datos de perfil, ni idea,quién sabe).Si en ese momento tienes el Administrador de Tareas abierto, veras el proceso en memoria. Al cabo de unos segundos verás como el proceso desaparece: Por fin ha recibido el WM_DESTROY.

Lo dicho, WM_DESTROY es un mensaje que no se manda a la ventana hasta que el programa no haya terminado de ejecutar TODO EL CÓDIGO DE WM_CLOSE. Si tuviéramos una función dentro de WM_CLOSE que se tardara 15 minutos en ejecutar(una burrada), entonces hasta dentro de 15 minutos la ventana no recibiría el WM_DESTROY.

 

¿Y donde estaba el Bug en ReactOS…?

 

Simplemente en una confusión. Fijaos de nuevo:

-    case WM_DESTROY:

+    case WM_CLOSE:

         FreeListItems(hwndDlg);

         EndDialog(hwndDlg, 0);

         return TRUE;

(el – indica que se ha removido esa línea de código, mientras el + indica que se ha añadido esa línea de código)

Estábamos indicándole que al recibir un WM_DESTROY cerráramos el diálogo( EndDialog). Sin embargo, quien debe cerrar la ventana es realmente WM_CLOSE. Esto sería un error de lógica.Pero,como hemos dicho,nadie nos impide que  ejecutemos “Paquito el chocolatero” o nadie nos impide poner la destrucción del diálogo dentro de WM_DESTROY.

El verdadero problema es que nunca estábamos recibiendo un mensaje WM_DESTROY pues no teníamos un WM_CLOSE!!(El WM_Destroy se manda después de ejecutar el código de WM_CLOSE). Por lo tanto nunca se ejecutaba ese trozo de código y nunca se cerraba la ventana.

Pero hay una forma de mandar un WM-DESTROY sin pasar por un WM_CLOSE.¿Cómo?Usando el ADMINISTRADOR DE TAREAS.

 

 

¿Por qué es útil que el Administrador mande directamente un WM_DESTROY?

 

end_process Si la aplicación se ha quedado colgada y no es capaz de procesar el WM_CLOSE(cuando se queda colgada por mucho que le demos a cerrar, la aplicación no reacciona¿verdad?) tenemos un medio de obligar el cierre.

Si el administrador mandara un WM_CLOSE estaríamos en las mismas, la aplicación está “pillada/bloqueada” y es incapaz de procesar el código de WM_CLOSE y por lo tanto nunca recibirá el deseado WM_DESTROY.

 

 

Por eso el Administrador manda directamente un mensaje para destruir el proceso(WM_DESTROY), de esta manera destruye el proceso de raíz  y lógicamente mata la ventana de la aplicación. Muerto el perro se acabó la rabia ;)

Sin embargo nosotros no podemos hacer eso, el “Open with…” está asociado al proceso explorer.exe. Matando el proceso mataríamos todo el explorador,  escritorio incluido. Ya que el “Open with…” no es un proceso independiente como podría ser el firefox.exe.

 

Espero que os haya quedado un poco más claro el tema de los mensajes, y que el commit de Gregor sea ahora un poco mas comprensible :)

2 comentarios:

  1. Añadir que estas funciones se usan en ensamblador, estoy programando MiniPad, un editor de textos (como el Bloc de Notas) y uso estas funciones de WM_CLOSE y después WM_DESTROY
    Gracias por explicar esto bien detallado. Saludos ;)

    ResponderEliminar