Kill All Spawned Chromes - c#

I have a project that has the following methods.
using System.Management;
public void KillAllSpawnedChromes() {
var chromeProcs = GetMyChildChromeProcesses();
_logger.Info("Found {0} chrome processess still running. Killing them", chromeProcs.Count());
foreach (var chromeProc in chromeProcs) {
chromeProc.Kill();
}
}
private static IEnumerable<Process> GetMyChildChromeProcesses() {
var myCurrentProcess = Process.GetCurrentProcess();
var children = new List<Process>();
var mos = new ManagementObjectSearcher(
$"Select * From Win32_Process Where ParentProcessID={myCurrentProcess.Id}");
foreach (var o in mos.Get()) {
var mo = (ManagementObject)o;
children.Add(Process.GetProcessById(Convert.ToInt32(mo["ProcessID"])));
}
return children.Where(x => x.ProcessName.Contains("chrome"));
}
}
I would like to delete this dependency from System.Managment. Is there any other way to kill Chrome processes?

You could use the following Extension Method:
// from https://code-examples.net/en/q/60640
public static class ProcessExtensions
{
private static string FindIndexedProcessName(int pid)
{
try
{
var processName = Process.GetProcessById(pid).ProcessName;
var processesByName = Process.GetProcessesByName(processName);
string processIndexdName = null;
for (var index = 0; index < processesByName.Length; index++)
{
processIndexdName = index == 0 ? processName : processName + "#" + index;
var processId = new PerformanceCounter("Process", "ID Process", processIndexdName);
if ((int)processId.NextValue() == pid)
{
return processIndexdName;
}
}
return processIndexdName;
}
catch(Exception)
{
return "";
}
}
private static Process FindPidFromIndexedProcessName(string indexedProcessName)
{
try
{
var parentId = new PerformanceCounter("Process", "Creating Process ID", indexedProcessName);
return Process.GetProcessById((int)parentId.NextValue());
}
catch(Exception)
{
return null;
}
}
public static Process Parent(this Process process)
{
return FindPidFromIndexedProcessName(FindIndexedProcessName(process.Id));
}
}
The kill loop is now rather simple:
Process[] processes = Process.GetProcessesByName("chrome");
int pid = Process.GetCurrentProcess().Id;
foreach (Process process in processes)
{
var parent = process.Parent();
if ((parent != null) && (pid == parent.Id))
{
process.Kill();
}
}
Note that a single instance of the Chrome browser usually spawns several processes. It might be necessary to kill them or properly shut them down in a specific order. Unforeseen damage of the Chrome data files might happen, unless you are careful.

Related

Get proccess by name of displayNameFile

Hello I have an application that shows all the installed apps that you have in the pc, for example:
google chrome
Mozilla Firefox 63.0.3(x64 en-GB)
etc...
I want to get the process of each app by name.
(like that Mozilla Firefox 63.0.3(x64 en-GB).getProcess())
Thanks for the help.
You could begin by building a dictionary of process using WMI and FileVersionInfo.
private static IList<ProcessInfo> BuildProcessDictionary()
{
var wmiQueryString = "SELECT ProcessId, ExecutablePath FROM Win32_Process";
using (var searcher = new ManagementObjectSearcher(wmiQueryString))
using (var results = searcher.Get())
{
var processList = from p in Process.GetProcesses()
join mo in results.Cast<ManagementObject>()
on p.Id equals (int)(uint)mo["ProcessId"]
select new ProcessInfo
{
Process = p,
Path = (string)mo["ExecutablePath"],
DisplayName = (string)mo["ExecutablePath"]!=null?FileVersionInfo.GetVersionInfo((string)mo["ExecutablePath"]).FileDescription:string.Empty
};
return processList.ToList();
}
return default;
}
public class ProcessInfo
{
public string Path { get; set; }
public string DisplayName { get; set; }
public Process Process { get; set; }
}
Then you can query the collection
var processDictionary = BuildProcessDictionary();
var found = processDictionary.Where(x => x.DisplayName!=null && x.DisplayName.Equals("Google Chrome"));
Update
var listOfPrograms = new[] { "Google Chrome", "Notepad" };
var processDictionary = BuildProcessDictionary();
foreach (var item in listOfPrograms)
{
try
{
var programInstances = processDictionary.Where(x => x.DisplayName != null && x.DisplayName.Equals(item));
foreach (var programInstance in programInstances)
{
programInstance.Process.Kill();
}
}
catch (Exception )
{
// Log items that wasn't found or coudn't be killed.
Console.WriteLine($"Could not find or kill process {item}");
}
}
You can get an array of all processes:
using System.Diagnostics;
...
Process[] allProcesses = Process.GetProcesses();
and then for each process you can find the information (ex. file name, module name,...) about the ProccessModule that was used to start the process with the Process.MainModule property.

