Fast Process Detection - c#

i have this piece of code to detect a Process:
private Boolean IsGameRunning()
{
Process[] game = Process.GetProcesses();
foreach (Process process in game)
{
if (process.ProcessName.Contains("GameWindow"))
{
return true;
}
}
return false;
}
Since the code has to run a lot of times because it is inside a Timer, is there any way to improve the speed of the process?
I do not have any control over the game.
This code is inside a timer always enabled with an interval of 2000-3000 ms:
if (IsGameRunning())
{
Do stuff
}
else
{
Status("Waiting for game to start");
}

Given that the process is launched by another, in this case Steam, we can narrow the list to search to only child processes.
First, need to get the parent process id (PID).
var parentProcess = Process.GetProcesses().FirstOrDefault(x => x.ProcessName == "Steam");
Then using the Windows Management Instrumentation (accessed using the System.Management.dll), you can then search only the child processes.
bool IsGameRunning(int parentProcess, string childExecutableName)
{
var query = string.Format("SELECT * FROM Win32_Process WHERE ParentProcessId = {0} AND Name = '{1}'", parentProcess, childExecutableName);
using (var searcher = new ManagementObjectSearcher(query))
using (var results = searcher.Get())
{
return (results.Count > 0);
}
}
e.g. IsGameRunning(parentProcess.Id, "SuperMeatBoy.exe")
No guarantee that this is faster as I haven't done any comparative testing, however from prior experience using the WMI is more performant than iterating a list of processes.
If you want to go further, a more advanced solution would be to hook up events to tell you process is created and deleted using a ManagementEventWatcher as shown in this blog post http://weblogs.asp.net/whaggard/438006.

WMI gives you a visible performance hit with noticeable recurring CPU spikes when looking at it run in task manager. The way I made my process hunting work was to use a linq statement on Process.GetProcessByName, which I've never seen go above 0% when scanning for processes every .3 seconds. I store the processes I already know about in the memory, and filter them out in the linq statement.
If your EXE has enough permission (or is elevated), you can set EnableRaisingEvents = true, and attach to the Exited event to know exactly when the process dies.

Related

When killing a process in C#, how can I be sure I'm killing the right one?

Overall Goal
I'm attempting to kill all of the processes by a certain name (notepad.exe below) that I currently own. Generally speaking, it's along the lines of:
Get all of the applications with a certain name that I'm the owner of
In this case, "I" will usually be a service account
Kill all of them.
Questions
How likely is it that from the time I grab a PID to the time I kill it, another application could have spawned that uses that PID? If I grab a PID of ID 123, how likely is it that it could have closed and a different application now owns PID 123?
What is the best way I can reasonably pull this off while limiting the potential that I kill off the wrong PID?
What I have so Far
The below code is based on another SO answer and uses WMI to get all the processes by a certain name and list the users.
What's next: The next step is to kill the processes that are owned by me; however, how can I tell that the PIDs I have here will be the same PIDs I'm trying to kill?
static void Main(string[] args)
{
const string PROCESS_NAME = "notepad.exe";
var queryString = string.Format("Name = '{0}'", PROCESS_NAME);
var propertiesToSelect = new[] { "Handle", "ProcessId" };
var processQuery = new SelectQuery("Win32_Process", queryString, propertiesToSelect);
using (var searcher = new ManagementObjectSearcher(processQuery))
{
using (var processes = searcher.Get())
foreach (var aProcess in processes)
{
var process = (ManagementObject)aProcess;
var outParameters = new object[2];
var result = (uint)process.InvokeMethod("GetOwner", outParameters);
if (result == 0)
{
var user = (string)outParameters[0];
var domain = (string)outParameters[1];
var processId = (uint)process["ProcessId"];
Console.WriteLine("PID: {0} | User: {1}\\{2}", processId, domain, user);
// TODO: Use process data...
}
else
{
// TODO: Handle GetOwner() failure...
}
}
}
Console.ReadLine();
}
Yes, there is a risk of killing the wrong process. The reuse of PIDs probably is a history accident that has caused a lot of grief over the years.
Do it like this:
Find the PIDs you want to kill.
Obtain handles to those processes to stabilize the PIDs. Note, that this might obtain handles to wrong processes.
Re-find the PIDs you want to kill.
Kill those processes that you have stabilized and that are in the second find result set.
By inserting this lock-and-validate step you can be sure.
How likely is it that from the time I grab a PID to the time I kill it, another application could have spawned that uses that PID?
Another application wouldn't be assigned the same PID if it was spawned whilst the other one was alive. So this condition wouldn't happen as Windows' PIDs are unique decimal numbers to that specific process.
If I grab a PID of ID 123, how likely is it that it could have closed and a different application now owns PID 123?
This is technically feasible that the process could be closed between the time you gain your handle on the process and when you want to kill it. However, that would depend entirely on the lifespan of the process handling within your code. I guess there will always be edge cases where the application could be closed just as you're about to hook onto it, but if you're talking milliseconds/a couple of seconds I imagine it would be few and far between. As for Windows assigning the same PID immediately afterwards, I don't know for sure but they seem pretty random and now allocated again immediately after use, but they eventually would do.
What is the best way I can reasonably pull this off while limiting the potential that I kill off the wrong PID?
There is the Management Event Watcher class which appears to allow you to monitor the starting and stopping of processes. Maybe this could be used to capture events whenever they are closed for your given process name, so this way you know that it no longer exists?
Another answer discussing Management Event Watcher
MSDN ManagementEventWatcher class with example usage
Consider opposite approach - adjust permissions on service account so it can't kill processes of other users.
I believe such permissions are very close to default for non-admin accounts (or just default) - so unless you run service as box admin/system you may be fine with no-code solution.
A process id is guaranteed to stay the same as long as the process continues to run. Once the process exits... there is no guarantee.
When a new process starts, Windows will pick a random process ID and assign it to the new process. Its unlikely, but possible that the id chosen was associated with a process that recently exited.
Have you looked at System.Diagnostics.Process?
They have a GetProcessesByName method that will return a list of Process objects.
Process [] localByName = Process.GetProcessesByName("notepad");
Then you can simply iterate through the Processes and kill them. Since the Process object has a handle to the process... an attempt to kill it will generate a useful exception, which you can catch.
foreach (Process p in localByName)
{
try
{
p.Kill();
}
catch (Exception e)
{
// process either couldn't be terminated or was no longer running
}
}

