How do I determine the owner of a process in C#?

Viewed 67339

I am looking for a process by the name of "MyApp.exe" and I want to make sure I get the process that is owned by a particular user.

I use the following code to get a list of the processes:

Process[] processes = Process.GetProcessesByName("MyApp");

This gives me a list of processes, but there does not appear to be a way in the Process class to determine who owns that process? Any thoughts on how I can do this?

10 Answers

You can use WMI to get the user owning a certain process. To use WMI you need to add a reference to the System.Management.dll to your project.

By process id:

public string GetProcessOwner(int processId)
{
    string query = "Select * From Win32_Process Where ProcessID = " + processId;
    ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
    ManagementObjectCollection processList = searcher.Get();

    foreach (ManagementObject obj in processList)
    {
        string[] argList = new string[] { string.Empty, string.Empty };
        int returnVal = Convert.ToInt32(obj.InvokeMethod("GetOwner", argList));
        if (returnVal == 0)
        {
            // return DOMAIN\user
            return argList[1] + "\\" + argList[0];
        }
    }

    return "NO OWNER";
}

By process name (finds the first process only, adjust accordingly):

public string GetProcessOwner(string processName)
{
    string query = "Select * from Win32_Process Where Name = \"" + processName + "\"";
    ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
    ManagementObjectCollection processList = searcher.Get();

    foreach (ManagementObject obj in processList)
    {
        string[] argList = new string[] { string.Empty, string.Empty };
        int returnVal = Convert.ToInt32(obj.InvokeMethod("GetOwner", argList));
        if (returnVal == 0)
        {
            // return DOMAIN\user
            string owner = argList[1] + "\\" + argList[0];
            return owner;       
        }
    }

    return "NO OWNER";
}
    var myApp = Process.GetProcessesByName("MyApp").FirstOrDefault();
    if (myApp != null)
    {
        string username = GetUsername(myApp.SessionId);
    }

Implementation of method GetUsername here: https://stackoverflow.com/a/35810391/10412686

WMI is really the worst possible way how to get this information from Process. But... sometimes you need to get that info from remote process, and in that case you sadly need WMI. So if you have to, or want to use WMI, I suggest to do it like this (it's more than 60% quicker then classic WMI methods above):

Method:

public struct WMIProcessProperties
{
    public string Owner;
    public int ID;
}


public static async Task<Dictionary<Process, WMIProcessProperties>> GetWMIProperties(this IEnumerable<Process> processes)
{
    Dictionary<Process, WMIProcessProperties> result = new Dictionary<Process, WMIProcessProperties>();

    if (processes == null || processes.Count() == 0) { return result; }

    string selectQuery = "SELECT Handle, ProcessID FROM Win32_Process";
    selectQuery += processes.Count() <= 10 ? string.Format(" WHERE ProcessID = {0}", string.Join(" OR ProcessID = ", processes.Select(p => p.Id))) : string.Empty;

    using (CimSession session = await Task.Run(() => CimSession.Create(processes.ElementAt(0).MachineName)))
    {
        List<CimInstance> instances = await Task.Run(() => session.QueryInstances(@"root\cimv2", "WQL", selectQuery).ToList());

        List<Task<WMIProcessProperties>> tasks = new List<Task<WMIProcessProperties>>();

        for (int i = 0; i < instances.Count; i++)
        {
            CimInstance currentInstance = instances[i];

            tasks.Add(Task.Run(() =>
            {
                int id = Convert.ToInt32(currentInstance.CimInstanceProperties["ProcessID"].Value);
                string owner;
                using (CimMethodResult getOwnerResult = session.InvokeMethod(currentInstance, "GetOwner", null))
                {
                     owner = getOwnerResult.OutParameters["User"]?.Value?.ToString();
                }

                currentInstance.Dispose();

                return new WMIProcessProperties { Owner = owner, ID = id };

            }));
        }

        WMIProcessProperties[] wmiProcessProperties = await Task.WhenAll(tasks).ConfigureAwait(false);

        for (int i = 0; i < wmiProcessProperties.Length; i++)
        {
            result.Add(processes.Single(p => p.Id == wmiProcessProperties[i].ID), wmiProcessProperties[i]);
        }
    }

    return result;
}

If you want to see little time comparison, see this answer.

Loop through collection to check for permissions. Most cases current user will not be administrator

List<Process> processes = Process.GetProcessesByName(Text).ToList();
for (int i = processes.Count - 1; i > -1; i--)
{
    try
    {
        if (processes[i].MainModule?.FileName is null)
            processes.RemoveAt(i);
    }
    catch (Exception)
    {
        processes.RemoveAt(i);
    }
}

With the help from detecting-user-name-from-process-id I have written the better - quicker - version of this function:

public static string GetProcessOwnerByID(int processId)
{
  IntPtr processHandle = IntPtr.Zero;
  IntPtr tokenHandle = IntPtr.Zero;
  try
  {
    processHandle = OpenProcess(PROCESS_QUERY_INFORMATION, false, processId);
    if (processHandle == IntPtr.Zero)
      return "NO ACCESS";

    OpenProcessToken(processHandle, TOKEN_QUERY, out tokenHandle);
    using (WindowsIdentity wi = new WindowsIdentity(tokenHandle))
    {
      string user = wi.Name;
      return user.Contains(@"\") ? user.Substring(user.IndexOf(@"\") + 1) : user;
    }
  }
  finally
  {
    if (tokenHandle != IntPtr.Zero) CloseHandle(tokenHandle);
    if (processHandle != IntPtr.Zero) CloseHandle(processHandle);
  }
}

Whole file can be found on GitHub gist

Related