Process.StartInfo.Username is empty

I start a Process by
Process app = new Process();
app.StartInfo.UseShellExecute = false;
app.StartInfo.FileName = path;
app.StartInfo.Domain = "Domain";
app.StartInfo.UserName = "userName";
string password = "Password";
System.Security.SecureString ssPwd = new System.Security.SecureString();
for (int x = 0; x < password.Length; x++)
{
ssPwd.AppendChar(password[x]);
}
password = "";
app.StartInfo.Password = ssPwd;
app.Start();
Then I confirm it is running by:
private bool IsRunning(string name)
{
Process[] processlist = Process.GetProcesses();
if (Process.GetProcessesByName(name).Length > 0)
{
string user = Process.GetProcessesByName(name)[0].StartInfo.UserName;
log.Debug("Process " + name + " is running by : " + user);
return true;
}
else
{
return false;
}
}
I get back true and find the process but the UserName is empty.
Why is that?
I also found some code to get the Owner of the Process but when I use this the Owner is also empty.
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";
}
Please can you explain me why this is so?
This is by design; when requesting information about a Process via eg. GetProcessesByName the UserName doesn't get retrieved/resolved.
GetProcessByName internally retrieves its info via the code below, constructing Process instances from the obtained ProcesInfo data.
public static Process[] GetProcesses(string machineName)
{
bool isRemoteMachine = ProcessManager.IsRemoteMachine(machineName);
ProcessInfo[] processInfos = ProcessManager.GetProcessInfos(machineName);
Process[] processes = new Process[processInfos.Length];
for (int i = 0; i < processInfos.Length; i++) {
ProcessInfo processInfo = processInfos[i];
processes[i] = new Process(machineName, isRemoteMachine, processInfo.processId, processInfo);
}
return processes;
}
A ProcessInfo instance doesn't contain any user/owner information.
internal class ProcessInfo
{
public ArrayList threadInfoList = new ArrayList();
public int basePriority;
public string processName;
public int processId;
public int handleCount;
public long poolPagedBytes;
public long poolNonpagedBytes;
public long virtualBytes;
public long virtualBytesPeak;
public long workingSetPeak;
public long workingSet;
public long pageFileBytesPeak;
public long pageFileBytes;
public long privateBytes;
public int mainModuleId;
public int sessionId;
}
The private constructor of the Process class accepts this ProcessInfo.
Because no ProcessStartInfo has been set, it instantiates one on retrieval, having an empty UserName.
public string UserName {
get {
if( userName == null) {
return string.Empty;
}
else {
return userName;
}
}
set { userName = value; }
}
About the GetProcessOwner code; this results from the answer to the How do I determine the owner of a process in C#? question.
This is rather an other WMI related topic; not receiving any owner info might have to do with permissions as suggested in the comments.
For me, "it works on my machine" ...
Better start a separate question for this one.
I tried with solution using WinAPIs
You would get "NO OWNER" when the application you are trying to get the owner, is not run as admin
Please check by running your application as Administrator

