Process.HasExited возвращает true, даже если процесс запущен? - PullRequest
30 голосов
/ 26 марта 2010

Я заметил, что Process.HasExited иногда возвращает true, даже если процесс все еще выполняется.

Мой код ниже запускает процесс с именем "testprogram.exe" и ждет его завершения. Проблема в том, что иногда я получаю исключение; кажется, что даже если HasExited возвращает true, сам процесс все еще жив в системе - как это может быть ??

Моя программа записывает в файл журнала непосредственно перед его завершением, и, таким образом, я должен быть абсолютно уверен, что этот файл журнала существует (иначе процесс завершился / завершился), прежде чем читать его. Постоянно проверять его существование не вариант.

// Create new process object
process = new Process();

// Setup event handlers
process.EnableRaisingEvents = true;
process.OutputDataReceived += OutputDataReceivedEvent;
process.ErrorDataReceived += ErrorDataReceivedEvent;
process.Exited += ProgramExitedEvent;

// Setup start info
ProcessStartInfo psi = new ProcessStartInfo
                           {
                               FileName = ExePath,
                               // Must be false to redirect IO
                               UseShellExecute = false,
                               RedirectStandardOutput = true,
                               RedirectStandardError = true,
                               Arguments = arguments
                           };

process.StartInfo = psi;

// Start the program
process.Start();

while (!process.HasExited)
    Thread.Sleep( 500 );

Process[] p = Process.GetProcessesByName( "testprogram" );

if ( p.Length != 0 )
    throw new Exception("Oh oh");

ОБНОВЛЕНИЕ: Я только что попытался ждать с process.WaitForExit() вместо цикла опроса, и результат точно такой же.

Дополнение: приведенный выше код предназначен только для демонстрации более «ясной» проблемы. Чтобы было понятно; моя проблема НЕ в том, что я все еще могу удержать процесс на Process.GetProcessesByName( "testprogram" ); после того, как для HasExited установлено значение true.

Настоящая проблема заключается в том, что программа, которую я запускаю, внешне записывает файл - только до того, как он завершает работу (изящно). Я использую HasExited, чтобы проверить, когда процесс завершился, и, таким образом, я знаю, что могу прочитать файл (потому что процесс завершился!), Но кажется, что HasExited возвращает true даже иногда, когда программа НЕ записала файл на диск еще. Вот пример кода, который иллюстрирует точную проблему:

// Start the program
process.Start();

while (!process.HasExited)
    Thread.Sleep( 500 );
// Could also be process.WaitForExit(), makes no difference to the result

// Now the process has quit, I can read the file it has exported
if ( !File.Exists( xmlFile ) )
{
    // But this exception is thrown occasionally, why?
    throw new Exception("xml file not found");
}

Ответы [ 11 ]

11 голосов
/ 22 октября 2014

Я понимаю, что это старый пост, но в своем стремлении выяснить, почему мое приложение запускает событие Exited до того, как приложение даже открылось, я обнаружил кое-что, что, хотя я мог бы быть полезным для людей, испытывающих эту проблему в будущем.

Когда процесс запущен, ему назначается PID. Если пользователю затем предлагается диалоговое окно «Контроль учетных записей» и он выбирает «Да», процесс перезапускается и ему назначается новый PID.

Я сидел с этим несколько часов, надеюсь, это может сэкономить кому-то время.

7 голосов
/ 15 января 2013

Я бы предложил вам попробовать этот способ:

process.Start();

while (!process.HasExited)
{
    // Discard cached information about the process.
    process.Refresh();

    // Just a little check!
    Console.WriteLine("Physical Memory Usage: " + process.WorkingSet64.ToString());

    Thread.Sleep(500);
}

foreach (Process current in Process.GetProcessesByName("testprogram"))
{
    if ((current.Id == process.Id) && !current.HasExited)
        throw new Exception("Oh oh!");
}

Во всяком случае ... на странице MSDN HasExited я читаю следующую выделенную заметку:

Когда стандартный вывод был перенаправлен на асинхронное событие обработчики, возможно, что обработка вывода не будет иметь завершается, когда это свойство возвращает true. Чтобы убедиться, что асинхронный обработка событий завершена, вызовите перегрузку WaitForExit () который не принимает никаких параметров перед проверкой HasExited.

Это может быть как-то связано с вашей проблемой, поскольку вы все перенаправляете.

4 голосов
/ 04 января 2015

Я знаю, это старый пост, но, возможно, я могу кому-то помочь.
Класс Process может вести себя неожиданно! HasExited вернет true, если процесс завершил работу или , если процесс выполняется с правами администратора и ваша программа имеет только привилегии пользователя .

4 голосов
/ 18 января 2013

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

