Как определить владельца процесса на С#?
Я ищу процесс под именем "MyApp.exe", и я хочу убедиться, что я получаю процесс, принадлежащий определенному пользователю.
Я использую следующий код для получения списка процессов:
Process[] processes = Process.GetProcessesByName("MyApp");
Это дает мне список процессов, но в классе Process не существует способа определить, кто владеет этим процессом? Любые мысли о том, как я могу это сделать?
Ответы
Ответ 1
Вы можете использовать WMI, чтобы заставить пользователя владеть определенным процессом. Чтобы использовать WMI, вам нужно добавить ссылку на System.Management.dll
в свой проект.
По идентификатору процесса:
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";
}
По имени процесса (только для первого процесса) отрегулируйте его соответствующим образом:
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";
}
Ответ 2
Так как WMI не всегда является быстрым способом получения информации, вот собственный способ P/Invoke:
Возвращаемое значение 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);
Ответ 3
Вот версия VB для колонок non С#:
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
Ответ 4
К сожалению, нет собственного .Net-способа получения владельца процесса.
Посмотрите на них для потенциального решения:
Ответ 5
var myApp = Process.GetProcessesByName("MyApp").FirstOrDefault();
if (myApp != null)
{
string username = GetUsername(myApp.SessionId);
}
Реализация метода GetUsername здесь: fooobar.com/questions/7997729/...
Ответ 6
Добавьте ссылку на свой проект:
System.Management
Затем добавьте в свой проект следующий метод:
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;
}
Затем добавьте этот метод:
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;
}
Вызов GetProcessTable() дает вам DataTable всех запущенных процессов вместе с их идентификатором и именем, что удобно, поскольку его можно использовать в качестве параметра DataGridView Datasource.
Сообщите мне, нужны ли вам дополнительные поля, добавляющие в таблицу.
Ответ 7
System.Security.Principal.WindowsIdentity.GetCurrent().Name
Ответ 8
@Bytecode77:
Прямо сейчас, глядя на ваш код, я могу сказать вам, что вы будете генерировать потенциальные исключения FirstChanceException… в частности, из-за "ACCESS DENIED" (Win32Exception) и "NOT RUNNING" (ArgumentException) для этих. В вашем блоке "catch" из выражение выражения 'process.Handle'.
Дескриптор процесса является частным для приложения - другими словами, дескрипторы процесса не могут быть общими.
Смотрите также LinkDemand= 6 и SecurityCriticalAttribute.
Хотя для этого атрибута вам может понадобиться "Инструменты → Отладка → Включен только мой код", исключения FirstChanceException все равно будут вызываться.
Кроме этого, я согласен с тем, что ваш ответ в вызове Win32 pinvoke быстрее, чем 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();
Ответ 9
WMI - действительно худший из возможных способов получения этой информации от Process. Но... иногда вам нужно получить эту информацию из удаленного процесса, и в этом случае вам, к сожалению, нужен WMI. Поэтому, если вам нужно или вы хотите использовать WMI, я предлагаю сделать это следующим образом (это более чем на 60% быстрее, чем классические методы WMI выше):
Метод:
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;
}
Если вы хотите увидеть небольшое сравнение времени, посмотрите этот ответ.
Ответ 10
Это самый простой способ, который я нашел для этого:
Process[] processes = Process.GetProcessesByName("MyApp");
foreach (Process process in processes)
{
string username = process.StartInfo.Environment["USERNAME"];
// do some stuff
}