c# winapi winmain message-pump

c# - ¿Qué es una bomba de mensajes?



winapi winmain (6)

En este hilo (publicado hace aproximadamente un año) hay una discusión de los problemas que pueden surgir al ejecutar Word en una sesión no interactiva. El (bastante fuerte) consejo dado allí es no hacerlo. En una publicación se afirma que "todas las API de Office asumen que está ejecutando Office en una sesión interactiva en un escritorio, con un monitor, teclado y mouse y, lo que es más importante, una bomba de mensajes". No estoy seguro de qué es eso. (He estado programando en C # por solo un año, mi otra experiencia de programación ha sido principalmente con ColdFusion).

Actualizar:

Mi programa se ejecuta a través de una gran cantidad de archivos RTF para extraer dos piezas de información utilizadas para construir un número de informe médico. En lugar de intentar averiguar cómo funcionan las instrucciones de formato en RTF, decidí simplemente abrirlas en Word y sacar el texto de allí (sin realmente iniciar la GUI). Ocasionalmente, el programa caía en el medio de procesar un archivo y dejaba un hilo de Word abierto adjunto a ese documento (aún tengo que encontrar la manera de cerrarlo). Cuando volví a ejecutar el programa, por supuesto recibí una notificación de que había un hilo que usaba ese archivo, y ¿quería abrir una copia de solo lectura? Cuando dije Sí, la GUI de Word apareció repentinamente de la nada y comenzó a procesar los archivos. Me preguntaba por qué sucedió eso; pero parece que una vez que el cuadro de diálogo apareció, ¿la bomba de mensajes también comenzó a presionar la GUI principal hacia Windows?


Creo que esta discusión del Canal 9 tiene una bonita explicación sucinta:

Este proceso de comunicación de ventana es posible gracias a la llamada Bomba de mensaje de Windows. Piense en Message Pump como una entidad que permite la cooperación entre las ventanas de aplicaciones y el escritorio.


En COM , un mensaje bomba serializa y deserializa mensajes enviados entre apartamentos. Un apartamento es un mini proceso en el que se pueden ejecutar componentes COM. Los apartamentos vienen en modos de rosca simple y rosca libre. Los apartamentos de una sola rosca son principalmente un sistema heredado para aplicaciones de componentes COM que no admiten multi-threading. Normalmente se usaban con Visual BASIC (ya que no era compatible con el código de subprocesos múltiples) y aplicaciones heredadas.

Supongo que el requisito de bomba de mensajes para Word proviene de la API COM o de partes de la aplicación que no son seguras para subprocesos. Tenga en cuenta que los modelos .NET threading y recolección de basura no funcionan muy bien con COM desde el primer momento. COM tiene un mecanismo de recolección de basura muy simplista y un modelo de subprocesamiento que requiere que haga las cosas de la manera COM. El uso de las PIA de Office estándar aún requiere que cierre explícitamente las referencias de objetos COM, por lo que debe realizar un seguimiento de cada identificador COM creado. Los PIA también crearán cosas entre bastidores si no tienes cuidado.

La integración de .NET-COM es un tema completo por sí mismo, e incluso hay libros escritos sobre el tema. Incluso el uso de las API COM para Office desde una aplicación de escritorio interactiva requiere que salte los aros y asegúrese de que las referencias se publiquen de manera explícita.

Se puede asumir que Office es inseguro, por lo que necesitará una instancia separada de Word, Excel u otras aplicaciones de Office para cada subproceso. Tendría que incurrir en la sobrecarga inicial o mantener un grupo de subprocesos. Un grupo de subprocesos tendría que probarse meticulosamente para asegurarse de que todas las referencias COM se publicaran correctamente. Incluso al iniciar y cerrar instancias, debe asegurarse de que todas las referencias se publiquen correctamente. Si no se salpican sus i''s y se cruzan las t, se producirán grandes cantidades de objetos COM muertos e incluso se perderán todas las instancias de Word en ejecución.


John está hablando de cómo el sistema de Windows (y otros sistemas basados ​​en ventanas - X Window , Mac OS original ...) implementan interfaces de usuario asíncronas usando eventos a través de un sistema de mensajes.

Detrás de escena para cada aplicación hay un sistema de mensajería en el que cada ventana puede enviar eventos a otras ventanas o detectores de eventos: esto se implementa agregando un mensaje a la cola de mensajes. Hay un bucle principal que siempre se ejecuta mirando esta cola de mensajes y luego enviando los mensajes (o eventos) a los oyentes.

El artículo de Wikipedia Message loop en Microsoft Windows muestra un código de ejemplo de un programa básico de Windows, y como se puede ver en el nivel más básico, un programa de Windows es simplemente la "bomba de mensajes".

Entonces, juntarlo todo. La razón por la que un programa de Windows diseñado para admitir una IU no puede actuar como un servicio es porque necesita que el bucle de mensajes se ejecute todo el tiempo para habilitar el soporte de UI. Si lo implementa como un servicio como se describe, no podrá procesar el manejo de eventos asíncronos internos.


La "bomba de mensajes" es una parte central de cualquier programa de Windows que se encarga de enviar mensajes de ventanas a las diversas partes de la aplicación. Este es el núcleo de la programación de la interfaz de usuario de Win32. Debido a su ubicuidad, muchas aplicaciones utilizan la bomba de mensajes para pasar mensajes entre diferentes módulos, por lo que las aplicaciones de Office se romperán si se ejecutan sin ninguna interfaz de usuario.

Wikipedia tiene una descripción básica .


Un bucle de mensaje es una pequeña porción de código que existe en cualquier programa nativo de Windows. Parece más o menos así:

MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); }

