How to kill a process that have been forked? - c#

I'm trying to start a process, here calc.exe, waiting X seconds, and kill it:
Process p = Process.Start("calc.exe");
Console.WriteLine(p.Id);
Thread.Sleep(3000);
p.Kill();
When i execute this code, output give me the PID of the new process (e.g. 7152), BUT after sleep, the Kill() doesn't work, it says the process is not running anymore. BUT i saw the calc still running, i can use it.
When i look in details of task manager, the PID of calc.exe is not the same than the previous... why?
How can i kill the process i've created? (calc.exe is just an example, but it could be a.txt, b.word...)
Thanks and sorry for my english! :)

Are you sure about the pid changing? what happens if you compare it using
Process[] p = Process.GetProcessesByName( "calc.exe" );
foreach(var proc in p)
Console.WriteLine("Found: "+proc.Id == myExpectedProcId);
After you have the pid here is a hacky way to kill it
/// <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.
}
}

Related

C# how to get process from all sessions

I am running code on Windows Server 2012 R2 where i have multiple sessions.
On each session there is few Notepad.exe opened with different window name.
How i can get a list of all Notepad.exe processes from all sessions so i can minimize window or close process for selected process from a list ?
I used this code, but it gives me only processes from current session where i am logged in.
Process[] processlist = Process.GetProcesses();
foreach (Process process in processlist)
{
if (!string.IsNullOrEmpty(process.MainWindowTitle))
{
if (process.ProcessName == "notepad")
{
Console.WriteLine("Process: {0} - ID: {1} - Window title: {2}", process.ProcessName, process.Id, process.MainWindowTitle);
}
}
}
I solved my issue thanks to #Lex Li, used library from nuget.
I used this line if (process.ProcessName == "notepad.exe" && session.SessionId == process.SessionId) to extract only process name i need and match it to session.
using Cassia;
// call all process from sessions
private void GetSessionsProcess()
{
ITerminalServicesManager manager = new TerminalServicesManager();
ITerminalServer server = manager.GetLocalServer(); // server name
server.Open();
WriteProcesses(server.GetProcesses(), manager);
}
// get all process that is running in all sessions
private void WriteProcesses(IEnumerable<ITerminalServicesProcess> processes, ITerminalServicesManager manager)
{
ITerminalServer server = manager.GetLocalServer();
foreach (ITerminalServicesProcess process in processes)
{
foreach (ITerminalServicesSession session in server.GetSessions())
{
if (process.ProcessName == "notepad.exe" && session.SessionId == process.SessionId)
{
PopulateTerminalsList(process.SessionId.ToString(), process.ProcessId.ToString(), process.ProcessName, session.UserAccount.ToString(), session.UserName);
}
}
}
}

Processes: receiving current open

I want to troll my brother a Little bit ;) by writing a program, which I can put in his "StartUp"-Folder. This program should scan his default system tasks so that it doesn't destroy his computer so I wrote all currently open processes in a list. Now I want to check when he opens a program (Process) is it in the list("Taskmgr" is also in the list, so you can exit the troll anytime)? If the opened Process is not in the list, kill it. If you Need any further information, please ask...
My current code is this:
void CloseProcesses()
{
Process[] arrProcesses = Process.GetProcesses();
List<string> lststrProcessNames = new List<string>();
/*Writes current running processes(+ taskmanager process) in a list*/
foreach (Process CurrentProcess in arrProcesses)
{
lststrProcessNames.Add(CurrentProcess.ProcessName);
}
lststrProcessNames.Add("taskmgr");
try
{
Process[] arrNewProcesses = Process.GetProcesses();
foreach (Process NewCurrentProcess in arrNewProcesses)
{
if (lststrProcessNames.Contains(NewCurrentProcess.ProcessName))
{
CloseProcesses();
}
else
{
NewCurrentProcess.Kill();
}
}
}
catch
{
this.Close();
}
}

Process.Kill() doesn't seem to kill the process