Monitor child processes of a process

I'm running .exe file using this code:
Process proc = Process.Start("c:\program.exe");
proc.WaitForExit();
If I start Stopwatch before starting the process and stop it after proc.WaitForExit(); line, I can get the time that user was using that particular program.
The problem I'm facing is that some programs (and games) use launchers - some small .exe file that usually checks something and then launches another .exe file that is actually the program/game that the user wants to run. In these cases the code above doesn't work because it returns after launcher exists.
How can I track all processes that proc runs, and wait unitl all of them are terminated?
Here is the solution that the asker found:
// using System.Management;
public static class ProcessExtensions
{
public static IEnumerable<Process> GetChildProcesses(this Process process)
{
List<Process> children = new List<Process>();
ManagementObjectSearcher mos = new ManagementObjectSearcher(String.Format("Select * From Win32_Process Where ParentProcessID={0}", process.Id));
foreach (ManagementObject mo in mos.Get())
{
children.Add(Process.GetProcessById(Convert.ToInt32(mo["ProcessID"])));
}
return children;
}
}
[Updated]
Slightly more modern code:
// using System.Management;
public static class ProcessExtensions
{
public static IList<Process> GetChildProcesses(this Process process)
=> new ManagementObjectSearcher(
$"Select * From Win32_Process Where ParentProcessID={process.Id}")
.Get()
.Cast<ManagementObject>()
.Select(mo =>
Process.GetProcessById(Convert.ToInt32(mo["ProcessID"])))
.ToList();
}
Take a look at this - Find all child processes of my own .NET process / find out if a given process is a child of my own? or http://social.msdn.microsoft.com/Forums/vstudio/en-US/d60f0793-cc92-48fb-b867-dd113dabcd5c/how-to-find-the-child-processes-associated-with-a-pid. They provide ways to find child processes by a parent PID (which you have).
You can write monitor the process you create and also get its children. You could then track everything, and wait for them all to finish. I say "try" because I'm not sure you could catch very rapid changes (a process starting others and then dying before you get his children).
you can't wait for process(B) another process(A) is running, if that process(A) isn't waiting for the process(B). what you can do is track the process using Process.GetProcessesByName() if you know it's name

Verify that another application is always running

