C# - find parent process through P/Invoke NtQue...

In previous post, we have seen some native method that can find out the Process three, see title -  C# - Find the process tree given the parent process id .
The problem with that approach is that it does not scale well because it has go through the process list repeately, however, from the internet search, we did find that we can use the P/Invoke method into the NtQueryInformationProcess. Here is information on how to use the it.

msdn - NtQueryInformationProcess 
Also, from codeproject, there is an example on how to use it (in C++ code)
codeproject - Get Process Info with NtQueryInformationProcess 

Here is the code, and I tried to add more things around the call to Format error messages if possible.
c#

[StructLayout(LayoutKind.Sequential)]
  public struct ParentProcessUtilities
  {
    // these members must match PROCESS_BASIC_INFORMATION
    // you can find more information and example here :   http://www.codeproject.com/Articles/19685/Get-Process-Info-with-NtQueryInformationProcess
    // also, check this out:  http://msdn.microsoft.com/en-us/library/windows/desktop/ms684280(v=vs.85).aspx
    internal IntPtr Reserverd1;
    internal IntPtr pebBaseAddress;
    internal IntPtr Reserved2_0;
    internal IntPtr Reserved2_1;
    internal IntPtr UniqueProcessId;
    internal IntPtr InheritedFromUniqueProcessId;

    // there is a good site that tells you how you can use the P/Invoke from  C#
    //    http://www.pinvoke.net/default.aspx/kernel32.formatmessage
    [DllImport("Kernel32.dll", SetLastError=true)]
    private static extern uint FormatMessage(
      uint dwFalgs,
      IntPtr lpSource,
      uint dwMessageId,
      uint dwLanguageId,
      ref IntPtr lpBuffer,
      uint nSize,
      IntPtr Arguments);

    // from header files
    const uint FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
    const uint FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
    const uint FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
    const uint FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000;
    const uint FORMAT_MESSAGE_FROM_HMODULE = 0x00000800;
    const uint FORMAT_MESSAGE_FROM_STRING = 0x00000400;


    // you will need to p/invoke the NtQueryProcessInformation
    // see pinvoke.net for more information.
    //
    [DllImport("ntdll.dll")]
    private static extern int NtQueryInformationProcess(
      IntPtr processHandle,
      int processInformationClass,
      ref ParentProcessUtilities processInformation,
      int processInformationLength, out int returnLength);

    // you may use the LocalFree to free memory (usually the buffer) allocated
    // on unmanaged world
    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern IntPtr LocalFree(IntPtr hMem);

    // built around the p/invoke to get the parent process
    /// <summary>
    /// Gets the parent process of the current process.
    /// </summary>
    /// <returns>An instance of the Process class.</returns>
    public static Process GetParentProcess()
    {
      return GetParentProcess(Process.GetCurrentProcess().Id);
    }

     /// <summary>
     /// Gets the parent process of specified process.
     /// </summary>
     /// <param name="id">The process id.</param>
     /// <returns>An instance of the Process class.</returns>
     public static Process GetParentProcess(int id)
     {
       // you cannot do this, because it you cannot expect the process id 
       // can automatically convert to system internal handle
       //IntPtr handle = new IntPtr(id);
       //return GetParentProcess(handle);
       // you will need to get the handle through the process class
       Process process = Process.GetProcessById(id);
       return GetParentProcess(process.Handle);
     }

     /// <summary>
     /// Gets the parent process of a specified process.
     /// </summary>
     /// <param name="handle">The process handle.</param>
     /// <returns>An instance of the Process class.</returns>
     public static Process GetParentProcess(IntPtr handle)
     {
       var pbi = new ParentProcessUtilities();

       int returnLength;
       int status = NtQueryInformationProcess(handle,
                                           0,
                                           ref pbi,
                                           Marshal.SizeOf(pbi), // you cannot use the "sizeof(ParentProcessUtilities)"
                                           out returnLength);
       if (status != 0)
       {
         IntPtr lpMsgBuf = IntPtr.Zero;

         uint dwFlags = FormatMessage(
            FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
            IntPtr.Zero,
            (uint) Marshal.GetLastWin32Error(), // (uint) Marshal.GetLastWin32Error(),
            0,
            ref lpMsgBuf,
            0,
            IntPtr.Zero);

         if (dwFlags == 0)
         {
           // handle the error
           int le = Marshal.GetLastWin32Error();
           throw new Win32Exception(status);
         }
         else
         {
           string sRet = Marshal.PtrToStringAnsi(lpMsgBuf);
           // free the buffer
           lpMsgBuf = LocalFree(lpMsgBuf);
           Console.WriteLine("Error : {0}", sRet);
         }
       }
       try
       {
         return Process.GetProcessById(pbi.InheritedFromUniqueProcessId.ToInt32());
       }
       catch (ArgumentException)
       {
         // not found
         return null;
       }
     }
Below is the code that I used to drive this piece of code.
static void Main(string[] args)
    {
      //GetProcessAndChildrenMain();
      if (args.Length < 1)
      {
        Console.WriteLine("Usage : ProcessTree <pid>");
        return;
      }
      Process process = ParentProcessUtilities.GetParentProcess(Convert.ToInt32(args[0]));
      if (process != null)
      {
        Console.WriteLine(process.Id + " " + process.MainModule.ModuleName);
      }

    }
相關文章
相關標籤/搜索