Sytuacja przedstawiona przez Temporala:
Klasa aplikacji tworząca w funkcji Run() obiekt managera zadań wykonywanych przez system.
Jednym z zadań jest przechwytywanie faktów wciśnięcia klawiszy niezależnie od tego jakie okno jest aktualnie aktywne (mam nadzieję, że Temp wkrótce szerzej opisze rozwiązanie). Istotą jest fakt, że dokonuje się to przez modyfikację zachowania pętli komunikatów aktywowanej przez Application.Run().
Oczywistym jest, że zadanie to jest reprezentowane przez odrębną klasę, której instację tworzy Manger. W tym konstruktorze wykonywane są wszystkie czynności związane ze zmianą zachowania pętli komunikatów (inicjalizacja kolejnego obiektu zarządzającego wywołaniami WinApi z C#). Następnym krokiem jest utworzenie przez Managera wątku uruchamiającego funkcję Start() modułu, która jest zawieszana w pętli komunikatów przez wywołanie Application.Run().
Dodać należy że zmiana zachowania pętli komunikatów dokonywana jest lokalnie dla wątku.
Efektem wykonania niniejszej sekwencji było NIC. Aplikacja się wykonywała, nie prezentując żadnych efektów. Po wielu próbach i stworzeniu dodatkowych dwóch wątków udało się stwierdzić, że system pracuje poprawnie tylko w sytuacji gdy zablokuje w się pętli komunikatów wątek mangera. TeMPOraL zaczął się denerwować, powiedziałem mu „Daj mi tego Kompa, wątki nie znają telepatii, chcę to prześledzić”. On mi na to, to już jest dość złożony projekt, i nie przebiję się. Nie dałem za wygraną, zacząłem od EntryPoint’a. Mniejsza. Po opisie który przytoczyłem i po tytule tego posta już widać, gdzie tkwi problem. Konstruktor obiektu roboczego modyfikował nie tę pętlę komunikatów, którą powinien.
Solution było proste. Przenieść inicjalizację obiektu zarządzającego WinApi do wnętrza funkcji Start(). To pokazuje dlaczego kolejność tworzenia „Wątkowych” obiektów jest bardzo ważna. Dobra rada – uważajcie!
Z każdą kolejną napisaną klasą przekonuję się, że jednak lepiej samemu pisać funkcje Init()/DeInit() i wywoływać je explicite, niż polegać na jakimkolwiek konstruktorze…