I'm trying to make a console app in c# that will confirm that another application is always running. The other application periodically crashes, and I need to check every few minutes and relaunch it if it has stopped working.
There are many questions on SO that address making sure than no more than one instance of the application is running. I'm trying to confirm that one (no more or less) is running at all times.
Does anybody know how to even begin approaching this?
I would suggest using System.Diagnostics.Process.GetProcessesByName to see if your process is running and then, if not, using System.Diagnostics.Process.Start to start the process.
var processes = Process.GetProcessesByName("yourProcessName");
if(processes.Length == 0)
{
Process.Start(#"C:\Path\To\Your\Process.exe");
}
// Kill the extras
for(int i = 1; i < process.Length; i++)
{
processes[i].Kill();
}
These commands are useful to control processes:
// check for processes
Process[] processes = Process.GetProcessesByName("nameOfExecutable");
foreach (Process proc in processes)
{
// do stuff
}
// start process (need path)
Process.Start("pathToExecutable");
// close gui process gently (if needed)
bool status = proc.CloseMainWindow();
// wait for process to close gently
bool status = proc.WaitForExit(killTimeMS);
// force close (kill) process
proc.Kill();
If you implement a "no more than one" rule (which is well-documented, as you point out) and then implement the periodic crash-checker, that should be sufficient to ensure that one and only one copy is running.
In fact, the periodic process doesn't even have to check for a crash. It can just fire up an instance, which will immediately exit if another instance is already running, thanks to whatever "no more than one" mechanism you implement. This has the added benefit of avoiding a possible race-condition between detecting a dead process and starting a new one.
You have a few options. The first is checking for a running process using the Process class. I got this from a Microsoft site, but it looks like it works:
public bool IsProcessRunning(string name)
{
//here we're going to get a list of all running processes on
//the computer
foreach (Process clsProcess in Process.GetProcesses())
{
if (clsProcess.ProcessName.StartsWith(name))
{
//process found so it's running so return true
return true;
}
}
//process not found, return false
return false;
}
You could also use interprocess communications. This is something we do in house. We have a watcher application that sends a message to a service being monitored. If the service doesn't return an ACK in a timeout period, we attempt to restart it.
I suggest you to check if your application is in the list of running processes:
System.Diagnostics.Process.GetProcesses();

Iterating through process names takes AGES

I have a question concerning the Visual Studio COM interface. I use the code below to get all processes on a remote machine and attach to MyExe.exe. The problem is that iterating through the processes' names takes very long. There are only ~20 processes, but the iteration takes ~8 seconds (that means the foreach-loop, not the code before that).
// Get processes
EnvDTE80.Debugger2 debugger2 = (EnvDTE80.Debugger2)dte.Debugger;
EnvDTE80.Transport trans = debugger2.Transports.Item("Default");
processes = debugger2.GetProcesses(trans, remoteName);
// Find exe and attach to it
foreach (EnvDTE.Process proc in processes)
{
if (proc.Name.Contains("MyExe.exe"))
{
proc.Attach();
break;
}//end if
}//end foreach
Is that normal? Is there anything I can do to speed things up?
Thanks!

Kill process tree programmatically in C#

I am starting Internet Explorer programmatically with code that looks like this:
ProcessStartInfo startInfo = new ProcessStartInfo("iexplore.exe");
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.Arguments = "http://www.google.com";
Process ieProcess = Process.Start(startInfo);
This generates 2 processes visible in the Windows Task Manager. Then, I attempt to kill the process with:
ieProcess.Kill();
This results in one of the processes in Task Manager being shut down, and the other remains. I tried checking for any properties that would have children processes, but found none. How can I kill the other process also? More generally, how do you kill all the processes associated with a process that you start with Process.Start?
This worked very nicely for me:
/// <summary>
/// Kill a process, and all of its children, grandchildren, etc.
/// </summary>
/// <param name="pid">Process ID.</param>
private static void KillProcessAndChildren(int pid)
{
// Cannot close 'system idle process'.
if (pid == 0)
{
return;
}
ManagementObjectSearcher searcher = new ManagementObjectSearcher
("Select * From Win32_Process Where ParentProcessID=" + pid);
ManagementObjectCollection moc = searcher.Get();
foreach (ManagementObject mo in moc)
{
KillProcessAndChildren(Convert.ToInt32(mo["ProcessID"]));
}
try
{
Process proc = Process.GetProcessById(pid);
proc.Kill();
}
catch (ArgumentException)
{
// Process already exited.
}
}
Update 2016-04-26
Tested on Visual Studio 2015 Update 2 on Win7 x64. Still works as well now as it did 3 years ago.
Update 2017-11-14
Added check for system idle process if (pid == 0)
Update 2018-03-02
Need to add a reference to the System.Management namespace, see comment from #MinimalTech below. If you have ReSharper installed, it will offer to do this for you automatically.
Update 2018-10-10
The most common use case for this is killing any child processes that our own C# process has started.
In this case, a better solution is to use Win32 calls within C# to make any spawned process a child process. This means that when the parent process exits, any child processes are automatically closed by Windows, which eliminates the need for the code above. Please let me know if you want me to post the code.
If anyone needs a dotnet core solution,
Dotnet core 3.0
process.Kill(true);
See official documentation
Dotnet core 2.0
For .Net 2.0 dotnet cli came up with an implementation based on taskill as mentioned above and recursive pgrep/kill for unix based systems. Full implementation can be found on github. Sadly, the class is internal so you'll have to copy it into your code base.
List Child processes (has to be done recursively):
$"pgrep -P {parentId}"
Kill on process:
$"kill -TERM {processId}"
I'm not a fan of any of the solutions presented here.
Here's what I came up with:
private static void EndProcessTree(string imageName)
{
Process.Start(new ProcessStartInfo
{
FileName = "taskkill",
Arguments = $"/im {imageName} /f /t",
CreateNoWindow = true,
UseShellExecute = false
}).WaitForExit();
}
How to use:
EndProcessTree("chrome.exe");
Process Class (System.Diagnostics)
ProcessStartInfo Class (System.Diagnostics)
Taskkill
You should call Process.CloseMainWindow() which will send a message to the main window of the process. Think of it as having the user click the "X" close button or File | Exit menu item.
It is safer to send a message to Internet Explorer to close itself down, than go and kill all its processes. Those processes could be doing anything and you need to let IE do its thing and finish before just killing it in the middle of doing something that may be important for future runs. This goes true for any program you kill.
If anyone is interested, I took one of the answers from the other page and modified it slightly. It is a self contained class now with static methods. It does not have proper error handling or logging. Modify to use for your own needs. Providing your root Process to KillProcessTree will do it.
class ProcessUtilities
{
public static void KillProcessTree(Process root)
{
if (root != null)
{
var list = new List<Process>();
GetProcessAndChildren(Process.GetProcesses(), root, list, 1);
foreach (Process p in list)
{
try
{
p.Kill();
}
catch (Exception ex)
{
//Log error?
}
}
}
}
private static int GetParentProcessId(Process p)
{
int parentId = 0;
try
{
ManagementObject mo = new ManagementObject("win32_process.handle='" + p.Id + "'");
mo.Get();
parentId = Convert.ToInt32(mo["ParentProcessId"]);
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
parentId = 0;
}
return parentId;
}
private static void GetProcessAndChildren(Process[] plist, Process parent, List<Process> output, int indent)
{
foreach (Process p in plist)
{
if (GetParentProcessId(p) == parent.Id)
{
GetProcessAndChildren(plist, p, output, indent + 1);
}
}
output.Add(parent);
}
}
Another solution is to use the taskill command. I use the next code in my applications:
public static void Kill()
{
try
{
ProcessStartInfo processStartInfo = new ProcessStartInfo("taskkill", "/F /T /IM your_parent_process_to_kill.exe")
{
WindowStyle = ProcessWindowStyle.Hidden,
CreateNoWindow = true,
UseShellExecute = false,
WorkingDirectory = System.AppDomain.CurrentDomain.BaseDirectory,
RedirectStandardOutput = true,
RedirectStandardError = true
};
Process.Start(processStartInfo);
}
catch { }
}
Are you using IE8 or IE9? That would absolutely start more than one process due to its new multi-process architecture. Anyway, have a look at this other answer for getting a process tree and killing it.
Another approach that can be very useful is using the Windows API for Job Objects. A process can be assigned to a job object. The child processes of such a process are automatically assigned to the same job object.
All processes assigned to a job object can be killed at once e.g. with TerminateJobObject which:
Terminates all processes currently associated with the job.
The C# example in this answer (based on this answer) uses the JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE flag instead, which:
Causes all processes associated with the job to terminate when the last handle to the job is closed.
With .NET Core 3.0 there is a method just for that, namely new overload of the already existing Process.Kill() method. IOW, doing process.Kill(true) on the variable process of type Process kills the process with all its descendants. This is cross-platform, naturally.
As per documentation
The Kill method executes asynchronously. After calling the Kill method, call the WaitForExit method to wait for the process to exit, or check the HasExited property to determine if the process has exited.
ProcessStartInfo startInfo = new ProcessStartInfo("iexplore.exe");
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.Arguments = "http://www.google.com";
Process ieProcess = Process.Start(startInfo);
ieProcess.Kill();
ieProcess.WaitForExit();
How to properly close Internet Explorer when launched from PowerShell?
Several of those commented in the above thread that this is caused by a bug in Win7 (as it does not seem to occur for users that are using other versions of windows). Many pages on the internet, including microsoft's page claim user error, and tell you to simply use the available quit method on the IE object which is SUPPOSED to close all child processes as well (and reportedly does in Win8/XP etc)
I must admit, for my part, it WAS user error. I am in win7 and the reason the quit method was not working for me was because of an error in coding. Namely I was creating the IE object at declaration, and then creating another (attached to the same object) later on in the code... I had almost finished hacking the parent-child killing routine to work for me when I realized the issue.
Because of how IE functions, the processID you spawned as the parent could be attached to other windows/subprocesses that you did NOT create. Use quit, and keep in mind that depending on user settings (like empty cache on exit) it could take a few minutes for the processes to finish their tasks and close.

Categories

Resources