C#, Process Viewer, which is service vs desktop app - c#

I'm not writing a process viewer, but need somewhat the same concept.
I can do
List<Process> allProcesses = Process.GetProcesses().ToList();
Question:
How can I tell which process is a normal process and which is a windows service? I've dug through the properties of Process and not finding a flag of any sort.

private bool IsService(Process p, out string serviceName)
{
bool retVal = false;
serviceName = "";
string query = $"SELECT Name FROM Win32_Service WHERE ProcessId={p.Id}";
ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
foreach (ManagementObject obj in searcher.Get())
{
serviceName = obj["Name"].ToString();
retVal = true;
break;
}
return retVal;
}

Related

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 to get HDD serial number

May be the title is duplicate. I am getting HDD of the laptop serial number successfully when no USB devices are connected. But when any USB is connected, the code gets the serial number of connected device. I only want the serial number of HDD of laptop or desktop even though USBs are connected.
Below is the code.
using System.Management;
namespace SystemInfo
{
public class Info1
{
public static String GetHDDSerialNo()
{
ManagementClass mangnmt = new ManagementClass("Win32_LogicalDisk");
ManagementObjectCollection mcol = mangnmt.GetInstances();
string result = "";
foreach (ManagementObject strt in mcol)
{
result += Convert.ToString(strt["VolumeSerialNumber"]);
}
return result;
}
}
}
try this
ManagementObjectSearcher theSearcher = new ManagementObjectSearcher("SELECT * FROM Win32_DiskDrive WHERE InterfaceType='USB'");
foreach (ManagementObject currentObject in theSearcher.Get())
{
ManagementObject theSerialNumberObjectQuery = new ManagementObject("Win32_PhysicalMedia.Tag='" + currentObject["DeviceID"] + "'");
MessageBox.Show(theSerialNumberObjectQuery["SerialNumber"].ToString());
}
You can use WMI Win32_DiskDrive, filter on MediaType containing "fixed" and get the SerialNumber
Something like :
public static String GetHDDSerialNo()
{
ManagementClass mangnmt = new ManagementClass("Win32_DiskDrive");
ManagementObjectCollection mcol = mangnmt.GetInstances();
string result = "";
foreach (ManagementObject strt in mcol)
{
if (Convert.ToString(strt["MediaType"]).ToUpper().Contains("FIXED"))
{
result += Convert.ToString(strt["SerialNumber"]);
}
}
return result;
}
Media type can contain "External", "Removable", "fixed". Exact string depends on OS. On Seven and XP, that String can be different. That's why we use Contains.
little reading

How to get current windows username from windows service in multiuser environment using .NET