I am having trouble using Process.Kill(). I think I must be misunderstanding how it works. This is my test function. I start a long-running process (ping -t) and then kill it five seconds later.
I can see the ping process show up, but the process is still there after my program finishes. I have to kill it manually.
Console.WriteLine("Total number of ping processes is {0}", Process.GetProcessesByName("ping").Length);
ProcessStartInfo startInfo = new ProcessStartInfo("cmd.exe");
Process process = new Process();
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
startInfo.Arguments = "/c ping -t 8.8.8.8";
Console.WriteLine("Staring ping process");
process.StartInfo = startInfo;
process.Start();
Thread.Sleep(5000);
Console.WriteLine("Total number of ping processes is {0}", Process.GetProcessesByName("ping").Length);
Thread.Sleep(5000);
Console.WriteLine("Killing ping process");
process.Kill();
Thread.Sleep(5000);
Console.WriteLine("Total number of ping processes is {0}", Process.GetProcessesByName("ping").Length);
What am I doing wrong here?
You started cmd.exe, then cmd.exe starts child process ping.exe. To kill ping.exe you can kill all process hierarchy. For example with WMI(add System.Management reference):
private static void KillProcessAndChildrens(int pid)
{
ManagementObjectSearcher processSearcher = new ManagementObjectSearcher
("Select * From Win32_Process Where ParentProcessID=" + pid);
ManagementObjectCollection processCollection = processSearcher.Get();
try
{
Process proc = Process.GetProcessById(pid);
if (!proc.HasExited) proc.Kill();
}
catch (ArgumentException)
{
// Process already exited.
}
if (processCollection != null)
{
foreach (ManagementObject mo in processCollection)
{
KillProcessAndChildrens(Convert.ToInt32(mo["ProcessID"])); //kill child processes(also kills childrens of childrens etc.)
}
}
}
This is a patch for the #SulNR answer since its answer leak child processes of child processes.
private static void KillProcessAndChildrens(int pid)
{
ManagementObjectSearcher processSearcher = new ManagementObjectSearcher
("Select * From Win32_Process Where ParentProcessID=" + pid);
ManagementObjectCollection processCollection = processSearcher.Get();
// We must kill child processes first!
if (processCollection != null)
{
foreach (ManagementObject mo in processCollection)
{
KillProcessAndChildrens(Convert.ToInt32(mo["ProcessID"])); //kill child processes(also kills childrens of childrens etc.)
}
}
// Then kill parents.
try
{
Process proc = Process.GetProcessById(pid);
if (!proc.HasExited) proc.Kill();
}
catch (ArgumentException)
{
// Process already exited.
}
}
process.Kill() is working, just not on the process you think. What you're doing is actually starting 2 processes and only killing the first process, while the second process keeps running.
The code you have is starting a new command shell and saving that process info to process. When you call process.Kill() only the command shell is exiting You can run
Console.WriteLine(process.ProcessName);
before you process.Kill() to see which process is actually going to be killed. By setting \c ping -t 8.8.8.8 as arguments to the command shell, you're telling the command shell to start another process (in this case ping) and disjoin it from itself. Your program has no knowledge about the child process and will not kill it. If all you really want is to kill the ping process you can change your code to:
Console.WriteLine("Total number of ping processes is {0}", Process.GetProcessesByName("ping").Length);
ProcessStartInfo startInfo = new ProcessStartInfo("ping");
Process process = new Process();
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
startInfo.Arguments = "-t 8.8.8.8";
Console.WriteLine("Staring ping process");
process.StartInfo = startInfo;
process.Start();
Thread.Sleep(5000);
Console.WriteLine("Total number of ping processes is {0}", Process.GetProcessesByName("ping").Length);
Thread.Sleep(5000);
Console.WriteLine("Killing ping process");
process.Kill();
Thread.Sleep(5000);
Console.WriteLine("Total number of ping processes is {0}", Process.GetProcessesByName("ping").Length);
If, however, you really need to start the command shell first you'll need to find the child processes and have logic to kill that. Something like:
foreach( var p in Process.GetProcessesByName("ping"))
{
p.Kill();
}
[EDIT]
*Sorry, I didn't see the comments from #Adriano Repetti at first. I didn't mean to be redundant.
FWIW a VS2019 Visual Basic version of Julio's modification.
Private Sub KillProcessAndChildrens(pintPID As Integer)
Dim processSearcher As New ManagementObjectSearcher("Select * From Win32_Process Where ParentProcessID=" + pintPID.ToString)
Dim processCollection As ManagementObjectCollection = processSearcher.Get()
' We must kill child processes first!
If Not IsNothing(processCollection) Then
For Each mo As ManagementObject In processCollection
KillProcessAndChildrens(Convert.ToInt32(mo.Item("ProcessID")))
Next
End If
' // Then kill parents.
Try
Dim proc As Process = Process.GetProcessById(pintPID)
If Not proc.HasExited Then
proc.Kill()
End If
Catch ex As Exception
' Process already exited.
End Try
End Sub
It's very simple:
foreach (var process in Process.GetProcessesByName(processName))
{
try
{
process.Kill();
}
catch { }
}
With this method, you can also kill processes with a higher protection level like taskmgr.exe. If you want to prevent a process from starting, here is a piece of code suitable for that, at least as long as the process running this code is active:
private void Disable(string processName)
{
var timer = new Timer()
{
Interval = 1,
Enabled = true
};
timer.Tick += (s, e) =>
{
foreach (var process in Process.GetProcessesByName(processName))
{
try
{
process.Kill();
}
catch { }
}
GC.Collect();
};
}
In order to kill a process you have to run under an administrative
account. This means either that you are a 'true' administrator or you
User Account Control (UAC) turned off.
Otherwise Process.Kill() will fail.
From here.

