我一直在观察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");
}
答案 0 :(得分:11)
我意识到这是一篇很老的帖子,但是为了找出为什么我的应用程序在应用程序打开之前运行Exited事件,我发现了一些东西,但我对将来遇到这个问题的人有用。
启动进程时,会为其分配PID。 如果随后通过“用户帐户控制”对话框提示用户并选择“是”,则重新启动该过程并为其分配新的PID。
我和他坐了几个小时,希望这可以节省一些时间。
答案 1 :(得分:7)
我建议您尝试这种方式:
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时完成。确保异步 事件处理已经完成,调用WaitForExit()重载 在检查HasExited之前没有参数。
当您重定向所有内容时,这可能会以某种方式与您的问题相关联。
答案 2 :(得分:4)
因此,为了进一步调查问题的根本原因,您应该使用Process Monitor查看实际发生的情况。只需启动它并包含外部程序和您自己的工具,并让它记录发生的情况。
在日志中,您应该看到外部工具如何写入输出文件以及如何打开该文件。但是在这个日志中你应该看到所有这些访问的顺序。
我想到的第一件事就是Process
类没有说谎而且当它说明时,过程真的消失了。所以问题是在这个时间点,文件似乎仍然没有完全可用。我认为这是OS 的问题,因为它将文件的某些部分保留在未完全写入磁盘的缓存中,并且该工具只是退出自己而不刷新其文件句柄。
考虑到这一点,您应该在日志中看到外部工具创建了文件,退出并在文件将被刷新/关闭之后(通过操作系统[当您在日志中找到此点时可能删除任何过滤器] )。
因此,如果我的假设是正确的,那么根本原因就是外部工具的不良行为,你无法改变,因此导致在进程退出后只需等待一点,并希望超时足够长操作系统刷新/关闭文件(可能尝试在超时循环中打开文件,直到成功为止)。
答案 3 :(得分:4)
首先,你确定testprogram不会产生自己的进程并退出而不等待该进程完成吗?我们在这里处理某种竞争条件,测试程序可能很重要。
我想说的第二点是 - “我需要绝对确定这个日志文件存在”。好吧,没有这样的事情。您可以进行检查,然后文件就消失了。解决这个问题的常用方法不是检查,而是要执行您想要对文件执行的操作。继续,阅读它,捕获异常,如果事情看起来不稳定并且你不想改变任何东西,则重试。如果系统中有多个actor(线程或其他),则功能性检查和操作不会很好。
接下来是一堆随意的想法。
您是否尝试过使用FileSystemWatcher而不依赖于流程完成?
如果您尝试在该过程中尝试读取该文件(不检查它是否存在,而是代理),它是否会变得更好。退出事件? [它不应该]
系统健康吗?事件日志中有什么可疑的东西吗?
是否可以参与一些非常积极的防病毒政策?
(如果没有看到所有代码并查看测试程序,就无法说清楚。)
答案 4 :(得分:4)
我知道,这是一个老帖子,但也许我可以帮助别人。
Process类可能会出乎意料!如果流程已退出或,如果流程以管理员权限运行且您的程序仅具有用户权限,则HasExited
将返回true。
答案 5 :(得分:2)
有两种可能性,进程对象继续保持对进程的引用,因此它已退出,但尚未删除。或者您正在运行该进程的第二个实例。您还应该比较进程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");
答案 6 :(得分:1)
首先,使用Process.WaitForExit而不是轮询它是否存在问题?
无论如何,从技术上讲,进程从可用的角度退出是可能的,但是当进程像刷新磁盘缓存这样时,进程仍然是短暂的。日志文件特别大(或者它在磁盘写入时执行的任何操作都很重)?
答案 7 :(得分:1)
根据HasExited
的{{3}}。
如果句柄对该过程开放, 操作系统发布了 进程内存时进程内存 退出,但保留行政 有关过程的信息,例如 句柄,退出代码和退出时间。
可能没有关系,但值得注意。
如果这只是问题的1/10,并且该过程在一秒之后消失,取决于您对HasExited的使用情况,请尝试在HasExited检查工作后添加另一个延迟,例如
while (!process.HasExited)
DoStuff();
Thread.Sleep(500);
Cleanup();
并查看问题是否仍然存在。
就个人而言,我总是只使用Exited
事件处理程序而不是任何类型的轮询,以及围绕System.Diagnostics.Process
的简单自定义包装来处理线程安全,包装对{{}的调用。 1}}后跟CloseMainWindow()
,最后是WaitForExit(timeout)
,记录等等,从未遇到过问题。
答案 8 :(得分:1)
问题可能出在测试程序中?这段代码是否很好地刷新/关闭等?在我看来,如果testprogram将文件写入磁盘,该文件应至少可用(空或不)
答案 9 :(得分:1)
在检查进程是否已退出之前使用process_name.Refresh()
。 Refresh()
将清除与该过程相关的所有缓存信息。
答案 10 :(得分:0)
如果您有Web应用程序,并且您的外部程序/进程正在生成文件(写入磁盘),请检查您的IIS是否有权写入该文件夹(如果没有为IIS用户添加属性安全性添加权限),这就是原因在我的情况下,我正在接收process.HasExited = true,但是从进程中生成的文件没有完成,经过一段时间的努力,我向进程正在扭曲的文件夹和process.Refresh()添加完全权限,如上所述Zarathos一切都按预期工作。