Process.WaitForExit在不同的计算机之间不一致

本文关键字:计算机 之间 不一致 WaitForExit Process | 更新日期: 2023-09-27 18:20:37

此代码在大量机器上按预期运行。然而,在一台特定的机器上,对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获取"现有"进程Id,但这只是在问题机器上返回一个空数组。因此,很难说问题出在WaitForExit上,因为即使在调用WaitForExit之前,也不会通过调用GetProcessesByName来返回进程
  • 在问题机器上,生成的记事本进程的ParentID是代码手动启动的记事本进程ID,或者换句话说,记事本正在生成一个子进程并自行终止

Process.WaitForExit在不同的计算机之间不一致

问题是默认情况下Process.StartInfo.UseShellExecute设置为true。当这个变量设置为true时,您不是自己启动这个过程,而是要求shell为您启动它。这可能非常有用——它允许你做一些事情,比如"执行"一个HTML文件(shell将使用适当的默认应用程序)。

当你想在执行应用程序后跟踪它时(正如你所发现的),情况就不那么好了,因为启动应用程序有时会混淆它应该跟踪哪个实例。

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

编辑:经过一点挖掘:

这个问题让我想到了SEE_MASK_NOCLOSEPROCESS标志,它似乎确实是在使用ShellExecute时设置的。掩码值的文档说明:

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

因此,它确实表明返回进程句柄是不可靠的。不过,我还没有深入到知道你可能会在这里遇到哪个特定的边缘案例。

原因可能是一种病毒取代了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的进程id,并验证它是否与进程相同。Id;

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

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