wake-up-neo.net

Wie bestimme ich den Besitzer eines Prozesses in C #?

Ich suche nach einem Prozess mit dem Namen "MyApp.exe" und möchte sicherstellen, dass ich den Prozess erhalte, der einem bestimmten Benutzer gehört. 

Ich verwende den folgenden Code, um eine Liste der Prozesse zu erhalten:

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

Dies gibt mir eine Liste von Prozessen, aber es scheint keine Möglichkeit in der Process-Klasse zu geben, zu bestimmen, wem dieser Prozess gehört. Irgendwelche Gedanken, wie ich das machen kann?

38
adeel825

Sie können WMI verwenden, um den Benutzer über einen bestimmten Prozess zu verfügen. Um WMI verwenden zu können, müssen Sie Ihrem Projekt einen Verweis auf System.Management.dll hinzufügen.

Nach Prozess-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";
}

Nach Prozessname (findet nur den ersten Prozess, passen Sie ihn entsprechend an):

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";
}
58
Dirk Vollmar

Da WMI nicht immer eine schnelle Methode zum Abrufen von Informationen ist, haben wir hier die native Methode von P/Invoke:

Der Rückgabewert ist null, wenn er nicht erfolgreich ist. Um die Namen von Prozessen abzurufen, die unter dem SYSTEM-Benutzer ausgeführt werden, müssen Sie diesen Code als Administrator ausführen.

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);
20
bytecode77

Hier ist die VB Version für die Nicht-C # -Lautsprecher:

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
7
Stefano

Leider gibt es keine native .NET-Methode, um den Prozessbesitzer zu erhalten.

Sehen Sie sich diese für eine mögliche Lösung an:

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

Implementierung der Methode GetUsername hier: https://stackoverflow.com/a/35810391/10412686

1
Igor

Fügen Sie Ihrem Projekt einen Verweis hinzu:

System.Management

Fügen Sie Ihrem Projekt dann die folgende Methode hinzu:

    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;
    }

Dann fügen Sie diese Methode hinzu:

    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;
    }

Wenn Sie GetProcessTable () aufrufen, erhalten Sie eine DataTable aller laufenden Prozesse zusammen mit ihrer ID und ihrem Namen. Dies ist praktisch, da sie als DataGridView-Parameter für Datenquellen verwendet werden kann.

Lassen Sie mich wissen, wenn Sie weitere Felder zur Tabelle hinzufügen möchten.

0

WMI ist wirklich der schlechteste Weg, um diese Informationen von Process zu erhalten. Aber ... manchmal müssen Sie diese Informationen vom Remote-Prozess abrufen, und in diesem Fall benötigen Sie leider WMI. Wenn Sie also WMI verwenden müssen oder möchten, empfehle ich Folgendes (es ist mehr als 60% schneller als die oben beschriebenen klassischen WMI-Methoden):

Methode:

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;
}

Wenn Sie nur einen kleinen Zeitvergleich sehen möchten, lesen Sie diese Antwort .

0
Blaato
System.Security.Principal.WindowsIdentity.GetCurrent().Name
0
Ben Lin

@ bytecode77:

Ich kann Ihnen gleich anhand Ihres Codes sagen, dass Sie potenzielle FirstChanceExceptions generieren werden ... speziell aufgrund von "ACCESS DENIED" (Win32Exception) und "NOT RUNNING" (ArgumentException) für diese Ausdrucksauswertung von 'process.Handle'.

Das Prozesshandle ist für eine Anwendung privat - mit anderen Worten, Prozesshandles können nicht gemeinsam genutzt werden.

Siehe auch LinkDemand = 6 und SecurityCriticalAttribute .

Möglicherweise müssen Sie für dieses Attribut noch 'Extras -> Debuggen -> Nur meinen Code aktivieren', aber die FirstChanceExceptions werden weiterhin ausgelöst.

Abgesehen davon bin ich damit einverstanden, dass Ihre Antwort beim Pinvoke-Win32-Aufruf schneller als bei WMI ist, insbesondere wenn Sie ALLE Prozesse durchlaufen.

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