Get the name of the user running a process in C# [duplicate]

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?
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";
}
Since WMI is not always a fast way of retrieving information, here is the native P/Invoke way of doing it:
The return value is null when unsuccessful. In order to get the names of processes running under the SYSTEM user, you need to execute this code as administrator.
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);
Here is the VB version for the non C# speakers:
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
Unfortunately there's no native .Net way of getting the process owner.
Have a look at these for a potential solution:
http://msmvps.com/blogs/siva/archive/2006/10/02/Getting-Windows-Process-Owner-Name.aspx
http://www.codeproject.com/KB/cs/processownersid.aspx
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
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
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.
Let me know if you need any more fields adding to the table.
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);
}
}
System.Security.Principal.WindowsIdentity.GetCurrent().Name

Get a list of all excel processes started by winform application

I am looking for a way to get all the excel processes initiated by my winform application. Is it even possible?
If I can get list of all running processes using
Process[] processes = Process.GetProcesses();
Shouldn't there be a way to get processes started by a certain applciation?
First add this namespaces to your application
using Excel = Microsoft.Office.Interop.Excel;
using System.Runtime.InteropServices;
using System.Diagnostics;
then in your application reference user32 dll
class ExcelClass
{
[DllImport("user32.dll")]
static extern int GetWindowThreadProcessId(int hWnd, out int lpdwProcessId);
Process GetId(Excel.Application excelApp)
{
int id;
GetWindowThreadProcessId(excelApp.Hwnd, out id);
return Process.GetProcessById(id);
}
}
Okay I have found the answer myself. I am sharing it just in case someone needs it.
int myappid = Process.GetCurrentProcess().Id;
Process[] processes = Process.GetProcessesByName("EXCEL");
foreach (Process prs in processes)
{
var query = string.Format("SELECT ParentProcessId FROM Win32_Process WHERE ProcessId = {0}", prs.Id);
var search = new ManagementObjectSearcher("root\\CIMV2", query);
var results = search.Get().GetEnumerator();
results.MoveNext();
var queryObj = results.Current;
var parentId = (uint)queryObj["ParentProcessId"];
if (ProcessExists((int)parentId))
{
var parent = Process.GetProcessById((int)parentId);
if (parent.Id == myappid)
{
prs.Kill();
}
}
}
private bool ProcessExists(int id)
{
return Process.GetProcesses().Any(x => x.Id == id);
}
In the above code Process[] processes = Process.GetProcesses(); would give me all the processes. I am looping though the collection and finding parent process id of each. If parent id matches my application process id and the name of the child process is excel, I kill it.
Just an FYI, this bit of code I used to get existing excel processes and then kill excel processes created by my app.
Hashtable myHashtable;
private void Get_Already_Running_Excel()
{
Process[] AllProcesses = Process.GetProcessesByName("excel");
myHashtable = new Hashtable();
int iCount = 0;
foreach (Process ExcelProcess in AllProcesses)
{
myHashtable.Add(ExcelProcess.Id, iCount);
iCount = iCount + 1;
}
}
public void Close_User_Excel()
{
Process[] AllProcesses = Process.GetProcessesByName("excel");
// check to kill the right process
foreach (Process ExcelProcess in AllProcesses)
{
if (myHashtable.ContainsKey(ExcelProcess.Id) == false)
{ ExcelProcess.Kill(); }
}
AllProcesses = null;
}

How do i determine if running process is started by system or by user using c# [duplicate]

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?
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";
}
Since WMI is not always a fast way of retrieving information, here is the native P/Invoke way of doing it:
The return value is null when unsuccessful. In order to get the names of processes running under the SYSTEM user, you need to execute this code as administrator.
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);
Here is the VB version for the non C# speakers:
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
Unfortunately there's no native .Net way of getting the process owner.
Have a look at these for a potential solution:
http://msmvps.com/blogs/siva/archive/2006/10/02/Getting-Windows-Process-Owner-Name.aspx
http://www.codeproject.com/KB/cs/processownersid.aspx
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
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
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.
Let me know if you need any more fields adding to the table.
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);
}
}
System.Security.Principal.WindowsIdentity.GetCurrent().Name

Categories