How to get the parent process that launched a C# application?

Hey all,

I have a process that is a C# process.  I need to do something different if someone just double-clicks the application than if it is launched by a certain other process.  So I decided to check the parent process.

I couldn’t find a simple C# only method. But I did find a code snippet that works. There were actually lots of posts on that provided the following code snippet or variations thereof, so I consider it to be public domain.  So obviously I didn’t write this part.

        private static Process GetParentProcess()
        {
            int iParentPid = 0;
            int iCurrentPid = Process.GetCurrentProcess().Id;

            IntPtr oHnd = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

            if (oHnd == IntPtr.Zero)
                return null;

            PROCESSENTRY32 oProcInfo = new PROCESSENTRY32();

            oProcInfo.dwSize =
            (uint)System.Runtime.InteropServices.Marshal.SizeOf(typeof(PROCESSENTRY32));

            if (Process32First(oHnd, ref oProcInfo) == false)
                return null;

            do
            {
                if (iCurrentPid == oProcInfo.th32ProcessID)
                    iParentPid = (int)oProcInfo.th32ParentProcessID;
            }
            while (iParentPid == 0 && Process32Next(oHnd, ref oProcInfo));

            if (iParentPid > 0)
                return Process.GetProcessById(iParentPid);
            else
                return null;
        }

        static uint TH32CS_SNAPPROCESS = 2;

        [StructLayout(LayoutKind.Sequential)]
        public struct PROCESSENTRY32
        {
            public uint dwSize;
            public uint cntUsage;
            public uint th32ProcessID;
            public IntPtr th32DefaultHeapID;
            public uint th32ModuleID;
            public uint cntThreads;
            public uint th32ParentProcessID;
            public int pcPriClassBase;
            public uint dwFlags;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
            public string szExeFile;
        };

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern IntPtr CreateToolhelp32Snapshot(uint dwFlags, uint th32ProcessID);

        [DllImport("kernel32.dll")]
        static extern bool Process32First(IntPtr hSnapshot, ref PROCESSENTRY32 lppe);

        [DllImport("kernel32.dll")]
        static extern bool Process32Next(IntPtr hSnapshot, ref PROCESSENTRY32 lppe);
    }

I took this code snippet and improved upon it and made myself the following static class. This class more easily exposes:

  • Parent Process Id
  • Parent Process name
  • Parent process executable name
  • Full path to parent process executable
  • Directory name where the parent process executable resides
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
using System.Runtime.InteropServices;

namespace ParentProcess
{
    public class ParentProcess
    {
        public static String ProcessName
        {
            get { return GetParentProcess().ProcessName; }
        }

        public static int ProcessId
        {
            get { return GetParentProcess().Id; }
        }

        public static String FullPath
        {
            get
            {
                return GetParentProcess().MainModule.FileName;
            }
        }

        public static String FileName
        {
            get
            {
                return System.IO.Path.GetFileName(GetParentProcess().MainModule.FileName);
            }
        }

        public static String DirectoryName
        {
            get
            {
                return System.IO.Path.GetDirectoryName(GetParentProcess().MainModule.FileName);
            }
        }

        private static Process GetParentProcess()
        {
            int iParentPid = 0;
            int iCurrentPid = Process.GetCurrentProcess().Id;

            IntPtr oHnd = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);

            if (oHnd == IntPtr.Zero)
                return null;

            PROCESSENTRY32 oProcInfo = new PROCESSENTRY32();

            oProcInfo.dwSize =
            (uint)System.Runtime.InteropServices.Marshal.SizeOf(typeof(PROCESSENTRY32));

            if (Process32First(oHnd, ref oProcInfo) == false)
                return null;

            do
            {
                if (iCurrentPid == oProcInfo.th32ProcessID)
                    iParentPid = (int)oProcInfo.th32ParentProcessID;
            }
            while (iParentPid == 0 && Process32Next(oHnd, ref oProcInfo));

            if (iParentPid > 0)
                return Process.GetProcessById(iParentPid);
            else
                return null;
        }

        static uint TH32CS_SNAPPROCESS = 2;

        [StructLayout(LayoutKind.Sequential)]
        public struct PROCESSENTRY32
        {
            public uint dwSize;
            public uint cntUsage;
            public uint th32ProcessID;
            public IntPtr th32DefaultHeapID;
            public uint th32ModuleID;
            public uint cntThreads;
            public uint th32ParentProcessID;
            public int pcPriClassBase;
            public uint dwFlags;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
            public string szExeFile;
        };

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern IntPtr CreateToolhelp32Snapshot(uint dwFlags, uint th32ProcessID);

        [DllImport("kernel32.dll")]
        static extern bool Process32First(IntPtr hSnapshot, ref PROCESSENTRY32 lppe);

        [DllImport("kernel32.dll")]
        static extern bool Process32Next(IntPtr hSnapshot, ref PROCESSENTRY32 lppe);
    }
}

This makes it easier for me to simply include the class above in my code and make simple calls:

String exename = ParentProcess.FileName;
String FullPathToExe = ParentProcess.FullPath;
String DirectoryInWhichExeResides= ParentProcess.DirectoryName;

…and the pid and process name, etc…

I hope this helps you.

References

http://www.facebook.com/note.php?note_id=73447531256

http://www.debugging.com/bug/6657

http://www.eggheadcafe.com/software/aspnet/35541264/how-to-get-the-parent-pro.aspx


No Copyright.

8 Comments

  1. kiquenet says:

    I get this error

    System.ComponentModel.Win32Exception (0x80004005): A 32 bit processes cannot access modules of a 64 bit process.
    at System.Diagnostics.NtProcessManager.GetModuleInfos(Int32 processId, Boolean firstModuleOnly)
    at System.Diagnostics.NtProcessManager.GetFirstModuleInfo(Int32 processId)
    at System.Diagnostics.Process.get_MainModule()
    at ParentProcess.get_FileName()

    I use unit test in VS2010 (32 bits,x86), Windows 7 64 bits

    My unit test use VS Automation

    EnvDTE80.DTE2 dte = null;
    try
    {
    Type type = System.Type.GetTypeFromProgID("VisualStudio.DTE.10.0");
    object inst = System.Activator.CreateInstance(type, true);
    dte = (EnvDTE80.DTE2)inst;
    ..

    The error appears in my Addin VS source code when I use ParentProcess.

    Any suggestions?

  2. Reinhard says:

    Nice one, but you should really use a try-finally clause around the code, because the oHnd must be freed using CloseHandle to prevent memory leaks!
    (i.e:

    try
    {
    IntPtr oHnd = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    ...etc.
    }
    finally
    {
    if (oHnd != IntPtr.Zero)
    {
    CloseHandle(oHnd);
    }
    }

    [DllImport("kernel32.dll", SetLastError = true, CallingConvention = CallingConvention.Winapi, CharSet = CharSet.Auto)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool CloseHandle(IntPtr hObject);

  3. 0xG says:

    This works OK, but I wish that there were some comments for n00bies like me.

    Also, copying the text results in line numbers, which is unpleasant.

    Thanks for the code though (wish I understood it better!)

    • rhyous says:

      OxG,

      On wordpress, code is displayed with line numbers and if you copy and paste directly from the post, you get line numbers. However, when your cursor is over the code block, you get buttons on the right, two which allow for copying. One opens the code without line numbers so you can copy and paste, one is a flash tool that puts the code without line numbers into your clipboard.

      Comments are definitely in order. I'll see what I can do.

Leave a Reply

Powered by sweet Captcha