How to kill only processes started by my application

I am using Selenium WebDriver in an application and I have code to kill the webdrivers and browser instances. However, I am thinking that if the user had any IE browsers open before running the application that this code will kill not only the IE processes spawned by my application but also the IE instances that user had open prior to running the application.
Is there a way to track the processes started by my application so I can filter this method to kill only IE processes spawned by my application, or determine that IE driver and browser instance was spawned by my application, or perhaps both?
public void KillAllBrowsersAndWebDrivers()
{
var webDrivers = Process.GetProcessesByName("IEDriverServer").Select(p => p.Id);
var browsers = Process.GetProcessesByName("iexplore").Select(p => p.Id);
var processIds = webDrivers.Concat(browsers);
// do some stuff with PID, if you want to kill them, do the following
foreach (var pid in processIds)
{
try
{
Process.GetProcessById(pid).Kill();
Logger.Log(Loglevel.Debug, "Kill Process:{0}", pid);
}
catch (Exception)
{
Logger.Log(Loglevel.Error, "Error killing process: {0}", pid);
}
}
}
All you would have to do is keep a list of all the processes you've created.
this is a very simple process manager. This code is error prone, and there is no exception handling
private static List<Process> processes = new List<Process>();
static void Main(string[] args)
{
int PID = StoreProcess (yourProcess);
KillProcess(PID);
}
/// <summary>
/// Stores the process in a list
/// </summary>
/// <returns>The PID</returns>
/// <param name="prc">The process to be stored</param>
public static int StoreProcess(Process prc)
{
int PID = prc.Id; // Get the process PID and store it in an int called PID
processes.Add (prc); // Add this to our list of processes to be kept track of
return PID; // Return the PID so that the process can be killed/changed at a later time
}
/// <summary>
/// Kills a process
/// </summary>
/// <param name="PID">The PID of the process to be killed.</param>
public static void KillProcess(int PID)
{
// Search through the countless processes we have and try and find our process
for (int i = 0; i <= processes.Count; i++) {
if (processes [i] == null)
{
continue; // This segment of code prevents NullPointerExceptions by checking if the process is null before doing anything with it
}
if (processes [i].Id == PID) { // Is this our process?
processes [i].Kill (); // It is! Lets kill it
while (!processes [i].HasExited) { } // Wait until the process exits
processes [i] = null; // Mark this process to be skipped the next time around
return;
}
}
// Couldn't find our process!!!
throw new Exception ("Process not found!");
}
Advantages:
You can keep track of all the processes you've initialized, and terminate them one by one at any time
Drawbacks:
I don't believe there is any
Another possible solution is to get a list of the processes running BEFORE spawning any new processes. Then just kill the ones that are not in the list of previously running processes.
public void KillOnlyProcessesSpawnedBySelenium()
{
// get a list of the internet explorer processes running before spawning new processes
var pidsBefore = Process.GetProcessesByName("iexplore").Select(p => p.Id).ToList();
var driver = new Driver(Settings);
var driver1 = driver.InitiateDriver(); // this method creates new InternetExplorerDriver
var driver2 = driver.InitiateDriver();
var driver3 = driver.InitiateDriver();
driver1.Navigate().GoToUrl("http://google.com");
driver2.Navigate().GoToUrl("http://yahoo.com");
driver3.Navigate().GoToUrl("http://bing.com");
var pidsAfter = Process.GetProcessesByName("iexplore").Select(p => p.Id);
var newInternetExplorerPids = pidsAfter.Except(pidsBefore);
// do some stuff with PID, if you want to kill them, do the following
foreach (var pid in newInternetExplorerPids)
{
Debug.WriteLine("Killing pid: {0}", pid);
Process.GetProcessById(pid).Kill();
}
Assert.IsTrue(pidsBefore.Count > 0);
// determine if each process before the drivers spawned are running
foreach (var running in pidsBefore.Select(pid => Process.GetProcessById(pid).IsRunning()))
{
Assert.IsTrue(running);
}
}
Here is an extension method to use to determine if a process is still running or not...
public static bool IsRunning(this Process process)
{
if (process == null)
throw new ArgumentNullException("process");
try
{
Process.GetProcessById(process.Id);
}
catch (ArgumentException)
{
return false;
}
return true;
}

