Qu'est-ce qu'une pompe à messages?

104

Dans ce fil (publié il y a environ un an), il y a une discussion sur les problèmes qui peuvent survenir lors de l'exécution de Word dans une session non interactive. Le conseil (assez fort) qui y est donné est de ne pas le faire. Dans un article, il est indiqué «Les API Office supposent toutes que vous exécutez Office dans une session interactive sur un bureau, avec un moniteur, un clavier et une souris et, surtout, une pompe à messages». Je ne sais pas ce que c'est. (Je programme en C # depuis environ un an seulement; mon autre expérience de programmation a été principalement avec ColdFusion.)

Mettre à jour:

Mon programme parcourt un grand nombre de fichiers RTF pour extraire deux informations utilisées pour construire un numéro de rapport médical. Plutôt que d'essayer de comprendre comment fonctionnent les instructions de formatage dans RTF, j'ai décidé de simplement les ouvrir dans Word et d'extraire le texte de là (sans réellement démarrer l'interface graphique). Parfois, le programme a eu un hoquet au milieu du traitement d'un fichier et a laissé un fil Word ouvert attaché à ce document (je dois encore trouver comment fermer celui-ci). Lorsque j'ai relancé le programme, bien sûr, j'ai reçu une notification indiquant qu'un fil de discussion utilisait ce fichier et est-ce que je voulais ouvrir une copie en lecture seule? Quand j'ai dit Oui, l'interface graphique de Word est soudainement sortie de nulle part et a commencé à traiter les fichiers. Je me demandais pourquoi cela arrivait;

Matt Gutting
la source
3
Pourquoi est-ce étiqueté comme win32? - Le système de messagerie était dans Windows V1 (qui, si je me souviens bien, était 8 bits.)
Hogan

Réponses:

187

Une boucle de message est un petit morceau de code qui existe dans n'importe quel programme Windows natif. Cela ressemble à peu près à ceci:

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

L'API Win32 GetMessage () récupère un message de Windows. Votre programme y passe généralement 99,9% de son temps à attendre que Windows lui dise que quelque chose d'intéressant s'est produit. TranslateMessage () est une fonction d'assistance qui traduit les messages du clavier. DispatchMessage () garantit que la procédure de fenêtre est appelée avec le message.

Chaque programme .NET activé par l'interface graphique a une boucle de message, il est démarré par Application.Run ().

La pertinence d'une boucle de message pour Office est liée à COM. Les programmes Office sont des programmes compatibles COM, c'est ainsi que fonctionnent les classes Microsoft.Office.Interop. COM s'occupe du threading pour le compte d'une coclasse COM, il garantit que les appels effectués sur une interface COM sont toujours effectués à partir du thread correct. La plupart des classes COM ont une clé de registre dans le registre qui déclare leur ThreadingModel, de loin les plus courants (y compris Office) utilisent "Appartement". Ce qui signifie que le seul moyen sûr d'appeler une méthode d'interface est de faire l'appel depuis le même thread qui a créé l'objet de classe. Ou pour le dire autrement: de loin, la plupart des classes COM ne sont pas thread-safe.

Chaque thread compatible COM appartient à un cloisonnement COM. Il existe deux types, les appartements à un seul filetage (STA) et un appartement à plusieurs threads (MTA). Une classe COM à thread cloisonné doit être créée sur un thread STA. Vous pouvez le voir dans les programmes .NET, le point d'entrée du thread d'interface utilisateur d'un programme Windows Forms ou WPF a l'attribut [STAThread]. Le modèle d'appartement pour les autres threads est défini par la méthode Thread.SetApartmentState ().

De grandes parties de la plomberie Windows ne fonctionneront pas correctement si le filetage de l'interface utilisateur n'est pas STA. Notamment Drag + Drop, le presse-papiers, les boîtes de dialogue Windows comme OpenFileDialog, des contrôles comme WebBrowser, des applications UI Automation comme des lecteurs d'écran. Et de nombreux serveurs COM, comme Office.

Une exigence absolue pour un thread STA est qu'il ne doit jamais se bloquer et doit pomper une boucle de message. La boucle de message est importante car c'est ce que COM utilise pour marshaler un appel de méthode d'interface d'un thread à un autre. Bien que .NET facilite le marshaling des appels (Control.BeginInvoke ou Dispatcher.BeginInvoke par exemple), c'est en fait une chose très délicate à faire. Le thread qui exécute l'appel doit être dans un état connu. Vous ne pouvez pas simplement interrompre arbitrairement un thread et le forcer à faire un appel de méthode, ce qui causerait d'horribles problèmes de rentrée. Un thread doit être "inactif", pas occupé à exécuter un code qui modifie l'état du programme.

Vous pouvez peut-être voir où cela mène: oui, lorsqu'un programme exécute la boucle de message, il est inactif. Le marshaling réel a lieu via une fenêtre masquée créée par COM, il utilise PostMessage pour que la procédure de fenêtre de cette fenêtre exécute le code. Sur le fil STA. La boucle de message garantit que ce code s'exécute.

Hans Passant
la source
Réponse très agréable et détaillée. Juste pour ajouter - il existe également un STA spécial appelé STA principal, qui est le premier STA créé. Qui devrait idéalement être celui créé par votre thread d'interface utilisateur. Le STA principal est l'endroit où les composants avec le modèle de thread = aucun sont créés. Si votre STA principal n'est pas celui créé par votre thread d'interface utilisateur, vous pourriez rencontrer des problèmes intéressants lors de l'utilisation d'anciens contrôles Activex qui n'ont aucun modèle de thread.
quixver
12