I have a windows service which run a WPF application all the time for all the logged in users which works fine, now in the WPF application i can not get a current username as Environment.UserName; returns 'SYSTEM' which is understandable. so what i thought was to find session id of current process which could be retrieved by Process.GetCurrentProcess().SessionId and then get the list of all users logged in to the machine and looping through it to find the session id match with process session id and later his username.
but i don't how to get the list of all logged in users or i would appreciate if someone can help me with alternative.
I ran into a similar problem while building a Windows Service. Just like you, I had the Session ID and needed to get the corresponding username. Syed's answer above did not work on my machine (Windows 10) as Microsoft seems to have removed the quser executable. After several unsuccessful solution hereon SO, I ran into this particular answer and it inspired my solution:
Here's my code (all of them residing inside a class; in my case, the class inheriting ServiceBase)
[DllImport("Wtsapi32.dll")]
private static extern bool WTSQuerySessionInformation(IntPtr hServer, int sessionId, WtsInfoClass wtsInfoClass, out IntPtr ppBuffer, out int pBytesReturned);
[DllImport("Wtsapi32.dll")]
private static extern void WTSFreeMemory(IntPtr pointer);
private enum WtsInfoClass
{
WTSUserName = 5,
WTSDomainName = 7,
}
private static string GetUsername(int sessionId, bool prependDomain = true)
{
IntPtr buffer;
int strLen;
string username = "SYSTEM";
if (WTSQuerySessionInformation(IntPtr.Zero, sessionId, WtsInfoClass.WTSUserName, out buffer, out strLen) && strLen > 1)
{
username = Marshal.PtrToStringAnsi(buffer);
WTSFreeMemory(buffer);
if (prependDomain)
{
if (WTSQuerySessionInformation(IntPtr.Zero, sessionId, WtsInfoClass.WTSDomainName, out buffer, out strLen) && strLen > 1)
{
username = Marshal.PtrToStringAnsi(buffer) + "\\" + username;
WTSFreeMemory(buffer);
}
}
}
return username;
}
I solve it by executing powershell command "quser" in my WPF application which returns all the logged in users then I am iterating to find session id in which the application is running with user session id and then retrieving his name. below is the function which fetch the username by passing his session id
private string GetUserName(int SessionId)
{
try
{
Runspace runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
Pipeline pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript("Quser");
pipeline.Commands.Add("Out-String");
Collection<PSObject> results = pipeline.Invoke();
runspace.Close();
StringBuilder stringBuilder = new StringBuilder();
foreach (PSObject obj in results)
{
stringBuilder.AppendLine(obj.ToString());
}
foreach (string User in stringBuilder.ToString().Split(new string[] { Environment.NewLine }, StringSplitOptions.RemoveEmptyEntries).Skip(1))
{
string[] UserAttributes = User.Split(new string[]{" "},StringSplitOptions.RemoveEmptyEntries);
if (int.Parse(UserAttributes[2].Trim()) == SessionId)
{
return UserAttributes[0].Replace(">", string.Empty).Trim();
}
}
return stringBuilder.ToString();
}
catch (Exception ex)
{
}
return string.Empty;
}
the function can be called by
string CurrentUser = GetUserName(Process.GetCurrentProcess().SessionId);
You can try this Code spinet.
Whenever a user logged onto windows, the Username attribute will contain the username of the user. In the case when there are no users in the windows system, there will be no instances of the Win32_ComputerSystem class.
ManagementScope ms = new ManagementScope("\\\\.\\root\\cimv2");
ObjectQuery query = new ObjectQuery("SELECT * FROM Win32_ComputerSystem");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(ms, query);
foreach(ManagementObject mo in searcher.Get())
{
Console.WriteLine(mo["UserName"].ToString());
}
Found this solution on MSDN forums:
using System.Security.Principal;
.
.
.
WindowsPrincipal wp = new WindowsPrincipal(WindowsIdentity.GetCurrent());
String username = wp.Identity.Name;
Now this is easily tested by creating a new Console Application, pasting the above code, and writing the username string to Console. Seems to work fine, but for services it's apparently a more complex situation. Since all services are run in a container that runs under the SYSTEM user, that's what they return. For more information see the below link, esp. all Harry Zhu's answers.
http://social.msdn.microsoft.com/Forums/vstudio/en-US/3be119b0-88b4-442e-9613-6856cbb27adb/how-can-i-get-current-username-in-windows-service?forum=csharpgeneral
It would seem it's not possible to achieve what you are trying to get, as services are totally seperate from users' session.
i made a little search and find this code that should work for you:
it will get the username whom is running the process...
reference: How do I determine the owner of a process in C#?
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";
}
You can try this Code:
string username = "SYSTEM";
var explorer = Process.GetProcessesByName("explorer").FirstOrDefault();
if (explorer != null)
{
username = GetUsername(explorer.SessionId);
}
Implementation of method GetUsername here: https://stackoverflow.com/a/35810391/10412686

using WMI to uninstall applications remotely

