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

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

Related

Kill All Spawned Chromes

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.

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

Determine from which location an application was being started

Is there any possibility to determine how a c# application was being started?
In my case I want to check if this application (wpf) is being started by a shortcut located in a specific folder.
So, there are two ways to open my application
using direct shortcut
starting another application which is like an update manager to keep my application up to date. After checking, it starts my application with Process.Start()
And I want to ensure that the application is only able to be started with the update manager.
A trick you could use is to check the parent's PID, and then get some of the parent's process information.
If the parent's process name is something like "explorer.exe" then the application was started from the shortcut or directly by double-clicking it on explorer.
Otherwise, it was started from another application: it could be your updater application, it could also be another application with the same name as your updater application...
This means you have to re-think how deep you want to go for such a solution, and how deep do you want security control. You could pass arguments from your updater to your main application, or implement some inter-process communication with token exchanges... it is impossible to make a 100% secure system.
As someone commented above, this seems like a XY problem... or maybe not. Maybe it is just a security concern. It's recommended to revise what exactly are you aiming for this software.
In case you need sample code for retrieving process information in .NET (by using System.Management), then just give a try to the code listed below. All you have to do is to place it in a console application project named 'Updater', and correctly set the path to your main application in the code.
If you play a little bit with this example by starting and closing YourApplication.exe in different situations, then you should be able to see an output like this:
Parent process 'Updater.exe' [PID=5472]
Parent process 'explorer.exe' [PID=12052]
The code below was tested on VS2017 .Net 4.6.1
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Management;
class Program
{
static void Main(string[] args)
{
Process.Start(new ProcessStartInfo()
{
FileName = "YourApplication.exe" // path to your application
});
while (Console.ReadKey(true).Key != ConsoleKey.Escape)
{
Process process = Process.GetProcessesByName("YourApplication").FirstOrDefault(); // your application's process name
if (process == null)
{
Console.WriteLine($"Process is not running...");
continue;
}
ProcessManager pm = ProcessManager.FromLocalMachine();
var processProperties = pm.GetProcessProperties(process.Id);
int parentProcessId = Convert.ToInt32(processProperties[EProcessProperty.ParentProcessId]);
try
{
var parentProcessProperties = pm.GetProcessProperties(parentProcessId);
string parentProcessName = parentProcessProperties[EProcessProperty.Name].ToString();
Console.WriteLine($"Parent process '{parentProcessName ?? "Unknown"}' [PID={parentProcessId}]");
Console.WriteLine("---------------------------------");
}
catch { Console.WriteLine("Parent process information not found."); }
}
}
}
public class ProcessConnection
{
internal ManagementScope ManagementScope { get; }
internal ProcessConnection(string machineName, string user = null, string password = null, string domain = null)
{
ManagementScope = new ManagementScope
{
Path = new ManagementPath(#"\\" + machineName + #"\root\CIMV2"),
Options = new ConnectionOptions
{
Impersonation = ImpersonationLevel.Impersonate,
Authentication = AuthenticationLevel.Default,
EnablePrivileges = true,
Username = user == null ? null : (string.IsNullOrWhiteSpace(domain) ? user : $"{domain}\\{user}"),
Password = user == null ? null : password,
},
};
ManagementScope.Connect();
}
}
public class ProcessManager
{
public static ProcessManager FromLocalMachine() => new ProcessManager()
{
Machine = Environment.MachineName,
};
public static ProcessManager FromRemoteMachine(string machine, string user = null, string password = null, string domain = null) => new ProcessManager()
{
Machine = machine,
User = user,
Password = password,
Domain = domain,
};
private ProcessManager() { }
public string Machine { get; private set; }
public string User { get; private set; }
public string Password { get; private set; }
public string Domain { get; private set; }
private ProcessConnection Connection { get; set; }
private ManagementScope ManagementScope => Connection == null ? (Connection = new ProcessConnection(Machine, User, Password, Domain)).ManagementScope : Connection.ManagementScope;
public EProcessStartStatus StartProcess(string processPath)
{
ManagementClass mc = new ManagementClass($"\\\\{Machine}\\root\\CIMV2", "Win32_Process", null);
ManagementBaseObject process = mc.GetMethodParameters("Create");
process["CommandLine"] = processPath;
ManagementBaseObject createCode = mc.InvokeMethod("Create", process, null);
string createCodeStr = createCode["ReturnValue"].ToString();
return (EProcessStartStatus)Convert.ToInt32(createCodeStr);
}
public bool KillProcess(string processName)
{
try
{
SelectQuery query = new SelectQuery($"SELECT * FROM Win32_Process WHERE Name = '{processName}'");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(ManagementScope, query);
foreach (ManagementObject mo in searcher.Get()) mo.InvokeMethod("Terminate", null);
return true;
}
catch { return false; }
}
public bool KillProcess(int processId)
{
try
{
SelectQuery query = new SelectQuery($"SELECT * FROM Win32_Process WHERE ProcessId = '{processId}'");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(ManagementScope, query);
foreach (ManagementObject mo in searcher.Get()) mo.InvokeMethod("Terminate", null);
return true;
}
catch { return false; }
}
public void SetProcessPriority(string processName, EProcessPriority priority)
{
SelectQuery query = new SelectQuery($"SELECT * FROM Win32_Process WHERE Name = '{processName}'");
ManagementObjectSearcher managementObjectSearcher = new ManagementObjectSearcher(ManagementScope, query);
foreach (ManagementObject managementObject in managementObjectSearcher.Get())
{
ManagementBaseObject methodParams = managementObject.GetMethodParameters("SetPriority");
methodParams["Priority"] = priority;
managementObject.InvokeMethod("SetPriority", methodParams, null);
}
}
public string GetProcessOwner(string processName)
{
SelectQuery query = new SelectQuery($"SELECT * FROM Win32_Process WHERE Name = '{processName}'");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(ManagementScope, query);
foreach (ManagementObject mo in searcher.Get())
{
ManagementBaseObject methodParams = mo.GetMethodParameters("GetOwner");
ManagementBaseObject owner = mo.InvokeMethod("GetOwner", null, null);
return owner["User"].ToString();
}
return null;
}
public string GetProcessOwnerSID(string processName)
{
SelectQuery query = new SelectQuery($"SELECT * FROM Win32_Process WHERE Name = '{processName}'");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(ManagementScope, query);
foreach (ManagementObject mo in searcher.Get())
{
ManagementBaseObject methodParams = mo.GetMethodParameters("GetOwnerSid");
ManagementBaseObject OwnerSid = mo.InvokeMethod("GetOwnerSid", null, null);
return OwnerSid["Sid"].ToString();
}
return null;
}
public IList<int> GetRunningProcesses()
{
IList<int> processes = new List<int>();
SelectQuery query = new SelectQuery("SELECT * FROM Win32_Process");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(ManagementScope, query);
foreach (ManagementObject mo in searcher.Get()) processes.Add(int.Parse(mo["ProcessId"].ToString()));
return processes;
}
public IDictionary<EProcessProperty, object> GetProcessProperties(int processId)
{
SelectQuery query = new SelectQuery($"SELECT * FROM Win32_Process WHERE ProcessId = '{processId}'");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(ManagementScope, query);
Dictionary<EProcessProperty, object> properties = new Dictionary<EProcessProperty, object>();
foreach (ManagementObject mo in searcher.Get())
{
foreach (PropertyData pd in mo.Properties)
{
if (Enum.TryParse(pd.Name, out EProcessProperty e)) properties[e] = pd.Value;
else Console.WriteLine(pd.Name + " is not mapped in the properties enumeration.");
}
}
return properties;
}
public IDictionary<EProcessProperty, object> GetProcessProperties(string processName)
{
SelectQuery query = new SelectQuery($"SELECT * FROM Win32_Process WHERE Name = '{processName}'");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(ManagementScope, query);
Dictionary<EProcessProperty, object> properties = new Dictionary<EProcessProperty, object>();
foreach (ManagementObject mo in searcher.Get())
{
foreach (PropertyData pd in mo.Properties)
{
if (Enum.TryParse(pd.Name, out EProcessProperty e)) properties[e] = pd.Value;
else Console.WriteLine(pd.Name + " is not mapped in the properties enumeration.");
}
}
return properties;
}
public IList<int> GetProcessessFromExecutablePath(string executablePath)
{
SelectQuery query = new SelectQuery($"SELECT * FROM Win32_Process WHERE ExecutablePath = '{executablePath.Replace("\\", "\\\\")}'");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(ManagementScope, query);
return searcher.Get().Cast<ManagementObject>().Select(mo => Convert.ToInt32(mo["ProcessId"])).ToList();
}
}
public enum EProcessPriority : uint
{
IDLE = 0x40,
BELOW_NORMAL = 0x4000,
NORMAL = 0x20,
ABOVE_NORMAL = 0x8000,
HIGH_PRIORITY = 0x80,
REALTIME = 0x100
}
public enum EProcessStartStatus
{
Success = 0,
AccessDenied = 2,
NoPermissions = 3,
Unknown = 8,
FileNotFound = 9,
Invalid = 21,
}
public enum EProcessProperty
{
Caption,
CommandLine,
CreationClassName,
CreationDate,
CSCreationClassName,
CSName,
Description,
ExecutablePath,
ExecutionState,
Handle,
HandleCount,
InstallDate,
KernelModeTime,
MaximumWorkingSetSize,
MinimumWorkingSetSize,
Name,
OSCreationClassName,
OSName,
OtherOperationCount,
OtherTransferCount,
PageFaults,
PageFileUsage,
ParentProcessId,
PeakPageFileUsage,
PeakVirtualSize,
PeakWorkingSetSize,
Priority,
PrivatePageCount,
ProcessId,
QuotaNonPagedPoolUsage,
QuotaPagedPoolUsage,
QuotaPeakNonPagedPoolUsage,
QuotaPeakPagedPoolUsage,
ReadOperationCount,
ReadTransferCount,
SessionId,
Status,
TerminationDate,
ThreadCount,
UserModeTime,
VirtualSize,
WindowsVersion,
WorkingSetSize,
WriteOperationCount,
WriteTransferCount,
}
If there are only 2 ways of starting your app, the second method should pass a parameter (a GUID?) to Process.Start() - generated by your updater app.
Maybe devise some kind of algorithm that allows the app to start only with the token.
From what I know this is impossible in the way you would like it to be but there's one trick which you can use. Firstly change your WPF application's entry method to get the command line arguments, and ( for example ) use -u argument to distinct from where the application was started. Then after -u you can pass a HWND or a process ID that matches your updater. Of course you have to then check if that application is running and if it's your updater.
example :
// updated process start
ProcessStartInfo psi = new ProcessStartInfo("your/WPF/application.exe");
psi.Arguments = "-u " + Process.GetCurrentProcess().Id;
// fill up rest of the properties you need
Process.Start(psi);
// wpf application's entry point
void Main(string[] args)
{
string updaterProcessIdstr = string.Empty;
for (int i = 0; i < args.Length; i++)
{
if(args[i] == "-u")
{
updaterProcessIdstr = args[i + 1];
i++;
}
}
int pid = int.Parse(updaterProcessIdstr);
Process updaterProcess = Process.GetProcessById(pid);
// do some validation here
// send something to stdin and read from stdout
// to determine if it was started from that updater.
}

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

Get user login name in C#

How to check login user name from the system in c#
I tried it using this method
static string whoisLoggedIn(string HostOrIP)
{
GUFlag = true;
HostOrIP = Environment.MachineName;
System.Management.ConnectionOptions myConnectionOptions = new System.Management.ConnectionOptions();
myConnectionOptions.Impersonation = System.Management.ImpersonationLevel.Impersonate;
System.Management.ManagementScope objwmiservice;
System.Management.ManagementObjectSearcher myObjectSearcher2;
System.Management.ManagementObjectCollection myCollection2;
try
{
objwmiservice = new System.Management.ManagementScope(("\\\\" + (HostOrIP +
"\\root\\cimv2")), myConnectionOptions);
objwmiservice.Connect();
myObjectSearcher2 = new System.Management.ManagementObjectSearcher(objwmiservice.Path.ToString(),
"Select UserName from Win32_ComputerSystem");
myObjectSearcher2.Options.Timeout = new TimeSpan(0, 0, 0, 0, 7000);
myCollection2 = myObjectSearcher2.Get();
GUFlag = false;
foreach (System.Management.ManagementObject myObject in myCollection2)
{
if (!(myObject.GetPropertyValue("Username") == null))
{
string Userx = myObject.GetPropertyValue("Username").ToString();
int posx = Userx.LastIndexOf("\\");
if ((posx > 0))
{
Userx = Userx.Substring((posx + 1));
return Userx.ToUpper();
}
}
}
return "<Nobody>";
}
catch (Exception)
{
return "<Nobody>";
}
finally {
GUFlag = false;
}
}
But the problem is some time deadlock occur on myObjectSearcher2.Get();
Is there any way available to get login username
did you try that?
Environment.UserName
it will give you the user name of the user currently login on windows
EDIT
I found this bit of code here http://www.debugging.com/bug/20243, it may solve your issue.
solution by using WMI ( http://msdn.microsoft.com/en-us/library/system.management.aspx ):
private string GetUserName()
{
string result = "";
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher("SELECT UserName, Name FROM Win32_ComputerSystem"))
{
foreach (ManagementObject mo in searcher.Get())
{
if (mo["UserName"] != null)
result = mo["UserName"].ToString();
if (mo["Name"] != null)
result += " (" + mo["Name"].ToString() + ")";
}
}
return result;
}
Unless I'm not understanding you correctly, I believe it's just:
using System.Security.Principal;
this.nametext = WindowsIdentity.GetCurrent().Name;

Categories