我有以下代码部分,旨在计算当前打开的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实例,这个实例非常腐败甚至“进程”操作也不一定稳定。没有什么结论,但这是我现在能想到的全部。
对于那些花时间回答并帮助我的响应者,我感谢你。
答案 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)
使用此方法创建一个数组 新的流程组件和关联 他们拥有所有流程资源 正在运行相同的可执行文件 本地计算机上的文件。
在我看来,您不需要检查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);
似乎减少了问题。