Как узнать, истек ли срок действия Process.WaitForExit() из-за закрытия основного приложения
У меня есть консольная программа C# "A", чтобы контролировать 4 экземпляра другого приложения "B".
Консольная программа создает один поток на экземпляр "B", и каждый поток запускает "B", используя Process.Start()
, Затем поток ждет 3 секунды, используя Process.WaitForExit(3000);
По истечении этого времени он проверяет, работает ли экземпляр приложения "B". Если все в порядке, то снова ждет. В противном случае, если он не работает или если он закончил, он перезапускает его. Когда пользователь закрывает консольную программу, все приложения должны завершиться.
Однако когда приложение "A" закрывается с помощью кнопки закрытия консоли, функция WaitForExit() возобновляется во всех потоках, и это вызывает перезапуск приложений "B".
Я хотел бы обнаружить, если WaitForExit()
возобновлено из-за сбоя отслеживаемого приложения "B" или выхода из основного приложения "A". В этом случае приложение "B" не будет перезапущено, и проблема будет решена.
Я попытался захватить событие закрытия, используя:
[DllImport("Kernel32")]
public static extern bool SetConsoleCtrlHandler(HandlerRoutine Handler, bool Add);
Проблема заключалась в том, что WaitForExit() была возобновлена до вызова процедуры обработчика.
Дополнительная информация:
Метод Main() запускает потоки:
for(int i = 0; i < 4; i++)
{
Thread t = new Thread(() => MonitorizeApp(arguments));
t.IsBackground = true;
t.Start();
}
Я также попытался изменить потоки с фона на передний план, чтобы основное приложение не закрывалось, но результат тот же.
Каждый поток запускает и контролирует один экземпляр 2-го приложения:
MonitorizeApp(string arguments)
{
LaunchProcess(arguments);
while(!_closing)
{
appProcess.WaitForExit(3000);
if (!_closing)
{
if ((appProcess != null) && (!appProcess.HasExited))
DoSomeMonitoring();
else
{
Console.WriteLine("WARNING: The application finished");
Thread.Sleep(500);
if (!_closing)
{
Console.WriteLine("Re-launch");
LaunchProcess(arguments);
inicial = true;
}
}
}
}
Console.WriteLine("\nClosing...\n");
if(appProcess != null)
{
if(!appProcess.HasExited)
appProcess.Kill();
appProcess.Dispose();
}
}
_closing - это переменная, установленная из основного потока, когда консоль закрыта. Я сделал это с помощью SetConsoleCtrlHandler:
[DllImport("Kernel32")]
public static extern bool SetConsoleCtrlHandler(HandlerRoutine Handler, bool Add);
public delegate bool HandlerRoutine(CtrlTypes CtrlType);
private static bool ConsoleCtrlCheck(CtrlTypes ctrlType)
{
MonitoredApp.closing = true;
Thread.Sleep(1000);
closing = true;
Environment.Exit(-1);
return true;
}
1 ответ
Я подозреваю, что единственным вариантом является проверка программы выхода из основной программы, чтобы увидеть, запущен ли какой-либо из дочерних процессов, и, если это так, завершить их. Я немного удивлен, что ваш сон 500 мс сопровождается _closing
проверка не улавливает это. Хотя, глядя на ваш пример кода, я не вижу _closing
установлен флаг (если _closing
является вспомогательным полем для MonitoredApp.closing
).
Вы могли бы рассмотреть возможность изменения этого _closing
логическое к ManualResetEvent
что основная программа устанавливает. Вполне возможно, что компилятор оптимизирует проверку _closing
, Вы также можете отметить _closing
как volatile
, что может изменить поведение. Помните, что все это происходит асинхронно, поэтому вам нужно беспокоиться о состоянии гонки.
Несмотря на это, вы, скорее всего, все равно столкнетесь с проблемами, и вам нужно будет убедиться, что основное приложение закрывает все существующие дочерние процессы, прежде чем завершится. Вы просто не можете гарантировать, в каком порядке операционная система завершит работу.
Вы можете избежать этой проблемы, отключив кнопку закрытия. См. Мою запись в блоге, http://blog.mischel.com/2008/07/14/going-too-far-back/, например. Если вы удалите пункт меню Close, нажатие на X не закроет окно.
Конечно, тогда ваши пользователи должны будут ввести команду выхода в вашем окне. И у вас все еще могут быть проблемы, если пользователь закрывает окно из диспетчера задач или другого подобного приложения.