views:

280

answers:

2

I have the following code section, designed to count how many Excel processes are currently open:

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

And then later I retrieve the count at various points, with code such as the following:

int excelAppCount = OpenExcelProcessesCount();

This code has been running 100% fine for months. Then suddenly, today, it is consistently giving me an exception that reads the following:

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...

Basically, the call to Process.HasExited (which shows up as System.Diagnostics.Process.get_HasExited() in the stack trace, above) is failing. The error message "Access is denied" sounds like I don't have administrative privileges for the process, but the only Excel processes that exist would be created under my current user log-in, and the user can always access their own processes. My .NET code is also running under full trust.

The line that is ultimately failing is System.Diagnostics.ProcessManager.OpenProcess(Int32 processId, Int32 access, Boolean throwIfExited). I wonder if it is being passed in a value of 'true' for the 'throwIfExited' parameter. If this is the case, then I suppose that I could protect the call to Process.HasExited with a try-catch block and assume that if this fails that HasExited is, in fact, 'true'. But is this a safe assumption??

I am uneasy making a presumption like this, especially since the error message is "Access is denied." Does anyone have any ideas on how I might address this, or what I might test in an attempt to figure out what is going on?

The only similar thread I could find on Stack Overflow was the following: Why did hasExited throw ‘System.ComponentModel.Win32Exception’?. The answer given there was:

"Since you are doing runas, you only get SYNCHRONIZE access on the handle, not PROCESS_QUERY_INFORMATION access, hence GetExitCodeProcess fails, which results in hasEnded throwing a Win32 exception."

I don't really understand this answer and do not know if this applies in my case, but I thought I should mention it. If anyone feels that it is likely that this is the situation that I am facing, then if someone could try to clarify this answer for me, I would greatly appreciate it. (I'm an Excel programmer, I do not have much experience working with processes.)

Much thanks in advance...

Update:

Best I can tell, this is was a one-off corruption of some sort. The issues I faced started becoming increasingly bizarre as what had been a perfectly functioning set of unit tests was starting to have breakdowns at other "impossible" locations. A simple reboot corrected this issue and everything else I was facing.

My best guess is that I had some sort of bizarre corruption. Perhaps the ROT was corupted, and/or I had a hanging instance of Excel that was so corrupt that even 'Process' operations were not necessarily stable. Nothing conclusive, but this is all I can figure for now.

To the responders who took the time answer and help me out, I thank you.

+3  A: 

The answer you quote may apply in your case. As I understand it, it's basically saying that if the process you're looking at is running under a different user account, then HasExited can't get the permissions it needs to determine if the process has exited.

Now you say "the only Excel processes that exist would be created under my current user log-in." But suppose that's not the case. Suppose there's another Excel process running on the same box, under another user account? (Maybe one got started by somebody else's process using OLE Automation, and didn't get cleaned up properly or has hung.) Then GetProcessesByName would pick it up, but HasExited would fail.

So before calling your OpenExcelProcessesCount() method, add a bit of logging to dump the process IDs of all the processes returned from GetProcessesByName("Excel"). Then check these against Task Manager, or the number of Excel processes you expect to be running under your account. If there's an ID there that doesn't correspond to an "expected" Excel process, you could have a culprit.

itowlson
Thanks a lot, I really appreciate your thoughts and efforts here. I'll give this a look. I don't see how this could be a process created on any other login, because I'm the only one on this machine and there are no other accounts. However, I think that a corrupt/crashed instance of Excel *could* have caused this. I'll be back after more testing...
Mike Rosenblum
My best guess is that I had some sort of bizarre corruption. Rebooting solved everything. My guess is that the ROT was corupted, and/or I had a hanging instance of Excel that was so corrupt that even 'Process' operations were not necessarily stable. Nothing conclusive, but this is all I can figure for now... I appreciate the help.
Mike Rosenblum
+1  A: 

After reading the documentation:

Use this method to create an array of new Process components and associate them with all the process resources that are running the same executable file on the local computer.

It looks to me like you don't need to check HasExited, since it only returns running processes.

jasonh
Good point. My code is redundant at best and amounts to a race condition where 'Process.HasExited' could only return 'false' if a process returned via 'Process.GetProcessesByName' had exited in the microsecond between recieving the 'Process' instance and testing it for 'HasExited'. So I have now removed the call to 'HasExited'. The previous mystery remains, however, but thanks for your help!
Mike Rosenblum