如何在 C# 中确定进程的所有者?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/777548/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 00:45:14  来源:igfitidea点击:

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

c#.netprocess

提问by adeel825

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.

我正在寻找一个名为“MyApp.exe”的进程,我想确保我获得了特定用户拥有的进程。

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?

这给了我一个进程列表,但在 Process 类中似乎没有办法确定谁拥有该进程?关于我如何做到这一点的任何想法?

采纳答案by Dirk Vollmar

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.dllto your project.

您可以使用 WMI 来让用户拥有某个进程。要使用 WMI,您需要添加System.Management.dll对项目的引用。

By process id:

按进程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";
}

回答by Damovisa

Unfortunately there's no native .Net way of getting the process owner.

不幸的是,没有获得流程所有者的本地 .Net 方式。

Have a look at these for a potential solution:

看看这些潜在的解决方案:

回答by Stefano

Here is the VB version for the non C# speakers:

这是非 C# 演讲者的 VB 版本:

Function GetProcessOwner(ProcessName As String) As String
    Dim query = "Select * from Win32_Process Where Name = """ + ProcessName + """"
    Dim searcher = New ManagementObjectSearcher(query)
    Dim processList = searcher.Get()

    For Each obj As ManagementObject In processList
      Dim argList As String() = {String.Empty, String.Empty}
      Dim returnVal = Convert.ToInt32(obj.InvokeMethod("GetOwner", argList))
      If returnVal = 0 Then
        ' return DOMAIN\user
        Dim owner = argList(1) + "\" + argList(0)
        Return owner
      End If
    Next

    Return "NO OWNER"
  End Function

  Function GetProcessOwner(processId As Integer) As String
    Dim query = "Select * From Win32_Process Where ProcessID = " & processId
    Dim searcher = New ManagementObjectSearcher(query)
    Dim processList = searcher.Get()

    For Each obj As ManagementObject In processList
      Dim argList As String() = {String.Empty, String.Empty}
      Dim returnVal = Convert.ToInt32(obj.InvokeMethod("GetOwner", argList))
      If returnVal = 0 Then
        ' return DOMAIN\user
        Return argList(1) + "\" + argList(0)
      End If
    Next

    Return "NO OWNER"
  End Function

回答by WonderWorker

Add a reference to your project:

添加对您的项目的引用:

System.Management

Then add the following method to your project:

然后将以下方法添加到您的项目中:

    public string GetProcessOwner(int processId)
    {
        string MethodResult = null;
        try
        {
            StringBuilder sb = new StringBuilder();

            sb.Append(" SELECT ");
            sb.Append("     * ");
            sb.Append(" FROM ");
            sb.Append("     WIN32_PROCESS");
            sb.Append(" WHERE ");
            sb.Append("     ProcessId = " + processId);

            string Query = sb.ToString();

            ManagementObjectCollection Processes = new ManagementObjectSearcher(Query).Get();

            foreach (ManagementObject Process in Processes)
            {
                string[] Args = new string[] { "", "" };

                int ReturnCode = Convert.ToInt32(Process.InvokeMethod("GetOwner", Args));

                switch(ReturnCode)
                {
                    case 0:
                        MethodResult = Args[1] + "\" + Args[0];
                        break;

                    default:
                        MethodResult = "None";
                        break;

                }

            }

        }
        catch //(Exception ex)
        {
            //ex.HandleException();
        }
        return MethodResult;
    }

Then add this method:

然后添加这个方法:

    public DataTable GetProcessTable()
    {
        DataTable MethodResult = null;
        try
        {
            List<Process> Processes = Process.GetProcesses().ToList<Process>();

            DataTable dt = new DataTable();
            dt.Columns.Add("Name", typeof(string));
            dt.Columns["Name"].ReadOnly = true;

            dt.Columns.Add("Id", typeof(string));
            dt.Columns["Id"].ReadOnly = true;

            dt.Columns.Add("Owner", typeof(string));
            dt.Columns["Owner"].ReadOnly = true;

            foreach (Process p in Processes)
            {
                DataRow r = dt.NewRow();

                bool Match = false;

                r["Id"] = p.Id.ToString();
                r["Name"] = p.ProcessName;
                r["Owner"] = GetProcessOwner(p.Id);

                dt.Rows.Add(r);

            }

            MethodResult = dt;

        }
        catch //(Exception ex)
        {
            //ex.HandleException();
        }
        return MethodResult;
    }

Calling GetProcessTable() gives you a DataTable of all running processes along with their Id and Name, which is handy because it can be used as a DataGridView's Datasource parameter.

调用 GetProcessTable() 会为您提供一个包含所有正在运行的进程及其 Id 和 Name 的 DataTable,这很方便,因为它可以用作 DataGridView 的 Datasource 参数。

Let me know if you need any more fields adding to the table.

如果您需要向表中添加更多字段,请告诉我。

回答by bytecode77

Since WMI is not always a fast way of retrieving information, here is the native P/Invoke way of doing it:

由于 WMI 并不总是检索信息的快速方式,这里是执行此操作的本机 P/Invoke 方式:

The return value is nullwhen unsuccessful. In order to get the names of processes running under the SYSTEM user, you need to execute this code as administrator.

返回值是null不成功时。为了获取在 SYSTEM 用户下运行的进程的名称,您需要以管理员身份执行此代码。

private static string GetProcessUser(Process process)
{
    IntPtr processHandle = IntPtr.Zero;
    try
    {
        OpenProcessToken(process.Handle, 8, out processHandle);
        WindowsIdentity wi = new WindowsIdentity(processHandle);
        string user = wi.Name;
        return user.Contains(@"\") ? user.Substring(user.IndexOf(@"\") + 1) : user;
    }
    catch
    {
        return null;
    }
    finally
    {
        if (processHandle != IntPtr.Zero)
        {
            CloseHandle(processHandle);
        }
    }
}

[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);

回答by Ben Lin

System.Security.Principal.WindowsIdentity.GetCurrent().Name

回答by Fallon Yager

This is the easiest way I've found to do it:

这是我发现的最简单的方法:

Process[] processes = Process.GetProcessesByName("MyApp");
foreach (Process process in processes)
{
     string username = process.StartInfo.Environment["USERNAME"];

     // do some stuff
} 

回答by Latency

@bytecode77:

@字节码77:

I can tell you right now just from looking at your code, that you will be generating potential FirstChanceExceptions … specifically due to "ACCESS DENIED" (Win32Exception) and "NOT RUNNING" (ArgumentException) for these.in your 'catch' block from the expression evaluator of 'process.Handle'.

我现在可以通过查看您的代码告诉您,您将生成潜在的 FirstChanceExceptions ......特别是由于这些的“访问被拒绝”(Win32Exception)和“未运行”(ArgumentException)。在您的“catch”块中'process.Handle' 的表达式计算器。

The process handle is private to an application--in other words, process handles cannot be shared.

进程句柄是应用程序私有的——换句话说,进程句柄不能共享。

See also LinkDemand=6 and SecurityCriticalAttribute.

另请参阅LinkDemand=6 和SecurityCriticalAttribute

While you may still need to 'Tools -> Debugging -> Enabled Just My Code' for this attribute, the FirstChanceExceptions will still get raised.

虽然您可能仍需要为此属性“工具 -> 调试 -> 仅启用我的代码”,但仍会引发 FirstChanceExceptions。

Other than that, I do agree with your answer in pinvoke Win32 call being faster than WMI, especially when iterating over ALL processes.

除此之外,我确实同意您在 pinvoke Win32 调用中的回答比 WMI 快,尤其是在迭代所有进程时。

[DebuggerNonUserCode]
private static IEnumerable<Process> GetProcesses() =>
  Process.GetProcesses().Where(p => {
    var hasException = false;
    try {
      var x = p.Handle;
    } catch {
      hasException = true;
    }
    return !hasException;
  }).ToArray();

回答by Igor

    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

在此处实现方法 GetUsername:https://stackoverflow.com/a/35810391/10412686

回答by Blaato

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):

WMI 确实是如何从 Process 获取此信息的最糟糕的方法。但是...有时您需要从远程进程获取该信息,在这种情况下,您很遗憾需要 WMI。因此,如果您必须或想要使用 WMI,我建议您这样做(比上述经典 WMI 方法快 60% 以上):

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.

如果您想查看时间比较少,请参阅此答案