La API GetMessage () Win32 recupera un mensaje de Windows. Su programa generalmente pasa el 99.9% de su tiempo allí, esperando que Windows le diga que sucedió algo interesante. TranslateMessage () es una función auxiliar que traduce los mensajes del teclado. DispatchMessage () garantiza que se invoque el procedimiento de ventana con el mensaje.

Cada programa .NET habilitado para GUI tiene un bucle de mensaje, es iniciado por Application.Run ().

La relevancia de un bucle de mensaje para Office está relacionada con COM. Los programas de Office son programas COM-habilitados, así es como funcionan las clases Microsoft.Office.Interop. COM se encarga de enhebrar en nombre de un COM coclass, asegura que las llamadas realizadas en una interfaz COM siempre se realicen desde el hilo correcto. La mayoría de las clases COM tienen una clave de registro en el registro que declara su ThreadingModel, de lejos los más comunes (incluido Office) utilizan "Apartamento". Lo que significa que la única manera segura de llamar a un método de interfaz es haciendo la llamada desde el mismo hilo que creó el objeto de clase. O para decirlo de otra manera: la mayoría de las clases COM no son seguras para subprocesos.

Todos los hilos habilitados para COM pertenecen a un apartamento COM. Hay dos tipos, Single Threaded Apartments (STA) y un Multi Thread Apartment (MTA). Se debe crear una clase COM de apartamento encadenado en un subproceso STA. Puede ver esto en los programas .NET, el punto de entrada del subproceso UI de un programa Windows Forms o WPF tiene el atributo [STAThread]. El modelo de apartamento para otros subprocesos se establece mediante el método Thread.SetApartmentState ().

Las partes grandes de las tuberías de Windows no funcionarán correctamente si el hilo de la interfaz de usuario no es STA. En particular, arrastrar y soltar, el portapapeles, los cuadros de diálogo de Windows como OpenFileDialog, controles como WebBrowser, aplicaciones de automatización de la interfaz de usuario como lectores de pantalla. Y muchos servidores COM, como Office.

Un requisito difícil para un hilo STA es que nunca debe bloquearse y debe bombear un bucle de mensaje. El ciclo de mensajes es importante porque eso es lo que COM usa para ordenar una llamada de método de interfaz de un hilo a otro. Aunque .NET facilita las llamadas de referencia (Control.BeginInvoke o Dispatcher.BeginInvoke por ejemplo), en realidad es algo muy complicado de hacer. El hilo que ejecuta la llamada debe estar en un estado conocido. No se puede simplemente interrumpir arbitrariamente un hilo y forzarlo a realizar una llamada a un método, lo que causaría problemas de reentrada horribles. Un hilo debe estar "inactivo", no está ocupado ejecutando ningún código que esté mutando el estado del programa.

Quizás pueda ver a dónde lleva eso: sí, cuando un programa está ejecutando el ciclo de mensajes, está inactivo. El cálculo real se lleva a cabo a través de una ventana oculta que crea COM, utiliza PostMessage para que el procedimiento de ventana de esa ventana ejecute el código. En el hilo STA. El ciclo de mensajes garantiza que este código se ejecute.