Wychwycenie zamknięcia programu

0

Jest program A i program B, oba napisane przeze mnie w C#. Program A uruchamia program B za pomocą ProcessStartInfo przy włączonym RedirectStandardInput i RedirectStandardOutput. Program B pracuje w pętli, w której oczekuje na linię tekstu w konsoli za pomocą Console.ReadLine, a informację zwrotną przekazuje za pomocą Console.WriteLine. Oba programy działają równolegle, w momencie, jak program B ma wykonać czynność, to program A wykonuje Process_.StandardInput.WriteLine("JakisTekst") i odczytuje Process_.StandardOutput.ReadLine().

Wszystko działa prawidłowo i zgodnie z oczekiwaniem, ale jest jeden problem: Jak program A zostanie zakończony, to program B będzie działać dalej. W jaki sposób z poziomu programu B można wychwycić zakończenie programu A? Jakby nie było, zamknięcie jednego z tych dwóch programów powoduje zerwanie podłączenia wejścia i wyjścia standardowego programu B z programem A. Udało mi się to obejść, że program B ma timer, gdzie jak nie dostanie komunikatu przez pewien czas to się zamknie. W programie A udało mi się wychwycić fakt zamknięcia programu B poprzez stwierdzenie pustego obiektu Process_ lub Process_.StartInfo (obiekt Process_ jest klasy Process).

0
MexikanoS napisał(a):

https://stackoverflow.com/a/3147949/3238517

Jak dobrze rozumiem, przekładając na opisany przeze mnie problem, pod tym linkiem jest wychwycenie zamknięcia B w aplikacji A, która uruchomiła aplikację B i aplikacja A sprawdza status procesu bądź oczekuje na zakończenie procesu B. Natomiast mój problem jest taki, że:

  1. Uruchamiam aplikację A.
  2. Aplikacja A tworzy obiekt klasy Process i uruchamia aplikację B.
  3. Aplikacja B pracuje, a aplikacja A jest zamykana (obojętnie, czy robi to użytkownik w standardowy sposób, czy odbywa się poprzez uwalenie procesu A w menedżerze zadań).
  4. Aplikacja B pracuje nadal i ja chciałbym wychwycić fakt zamknięcia aplikacji A bądź chciałbym aplikację B wywołać w taki sposób (może to kwestia konfiguracji ProcessInfo), że mówiąc po programistycznemu zniszczenie obiektu klasy Process (następuje najpóźniej w chwili zamknięcia aplikacji A) powoduje natychmiastowe uwalenie wywołanej aplikacji (w tym przypadku aplikacji B) bądź poinformowanie jej o tym (np. wysłanie określonego tekstu na standardowe wejście B).
0

Jedyne co mi do głowy przychodzi, to możesz w aplikacji A wychwycić event zamknięcia i w tym evencie zamknąć aplikację B

    class Program
    {
        static void Main(string[] args)
        {
            AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
        }

        private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
        {
            var processes = Process.GetProcessesByName("whatever");
            foreach(var process in processes)
            {
                process.Kill();
            }
        }
    }
0

Wg mnie fakty które są związane z (jakimkolwiek) kończeniem procesu są potencjalnie skomplikowane i niestabilne.

Co jest rzeczywistym celem? Ze wszystkich sił bym zagadnienie ustawił inaczej

0

Najprościej wpisać w Google "c# pobranie listy procesów", sprawdzać co jakiś intetwał czasu i jeżeli szukanego procesu nie ma na liście wykonać akcję

0
AdamWox napisał(a):

Jedyne co mi do głowy przychodzi, to możesz w aplikacji A wychwycić event zamknięcia i w tym evencie zamknąć aplikację B

    class Program
    {
        static void Main(string[] args)
        {
            AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit;
        }

        private static void CurrentDomain_ProcessExit(object sender, EventArgs e)
        {
            var processes = Process.GetProcessesByName("whatever");
            foreach(var process in processes)
            {
                process.Kill();
            }
        }
    }

Przetestowałem ten sposób i daje efekt zgodny z oczekiwaniem. Dodatkowo, jako alternatywę zaimplementowałem destruktor i w num wywołałem zamknięcie B i również jest tak, jak oczekiwałem, ale to działa dobrze tylko w przypadku standardowego zamknięcia A. W przypadku zakończenia procesu A w menedżerze zadań ani jeden ani drugi sposób nie działa.

AnyKtokolwiek napisał(a):

Wg mnie fakty które są związane z (jakimkolwiek) kończeniem procesu są potencjalnie skomplikowane i niestabilne.

Zgadzam się z tym i docelowo zamiast zabijania procesu B będę chciał spowodować zwykłe zamknięcie B za pomocą odpowiedniego polecenia wysłanego na standardowe wejście B. Natomiast zabijanie A nie jest przewidziane w normalnym użytkowaniu, jednak na taką ewentualność chcę się przygotować, żeby aplikacja B nie wisiała.

AnyKtokolwiek napisał(a):

Co jest rzeczywistym celem? Ze wszystkich sił bym zagadnienie ustawił inaczej

Tak naprawdę, ten mechanizm będę chciał wykorzystywać wielokrotnie do różnych celów. W jednym z przypadków jest użycie wbudowanego WebBrowser w WinForms do uruchamiania JavaScript (np. odczyt jakiś informacji z map Google) i wcześniejsze testy wykazały, że jeżeli aplikacja wykorzystująca WebBrowser jest zawieszona, to WebBrowser również jest zawieszony, nie udało mi się uruchomić WebBrowsera w osobnym wątku, żeby mógł działać przy zawieszonym głównym interfejsie, a uruchomienie drugiej specjalnej aplikacji rozwiązało problem (aplikacja macierzysta jest dużym systemem, na którego implementację nie mam pełnego wpływu).

Porobiłem dalsze testy i w aplikacji B musi być pętla uruchomiona w osobnym wątku (przykład, który zapamiętuje każde polecenie, zamyka aplikację na polecenie "exit"):

        void ProcessWorkLoop()
        {
            while (true)
            {
                string CMD_ = Console.ReadLine();
                if (CMD_ == null)
                {
                    CMD_ = "";
                    Environment.Exit(0);
                    Application.Exit();
                }
                LastCmdI = CMD_;
                Console.WriteLine(LastCmdO);

                string[] CMD = CMD_.Split('|');
                switch (CMD[0].ToUpperInvariant())
                {
                    case "exit":
                        Environment.Exit(0);
                        Application.Exit();
                        break;
                }
            }
        }

Stwierdziłem, że w B normalnie pętla wisi na Console.ReadLine() i odbiera polecenia, ale jak aplikacja A zniknie (zamknięcie lub zabicie z menedżera zadań), to Console.ReadLine() w B nie zatrzymuje przetwarzania, tylko zwraca null i to wystarczy wychwycić i w takiej sytuacji zamknąć aplikację B.

Ostatecznie problem natenczas wydaje się rozwiązany, przy jednoczesnym zastosowaniu tego, co proponuje AdamWox, destruktora w A i reakcji na null w B nie stwierdziłem przypadków wiszącej aplikacji B przy braku aplikacji A.

1 użytkowników online, w tym zalogowanych: 0, gości: 1