I was reading through some documentation on Sysinternals Process Explorer the other day, and one bit that caught my attention was some detail on the .NET tab. So, I pulled up my trusty managed StockViewer application, and went to go and look at that tab.

Only…

…it wasn’t there.

Fascinating.

So I found another managed code process on my system, and tried to look at it’s tab – the excellent Switcher application.

No joy.

OK, so after telling Mark he didn’t know how to write programs (don’t try this at home) it was time to pull out the debugger. With a little help, because I did ask Mark how he was checking to see whether he should be putting the .NET tab on there. He said he was using both perfmon APIs and the ICorPublish APIs.

So, off to the MSDN documentation to start brewing up a hypothesis, and to be honest it just seemed most likely that the ICorPublish APIs would be the culprit, since they were called per process and perfmon itself was working so those APIs were probably going to be working too. Just a hunch.

I browsed through the APIs, and fortunately there were only two. Of those two, one seemed the most likely to be called for this, so I set a breakpoint on it.

bp mscordbi!CorpubPublish::GetProcess.

Sure enough, it was hit. Good guess.

Of course, this bp was hit rather a lot (I had quite a few processes running at the time), so I didn’t want to have to stop and inspect every call. I wanted to stop and inspect the call when it was looking specifically at the process I expected was managed (Stock Viewer) but procexp didn’t think was managed. So, off to Task Manager (sorry, procexp, but you were frozen at a breakpoint in my debugger) to pull the PID for Stock Viewer, which I then translated to hex for the debugger. Voila – breakpoint set.

1:001> bl

1 e 00000642`ff91302c 0001 (0001) 1:**** mscordbi!CorpubPublish::GetProcess "j @rdx=0x16EC '' ; 'gc' "

I hit this breakpoint, and then stepped out of the function. I then inspected rax to see the return code, and found it was 0x80070057. the 8 already indicates bad news, so I was expecting an error message. It was COR_E_ARGUMENT. Now, that wasn’t particularly helpful this time, since there was only one argument (the pid), so the question is – why didn’t it like to answer this question about this process? It would take some additional stepping through.

So, I did some call tracing, and found that GetProcess was calling through some internal APIs, and one of the interfaces was returning 0x80131c30. It appeared to be another COM call, so it could be a failure hresult, but the utility I use to look up error messages wasn’t telling me anything. The error number was not found.

Along the way, Mark had reached out to another Technical Fellow, so at this point I’m thinking to myself, “holy crap, there had better actually be a bug here and not user error” – and this other Technical Fellow pointed me in the direction of Thomas, a Senior Development Lead for the CLR. And what a useful connection this was. He deciphered the error: it’s returned if there is a bitness difference between the caller and the target.

Aha.

Both of the managed apps happened to be specifically flagged as x86 apps. So, to do a quick check that this was the only thing wrong, I only needed to create an AnyCPU managed application, and see if ProcExp would display a .NET tab for it. Indeed it does!

So, mystery solved. ProcExp can’t (today) peer into .NET information for managed processes that are 32-bit on 64-bit Windows (the default compiler setting is to create AnyCPU binaries). The wheels are already churning to figure out how to close up this edge case. I just got unlucky with the two managed apps I happened to be running.