Finding the owner of a Process in C#

The System.Diagnostics.Process class provides lots of information about any running process, but unfortunately not its owner's identity. We need to invoke unmanaged code to get that. Every process executed on behalf of a user has a copy of its access token. That token is returned by the OpenProcessToken function, and can then be used in a WindowsIdentity constructor. I created a wrapper around this functionality as an extension method to the Process class. This allows you to get the associated Windows Identity of a Process by simply calling aProcess.WindowsIdentity.

Here's how you use the new method:

            ////
            // 'WindowsIdentity' Extension Method Demo: 
            //   Enumerate all running processes with their associated Windows Identity
            ////
 
            foreach (var p in Process.GetProcesses())
            {
                string processName;
 
                try
                {
                    processName = p.WindowsIdentity().Name;
 
                }
                catch (Exception ex)
                {
 
                    processName = ex.Message; // Probably "Access is denied"
                }
 
                Console.WriteLine(p.ProcessName + " (" + processName + ")");
            }


Here's the corresponding class:

//-----------------------------------------------------------------------
// <copyright file="ProcessExtensions.cs" company="DockOfTheBay">
//     http://www.dotbay.be
// </copyright>
// <summary>Defines the ProcessExtensions class.</summary>
//-----------------------------------------------------------------------
 
namespace DockOfTheBay
{
    using System;
    using System.Diagnostics;
    using System.Runtime.InteropServices;
    using System.Security.Principal;
 
    /// <summary>
    /// Extension Methods for the System.Diagnostics.Process Class.
    /// </summary>
    public static class ProcessExtensions
    {
        /// <summary>
        /// Required to query an access token.
        /// </summary>
        private static uint TOKEN_QUERY = 0x0008;
 
        /// <summary>
        /// Returns the WindowsIdentity associated to a Process
        /// </summary>
        /// <param name="process">The Windows Process.</param>
        /// <returns>The WindowsIdentity of the Process.</returns>
        /// <remarks>Be prepared for 'Access Denied' Exceptions</remarks>
        public static WindowsIdentity WindowsIdentity(this Process process)
        {
            IntPtr ph = IntPtr.Zero;
            WindowsIdentity wi = null;
            try
            {
                OpenProcessToken(process.Handle, TOKEN_QUERY, out ph);
                wi = new WindowsIdentity(ph);
            }
            catch (Exception)
            {
                throw;
            }
            finally
            {
                if (ph != IntPtr.Zero)
                {
                    CloseHandle(ph);
                }
            }
 
            return wi;
        }
 
        [DllImport("advapi32.dll", SetLastError = true)]
        private static extern bool OpenProcessToken(IntPtr processHandle, uint desiredAccess, out IntPtr tokenHandle);
 
        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool CloseHandle(IntPtr hObject);
    }
}

5 comments:

  1. Great post, and what I was looking for, BUT, it needed the following to get it working for me:
    I'm sure you can work out the tidyup and const definition code:-)

    OpenProcessToken(process.Handle, TOKEN_DUPLICATE | TOKEN_IMPERSONATE | TOKEN_QUERY, out ph);

    wi = new WindowsIdentity(DuplicateToken(ph, SecurityImpersonation, ref dupeTokenHandle)?dupeTokenHandle:ph);

    ReplyDelete
  2. Just to clarify, I was using your class like this:
    using (Process ownerProcess = Process.GetProcessById((int) info.ProcessId))
    {
    using (WindowsImpersonationContext impersonationContext = ownerProcess.WindowsIdentity().Impersonate())
    {
    // Do Access / ACL stuff
    }
    }

    ReplyDelete
  3. Hi,

    I know you wrote this a long time ago, but I'm currently writing a service that monitors all processes and your class does not work for me. I always have an access denied error...
    Can you help me ?

    ReplyDelete
  4. Hi,
    I added the code in my app, it returned me the following error.
    Token cannot be zero.

    Could you please help me out?

    ReplyDelete
  5. It only returns me the current users name, but the same process is executed by other users it can't access their name even the current users is administrator.

    ReplyDelete