Cos'è un message pump?


104

In questo thread (pubblicato circa un anno fa) c'è una discussione sui problemi che possono derivare dall'esecuzione di Word in una sessione non interattiva. Il consiglio (abbastanza forte) dato lì è di non farlo. In un post si afferma "Tutte le API di Office presumono che si stia eseguendo Office in una sessione interattiva su un desktop, con un monitor, una tastiera e un mouse e, soprattutto, un message pump". Non sono sicuro di cosa sia. (Sto programmando in C # solo da circa un anno; l'altra mia esperienza di programmazione è stata principalmente con ColdFusion.)

Aggiornare:

Il mio programma esegue un gran numero di file RTF per estrarre due informazioni utilizzate per costruire un numero di referto medico. Piuttosto che cercare di capire come funzionano le istruzioni di formattazione in RTF, ho deciso di aprirle semplicemente in Word ed estrarre il testo da lì (senza effettivamente avviare la GUI). Occasionalmente, il programma singhiozzava durante l'elaborazione di un file e lasciava aperto un thread di Word allegato a quel documento (devo ancora capire come chiuderlo). Quando ho rieseguito il programma, ovviamente ho ricevuto una notifica che c'era un thread che utilizzava quel file e volevo aprire una copia di sola lettura? Quando ho detto Sì, la GUI di Word è spuntata improvvisamente dal nulla e ha iniziato a elaborare i file. Mi chiedevo perché fosse successo;


3
Perché è etichettato come win32? - Il sistema di messaggi era in Windows V1 (che, come ricordo, era a 8 bit.)
Hogan

Risposte:


187

Un loop di messaggi è una piccola porzione di codice che esiste in qualsiasi programma Windows nativo. Sembra più o meno così:

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

L'API Win32 GetMessage () recupera un messaggio da Windows. Il tuo programma in genere trascorre il 99,9% del suo tempo lì, in attesa che Windows gli dica che è successo qualcosa di interessante. TranslateMessage () è una funzione di supporto che traduce i messaggi della tastiera. DispatchMessage () garantisce che la routine della finestra venga chiamata con il messaggio.

Ogni programma .NET abilitato per GUI ha un loop di messaggi, viene avviato da Application.Run ().

La rilevanza di un loop di messaggi per Office è correlata a COM. I programmi di Office sono programmi abilitati per COM, ecco come funzionano le classi Microsoft.Office.Interop. COM si occupa del threading per conto di una coclasse COM, garantisce che le chiamate effettuate su un'interfaccia COM siano sempre effettuate dal thread corretto. La maggior parte delle classi COM ha una chiave di registro nel registro che dichiara il proprio ThreadingModel, di gran lunga le più comuni (incluso Office) utilizzano "Apartment". Ciò significa che l'unico modo sicuro per chiamare un metodo di interfaccia è effettuare la chiamata dallo stesso thread che ha creato l'oggetto classe. O per dirla in un altro modo: di gran lunga la maggior parte delle classi COM non sono thread-safe.

Ogni thread abilitato per COM appartiene a un apartment COM. Esistono due tipi, Single Threaded Apartments (STA) e Multi Thread Apartment (MTA). È necessario creare una classe COM con thread apartment su un thread STA. È possibile visualizzarlo di nuovo nei programmi .NET, il punto di ingresso del thread dell'interfaccia utente di un programma Windows Form o WPF ha l'attributo [STAThread]. Il modello apartment per altri thread è impostato dal metodo Thread.SetApartmentState ().

Gran parte dell'impianto idraulico di Windows non funzionerà correttamente se il thread dell'interfaccia utente non è STA. In particolare Drag + Drop, gli appunti, finestre di dialogo di Windows come OpenFileDialog, controlli come WebBrowser, app di automazione interfaccia utente come lettori di schermo. E molti server COM, come Office.

Un requisito fondamentale per un thread STA è che non deve mai bloccare e deve pompare un loop di messaggi. Il ciclo di messaggi è importante perché è ciò che COM utilizza per effettuare il marshalling di una chiamata al metodo di interfaccia da un thread a un altro. Sebbene .NET semplifichi le chiamate di marshalling (Control.BeginInvoke o Dispatcher.BeginInvoke ad esempio), in realtà è una cosa molto complicata da fare. Il thread che esegue la chiamata deve essere in uno stato noto. Non puoi semplicemente interrompere arbitrariamente un thread e costringerlo a fare una chiamata al metodo, ciò causerebbe orribili problemi di rientro. Un thread dovrebbe essere "inattivo", non impegnato nell'esecuzione di codice che muta lo stato del programma.

Forse puoi vedere dove porta: sì, quando un programma sta eseguendo il loop dei messaggi, è inattivo. Il marshalling effettivo avviene tramite una finestra nascosta che crea COM, utilizza PostMessage per fare in modo che la routine della finestra di quella finestra esegua il codice. Sul thread STA. Il loop di messaggi garantisce che questo codice venga eseguito.


Risposta molto bella e dettagliata. Giusto per aggiungere, c'è anche una STA speciale chiamata STA principale, che è la prima STA creata. Che dovrebbe idealmente essere quello creato dal thread dell'interfaccia utente. La STA principale è dove vengono creati i componenti con modello di threading = nessuno. Se la STA principale non è quella creata dal thread dell'interfaccia utente, potresti incorrere in problemi interessanti quando utilizzi controlli Activex meno recenti che non hanno alcun modello di threading.
quixver

12

Il "message pump" è una parte fondamentale di qualsiasi programma Windows responsabile dell'invio di messaggi a finestre alle varie parti dell'applicazione. Questo è il cuore della programmazione dell'interfaccia utente di Win32. A causa della sua ubiquità, molte applicazioni utilizzano il message pump per trasmettere messaggi tra diversi moduli, motivo per cui le applicazioni di Office si interromperanno se vengono eseguite senza alcuna interfaccia utente.

Wikipedia ha una descrizione di base .


Credo che sia impossibile scrivere un'app per Windows senza un loop di messaggi, quindi tutte le applicazioni utilizzano il message pump.
Hogan

2
Puoi anche scrivere semplici app GUI senza una, ad esempio puoi visualizzare finestre di messaggio senza che la tua app abbia un loop di messaggi nella tua app.

Se crei una finestra di dialogo tramite DialogBox o DialogBox indiretto, non hai bisogno di un loop di messaggi, devi solo fornire una funzione (dlgproc) che verrà chiamata da Windows. (e una finestra di messaggio è solo una semplice finestra di dialogo)
quixver

6

John sta parlando di come il sistema Windows (e altri sistemi basati su finestre - X Window , Mac OS originale ...) implementano interfacce utente asincrone utilizzando eventi tramite un sistema di messaggi.

Dietro le quinte di ogni applicazione c'è un sistema di messaggistica in cui ogni finestra può inviare eventi ad altre finestre o listener di eventi - questo viene implementato aggiungendo un messaggio alla coda dei messaggi. C'è un ciclo principale che viene eseguito sempre guardando questa coda di messaggi e quindi inviando i messaggi (o eventi) agli ascoltatori.

L'articolo di Wikipedia Message loop in Microsoft Windows mostra il codice di esempio di un programma Windows di base - e come puoi vedere al livello più elementare un programma Windows è solo il "message pump".

Quindi, per mettere tutto insieme. Il motivo per cui un programma Windows progettato per supportare un'interfaccia utente non può agire come un servizio è perché ha bisogno del ciclo di messaggi in esecuzione tutto il tempo per abilitare il supporto dell'interfaccia utente. Se lo implementi come servizio come descritto, non sarà in grado di elaborare la gestione degli eventi asincroni interni.


6

In COM , una pompa di messaggi serializza e deserializza i messaggi inviati tra gli appartamenti. Un apartment è un mini processo in cui è possibile eseguire i componenti COM. Gli appartamenti sono disponibili in modalità thread singolo e thread gratuito. Gli appartamenti a thread singolo sono principalmente un sistema legacy per applicazioni di componenti COM che non supportano il multi-threading. In genere venivano utilizzati con Visual BASIC (poiché non supportava il codice multi-thread) e applicazioni legacy.

Immagino che il requisito del message pump per Word derivi dall'API COM o da parti dell'applicazione che non sono thread-safe. Tieni presente che i modelli di threading .NET e Garbage Collection non funzionano bene con COM out of the box. COM ha un meccanismo di raccolta dei rifiuti molto semplicistico e un modello di threading che richiede di fare le cose nel modo COM. L'utilizzo dei PIA standard di Office richiede ancora l'arresto esplicito dei riferimenti agli oggetti COM, quindi è necessario tenere traccia di ogni handle COM creato. I PIA creeranno anche cose dietro le quinte se non stai attento.

L'integrazione .NET-COM è un intero argomento da solo e ci sono persino libri scritti sull'argomento. Anche l'utilizzo di API COM per Office da un'applicazione desktop interattiva richiede di saltare attraverso i cerchi e assicurarsi che i riferimenti siano rilasciati in modo esplicito.

Si può presumere che Office non sia sicuro per i thread, quindi sarà necessaria un'istanza separata di Word, Excel o altre applicazioni di Office per ogni thread. Dovresti sostenere l'overhead iniziale o mantenere un pool di thread. Un pool di thread dovrebbe essere testato meticolosamente per assicurarsi che tutti i riferimenti COM siano stati rilasciati correttamente. Anche l'avvio e l'arresto delle istanze richiedono che tutti i riferimenti vengano rilasciati correttamente. La mancata puntatura delle i e incrociare le t qui comporterà la perdita di un gran numero di oggetti COM morti e persino di intere istanze di Word in esecuzione.


1
Ci sono alcune imprecisioni nella tua risposta. Ci sono 3 tipi di appartamenti: STA (Single threaded), MTA (Multi Threaded) e NTA (Neutral Threaded). Threaded gratuito viene utilizzato per descrivere un componente che aggrega il marshaller con thread gratuito, non esiste un appartamento con thread gratuito. COM utilizza i messaggi per comunicare con gli STA. Per i componenti che vivono in un MTA (o che aggregano il marshaller con thread gratuito) non sono richiesti loop di messaggi. AFAIK - lpc viene utilizzato per effettuare il marshalling dei dati dal thread chiamante a un thread dal pool di thread RPC che quindi richiama effettivamente il metodo.
quixver


0

Penso che questa discussione su Channel 9 abbia una bella spiegazione succinta:

Questo processo di comunicazione tramite finestra è reso possibile dal cosiddetto Windows Message Pump. Pensa a Message Pump come a un'entità che consente la cooperazione tra le finestre dell'applicazione e il desktop.


2
wow ... questa è una citazione orribile e fuorviante. (Una "entità"? Err .. no.)
Hogan

4
entità - oggetto: qualcosa che esiste come o è percepito come un singolo oggetto separato encarta.msn.com/dictionary_1861608661/entity.html
Matthew Whited
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.