Was ist eine Nachrichtenpumpe?


102

In diesem Thread (veröffentlicht vor ungefähr einem Jahr) werden Probleme besprochen, die beim Ausführen von Word in einer nicht interaktiven Sitzung auftreten können. Der dort gegebene (ziemlich starke) Rat ist, dies nicht zu tun. In einem Beitrag heißt es: "Die Office-APIs setzen alle voraus, dass Sie Office in einer interaktiven Sitzung auf einem Desktop mit Monitor, Tastatur und Maus und vor allem einer Nachrichtenpumpe ausführen." Ich bin mir nicht sicher, was das ist. (Ich programmiere erst seit ungefähr einem Jahr in C #. Meine andere Programmiererfahrung war hauptsächlich mit ColdFusion.)

Aktualisieren:

Mein Programm durchläuft eine große Anzahl von RTF-Dateien, um zwei Informationen zu extrahieren, die zum Erstellen einer medizinischen Berichtsnummer verwendet werden. Anstatt herauszufinden, wie die Formatierungsanweisungen in RTF funktionieren, habe ich beschlossen, sie einfach in Word zu öffnen und den Text von dort herauszuholen (ohne die GUI tatsächlich zu starten). Gelegentlich hatte das Programm während der Verarbeitung einer Datei Probleme und ließ einen Word-Thread offen, der an dieses Dokument angehängt war (ich muss noch herausfinden, wie ich diese herunterfahren kann). Als ich das Programm erneut ausführte, erhielt ich natürlich eine Benachrichtigung, dass ein Thread diese Datei verwendet, und wollte ich eine schreibgeschützte Kopie öffnen? Als ich Ja sagte, tauchte plötzlich die Word-Benutzeroberfläche aus dem Nichts auf und begann mit der Verarbeitung der Dateien. Ich habe mich gefragt, warum das passiert ist.


3
Warum ist dies als win32 markiert? - Das Nachrichtensystem war in Windows V1 (was, wie ich mich erinnere, 8 Bit war.)
Hogan

Antworten:


185

Eine Nachrichtenschleife ist ein kleiner Code, der in jedem nativen Windows-Programm vorhanden ist. Es sieht ungefähr so ​​aus:

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

Die GetMessage () Win32-API ruft eine Nachricht von Windows ab. Ihr Programm verbringt normalerweise 99,9% seiner Zeit dort und wartet darauf, dass Windows ihm mitteilt, dass etwas Interessantes passiert ist. TranslateMessage () ist eine Hilfsfunktion, die Tastaturmeldungen übersetzt. DispatchMessage () stellt sicher, dass die Fensterprozedur mit der Nachricht aufgerufen wird.

Jedes GUI-fähige .NET-Programm verfügt über eine Nachrichtenschleife, die von Application.Run () gestartet wird.

Die Relevanz einer Nachrichtenschleife für Office hängt mit COM zusammen. Office-Programme sind COM-fähige Programme. So funktionieren die Microsoft.Office.Interop-Klassen. COM kümmert sich um das Threading im Auftrag einer COM-Coclass. Es stellt sicher, dass Anrufe, die über eine COM-Schnittstelle getätigt werden, immer vom richtigen Thread aus erfolgen. Die meisten COM-Klassen haben einen Registrierungsschlüssel in der Registrierung, der ihr ThreadingModel deklariert. Die bei weitem häufigsten (einschließlich Office) verwenden "Apartment". Dies bedeutet, dass der einzige sichere Weg zum Aufrufen einer Schnittstellenmethode darin besteht, den Aufruf von demselben Thread aus durchzuführen, der das Klassenobjekt erstellt hat. Oder anders ausgedrückt: Die meisten COM-Klassen sind bei weitem nicht threadsicher.

Jeder COM-fähige Thread gehört zu einer COM-Wohnung. Es gibt zwei Arten, Single Threaded Apartments (STA) und Multi Threaded Apartments (MTA). Auf einem STA-Thread muss eine COM-Klasse mit Apartment-Thread erstellt werden. Sie können dies wieder in .NET-Programmen sehen. Der Einstiegspunkt des UI-Threads eines Windows Forms- oder WPF-Programms hat das Attribut [STAThread]. Das Apartmentmodell für andere Threads wird mit der Thread.SetApartmentState () -Methode festgelegt.

Große Teile der Windows-Installation funktionieren nicht ordnungsgemäß, wenn der UI-Thread nicht STA ist. Insbesondere Drag + Drop, die Zwischenablage, Windows-Dialoge wie OpenFileDialog, Steuerelemente wie WebBrowser, UI-Automatisierungs-Apps wie Screenreader. Und viele COM-Server wie Office.

Eine harte Anforderung für einen STA-Thread ist, dass er niemals blockieren und eine Nachrichtenschleife pumpen muss. Die Nachrichtenschleife ist wichtig, da COM damit einen Schnittstellenmethodenaufruf von einem Thread zum anderen marshallt. Obwohl .NET das Marshalling von Aufrufen vereinfacht (z. B. Control.BeginInvoke oder Dispatcher.BeginInvoke), ist dies eine sehr schwierige Aufgabe. Der Thread, der den Aufruf ausführt, muss sich in einem bekannten Zustand befinden. Sie können einen Thread nicht einfach willkürlich unterbrechen und ihn zu einem Methodenaufruf zwingen, der schreckliche Probleme beim Wiedereintritt verursachen würde. Ein Thread sollte "inaktiv" sein und keinen Code ausführen, der den Status des Programms verändert.

Vielleicht können Sie sehen, wohin das führt: Ja, wenn ein Programm die Nachrichtenschleife ausführt, ist es inaktiv. Das eigentliche Marshalling erfolgt über ein verstecktes Fenster, das COM erstellt. Es verwendet PostMessage, damit die Fensterprozedur dieses Fensters Code ausführt. Auf dem STA-Thread. Die Nachrichtenschleife stellt sicher, dass dieser Code ausgeführt wird.


Sehr schöne und detaillierte Antwort. Nur um es hinzuzufügen - es gibt auch eine spezielle STA, die als Haupt-STA bezeichnet wird und die erste STA ist, die erstellt wurde. Welches sollte idealerweise das sein, das von Ihrem UI-Thread erstellt wurde. In der Haupt-STA werden Komponenten mit Threading-Modell = keine erstellt. Wenn Ihre Haupt-STA nicht die von Ihrem UI-Thread erstellte ist, können bei Verwendung älterer ActiveX-Steuerelemente mit Threading-Modell keine interessante Probleme auftreten.
Quixver

12

Die "Nachrichtenpumpe" ist ein zentraler Bestandteil jedes Windows-Programms, das für das Versenden von Fensternachrichten an die verschiedenen Teile der Anwendung verantwortlich ist. Dies ist der Kern der Win32-UI-Programmierung. Aufgrund seiner Allgegenwart verwenden viele Anwendungen die Nachrichtenpumpe, um Nachrichten zwischen verschiedenen Modulen zu übertragen. Aus diesem Grund werden Office-Anwendungen unterbrochen, wenn sie ohne Benutzeroberfläche ausgeführt werden.

Wikipedia hat eine grundlegende Beschreibung .


Ich glaube, es ist unmöglich, eine Windows-App ohne eine Nachrichtenschleife zu schreiben, daher verwenden alle Anwendungen die Nachrichtenpumpe.
Hogan

2
Sie können auch einfache GUI-Apps ohne eine schreiben. Sie können beispielsweise Nachrichtenfelder öffnen, ohne dass Ihre eigene App eine Nachrichtenschleife in Ihrer App hat.

Wenn Sie einen Dialog über DialogBox oder DialogBox indirekt erstellen - Sie benötigen keine Nachrichtenschleife, müssen Sie nur eine Funktion (dlgproc) angeben, die von Windows aufgerufen wird. (und ein Meldungsfeld ist nur ein einfacher Dialog)
Quixver

6

John spricht darüber, wie das Windows-System (und andere fensterbasierte Systeme - X Window , ursprüngliches Mac OS ...) asynchrone Benutzeroberflächen mithilfe von Ereignissen über ein Nachrichtensystem implementieren.

Hinter den Kulissen jeder Anwendung befindet sich ein Nachrichtensystem, in dem jedes Fenster Ereignisse an andere Fenster oder Ereignis-Listener senden kann. Dies wird durch Hinzufügen einer Nachricht zur Nachrichtenwarteschlange implementiert. Es gibt eine Hauptschleife, die immer diese Nachrichtenwarteschlange betrachtet und dann die Nachrichten (oder Ereignisse) an die Listener sendet.

Der Wikipedia-Artikel Nachrichtenschleife in Microsoft Windows zeigt Beispielcode eines grundlegenden Windows-Programms - und wie Sie auf der grundlegendsten Ebene sehen können, ist ein Windows-Programm nur die "Nachrichtenpumpe".

Also, um alles zusammen zu ziehen. Der Grund, warum ein Windows-Programm zur Unterstützung einer Benutzeroberfläche nicht als Dienst fungieren kann, liegt darin, dass die Nachrichtenschleife ständig ausgeführt werden muss, um die Unterstützung der Benutzeroberfläche zu aktivieren. Wenn Sie es wie beschrieben als Service implementieren, kann es die interne asynchrone Ereignisbehandlung nicht verarbeiten.


6

In COM serialisiert und de-serialisiert eine Nachrichtenpumpe Nachrichten, die zwischen Wohnungen gesendet werden. Eine Wohnung ist ein Mini-Prozess, in dem COM-Komponenten ausgeführt werden können. Die Apartments sind in den Modi Single Threaded und Free Threaded erhältlich. Single-Threaded-Apartments sind hauptsächlich ein Legacy-System für Anwendungen von COM-Komponenten, die kein Multithreading unterstützen. Sie wurden normalerweise mit Visual BASIC (da dies keinen Multithread-Code unterstützte) und Legacy-Anwendungen verwendet.

Ich vermute, dass die Message Pump-Anforderung für Word entweder von der COM-API oder von Teilen der Anwendung herrührt, die nicht threadsicher sind. Beachten Sie, dass die .NET- Threading- und Garbage Collection-Modelle mit COM out of the box nicht gut funktionieren. COM verfügt über einen sehr vereinfachten Garbage Collection-Mechanismus und ein Threading-Modell, bei denen Sie die Dinge auf COM-Weise ausführen müssen. Bei Verwendung der Standard- Office-PIAs müssen Sie die COM-Objektreferenzen weiterhin explizit herunterfahren, sodass Sie jedes erstellte COM-Handle im Auge behalten müssen. Die PIAs erstellen auch Dinge hinter den Kulissen, wenn Sie nicht vorsichtig sind.

Die .NET-COM-Integration ist ein ganzes Thema für sich, und es gibt sogar Bücher zu diesem Thema. Selbst wenn Sie COM-APIs für Office aus einer interaktiven Desktop-Anwendung verwenden, müssen Sie durch die Rahmen springen und sicherstellen, dass Verweise explizit freigegeben werden.

Es kann davon ausgegangen werden, dass Office threadsicher ist. Daher benötigen Sie für jeden Thread eine separate Instanz von Word-, Excel- oder anderen Office- Anwendungen. Sie müssten den Startaufwand übernehmen oder einen Thread-Pool verwalten. Ein Thread-Pool müsste sorgfältig getestet werden, um sicherzustellen, dass alle COM-Referenzen korrekt freigegeben wurden. Selbst beim Starten und Herunterfahren von Instanzen müssen Sie sicherstellen, dass alle Referenzen korrekt freigegeben sind. Wenn Sie Ihr i nicht punktieren und Ihr t hier kreuzen, wird eine große Anzahl toter COM-Objekte und sogar ganze laufende Instanzen von Word durchgesickert.


1
Ihre Antwort enthält einige Ungenauigkeiten. Es gibt 3 Arten von Wohnungen - STA (Single Threaded), MTA (Multi Threaded) und NTA (Neutral Threaded). Mit freiem Gewinde wird eine Komponente beschrieben, die den Marshaller mit freiem Gewinde zusammenfasst. Es gibt keine Wohnung mit freiem Gewinde. COM verwendet Nachrichten, um mit STAs zu kommunizieren. Für Komponenten, die in einem MTA leben (oder die den Marshaller mit freiem Thread zusammenfassen), sind keine Nachrichtenschleifen erforderlich. AFAIK - lpc wird verwendet, um Daten vom aufrufenden Thread zu einem Thread aus dem RPC-Thread-Pool zu marshallen, der dann die Methode tatsächlich aufruft.
Quixver


0

Ich denke, dass diese Channel 9-Diskussion eine schöne, prägnante Erklärung hat:

Dieser Prozess der Fensterkommunikation wird durch die sogenannte Windows Message Pump ermöglicht. Stellen Sie sich die Message Pump als eine Einheit vor, die die Zusammenarbeit zwischen Anwendungsfenstern und dem Desktop ermöglicht.


2
wow ... das ist ein schreckliches und irreführendes Zitat. (Eine "Entität"? Err .. nein.)
Hogan

4
Entität - Objekt: etwas, das als einzelnes separates Objekt existiert oder als einzelnes Objekt wahrgenommen wird encarta.msn.com/dictionary_1861608661/entity.html
Matthew Whited
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.