Enumerating Managed Processes
I noticed Brad Abrams and Krzysztof Cwalina were blogging about how to find a list of all the managed processes on a machine.
Their solution involved looking for managed perf counters. The debugging APIs provide a more orthodox approach via the ICorPublish API (see corpub.idl in the SDK). ICorPublish is an unmanaged com-classic API, but MDbg contains managed wrappers for it.
Here’s a simple C# test app to demo this:
using System;
using Microsoft.Samples.Debugging.CorPublish; // Managed wrappers for ICorPublish
class Program
{
public static void Main()
{
CorPublish cp = new CorPublish();
Console.WriteLine("Active processes on current maschine:");
foreach (CorPublishProcess cpp in cp.EnumProcesses())
{
Console.WriteLine("(PID: " + cpp.ProcessId + ") " + cpp.DisplayName);
}
}
}
You need to link it against corapi.dll and corapi2.dll from the MDbg beta 1 sample.
The “processenum” command in MDbg and the “pro” command in Cordbg use this API to list all managed processes. This is particularly useful funcitonality when you want to attach to a running managed process. So it’s no surprise that Visual Studio’s Attach dialog has similar functionality.
Here’s the code for MDbg’s “processenum” command. (In BuiltInCommands.cs in the MDbg beta 1 sample). I used this to write the demo app above.
[
CommandDescription(
CommandName="processenum",
MinimumAbbrev=3,
ResourceManagerKey=typeof(BuiltInCommands)
)
]
public static void ProcessEnumCmd(string arguments)
{
CorPublish cp = new CorPublish();
WriteOutput("Active processes on current maschine:");
foreach(CorPublishProcess cpp in cp.EnumProcesses())
{
if(Process.GetCurrentProcess().Id!=cpp.ProcessId) // let's hide our process
{
WriteOutput("(PID: "+cpp.ProcessId+") "+cpp.DisplayName);
foreach(CorPublishAppDomain cpad in cpp.EnumAppDomains())
{
WriteOutput("\t(ID: "+cpad.Id+") "+cpad.Name);
}
}
}
}
Here’s the equivalent code from Cordbg’s “pro” command. (This is from the Cordbg sample in the V1.1 SDK in $\SDK\v1.1\Tool Developers Guide\Samples\debugger\Commands.cpp)
virtual void Do(DebuggerShell *shell, ICorDebug *cor, const WCHAR *args)
{
BOOL fPidSpecified = TRUE;
int ulPid;
if (!shell->GetIntArg(args, ulPid))
fPidSpecified = FALSE;
ICorPublish *pPublish;
HRESULT hr = ::CoCreateInstance (CLSID_CorpubPublish,
NULL,
CLSCTX_INPROC_SERVER,
IID_ICorPublish,
(LPVOID *)&pPublish);
if (SUCCEEDED (hr))
{
ICorPublishProcessEnum *pProcessEnum = NULL;
ICorPublishProcess *pProcess [1];
BOOL fAtleastOne = FALSE;
if (fPidSpecified == FALSE)
{
hr = pPublish->EnumProcesses (COR_PUB_MANAGEDONLY,
&pProcessEnum);
}
else
{
hr = pPublish->GetProcess (ulPid,
pProcess);
}
if (SUCCEEDED (hr))
{
ULONG ulElemsFetched;
if (fPidSpecified == FALSE)
{
pProcessEnum->Next (1, pProcess, &ulElemsFetched);
}
else
{
ulElemsFetched = 1;
}
while (ulElemsFetched != 0)
{
UINT pid;
WCHAR szName [64];
ULONG32 ulNameLength;
BOOL fIsManaged;
pProcess [0]->GetProcessID (&pid);
pProcess [0]->GetDisplayName (64, &ulNameLength, szName);
pProcess [0]->IsManaged (&fIsManaged);
if ((fPidSpecified == FALSE) || (pid == ulPid))
{
shell->Write (L"\nPID=0x%x (%d) Name=%s\n", pid, pid, szName);
fAtleastOne = TRUE;
ICorPublishAppDomainEnum *pAppDomainEnum;
hr = pProcess [0]->EnumAppDomains (&pAppDomainEnum);
if (SUCCEEDED (hr))
{
ICorPublishAppDomain *pAppDomain [1];
ULONG ulAppDomainsFetched;
pAppDomainEnum->Next (1, pAppDomain, &ulAppDomainsFetched);
while (ulAppDomainsFetched != 0)
{
ULONG32 uId;
WCHAR szName [64];
ULONG32 ulNameLength;
pAppDomain [0]->GetID (&uId);
pAppDomain [0]->GetName (64, &ulNameLength, szName);
shell->Write (L"\tID=%d AppDomainName=%s\n", uId, szName);
pAppDomain [0]->Release();
pAppDomainEnum->Next (1, pAppDomain, &ulAppDomainsFetched);
}
}
}
pProcess [0]->Release();
if (fPidSpecified == FALSE)
{
pProcessEnum->Next (1, pProcess, &ulElemsFetched);
}
else
{
ulElemsFetched--;
}
}
if (!fAtleastOne)
{
if (fPidSpecified)
shell->Error (L"No managed process with given ProcessId found\n");
else
shell->Error (L"No managed process found\n");
}
}
if (pProcessEnum != NULL)
pProcessEnum->Release();
}
}
As a little tangent, I find it interesting that the unmanaged code is about 120 lines and the managed goo is only about 20 lines. And they do the same thing. I love managed code.