Finding out Windows service's running process name .NET 1.1

We are using a badly written windows service, which will hang when we are trying to Stop it from code. So we need to find which process is related to that service and kill it.
Any suggestions?
You can use System.Management.MangementObjectSearcher to get the process ID of a service and System.Diagnostics.Process to get the corresponding Process instance and kill it.
The KillService() method in the following program shows how to do this:
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Management;
namespace KillProcessApp {
class Program {
static void Main(string[] args) {
KillService("YourServiceName");
}
static void KillService(string serviceName) {
string query = string.Format(
"SELECT ProcessId FROM Win32_Service WHERE Name='{0}'",
serviceName);
ManagementObjectSearcher searcher =
new ManagementObjectSearcher(query);
foreach (ManagementObject obj in searcher.Get()) {
uint processId = (uint) obj["ProcessId"];
Process process = null;
try
{
process = Process.GetProcessById((int)processId);
}
catch (ArgumentException)
{
// Thrown if the process specified by processId
// is no longer running.
}
try
{
if (process != null)
{
process.Kill();
}
}
catch (Win32Exception)
{
// Thrown if process is already terminating,
// the process is a Win16 exe or the process
// could not be terminated.
}
catch (InvalidOperationException)
{
// Thrown if the process has already terminated.
}
}
}
}
}
WMI has this information: the Win32_Service class.
A WQL query like
SELECT ProcessId FROM Win32_Service WHERE Name='MyServiceName'
using System.Management should do the trick.
From a quick look see: taskllist.exe /svc and other tools from the command line.
You can use
tasklist /svc /fi "SERVICES eq YourServiceName"
To find the process name and id, and also if the same process hosts other services.
To answer exactly to my question - how to find Process related to some service:
ManagementObjectSearcher searcher = new ManagementObjectSearcher
("SELECT * FROM Win32_Service WHERE DisplayName = '" + serviceName + "'");
foreach( ManagementObject result in searcher.Get() )
{
if (result["DisplayName"].ToString().ToLower().Equals(serviceName.ToLower()))
{
int iPID = Convert.ToInt32( result["ProcessId"] );
KillProcessByID(iPID, 1000); //some method that will kill Process for given PID and timeout. this should be trivial
}
}
}
Microsoft/SysInternals has a command-line tool called PsKill that allows you to kill a process by name. This tool also allows you to kill processes on other servers. Windows SysInternals
Usage: pskill [-t] [\computer [-u username [-p password]]] <process ID | name>
-t Kill the process and its descendants.
-u Specifies optional user name for login to remote computer.
-p Specifies optional password for user name. If you omit this you will be prompted to enter a hidden password.
I guess it's a two step process - if it's always the same service, you can easily find the process name using methods suggested in other answers.
I then have the following code in a class on a .NET 1.1 web server:
Process[] runningProcs =
Process.GetProcessesByName("ProcessName");
foreach (Process runningProc in runningProcs)
{
// NOTE: Kill only works for local processes
runningProc.Kill();
}
The Kill method can throw a few exceptions that you should consider catching - especially the Win32Exception, that is thrown if the process cannot be killed.
Note that the WaitForExit method and HasExited property also exist in the 1.1 world, but aren't mentioned on the documentation page for Kill in 1.1.

Categories