Process.WaitForExit在不同的机器上不一致

时间:2012-01-17 13:00:58

标签: c# .net process

此代码在大量计算机上按预期运行。但是在一台特定的机器上,对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上的similar question不同,被调用的进程是notepad.exe(出于测试原因),因此不太可能出现错误 - 即它不会产生第二个子 - 处理和关闭。即便如此,也无法解释为什么它适用于所有其他机器。

在问题机器上,对Console.WriteLine(proc.HasExited))的第二次调用会返回true,即使记事本仍然在屏幕上和任务管理器中都是明显打开的。

该计算机正在运行Windows 7和.NET 4.0。

我的问题是;该特定机器上的条件可能导致这种情况?我应该检查什么?

编辑 - 到目前为止我尝试过的事情/更新/可能相关的信息:

  • 重新安装.NET。
  • 关闭任务管理器中我不知道的任何进程。
  • 此计算机尚未激活Windows。
  • 根据评论中的建议,我尝试使用GetProcessesByName获取“现有”流程ID,但这只是在问题机器上返回一个空数组。因此,很难说问题与WaitForExit一致,因为即使在调用GetProcessesByName之前调用WaitForExit也不会返回此过程。
  • 在问题机器上,生成的记事本进程的ParentID是代码手动启动的记事本进程的ID,换句话说,记事本正在生成子进程并自行终止。

2 个答案:

答案 0 :(得分:7)

问题是默认情况下Process.StartInfo.UseShellExecute设置为true。将此变量设置为true,而不是自己启动该过程,您要求shell为您启动它。这可能非常有用 - 它允许您执行诸如“执行”HTML文件之类的操作(shell将使用适当的默认应用程序)。

当您想要在执行应用程序后跟踪应用程序(如您所发现的)时,它不太好,因为启动应用程序有时会对应该跟踪的实例感到困惑。

这里发生这种情况的内在细节可能超出了我的回答能力 - 我知道当UseShellExecute == true时,框架使用ShellExecuteEx Windows API,当它使用UseShellExecute == false时,它使用CreateProcessWithLogonW,但是为什么一个导致可跟踪的进程而另一个不是我不知道,因为他们似乎都返回进程ID。

编辑:经过一番挖掘:

This question向我指出SEE_MASK_NOCLOSEPROCESS标志,在使用ShellExecute时确实似乎设置了。掩码值的文档说明:

  

在某些情况下,例如通过DDE满足执行时   对话,将不会返回句柄。调用应用程序是   负责在不再需要时关闭手柄。

所以它确实表明返回进程句柄是不可靠的。我仍然没有深入了解你可能在这里遇到的特殊边缘情况。

答案 1 :(得分:1)

原因可能是病毒替换了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()之后,使用taskmanager检查notepad.exe的进程ID,并验证它与process.Id相同;

哦,你真的应该使用notepad.exe的完整路径

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