32

我一直在观察,即使进程仍在运行,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");
}
4

11 回答 11

15

我意识到这是一篇旧帖子,但是在我试图找出为什么我的应用程序在应用程序打开之前运行 Exited 事件的过程中,我发现了一些我认为可能对将来遇到此问题的人有用的东西。

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

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

于 2014-10-22T13:11:41.550 回答
9

我建议你尝试这种方式:

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() 重载。

当您重定向所有内容时,这可能与您的问题有关。

于 2013-01-15T15:52:38.747 回答
7

我知道,这是一个旧帖子,但也许我可以帮助某人。
Process 类可能会出现意外行为。HasExited如果进程已退出或者进程以管理员权限运行并且您的程序只有用户权限,则将返回 true 。

不久前我在这里发布了一个关于此的问题,但没有收到令人满意的答案。

于 2015-01-04T20:56:45.747 回答
5

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

我想说的第二点是关于这个 - “我需要绝对确定这个日志文件存在”。好吧,没有这样的事情。您可以进行检查,然后文件就消失了。解决此问题的常用方法不是检查,而是对文件执行您想要执行的操作。继续,阅读它,捕获异常,如果事情看起来不稳定并且您不想更改任何内容,请重试。如果系统中有多个参与者(线程或其他),则功能检查和执行将无法正常工作。

随之而来的是一堆随机的想法。

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

如果您尝试在 process.Exited 事件中读取文件(不检查它是否存在,而是采取行动),它会变得更好吗?[不应该]

系统是否健康?事件日志中有什么可疑之处吗?

是否可以涉及一些真正激进的防病毒策略?

(如果不查看所有代码并查看测试程序,就不能说太多。)

于 2013-01-17T21:46:13.477 回答
4

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

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

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

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

因此,如果我的假设是正确的,根本原因将是您无法更改的外部工具的不良行为,从而导致在进程退出后稍等片刻,并希望超时时间足够长以刷新文件/由操作系统关闭(可能尝试在循环中打开文件并超时,直到它成功)。

于 2013-01-16T12:36:15.997 回答
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");
于 2010-03-25T21:59:44.943 回答
1

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

无论如何,从可用的角度来看,该进程在技术上是有可能退出的,但是该进程在执行诸如刷新磁盘缓存之类的操作时仍会短暂存在。日志文件是否特别大(或者它在磁盘写入上执行的任何操作)?

于 2010-03-25T21:56:16.353 回答
1

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

于 2015-04-30T07:44:40.363 回答
1

根据MSDN 文档HasExited.

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

可能不相关,但值得注意。

如果只是 1/10 的时间出现问题,并且该过程无论如何都会在一秒钟后消失,这取决于您对 HasExited 的使用,请尝试在 HasExited 检查工作后添加另一个延迟,例如

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

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

就个人而言,我一直只是使用Exited事件处理程序而不是任何类型的轮询,以及一个简单的自定义包装器System.Diagnostics.Process来处理诸如线程安全之类的事情,包装一个调用,CloseMainWindow()然后是WaitForExit(timeout)finally Kill(),日志记录等等,从来没有遇到过问题.

于 2010-03-25T22:34:46.057 回答
1

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

于 2010-04-29T08:27:52.033 回答
1

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

于 2014-04-18T00:16:48.460 回答