Я пишу приложение для запуска и мониторинга других приложений на C#. Я использую класс System.Diagnostics.Process для запуска приложений, а затем отслеживаю приложения с помощью свойства Process.Responding для опроса состояния приложения каждые 100 миллисекунд. Я использую Process.CloseMainWindow, чтобы остановить приложение, или Process.Kill, чтобы убить его, если оно не отвечает.
Я заметил странное поведение, когда иногда объект процесса попадает в состояние, когда отвечающее свойство всегда возвращает истину, даже когда базовый процесс зависает в цикле и не отвечает на CloseMainWindow.
Один из способов воспроизвести это - опросить свойство Responding сразу после запуска экземпляра процесса. Так например
_process.Start();
bool responding = _process.Responding;
воспроизведет состояние ошибки, пока
_process.Start();
Thread.Sleep(1000);
bool responding = _process.Responding;
будет работать. Уменьшение периода ожидания до 500 снова вызовет состояние ошибки.
Что-то в вызове _process. Слишком быстрый ответ после запуска, похоже, мешает объекту получить правильный обработчик очереди сообщений Windows. Думаю, мне нужно дождаться, пока _process.Start завершит асинхронную работу. Есть ли лучший способ дождаться этого, чем вызывать Thread.Sleep? Я не слишком уверен, что 1000 мс всегда будет достаточно.





Я думаю, может быть лучше улучшить проверку _process.Responding, чтобы вы только пытались остановить / убить процесс, если свойство Responding возвращает false в течение более 5 секунд (например).
Я думаю, вы можете обнаружить, что довольно часто приложения могут «не отвечать» в течение доли секунды, пока они выполняют более интенсивную обработку.
Я считаю, что более мягкий подход будет работать лучше, позволяя процессу «не отвечать» в течение короткого промежутка времени, принимая меры только в том случае, если он неоднократно «не отвечает» в течение нескольких секунд (или сколько угодно времени).
Дополнительное примечание: в документации Microsoft указано, что свойство Responding конкретно относится к пользовательскому интерфейсу, поэтому у недавно запущенного процесса пользовательский интерфейс может не отвечать немедленно.
В этом случае достаточно простого счетчика для каждого процесса. Просто увеличивайте счетчик каждый раз, когда процесс не отвечает, и сбрасывайте счетчик, если он отвечает. Если вы делали проверку каждую секунду, вам просто нужно действовать, как только счетчик достигнет 60.
Это именно то, что я делаю. Проблема в том, что если я опрашиваю Responding слишком рано после Process.Start, он перестает работать. После этого ответ всегда возвращает истину, и Process.CloseMainWindow тоже перестает работать.
Теперь мне нужно проверить это позже, но я уверен, что есть метод, который сообщает потоку, чтобы он дождался, пока он не будет готов для ввода. Вы отслеживаете только процессы графического интерфейса?
Разве Process.WaitForInputIdle тебе не поможет? Или я упустил суть? :)
После болтовни в Твиттере (или твита?) С Мендельтом я подумал, что должен обновить свой ответ, чтобы сообщество было полностью осведомлено.
WaitForInputIdle будет работать только с приложениями с графическим интерфейсом.Надеюсь, это поможет :)
Спасибо за ответы. Этот
_process.Start();
_process.WaitForInputIdle();
Кажется, проблема решена. Это все еще странно, потому что Responding и WaitForInputIdle должны использовать один и тот же вызов api win32.
Дополнительная справочная информация У приложений с графическим интерфейсом есть главное окно с очередью сообщений. Responding и WaitForInputIdle работают, проверяя, обрабатывает ли процесс сообщения из этой очереди сообщений. Вот почему они работают только с приложениями с графическим интерфейсом. Почему-то кажется, что слишком быстрый вызов ответа мешает процессу получить дескриптор этой очереди сообщений. Вызов WaitForInputIdle, похоже, решает эту проблему.
Мне придется погрузиться в отражатель, чтобы понять, смогу ли я разобраться в этом.
Обновить
Кажется, что получение дескриптора окна, связанного с процессом сразу после запуска, достаточно, чтобы вызвать странное поведение. Нравится:
_process.Start();
IntPtr mainWindow = _process.MainWindowHandle;
Я проверил с помощью Reflector, и это то, что Responding делает под прикрытием. Кажется, что если вы получите MainWindowHandle слишком рано, вы получите неправильный обработчик, и он будет использовать этот неправильный дескриптор до конца жизненного цикла процесса или до тех пор, пока вы не вызовете Refresh ();
Обновить
Вызов WaitForInputIdle () иногда решает проблему. Вызов Refresh () каждый раз, когда вы читаете свойство Responding, кажется, работает лучше.
Я тоже заметил это в одном проекте года 2 назад. Я вызвал .Refresh () перед тем, как запросить определенные значения свойств. Это был подход проб и ошибок, чтобы найти, когда мне нужно вызвать .Refresh ().
Я исследовал это дальше, используя отражатель. Hwnd и некоторые другие свойства сохраняются после того, как вы их получите в первый раз. С Hwnd это может вызвать проблемы. Я описал это здесь blog.mendeltsiebenga.com/post/….
Я принимаю меры только в том случае, если процесс не отвечает более минуты. Но я хочу часто проверять, не отвечает ли программа все это время. Если я проверяю один раз в минуту, а ответ будет ложным, я все равно не знаю, только на короткое время или нет.