System.Diagnostics.Process.Start странное поведение - PullRequest
8 голосов
/ 24 сентября 2008

Я пишу приложение для запуска и мониторинга других приложений в 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 мс всегда будет достаточно.

Ответы [ 4 ]

8 голосов
/ 24 сентября 2008

Теперь мне нужно проверить это позже, но я уверен, что есть метод, который сообщает потоку, пока он не будет готов для ввода. Вы отслеживаете только процессы графического интерфейса?

Вам не поможет Process.WaitForInputIdle ? Или я упускаю суть? :)

Обновление

После чата в Твиттере (или твита-твита?) С Мендельтом я подумала, что мне следует обновить свой ответ, чтобы сообщество полностью осознало это.

  • WaitForInputIdle будет работать только в приложениях с графическим интерфейсом.
  • Вы указываете время ожидания, и метод возвращает bool, если процесс достигает состояния простоя в течение этого периода времени, вы, очевидно, можете использовать его для зацикливания, если требуется, или для обработки соответствующим образом.

Надеюсь, это поможет:)

4 голосов
/ 24 сентября 2008

Я думаю, что может быть лучше улучшить проверку _process.Responding, чтобы вы пытались остановить / убить процесс только в том случае, если свойство Responding возвращает false в течение более 5 секунд (например).

Я думаю, вы можете обнаружить, что довольно часто приложения могут «не отвечать» в течение доли секунды, пока они выполняют более интенсивную обработку.

Я полагаю, что более мягкий подход будет работать лучше, позволяя процессу «не отвечать» в течение короткого промежутка времени, принимая меры только в том случае, если он несколько раз «не отвечает» в течение нескольких секунд (или сколь угодно долго) .

Дальнейшее примечание: в документации Microsoft указывается, что свойство Responding относится конкретно к пользовательскому интерфейсу, поэтому недавно запущенный процесс может не иметь своего пользовательского интерфейса, отвечающего немедленно.

3 голосов
/ 24 сентября 2008

Спасибо за ответы. Это

_process.Start();
_process.WaitForInputIdle();

Кажется, чтобы решить проблему. Это все еще странно, потому что Responding и WaitForInputIdle должны использовать один и тот же вызов Win32 API под прикрытием.

Дополнительная информация
Приложения с графическим интерфейсом имеют главное окно с очередью сообщений. Responding и WaitForInputIdle работают, проверяя, обрабатывает ли процесс сообщения из этой очереди сообщений. Вот почему они работают только с приложениями с графическим интерфейсом. Почему-то кажется, что слишком быстрый вызов Responding мешает получению Process получения дескриптора этой очереди сообщений. Вызов WaitForInputIdle, похоже, решает эту проблему.

Мне придется погрузиться в отражатель, чтобы понять, смогу ли я понять это.

обновление
Кажется, что получение дескриптора окна, связанного с процессом, сразу после запуска, достаточно для запуска странного поведения. Как это:

_process.Start();
IntPtr mainWindow = _process.MainWindowHandle;

Я проверил с помощью Reflector, и именно это делает Responding под одеялом. Кажется, что если вы получаете MainWindowHandle слишком рано, вы получите неправильный, и он использует этот неправильный дескриптор его до конца жизни процесса или пока вы не вызовете Refresh ();

обновление
Вызов WaitForInputIdle () решает проблему только иногда. Вызов Refresh () каждый раз, когда вы читаете свойство Responding, кажется, работает лучше.

1 голос
/ 18 октября 2008

Я тоже заметил это в проекте около 2 лет назад. Я вызвал .Refresh () перед запросом определенных значений проп. Это был метод проб и ошибок, когда нужно было вызвать .Refresh ().

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...