如果'Process.HasExited'抛出异常,我可以假设该过程消失了吗?

时间:2009-12-26 01:07:59

标签: c# process

我有以下代码部分,旨在计算当前打开的Excel进程数:

Func<int> OpenExcelProcessesCount = 
    () => System.Diagnostics.Process.GetProcessesByName("Excel")
              .Where(p => !p.HasExited)
              .Count();

然后我在各个点检索计数,代码如下:

int excelAppCount = OpenExcelProcessesCount();

此代码已连续数月运行100%。然后突然,今天,它一直给我一个例外,内容如下:

Exception: ApplicationThreadException

Message: Access is denied

Stack Trace:

   at System.Diagnostics.ProcessManager.OpenProcess(Int32
     

processId,Int32 access,Boolean   throwIfExited)

   at System.Diagnostics.Process.GetProcessHandle(Int32
     

access,Boolean throwIfExited)

   at System.Diagnostics.Process.get_HasExited()

   etc...

基本上,对Process.HasExited的调用(在上面的堆栈跟踪中显示为System.Diagnostics.Process.get_HasExited())失败。错误消息“访问被拒绝”听起来像我没有该进程的管理权限,但是在我当前的用户登录下将创建唯一存在的Excel进程,并且用户始终可以访问自己的进程。我的.NET代码也在完全信任的情况下运行。

最终失败的行是System.Diagnostics.ProcessManager.OpenProcess(Int32 processId, Int32 access, Boolean throwIfExited)。我想知道它是否以'throwIfExited'参数的'true'值传递。如果是这种情况,那么我想我可以使用try-catch块保护对Process.HasExited的调用,并假设如果失败,那么HasExited实际上是'true'。但这是一个安全的假设吗?

我很难做出这样的推定,特别是因为错误信息是“访问被拒绝”。有没有人对我如何解决这个问题有任何想法,或者我可能会试图弄清楚发生了什么?

我在Stack Overflow上找到的唯一类似的线程如下:Why did hasExited throw ‘System.ComponentModel.Win32Exception’?。那里给出的答案是:

  

“因为你正在做runas,你只是   获取句柄上的SYNCHRONIZE访问权限,   不是PROCESS_QUERY_INFORMATION访问,   因此GetExitCodeProcess失败了   导致hasEnded抛出Win32   异常“。

我真的不明白这个答案,不知道这是否适用于我的情况,但我想我应该提一下。如果有人觉得这可能是我所面临的情况,那么如果有人可以尝试为我澄清这个答案,我将非常感激。 (我是一名Excel程序员,我没有太多使用流程的经验。)

提前多多感谢...

更新

我能说的最好,这是一种一次性的腐败。我所遇到的问题开始变得越来越奇怪,因为一组功能完善的单元测试开始在其他“不可能”的地方出现故障。一个简单的重新启动纠正了这个问题和我面临的其他一切。

我最好的猜测是,我有某种奇怪的腐败。也许ROT已经被破坏了,和/或我有一个挂起的Excel实例,这个实例非常腐败甚至“进程”操作也不一定稳定。没有什么结论,但这是我现在能想到的全部。

对于那些花时间回答并帮助我的响应者,我感谢你。

5 个答案:

答案 0 :(得分:6)

如果目标进程正在升级并且您的进程没有运行,则Process.HasExited可以抛出访问被拒绝的异常。 StartTime属性也是如此。

以下是有关此主题的博客文章,其中包含可能的解决方法:Bugs in System.Diagnostics.Process Class

答案 1 :(得分:4)

您引用的答案可能适用于您的情况。据我了解,它基本上是说,如果您正在查看的流程是在不同的用户帐户下运行,那么HasExited无法获得确定流程是否已退出所需的权限。

现在您说“将在我当前的用户登录下创建唯一存在的Excel进程”。但是假设事实并非如此。假设在另一个用户帐户下的同一个框上运行另一个Excel进程? (也许一个是由其他人使用OLE自动化的进程启动的,并且没有正确清理或挂起。)然后GetProcessesByName会把它拿起来,但HasExited会失败。

因此,在调用OpenExcelProcessesCount()方法之前,添加一些日志记录以转储从GetProcessesByName(“Excel”)返回的所有进程的进程ID。然后根据任务管理器检查这些内容,或者您​​希望在您的帐户下运行的Excel进程数。如果那里的ID与“预期的”Excel流程不对应,那么您可能会有罪魁祸首。

答案 2 :(得分:1)

阅读the documentation后:

  

使用此方法创建一个数组   新的流程组件和关联   他们拥有所有流程资源   正在运行相同的可执行文件   本地计算机上的文件。

在我看来,您不需要检查HasExited,因为它只返回正在运行的进程。

答案 3 :(得分:1)

8年后,.NET Process类中仍然存在此问题,迫使您使用非托管代码来检查受保护的进程。 Giorgio的回答和博客文章都非常好,尽管测试一个过程是否退出比他指出的要复杂得多:)这对我有用:

    hProcess = OpenProcess(ProcessAccessFlags.PROCESS_QUERY_LIMITED_INFORMATION, false, processId);

    GetExitCodeProcess(hProcess, out uint ExitCode);
    if (ExitCode != 259) // ExitCode of 259 is STILL_ACTIVE
    {
        CloseHandle(hProcess);
        return false;
    }

    // do your thing, this process is alive and you have a handle now

    CloseHandle(hProcess)

这是您需要进入非托管代码领域的声明:

    [DllImport("kernel32.dll")]
    static extern bool GetExitCodeProcess(IntPtr hProcess, out uint lpExitCode);

    [DllImport("kernel32.dll")]
    private static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, bool bInheritHandle, int dwProcessId);

    [DllImport("kernel32.dll", SetLastError = true)]
    static extern bool CloseHandle(IntPtr handle);

    [Flags]
    public enum ProcessAccessFlags : uint
    {
        PROCESS_QUERY_LIMITED_INFORMATION = 0x00001000,
    }

答案 4 :(得分:0)

return process.WaitForExit(0);

似乎减少了问题。

相关问题