Второй момент, о котором я хотел бы сказать, - «Мне нужно быть абсолютно уверенным, что этот файл журнала существует». Ну, нет такой вещи. Вы можете сделать чек, и файл исчезнет. Обычный способ решения этой проблемы - не проверка, а выполнение того, что вы хотите сделать с файлом. Идите дальше, прочитайте его, поймайте исключения, повторите попытку, если ситуация кажется нестабильной, и вы не хотите ничего менять. Функциональная проверка и выполнение не работают должным образом, если в системе имеется более одного участника (потока или чего-либо еще).

Далее следует куча случайных идей.

Вы пытались использовать FileSystemWatcher и не зависеть от завершения процесса?

Будет ли лучше, если вы попытаетесь прочитать файл (не проверяя, существует ли он, но действующий вместо) в событии process.Exited? [это не должно быть]

Здорова ли система? Что-нибудь подозрительное в журнале событий?

Может ли быть задействована какая-то действительно агрессивная антивирусная политика?

(Многое сказать не могу, не увидев весь код и не заглянув в тестовую программу.)

4 голосов
/ 16 января 2013

Так что просто для дальнейшего изучения первопричины проблемы вы, возможно, должны проверить, что на самом деле происходит, используя Process Monitor . Просто запустите его, включите внешнюю программу и свой собственный инструмент и позвольте ему записать, что происходит.

В журнале вы должны увидеть, как внешний инструмент записывает в выходной файл и как вы открываете этот файл. Но в этом журнале вы должны увидеть, в каком порядке происходят все эти обращения.

Первое, что пришло мне в голову, это то, что класс Process не лжет, и процесс действительно прекращается, когда он говорит об этом. Так что проблема в том, что на данный момент кажется, что файл все еще не полностью доступен. Я думаю, что это проблема ОС , потому что она содержит некоторые части файла, все еще находящиеся в кеше, который не полностью записан на диск, и инструмент просто вышел из себя, не очистив свои файловые дескрипторы.

Имея это в виду, вы должны увидеть в журнале, что внешний инструмент создал файл, выйти и ПОСЛЕ того, что файл будет очищен / закрыт (ОС [возможно, удалит все фильтры, когда вы обнаружите эту точку в журнале) ).

Так что, если мои предположения верны, основной причиной будет плохое поведение вашего внешнего инструмента, которое вы не сможете изменить, что приведет к тому, чтобы просто немного подождать после завершения процесса и надеяться, что время ожидания достаточно велико, чтобы получить файл сброшен / закрыт операционной системой (возможно, попытайтесь открыть файл в цикле с тайм-аутом, пока он не будет успешным).

2 голосов
/ 26 марта 2010

Существует две возможности: объект процесса продолжает хранить ссылку на процесс, поэтому он завершился, но еще не был удален. Или у вас запущен второй экземпляр процесса. Вы должны также сравнить идентификатор процесса, чтобы убедиться. Попробуйте это.

    ....

    // Start the program
    process.Start();


    while (!process.HasExited)
        Thread.Sleep( 500 );

    Process[] p = Process.GetProcessesByName( "testprogram" );
    if ( p.Length != 0 && p[0].Id == process.id && ! p[0].HasExited)
        throw new Exception("Oh oh");
1 голос
/ 30 апреля 2015

Используйте process_name.Refresh() перед проверкой, завершился ли процесс или нет. Refresh() очистит всю кэшированную информацию, связанную с процессом.

1 голос
/ 29 апреля 2010

Может проблема в тестовой программе? Этот код хорошо сбрасывает / закрывает и т. Д.? Мне кажется, если testprogram записывает файл на диск, файл должен быть хотя бы доступен (пустым или нет)

1 голос
/ 26 марта 2010

Согласно документации MSDN для HasExited.

Если ручка открыта для процесса, операционная система выпускает память процесса, когда процесс имеет выход, но сохраняет административный информация о процессе, такая как дескриптор, код выхода и время выхода.

Вероятно, не связано, но стоит отметить.

Если это проблема только 1/10 времени, и процесс все равно исчезает через секунду, в зависимости от того, как вы используете HasExited, попробуйте просто добавить еще одну задержку после проверки HasExited, например

while (!process.HasExited)
    DoStuff();
Thread.Sleep(500);
Cleanup();

и посмотрите, сохраняется ли проблема.

Лично я всегда просто использовал обработчик событий Exited вместо любого вида опроса и упрощенную пользовательскую оболочку вокруг System.Diagnostics.Process для обработки таких вещей, как безопасность потоков, заключая вызов в CloseMainWindow() с последующим WaitForExit(timeout) и, наконец, Kill(), ведение журнала и так далее, и никогда не сталкивался с проблемой.

1 голос
/ 26 марта 2010

Для начала, есть ли проблема с использованием Process.WaitForExit вместо его опроса?

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

...