views:

981

answers:

4

I'm looking for an easy way to find the process tree (as shown by tools like Process Explorer), in C# or other .Net language. It would also be useful to find the command-line arguments of another process (the StartInfo on System.Diagnostics.Process seems invalid for process other than the current process).

I think these things can only be done by invoking the win32 api, but I'd be happy to be proved wrong.

Thanks!

Robert

A: 

I'm pretty sure you'll need to go with the WinAPI for this. See this code snippet as an example. It will be a fairly standard algorithm to build up a tree from a list of objects and their parent objects.

Noldorin
+2  A: 

You could also try to look into WMI (Win32_Process class for example).

Here is an example to get the command line of a process by its process id:

                using (var mos = new ManagementObjectSearcher(
                    "SELECT CommandLine FROM Win32_Process WHERE ProcessId = " + pid))
                {
                    foreach (var obj in mos.Get())
                    {
                        object data = obj.Properties["CommandLine"].Value;
                        if (data != null)
                        {
                            Console.WriteLine("{0}: commandline = {1}", pid, data.ToString());
                        }
                    }
                }
            }
        }

I ripped this from some code I've recently written. For your purposes you might use other techniques (like getting multiple properties and/or processes at once). But you get the idea.

EDIT: The parent process id, which you'll need for building the tree is the "ParentProcessId" Property, for example.

I guess, however, using the Win32 API is faster. Note that not every functionality you required is rightly available there as well. For some stuff you would have resort to the (somewhat) unsupported NtQueryProcessInformation() function or others from NTDLL.

Christian.K
+2  A: 

I don't see why you don't want to p/invoke. If you look at System.Diagnostics in Reflector, you'll see that it uses p/invokes internally. Anyway, the Process class does not have a way of retrieving a process' parent PID. Instead:

The struct definition:

[StructLayout(LayoutKind.Sequential)]
struct PROCESS_BASIC_INFORMATION
{
    public int ExitStatus;
    public int PebBaseAddress;
    public int AffinityMask;
    public int BasePriority;
    public int UniqueProcessId;
    public int InheritedFromUniqueProcessId;
}

The (simplified) function import:

[DllImport("ntdll.dll")]
static extern int NtQueryInformationProcess(
    IntPtr ProcessHandle,
    int ProcessInformationClass,
    out PROCESS_BASIC_INFORMATION ProcessInformation,
    int ProcessInformationLength,
    out int ReturnLength
    );

The code:

Process p = Process.GetProcessById(1234);
PROCESS_BASIC_INFORMATION pbi;
int size;

NtQueryInformationProcess(p.Handle, 0, out pbi, Marshal.SizeOf(typeof(PROCESS_BASIC_INFORMATION)), out size);
// pbi.InheritedFromUniqueProcessId now contains the process' parent PID

You'll need to insert these usings at the top of your file:

using System.Runtime.InteropServices;
using System.Diagnostics;

If you want to enumerate processes, you'd be better off using NtQuerySystemInformation - although that code is a bit too long to post here.

wj32
+2  A: 

If you don't want to P/Invoke, you can grab the parent Id's with a performance counter:

foreach (var p in Process.GetProcesses())
{
   var performanceCounter = new PerformanceCounter("Process", "Creating Process ID", p.ProcessName);
   var parent = GetProcessIdIfStillRunning((int)performanceCounter.RawValue);
   Console.WriteLine(" Process {0}(pid {1} was started by Process {2}(Pid {3})",
              p.ProcessName, p.Id, parent.ProcessName, parent.ProcessId );
}

//Below is helper stuff to deal with exceptions from 
//looking-up no-longer existing parent processes:

struct MyProcInfo
{
    public int ProcessId;
    public string ProcessName;
}

static MyProcInfo GetProcessIdIfStillRunning(int pid)
{
    try
    {
        var p = Process.GetProcessById(pid);
        return new MyProcInfo() { ProcessId = p.Id, ProcessName = p.ProcessName };
    }
    catch (ArgumentException)
    {
        return new MyProcInfo() { ProcessId = -1, ProcessName = "No-longer existant process" };
    }
}

now just put it into whatever tree structure want and you are done.

DanO