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.
Related
If process.Kill() is called from another thread or even another program, the process never comes out of WaitForExit() if the batch script used robocopy.exe until it is finished as if it wasn't killed.
Robocopy.exe is called from the batch script. Every other script or program ends as you'd expect.
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "batch.bat";
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
startInfo.RedirectStandardOutput = true;
startInfo.OutputDataReceived += CaptureHandler;
startInfo.RedirectStandardError = true;
startInfo.ErrorDataReceived += CaptureHandler;
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
The batch script looks like:
#echo off
call "robocopy.exe" "somedir" "somedest" /mir /fp /ndl /njh /njs /ns
I have a feeling it has to do with the output handlers.
I tried using process.CancelErrorRead and process.CancelOutputRead() as well after the Kill() call and before, no luck.
Oddly, if you use process.WaitForExit(timeout) overload, it will return true immediately after Kill() from the other thread. However, it's lying. The process is still running! If you try process.WaitForExit() again, as per the MSDN doc, it will still wait for the process to finish despite HasExited saying true.
To ensure that asynchronous event handling has been completed, call the WaitForExit() overload that takes no parameter after receiving a true from this overload.
https://msdn.microsoft.com/en-us/library/ty0d8k56(v=vs.110).aspx
You are successfully killing the batch processor (cmd.exe) but doing so won't kill robocopy, which is a separate process.
It doesn't seem to be documented, but when we look at the .NET source code it turns out that the Process.WaitForExit() method doesn't just wait for the process to exit, it also waits for end-of-file on the standard output and standard error streams. In this scenario, that means that it waits for robocopy to finish even after the batch processor has been killed.
(The overload of Process.WaitForExit with a timeout does not have this extra logic.)
I think this constitutes a bug in the .NET framework. At the very least, it should be documented.
As a workaround, you can use .HasExited and/or the version of WaitForExit with a timeout to determine whether the process has exited or not. Of course, in your scenario you might prefer to wait for grandchild processes, in which case your code is already behaving as desired.
I ran into the same problem. In my case, dropping the /mt switch from the RoboCopy argument list seemed to fix the issue.
Having followed up on Harry Johnston's helpful answer, I found that the process completes normally when you avoid RedirectStandardOutput = true. If this isn't an acceptable solution I found that using robocopy's /LOG:"C:\logs\robocopy.txt" switch to send its standard output to an external log file also works (although you lose the ability to get the file/directory log output from the process object itself).
Looks like right now the only way to do this without the application knowing to terminate Robocopy.exe specifically is to do kill the children of the script process before killing the script itself:
Kill process tree programmatically in C#
/// <summary>
/// Kill a process, and all of its children, grandchildren, etc.
/// </summary>
/// <param name="pid">Process ID.</param>
private static void KillProcessAndChildren(int pid)
{
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.
}
}
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
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();
I want to kill a process programmatically in vista/windows 7 (I'm not sure if there's significant problems in the implementation of the UAC between the two to make a difference).
Right now, my code looks like:
if(killProcess){
System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName("MyProcessName");
// Before starting the new process make sure no other MyProcessName is running.
foreach (System.Diagnostics.Process p in process)
{
p.Kill();
}
myProcess = System.Diagnostics.Process.Start(psi);
}
I have to do this because I need to make sure that if the user crashes the program or exits abruptly, this secondary process is restarted when the application is restarted, or if the user wants to change the parameters for this secondary process.
The code works fine in XP, but fails in Windows 7 (and I assume in Vista) with an 'access is denied' message. From what the Almighty Google has told me, I need to run my killing program as administrator to get around this problem, but that's just weak sauce. The other potential answer is to use LinkDemand, but I don't understand the msdn page for LinkDemand as it pertains to processes.
I could move the code into a thread, but that has a whole host of other difficulties inherent to it that I really don't want to discover.
You are correct in that it's because you don't have administrative priveleges. You can solve this by installing a service under the local system user and running a custom command against it as needed.
In your windows form app:
private enum SimpleServiceCustomCommands { KillProcess = 128 };
ServiceControllerPermission scp = new ServiceControllerPermission(ServiceControllerPermissionAccess.Control, Environment.MachineName, "SERVICE_NAME");
scp.Assert();
System.ServiceProcess.ServiceController serviceCon = new System.ServiceProcess.ServiceController("SERVICE_NAME", Environment.MachineName);
serviceCon.ExecuteCommand((int)SimpleServiceCustomCommands.KillProcess);
myProcess = System.Diagnostics.Process.Start(psi);
In your service:
private enum SimpleServiceCustomCommands { KillProcess = 128 };
protected override void OnCustomCommand(int command)
{
switch (command)
{
case (int)SimpleServiceCustomCommands.KillProcess:
if(killProcess)
{
System.Diagnostics.Process[] process = System.Diagnostics.Process.GetProcessesByName("MyProcessName");
// Before starting the new process make sure no other MyProcessName is running.
foreach (System.Diagnostics.Process p in process)
{
p.Kill();
}
}
break;
default:
break;
}
}
I'll add the code for Simon Buchan's suggestion. It makes sense and should work as well, assuming your windows form is what launched the process in the first place.
Here's where you create the process. Notice the variable myProc. That's your handle on it:
System.Diagnostics.Process myProc = new System.Diagnostics.Process();
myProc.EnableRaisingEvents=false;
myProc.StartInfo.FileName="PATH_TO_EXE";
myProc.Start();
Later, just kill it with:
myProc.Kill();
I have C# winforms application that needs to start an external exe from time to time, but I do not wish to start another process if one is already running, but rather switch to it.
So how in C# would I so this in the example below?
using System.Diagnostics;
...
Process foo = new Process();
foo.StartInfo.FileName = #"C:\bar\foo.exe";
foo.StartInfo.Arguments = "Username Password";
bool isRunning = //TODO: Check to see if process foo.exe is already running
if (isRunning)
{
//TODO: Switch to foo.exe process
}
else
{
foo.Start();
}
This should do it for ya.
Check Processes
//Namespaces we need to use
using System.Diagnostics;
public bool IsProcessOpen(string name)
{
//here we're going to get a list of all running processes on
//the computer
foreach (Process clsProcess in Process.GetProcesses()) {
//now we're going to see if any of the running processes
//match the currently running processes. Be sure to not
//add the .exe to the name you provide, i.e: NOTEPAD,
//not NOTEPAD.EXE or false is always returned even if
//notepad is running.
//Remember, if you have the process running more than once,
//say IE open 4 times the loop thr way it is now will close all 4,
//if you want it to just close the first one it finds
//then add a return; after the Kill
if (clsProcess.ProcessName.Contains(name))
{
//if the process is found to be running then we
//return a true
return true;
}
}
//otherwise we return a false
return false;
}
You can use LINQ as well,
var processExists = Process.GetProcesses().Any(p => p.ProcessName.Contains("<your process name>"));
I have used the AppActivate function in VB runtime to activate an existing process.
You will have to import Microsoft.VisualBasic dll into the C# project.
using System;
using System.Diagnostics;
using Microsoft.VisualBasic;
namespace ConsoleApplication3
{
class Program
{
static void Main(string[] args)
{
Process[] proc = Process.GetProcessesByName("notepad");
Interaction.AppActivate(proc[0].MainWindowTitle);
}
}
}
You can simply enumerate processes using Process.GetProcesses method.
I found out that Mutex is not working like in the Console application. So using WMI to query processes that can be seen using Task Manager window will solved your problem.
Use something like this:
static bool isStillRunning() {
string processName = Process.GetCurrentProcess().MainModule.ModuleName;
ManagementObjectSearcher mos = new ManagementObjectSearcher();
mos.Query.QueryString = #"SELECT * FROM Win32_Process WHERE Name = '" + processName + #"'";
if (mos.Get().Count > 1)
{
return true;
}
else
return false;
}
NOTE: Add assembly reference "System.Management" to enable the type intellisense.
I think the complete answer to your problem requires understanding of what happens when your application determines that an instance of foo.exe is already running i.e what does '//TODO: Switch to foo.exe process' actually mean?
In a past project I needed to prevent multiple execution of a process, so I added a some code in the init section of that process which creates a named mutex. This mutext was created and acquired before continuing the rest of the process. If the process can create the mutex and acquire it, then it is the first one running. If another process already controls the mutex, then the one which fails is not the first so it exits immediately.
I was just trying to prevent a second instance from running, due to dependencies on specific hardware interfaces. Depending on what you need with that "switch to" line, you might need a more specific solution such as a process id or handle.
Also, I had source code access to the process I was trying to start. If you can not modify the code, adding the mutex is obviously not an option.
Two concerns to keep in mind:
Your example involved placing a
password on a command line. That
cleartext representation of a secret
could be a security vulnerability.
When enumerating processes, ask
yourself which processes you really
want to enumerate. All users, or
just the current user? What if the
current user is logged in twice (two
desktops)?
Mnebuerquo wrote:
Also, I had source code access to the
process I was trying to start. If you
can not modify the code, adding the
mutex is obviously not an option.
I don't have source code access to the process I want to run.
I have ended up using the proccess MainWindowHandle to switch to the process once I have found it is alread running:
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool SetForegroundWindow(IntPtr hWnd);