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.