I am trying to write a mini w32 executable to remotely uninstall an application using WMI.
I can list all the installed applications using this code below but i couldnt find a way to uninstall the application remotely thru WMI and C#
I know I can do same using msiexec as a process but I wish to solve this using WMI if its possible...
Thanks,
Cem
static void RemoteUninstall(string appname)
{
ConnectionOptions options = new ConnectionOptions();
options.Username = "administrator";
options.Password = "xxx";
ManagementScope scope = new ManagementScope("\\\\192.168.10.111\\root\\cimv2", options);
scope.Connect();
ObjectQuery query = new ObjectQuery("SELECT * FROM Win32_Product");
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query);
ManagementObjectCollection queryCollection = searcher.Get();
foreach (ManagementObject m in queryCollection)
{
// Display the remote computer information
Console.WriteLine("Name : {0}", m["Name"]);
if (m["Name"] == appname)
{
Console.WriteLine(appname + " found and will be uninstalled... but how");
//need to uninstall this app...
}
}
}
Have a look at WMI Code Creator (a free tool from Microsoft) — it can generate WMI code for you in various languages, including C#.
Here's an example illustrating the Win32_Product.Uninstall method usage. You need to know the GUID, name and version of the application you want to uninstall, as they are the key properties of the Win32_Product class:
...
ManagementObject app =
new ManagementObject(scope,
"Win32_Product.IdentifyingNumber='{99052DB7-9592-4522-A558-5417BBAD48EE}',Name='Microsoft ActiveSync',Version='4.5.5096.0'",
null);
ManagementBaseObject outParams = app.InvokeMethod("Uninstall", null);
Console.WriteLine("The Uninstall method result: {0}", outParams["ReturnValue"]);
If you have partial info about the application (e.g. only name or name and version), you can use a SELECT query to obtain the corresponding Win32_Process object:
...
SelectQuery query = new SelectQuery("Win32_Product", "Name='Microsoft ActiveSync'");
EnumerationOptions enumOptions = new EnumerationOptions();
enumOptions.ReturnImmediately = true;
enumOptions.Rewindable = false;
ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query, options);
foreach (ManagementObject app in searcher.Get())
{
ManagementBaseObject outParams = app.InvokeMethod("Uninstall", null);
Console.WriteLine("The Uninstall method result: {0}", outParams["ReturnValue"]);
}

Kill a process on a remote machine in C#

This only helps kills processes on the local machine. How do I kill processes on remote machines?
You can use wmi. Or, if you don't mind using external executable, use pskill
I like this (similar to answer from Mubashar):
ManagementScope managementScope = new ManagementScope("\\\\servername\\root\\cimv2");
managementScope.Connect();
ObjectQuery objectQuery = new ObjectQuery("SELECT * FROM Win32_Process Where Name = 'processname'");
ManagementObjectSearcher managementObjectSearcher = new ManagementObjectSearcher(managementScope, objectQuery);
ManagementObjectCollection managementObjectCollection = managementObjectSearcher.Get();
foreach (ManagementObject managementObject in managementObjectCollection)
{
managementObject.InvokeMethod("Terminate", null);
}
I use the following code. psKill is also a good way to go but sometimes you need to check the some other stuff, for example in my case remote machine was running multiple instances of same process but with different command line arguments, so following code worked for me.
ConnectionOptions connectoptions = new ConnectionOptions();
connectoptions.Username = string.Format(#"carpark\{0}", "domainOrWorkspace\RemoteUsername");
connectoptions.Password = "remoteComputersPasssword";
ManagementScope scope = new ManagementScope(#"\\" + ipAddress + #"\root\cimv2");
scope.Options = connectoptions;
SelectQuery query = new SelectQuery("select * from Win32_Process where name = 'MYPROCESS.EXE'");
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, query))
{
ManagementObjectCollection collection = searcher.Get();
if (collection.Count > 0)
{
foreach (ManagementObject mo in collection)
{
uint processId = (uint)mo["ProcessId"];
string commandLine = (string) mo["CommandLine"];
string expectedCommandLine = string.Format("MYPROCESS.EXE {0} {1}", deviceId, deviceType);
if (commandLine != null && commandLine.ToUpper() == expectedCommandLine.ToUpper())
{
mo.InvokeMethod("Terminate", null);
break;
}
}
}
}

Categories