Co to jest pompa wiadomości?

104

W tym wątku (opublikowanym około rok temu) znajduje się dyskusja na temat problemów, które mogą pojawić się podczas uruchamiania programu Word w sesji nieinteraktywnej. Podana (dość mocna) rada mówi, że nie należy tego robić. W jednym poście powiedziano: „Wszystkie interfejsy API pakietu Office zakładają, że uruchamiasz pakiet Office w interaktywnej sesji na komputerze stacjonarnym z monitorem, klawiaturą i myszą oraz, co najważniejsze, pompą komunikatów”. Nie jestem pewien, co to jest. (Programuję w języku C # tylko od około roku; moje inne doświadczenie w programowaniu dotyczyło głównie ColdFusion).

Aktualizacja:

Mój program przegląda dużą liczbę plików RTF, aby wyodrębnić dwie informacje użyte do skonstruowania numeru raportu medycznego. Zamiast próbować dowiedzieć się, jak działają instrukcje formatowania w formacie RTF, zdecydowałem się po prostu otworzyć je w programie Word i wyciągnąć stamtąd tekst (bez uruchamiania GUI). Czasami program miał czkawkę w trakcie przetwarzania jednego pliku i pozostawiał otwarty wątek programu Word dołączony do tego dokumentu (wciąż muszę wymyślić, jak go zamknąć). Po ponownym uruchomieniu programu otrzymałem oczywiście powiadomienie, że istnieje wątek korzystający z tego pliku i czy chciałem otworzyć kopię tylko do odczytu? Kiedy powiedziałem „Tak”, graficzny interfejs użytkownika programu Word pojawił się nagle znikąd i zaczął przetwarzać pliki. Zastanawiałem się, dlaczego tak się stało;

Matt Gutting
źródło
3
Dlaczego jest to oznaczone jako win32? - System wiadomości był w systemie Windows V1 (który, jak pamiętam, był 8-bitowy)
Hogan,

Odpowiedzi:

187

Pętla komunikatów to mały fragment kodu, który istnieje w każdym rodzimym programie Windows. Z grubsza wygląda to tak:

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

GetMessage () Win32 API pobiera komunikat z systemu Windows. Twój program zazwyczaj spędza tam 99,9% czasu, czekając, aż system Windows powie mu, że wydarzyło się coś interesującego. TranslateMessage () to funkcja pomocnicza, która tłumaczy komunikaty klawiatury. DispatchMessage () zapewnia wywołanie procedury okna wraz z komunikatem.

Każdy program .NET obsługujący graficzny interfejs użytkownika ma pętlę komunikatów, jest ona uruchamiana przez Application.Run ().

Znaczenie pętli komunikatów dla pakietu Office jest związane z COM. Programy pakietu Office są programami obsługującymi COM, tak działają klasy Microsoft.Office.Interop. COM zajmuje się wątkami w imieniu koklasy COM, zapewnia, że ​​wywołania wykonywane przez interfejs COM są zawsze wykonywane z właściwego wątku. Większość klas COM ma klucz rejestru w rejestrze, który deklaruje ich ThreadingModel, zdecydowanie najpopularniejsze (w tym Office) używają nazwy „Apartment”. Oznacza to, że jedynym bezpiecznym sposobem wywołania metody interfejsu jest wykonanie wywołania z tego samego wątku, który utworzył obiekt klasy. Albo inaczej: zdecydowanie większość klas COM nie jest bezpieczna wątkowo.

Każdy wątek z włączonym COM należy do apartamentu COM. Istnieją dwa rodzaje mieszkań, apartamenty jednowątkowe (STA) i apartamenty wielowątkowe (MTA). W wątku STA należy utworzyć klasę COM z wątkami mieszkania. Można to zobaczyć z powrotem w programach .NET, punkt wejścia wątku interfejsu użytkownika programu Windows Forms lub WPF ma atrybut [STAThread]. Model apartamentu dla innych wątków jest ustawiany przez metodę Thread.SetApartmentState ().

Duże części instalacji systemu Windows nie będą działać poprawnie, jeśli wątek interfejsu użytkownika nie jest STA. W szczególności Drag + Drop, schowek, okna dialogowe systemu Windows, takie jak OpenFileDialog, elementy sterujące, takie jak WebBrowser, aplikacje automatyzacji interfejsu użytkownika, takie jak czytniki ekranu. I wiele serwerów COM, takich jak Office.

Trudnym wymogiem dla wątku STA jest to, że nigdy nie powinien blokować i musi pompować pętlę komunikatów. Pętla komunikatów jest ważna, ponieważ właśnie tego używa COM do organizowania wywołania metody interfejsu z jednego wątku do drugiego. Chociaż .NET ułatwia organizowanie wywołań (na przykład Control.BeginInvoke lub Dispatcher.BeginInvoke), w rzeczywistości jest to bardzo trudne. Wątek, który wykonuje wywołanie, musi być w dobrze znanym stanie. Nie możesz po prostu arbitralnie przerwać wątku i zmusić go do wywołania metody, co spowodowałoby okropne problemy z ponownym włączaniem. Wątek powinien być „bezczynny”, wolny od wykonywania kodu, który zmienia stan programu.

Być może widzisz, do czego to prowadzi: tak, gdy program wykonuje pętlę komunikatów, jest bezczynny. Właściwe kierowanie odbywa się za pośrednictwem ukrytego okna, które tworzy COM, używa PostMessage, aby procedura okna tego okna wykonywała kod. W wątku STA. Pętla komunikatów zapewnia, że ​​ten kod działa.

Hans Passant
źródło
Bardzo miła i szczegółowa odpowiedź. Wystarczy dodać - istnieje również specjalna STA zwana główną STA, która jest pierwszą stworzoną STA. Który powinien być idealnie utworzony przez Twój wątek interfejsu użytkownika. W głównym STA są tworzone komponenty z modelem wątków = none. Jeśli twoja główna STA nie jest tą utworzoną przez twój wątek interfejsu użytkownika - możesz napotkać interesujące problemy podczas używania starszych kontrolek activex, które nie mają modelu wątków.
quixver
12

„Pompa komunikatów” jest podstawową częścią każdego programu Windows odpowiedzialnego za wysyłanie komunikatów okienkowych do różnych części aplikacji. To jest rdzeń programowania interfejsu użytkownika Win32. Ze względu na jego wszechobecność wiele aplikacji używa pompy komunikatów do przekazywania komunikatów między różnymi modułami, dlatego aplikacje pakietu Office ulegają awarii, jeśli są uruchamiane bez interfejsu użytkownika.

Wikipedia ma podstawowy opis .

JSB ձոգչ
źródło
Uważam, że nie da się napisać aplikacji Windows bez pętli komunikatów, dlatego wszystkie aplikacje używają pompy komunikatów.
Hogan
2
Możesz także pisać proste aplikacje GUI bez jednego - na przykład możesz wyświetlać okna komunikatów bez własnej aplikacji posiadającej pętlę komunikatów w aplikacji.
Jeśli tworzysz dialog pośrednio przez DialogBox lub DialogBox - nie potrzebujesz pętli komunikatów, wystarczy dostarczyć funkcję (dlgproc), która zostanie wywołana przez okna. (a okno komunikatu to tylko proste okno dialogowe)
quixver
6

John mówi o tym, jak system Windows (i inne systemy oparte na oknach - X Window , oryginalny Mac OS ...) implementują asynchroniczne interfejsy użytkownika wykorzystujące zdarzenia za pośrednictwem systemu wiadomości.

Za kulisami każdej aplikacji znajduje się system przesyłania wiadomości, w którym każde okno może wysyłać zdarzenia do innych okien lub detektorów zdarzeń - jest to realizowane poprzez dodanie wiadomości do kolejki wiadomości. Istnieje główna pętla, która zawsze działa, przeglądając tę ​​kolejkę komunikatów, a następnie wysyłając komunikaty (lub zdarzenia) do odbiorników.

Artykuł Wikipedii Pętla komunikatów w Microsoft Windows przedstawia przykładowy kod podstawowego programu Windows - i jak widać na najbardziej podstawowym poziomie, program Windows jest po prostu „pompą komunikatów”.

Więc zebrać to wszystko razem. Powodem, dla którego program Windows zaprojektowany do obsługi interfejsu użytkownika nie może działać jako usługa, jest to, że potrzebuje pętli komunikatów działającej przez cały czas, aby włączyć obsługę interfejsu użytkownika. Jeśli zaimplementujesz go jako usługę zgodnie z opisem, nie będzie w stanie przetworzyć wewnętrznej asynchronicznej obsługi zdarzeń.

Hogan
źródło
6

W COM komunikat pompy serializuje i deserializuje komunikaty przesyłane między mieszkaniami. Mieszkanie to mini proces, w którym można uruchomić komponenty COM. Apartamenty są dostępne w trybach jednowątkowych i swobodnych. Apartamenty jednowątkowe są głównie starszym systemem do zastosowań składników COM, które nie obsługują wielowątkowości. Były one zwykle używane z Visual BASIC (ponieważ nie obsługiwał kodu wielowątkowego) i starszymi aplikacjami.

Wydaje mi się, że wymóg pompy komunikatów dla programu Word wynika z interfejsu API COM lub części aplikacji, które nie są bezpieczne dla wątków. Należy pamiętać, że modele wątków i czyszczenia pamięci .NET nie współpracują dobrze z COM po wyjęciu z pudełka. COM ma bardzo uproszczony mechanizm wyrzucania elementów bezużytecznych i model wątków, który wymaga wykonywania czynności w sposób COM. Korzystanie ze standardowych zestawów PIA pakietu Office nadal wymaga jawnego zamknięcia odwołań do obiektów COM, dlatego należy śledzić każdy utworzony uchwyt COM. PIA będą również tworzyć rzeczy za kulisami, jeśli nie będziesz ostrożny.

Integracja .NET-COM to cały temat sam w sobie i istnieją nawet książki na ten temat. Nawet używanie interfejsów API COM dla pakietu Office z interaktywnej aplikacji komputerowej wymaga przeskakiwania przez obręcze i upewnienia się, że odwołania są jawnie zwolnione.

Można założyć, że pakiet Office jest niebezpieczny dla wątków, więc będziesz potrzebować oddzielnego wystąpienia programu Word, Excel lub innych aplikacji pakietu Office dla każdego wątku. Musiałbyś ponieść narzut początkowy lub utrzymywać pulę wątków. Pula wątków musiałaby zostać skrupulatnie przetestowana, aby upewnić się, że wszystkie odwołania COM zostały poprawnie wydane. Nawet uruchamianie i zamykanie instancji wymaga upewnienia się, że wszystkie odwołania są zwolnione poprawnie. Jeśli nie pominiesz swoich i i nie przekroczysz tutaj t, spowoduje to wyciek dużej liczby martwych obiektów COM, a nawet wycieków całych uruchomionych wystąpień programu Word.

ConcernedOfTunbridgeWells
źródło
1
Twoja odpowiedź zawiera kilka nieścisłości. Istnieją 3 rodzaje mieszkań - STA (jednowątkowe), MTA (wielowątkowe) i NTA (neutralne). Wolny gwintowany jest używany do opisania komponentu, który agreguje wolny gwintowany układ koordynacyjny, nie ma czegoś takiego jak wolny gwintowany apartament. COM używa wiadomości do komunikowania się ze stacjami STA. W przypadku komponentów żyjących w MTA (lub które agregują wolny wątkowy organizator) nie są wymagane żadne pętle komunikatów. AFAIK - lpc służy do kierowania danych z wywołania wątku do wątku z puli wątków RPC, który następnie faktycznie wywołuje metodę.
quixver
0

Myślę, że ta dyskusja na kanale 9 ma ładne, zwięzłe wyjaśnienie:

Ten proces komunikacji okienkowej jest możliwy dzięki tak zwanej pompie wiadomości systemu Windows. Pomyśl o Pompie komunikatów jako o jednostce, która umożliwia współpracę między oknami aplikacji a pulpitem.

Richard Ev
źródło
2
wow ... to okropny i mylący cytat. („Istota”? Eee… nie.)
Hogan,
4
podmiot - obiekt: coś, co istnieje lub jest postrzegane jako pojedynczy oddzielny obiekt encarta.msn.com/dictionary_1861608661/entity.html
Matthew Whited