La "pompe à messages" est une partie centrale de tout programme Windows qui est responsable de l'envoi de messages de fenêtrage aux différentes parties de l'application. C'est le cœur de la programmation de l'interface utilisateur Win32. En raison de son omniprésence, de nombreuses applications utilisent la pompe de messages pour transmettre des messages entre différents modules, c'est pourquoi les applications Office seront interrompues si elles sont exécutées sans interface utilisateur.

Wikipedia a une description de base .

JSB ձոգչ
la source
Je pense qu'il est impossible d'écrire une application Windows sans boucle de message, donc toutes les applications utilisent la pompe à messages.
Hogan
2
Vous pouvez également écrire des applications GUI simples sans une - par exemple, vous pouvez afficher des boîtes de message sans que votre propre application ait une boucle de message dans votre application.
Si vous créez un dialogue via DialogBox ou DialogBox indirect - vous n'avez pas besoin d'une boucle de message, il vous suffit de fournir une fonction (dlgproc) qui sera appelée par windows. (et une boîte de message n'est qu'un simple dialogue)
quixver
6

John parle de la façon dont le système Windows (et d'autres systèmes basés sur des fenêtres - X Window , Mac OS d'origine ...) implémentent des interfaces utilisateur asynchrones en utilisant des événements via un système de messagerie.

Dans les coulisses de chaque application, il y a un système de messagerie où chaque fenêtre peut envoyer des événements à d'autres fenêtres ou écouteurs d'événements - ceci est mis en œuvre en ajoutant un message à la file d'attente de messages. Il existe une boucle principale qui s'exécute toujours en regardant cette file d'attente de messages, puis en distribuant les messages (ou événements) aux écouteurs.

L'article de Wikipedia Boucle de message dans Microsoft Windows montre un exemple de code d'un programme Windows de base - et comme vous pouvez le voir au niveau le plus élémentaire, un programme Windows n'est que la «pompe à messages».

Donc, pour tout rassembler. La raison pour laquelle un programme Windows conçu pour prendre en charge une interface utilisateur ne peut pas agir en tant que service est qu'il a besoin que la boucle de messages s'exécute en permanence pour activer la prise en charge de l'interface utilisateur. Si vous l'implémentez en tant que service comme décrit, il ne pourra pas traiter la gestion des événements asynchrones internes.

Hogan
la source
6

Dans COM , une pompe de messages sérialise et désérialise les messages envoyés entre les appartements. Un cloisonnement est un mini processus dans lequel les composants COM peuvent être exécutés. Les appartements sont disponibles en mode fileté unique et fileté gratuit. Les appartements à un seul thread sont principalement un système hérité pour les applications de composants COM qui ne prennent pas en charge le multi-threading. Ils étaient généralement utilisés avec Visual BASIC (car cela ne prenait pas en charge le code multi-thread) et les applications héritées.

Je suppose que l'exigence de la pompe de message pour Word provient de l'API COM ou de parties de l'application qui ne sont pas thread-safe. Gardez à l'esprit que les modèles de thread .NET et de garbage collection ne fonctionnent pas correctement avec COM prêt à l'emploi. COM a un mécanisme de garbage collection et un modèle de thread très simplistes qui vous obligent à faire les choses à la manière COM. L'utilisation des assemblys PIA Office standard vous oblige toujours à arrêter explicitement les références d'objet COM, vous devez donc garder une trace de chaque handle COM créé. Les PIA créeront également des éléments dans les coulisses si vous ne faites pas attention.

L'intégration .NET-COM est un sujet à part entière, et il existe même des livres écrits sur le sujet. Même l'utilisation des API COM pour Office à partir d'une application de bureau interactive vous oblige à franchir des obstacles et à vous assurer que les références sont explicitement publiées.

Office peut être considéré comme non sécurisé pour les threads, vous aurez donc besoin d'une instance distincte de Word, Excel ou d'autres applications Office pour chaque thread. Vous devrez engager la surcharge de démarrage ou maintenir un pool de threads. Un pool de threads devrait être méticuleusement testé pour s'assurer que toutes les références COM ont été correctement libérées. Même le démarrage et l'arrêt des instances vous obligent à vous assurer que toutes les références sont libérées correctement. Le fait de ne pas mettre en point vos i et croiser vos t ici entraînera la fuite d'un grand nombre d'objets COM morts et même d'instances entières de Word en cours d'exécution.

ConcernedOfTunbridgeWells
la source
1
Il y a quelques inexactitudes dans votre réponse. Il existe 3 types d'appartements - STA (filetage unique), MTA (filetage multiple) et NTA (filetage neutre). Free threaded est utilisé pour décrire un composant qui agrège le marshaller thread gratuit, il n'y a pas d'appartements threadés libres. COM utilise des messages pour communiquer avec les STA. Pour les composants vivant dans un MTA (ou qui regroupent le marshaller threadé gratuit), aucune boucle de message n'est requise. AFAIK - lpc est utilisé pour rassembler les données du thread appelant vers un thread du pool de threads RPC qui appelle alors la méthode.
quixver
0

Je pense que cette discussion sur Channel 9 a une belle explication succincte:

Ce processus de communication de fenêtre est rendu possible par ce que l'on appelle Windows Message Pump. Pensez à Message Pump comme une entité qui permet la coopération entre les fenêtres d'application et le bureau.

Richard Ev
la source
2
wow ... c'est une citation horrible et trompeuse. (Une "entité"? Euh .. non.)
Hogan
4
entité - objet: quelque chose qui existe ou est perçu comme un seul objet séparé encarta.msn.com/dictionary_1861608661/entity.html
Matthew Whited