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.
Comments
Anonymous
November 25, 2004
linkAnonymous
August 08, 2005
I've given sample code for MDbg-based harnesses that launch an app and then print all loaded modules...Anonymous
March 08, 2006
Jan Stranik is on MSDN TV talking about MDbg, the managed-debugging sample written in C#.  See the...Anonymous
June 18, 2006
PingBack from http://blogs.msdn.com/jmstall/archive/2005/11/08/mdbg_linkfest.aspxAnonymous
May 30, 2007
Jason Zander posted sample code to enumerate all the CLRs (both regulars like 1.0, 1.1, 2.0 and SilverlightAnonymous
May 30, 2007
PingBack from http://msdnrss.thecoderblogs.com/2007/05/31/silverlight-and-clr-enumeration/Anonymous
May 30, 2007
PingBack from http://msdnrss.thecoderblogs.com/2007/05/31/silverlight-and-clr-enumeration-2/Anonymous
August 27, 2007
I mentioned earlier that ICorDebug does not cross the 32/64 boundary . If you want to debug a 32-bit