El lado oscuro de la aplicación. ProcessMessages

Artículo presentado por Marcus Junglas

Al programar un controlador de eventos en Delphi (como el Al hacer clic evento de un TButton), llega el momento en que su aplicación necesita estar ocupada por un tiempo, p. el código necesita escribir un archivo grande o comprimir algunos datos.

Si haces eso, notarás que su aplicación parece estar bloqueada. Su formulario ya no se puede mover y los botones no muestran signos de vida. Parece estar estrellado.

La razón es que una aplicación Delpi es de un solo subproceso. El código que está escribiendo representa solo un conjunto de procedimientos que son llamados por el hilo principal de Delphi cada vez que ocurre un evento. El resto del tiempo, el hilo principal maneja los mensajes del sistema y otras cosas como las funciones de manejo de formularios y componentes.

Por lo tanto, si no termina el manejo de su evento haciendo un trabajo largo, evitará que la aplicación maneje esos mensajes.

Una solución común para este tipo de problemas es llamar "Aplicación". ProcessMessages ". "Aplicación" es un objeto global de la clase TApplication.

instagram viewer

La aplicación. Processmessages maneja todos los mensajes en espera, como movimientos de ventanas, clics de botones, etc. Se usa comúnmente como una solución simple para mantener su aplicación "funcionando".

Lamentablemente, el mecanismo detrás de "ProcessMessages" tiene sus propias características, lo que puede causar una gran confusión.

¿Qué significa ProcessMessages?

PprocessMessages maneja todos los mensajes del sistema en espera en la cola de mensajes de las aplicaciones. Windows usa mensajes para "hablar" con todas las aplicaciones en ejecución. La interacción del usuario se lleva al formulario a través de mensajes y "ProcessMessages" los maneja.

Si el mouse se cae en un TButton, por ejemplo, ProgressMessages hace todo lo que debería suceder en este evento como el repinte el botón a un estado "presionado" y, por supuesto, una llamada al procedimiento de manejo de OnClick () si le asignó uno.

Ese es el problema: cualquier llamada a ProcessMessages podría contener una llamada recursiva a cualquier controlador de eventos nuevamente. Aquí hay un ejemplo:

Utilice el siguiente código para el controlador uniforme de OnClick de un botón ("trabajo"). La declaración for simula un trabajo de procesamiento largo con algunas llamadas a ProcessMessages de vez en cuando.

Esto se simplifica para una mejor legibilidad:

{en MyForm:}
Nivel de trabajo: entero;
{OnCreate:}
Nivel de trabajo: = 0;
procedimiento TForm1.WorkBtnClick (Remitente: TObject);
var
ciclo: entero;
empezar
inc (nivel de trabajo);
para ciclo: = 1 a 5 hacer
empezar
Memo1.Lines. Agregar ('- Trabajo' + IntToStr (Nivel de trabajo) + ', Ciclo' + IntToStr (ciclo);
Solicitud. ProcessMessages;
dormir (1000); // o algún otro trabajo
final;
Memo1.Lines. Add ('Trabajo' + IntToStr (WorkLevel) + 'finalizado');
dec (Nivel de trabajo);
final;

SIN "ProcessMessages", las siguientes líneas se escriben en la nota, si se presionó el botón DOS VECES en poco tiempo:

 - Trabajo 1, Ciclo 1
- Trabajo 1, Ciclo 2
- Trabajo 1, Ciclo 3
- Trabajo 1, Ciclo 4
- Trabajo 1, ciclo 5
Trabajo 1 terminado.
- Trabajo 1, Ciclo 1
- Trabajo 1, Ciclo 2
- Trabajo 1, Ciclo 3
- Trabajo 1, Ciclo 4
- Trabajo 1, ciclo 5
Trabajo 1 terminado.

Mientras el procedimiento está ocupado, el formulario no muestra ninguna reacción, pero Windows hizo el segundo clic en la cola de mensajes. Justo después de que "OnClick" haya finalizado, se volverá a llamar.

INCLUYENDO "ProcessMessages", el resultado puede ser muy diferente:

 - Trabajo 1, Ciclo 1
- Trabajo 1, Ciclo 2
- Trabajo 1, Ciclo 3
- Trabajo 2, ciclo 1
- Trabajo 2, ciclo 2
- Trabajo 2, ciclo 3
- Trabajo 2, ciclo 4
- Trabajo 2, ciclo 5
Trabajo 2 terminado.
- Trabajo 1, Ciclo 4
- Trabajo 1, ciclo 5
Trabajo 1 terminado.

Esta vez, el formulario parece estar funcionando nuevamente y acepta cualquier interacción del usuario. Por lo tanto, el botón se presiona hasta la mitad durante su primera función "trabajador" OTRA VEZ, que se manejará al instante. Todos los eventos entrantes se manejan como cualquier otra llamada de función.

En teoría, durante cada llamada a "ProgressMessages" CUALQUIER cantidad de clics y mensajes de usuario pueden ocurrir "en su lugar".

¡Así que ten cuidado con tu código!

Ejemplo diferente (en pseudocódigo simple):

procedimiento OnClickFileWrite ();
var mi archivo: = TFileStream;
empezar
mi archivo: = TFileStream.create ('myOutput.txt');
tratar
mientras BytesReady> 0 hacer
empezar
mi archivo. Escribir (DataBlock);
dec (BytesReady, sizeof (DataBlock));
DataBlock [2]: = # 13; {línea de prueba 1}
Solicitud. ProcessMessages;
DataBlock [2]: = # 13; {línea de prueba 2}
final;
finalmente
myfile.free;
final;
final;

Esta función escribe una gran cantidad de datos e intenta "desbloquear" la aplicación utilizando "ProcessMessages" cada vez que se escribe un bloque de datos.

Si el usuario vuelve a hacer clic en el botón, se ejecutará el mismo código mientras todavía se está escribiendo el archivo. Por lo tanto, el archivo no se puede abrir por segunda vez y el procedimiento falla.

Tal vez su aplicación recupere algunos errores, como liberar los búferes.

Como posible resultado, "Datablock" se liberará y el primer código generará "repentinamente" una "Infracción de acceso" cuando acceda a él. En este caso: la línea de prueba 1 funcionará, la línea de prueba 2 se bloqueará.

La mejor manera:

Para facilitarlo, puede configurar todo el formulario "habilitado: = falso", que bloquea toda la información del usuario, pero NO se lo muestra al usuario (todos los botones no están en gris).

Una mejor manera sería establecer todos los botones en "deshabilitados", pero esto podría ser complejo si desea mantener un botón "Cancelar", por ejemplo. También debe pasar por todos los componentes para deshabilitarlos y cuando se vuelven a habilitar, debe verificar si queda algo en el estado deshabilitado.

Tú podrías deshabilitar los controles secundarios de un contenedor cuando cambia la propiedad Enabled.

Como sugiere el nombre de la clase "TNotifyEvent", solo debe usarse para reacciones a corto plazo ante el evento. Para el código que consume tiempo, la mejor manera es en mi humilde opinión poner todo el código "lento" en un subproceso propio.

Con respecto a los problemas con "PrecessMessages" y / o la habilitación y deshabilitación de componentes, el uso de un segundo hilo parece no ser demasiado complicado en absoluto.

Recuerde que incluso las líneas de código simples y rápidas pueden colgarse por segundos, p. abrir un archivo en una unidad de disco podría tener que esperar hasta que finalice la rotación de la unidad. No se ve muy bien si su aplicación parece bloquearse porque la unidad es demasiado lenta.

Eso es. La próxima vez que agregue "Aplicación". ProcessMessages ", piénselo dos veces;)

instagram story viewer