即使进程正在运行,Process.HasExited也会返回true?

我一直在观察Process.HasExited有时会返回true即使进程仍在运行。

我的代码在下面启动一个名为“testprogram.exe”的进程,然后等待它退出。 问题是有时我会抛出exception; 似乎即使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()而不是轮询循环等,结果完全相同。

另外:上面的代码只是为了certificate一个“更清晰”的问题。 说清楚; 我的问题不是我仍然可以通过Process.GetProcessesByName( "testprogram" );获取进程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"); } 

我意识到这是一篇很老的post,但是为了找出为什么我的应用程序在应用程序打开之前运行Exited事件,我发现了一些东西,但我对将来遇到这个问题的人有用。

进程启动时,会为其分配PID。 如果随后通过“用户帐户控制”对话框提示用户并选择“是”,则重新启动该过程并分配新的PID。

我坐了几个小时,希望这可以节省一些时间。

我建议你这样试试:

 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!"); } 

无论如何…在HasExited的MSDN页面中我正在阅读以下高亮显示的注释:

当标准输出重定向到异步事件处理程序时,当此属性返回true时,输出处理可能无法完成。 要确保已完成异步事件处理,请在检查HasExited之前调用不带参数的WaitForExit()重载。

当您重定向所有内容时,这可能会以某种方式与您的问题相关联。

首先, 你确定testprogram不会产生自己的进程并退出而不等待该进程完成吗? 我们在这里处理某种竞争条件,测试程序可能很重要。

我要提出的第二点是 – “我需要绝对确定这个日志文件存在”。 好吧,没有这样的事情。 你可以进行检查,然后文件就消失了。 解决这个问题的常用方法不是检查,而是要对文件执行您想要执行的操作。 继续,阅读它,捕获exception,如果事情看起来不稳定并且你不想改变任何东西,则重试。 如果系统中有多个actor(线程或其他),则function检查和操作不会很好。

接下来是一堆随机的想法。

您是否尝试过使用FileSystemWatcher而不依赖于流程完成?

如果你尝试在进程中读取文件(不检查它是否存在,而是代替),它会变得更好吗。退出事件? [它不应该]

系统健康吗? 事件日志中有什么可疑的东西?

是否可以涉及一些非常积极的防病毒策略?

(如果没有看到所有代码并查看测试程序,就无法说清楚。)

我知道,这是一个老post,但也许我可以帮助别人。
Process类可能会出乎意料! 如果进程已退出, 或者进程以管理员权限运行且您的程序仅具有用户权限,HasExited将返回true。

因此,为了进一步调查问题的根本原因,您应该查看使用Process Monitor实际发生的情况。 只需启动它并包含外部程序和您自己的工具,并让它记录发生的事情。

在日志中,您应该看到外部工具如何写入输出文件以及如何打开该文件。 但是在这个日志中你应该看到所有这些访问发生的顺序。

我想到的第一件事就是Process类没有说谎,当它告诉时,这个过程真的消失了。 所以问题是在这个时间点,文件似乎仍然没有完全可用。 我认为这是操作系统问题 ,因为它将文件的某些部分保留在未完全写入磁盘的缓存中,并且工具只是退出自己而不刷新其文件句柄。

考虑到这一点,您应该在日志中看到外部工具创建文件,退出并在文件将被刷新/关闭之后(通过操作系统[当您在日志中找到此点时可能删除任何filter])。

因此,如果我的假设是正确的,那么根本原因就是外部工具的不良行为,你无法改变,因此导致在进程退出后等待一段时间,并希望超时足够长以使文件刷新/由OS关闭(可能尝试在超时循环中打开文件,直到成功为止)。

首先,使用Process.WaitForExit而不是轮询它是否存在问题?

无论如何,从技术上讲,进程从可用的角度退出是可能的,但是进程仍然是短暂的,而它就像刷新磁盘缓存一样。 日志文件特别大(或者在磁盘写入时执行的任何操作都很重)?

有两种可能性,进程对象继续保持对进程的引用,因此它已退出,但尚未删除。 或者你有第二个进程实例正在运行。 您还应该比较进程ID以确保。 试试这个。

  .... // 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"); 

根据HasExited MSDN文档 。

如果句柄对进程开放,则操作系统会在进程退出时释放进程内存,但会保留有关进程的管理信息,例如句柄,退出代码和退出时间。

可能没有关系,但值得注意。

如果它只有一个问题的1/10,并且该过程在一秒钟之后消失,取决于您对HasExited的使用,尝试在HasExited检查工作后添加另一个延迟,如

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

并查看问题是否仍然存在。

就个人而言,我总是只使用Exited事件处理程序而不是任何类型的轮询,以及围绕System.Diagnostics.Process的简单自定义包装器来处理线程安全,包含对CloseMainWindow()的调用以及WaitForExit(timeout)事情。最后Kill() ,记录等等,从未遇到过问题。

也许问题出在测试程序中? 这个代码是否很好地刷新/关闭等? 在我看来,如果testprogram将文件写入磁盘,该文件应该至少可用(空或不)

如果您有Web应用程序,并且您的外部程序/进程正在生成文件(写入磁盘),请检查您的IIS是否有权写入该文件夹,如果没有属性安全性为您的IIS用户添加权限,这就是我的理由,我正在接收process.HasExited = true,但是从进程中生成的文件没有完成,经过一段时间的努力,我向进程正在扭曲的文件夹和process.Refresh()添加完全权限,如上所述Zarathos所描述的一切都是按预期工作。

在检查进程是否已退出之前,请使用process_name.Refresh()Refresh()将清除与进程相关的所有缓存信息。