Process.WaitForExit несовместим на разных машинах

Этот код работает, как ожидалось, на большом количестве машин. Однако на одной конкретной машине вызов WaitForExit() кажется проигнорированным и фактически отмечает процесс как завершенный.

static void Main(string[] args)
{
    Process proc = Process.Start("notepad.exe");
    Console.WriteLine(proc.HasExited); //Always False
    proc.WaitForExit(); //Blocks on all but one machines
    Console.WriteLine(proc.HasExited); //**See comment below
    Console.ReadLine();
}

Обратите внимание, что в отличие от аналогичного вопроса на SO, вызываемый процесс notepad.exe (для целей тестирования), так что маловероятно, что с ним возникла ошибка - т.е. он не порождает второй подпроцесс и закрытие. Тем не менее, это не объяснит, почему он работает на всех других машинах.

На проблемной машине второй вызов Console.WriteLine(proc.HasExited)) возвращает true, хотя блокнот все еще явно открыт, как на экране, так и в диспетчере задач.

Аппарат работает под управлением Windows 7 и .NET 4.0.

Мой вопрос: какие условия на этой конкретной машине могут быть причиной этого? Что я должен проверять?

Изменить - все, что я пробовал до сих пор/Обновления/Возможно релевантная информация:

  • Переустановил .NET.
  • Закрытие любых процессов, которые я не знаю в диспетчере задач.
  • Windows еще не активирована на этом компьютере.
  • Следуя рекомендациям в комментариях, я попытался получить "существующий" идентификатор процесса, используя GetProcessesByName, но просто возвращает пустой массив на проблемную машину. Поэтому трудно сказать, что проблема даже с WaitForExit, так как процесс не возвращается вызовом GetProcessesByName еще до вызова WaitForExit.
  • На проблемной машине результирующий процесс Notepad ParentID - это идентификатор процесса блокнота, который запускается вручную, или, другими словами, блокнот порождает дочерний процесс и завершает себя.

Ответы

Ответ 1

Проблема заключается в том, что по умолчанию для параметра Process.StartInfo.UseShellExecute установлено значение true. Если эта переменная установлена ​​в true, а не запускает процесс самостоятельно, вы просите оболочку запустить ее для вас. Это может быть весьма полезно - это позволяет вам делать такие вещи, как "выполнить" HTML файл (оболочка будет использовать соответствующее приложение по умолчанию).

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

Внутренние подробности о том, почему это происходит, вероятно, превосходят мои возможности для ответа - я знаю, что когда UseShellExecute == true, среда использует ShellExecuteEx Windows API, а когда она UseShellExecute == false, она использует CreateProcessWithLogonW, но почему один приводит к отслеживаемым процессам, а другой - не знаю, поскольку оба они возвращают идентификатор процесса.

ИЗМЕНИТЬ: После небольшого рытья:

Этот вопрос указал мне на флаг SEE_MASK_NOCLOSEPROCESS, который действительно выглядит устанавливается при использовании ShellExecute. В документации для значения маски указано:

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

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

Ответ 2

Причиной может быть вирус, который заменил notepad.exe, чтобы скрыть себя. Если он выполняется, он запускает блокнот и выходы (просто догадка).

попробуйте этот код:

        var process = Process.Start("notepad.exe");
        var process2 = Process.GetProcessById(process.Id);
        while (!process2.HasExited)
        {
            Thread.Sleep(1000);
            try
            {
                process2 = Process.GetProcessById(process.Id);
            }
            catch (ArgumentException)
            {

                break;
            }

        }

        MessageBox.Show("done");

После Process.Start() проверьте идентификатор процесса notepad.exe с помощью диспетчера задач и убедитесь, что он такой же, как process.Id;

О, и вам действительно нужно использовать полный путь к notepad.exe

 var notepad = Path.Combine(Environment.GetFolderPath(
                   Environment.SpecialFolder.Windows), "notepad.exe");
 Process.Start(notepad);