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;
}
Related
My problem is this:
If Autodesk Inventor is not running, my app (console app) creates a new instance with Activator.CreateInstance(InventorType); and uses it as a COM object. When my app does not quit Inventor but leaves it open and the user later quits it by hand there is still a process inventor.exe running in TaskManager which can only be killed in TaskManager.
Curiously the problem only arises when these two things are combined. Whenever my app quits Inventor with InventorApp.Quit(); it is closed properly and there is no process left open.
If my app starts Inventor with Process.Start(..); or the user starts Inventor before starting the app and then my app grabs Inventor with Marshal.GetActiveObject(ProgId); there is no problem no matter if the app or the user quits Inventor.
If my app starts Inventor with Activator.CreateInstance(InventorType); then leaves Inventor open, the app is closed and then restarted, it grabs Inventor with Marshal.GetActiveObject(..); and then quits Inventor via InventorApp.Quit(); there is no problem.
So, the problem with the left open process only arises in this specific combination:
start Inventor via Activator.CreateInstance(InventorType);
the user quits Inventor by hand
The left open process is not in the Running Object Table anymore so it can't be handled as a COM object anymore and it has no visible UI, which means it can only be killed in TaskManager.
Using the 'bad combination' as described I even tried to call GC.WaitForPendingFinalizers(); GC.Collect(); several times (I know this is bad but I am just trying everything) in different combinations and before and/or after Marshal.ReleaseComObject(invApp); Marshal.FinalReleaseComObject(invApp);. I even tried a minimal app which literally does nothing else. See below for the code.
So, what is Activator.CreateInstance(InventorType); doing that is causing this? Is there any way to prevent this? Or is this a problem specific to Inventor?
Minimal app example:
Inventor.Application invApp = null;
string ProgId = "Inventor.Application";
try
{
invApp = (Inventor.Application)Marshal.GetActiveObject(ProgId);
}
catch (Exception e1)
{
try
{
Type InventorType = Type.GetTypeFromProgID(ProgId);
invApp = (Inventor.Application)Activator.CreateInstance(InventorType);
}
catch (Exception e2)
{
Console.WriteLine(e1);
Console.WriteLine(e2);
}
}
invApp = invApp as Inventor.Application;
invApp.Visible = true;
Console.Write("Quit Inventor? (y/n) ");
string quit = Console.ReadLine();
if (quit == "y")
{
invApp.Quit();
}
// desperately trying to release the COM object ...
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
if (invApp != null)
{
Marshal.ReleaseComObject(invApp);
Marshal.FinalReleaseComObject(invApp);
}
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
if (invApp != null)
{
Marshal.ReleaseComObject(invApp);
Marshal.FinalReleaseComObject(invApp);
}
GC.WaitForPendingFinalizers();
GC.Collect();
GC.WaitForPendingFinalizers();
GC.Collect();
invApp = null;
This is not specific for Inventor but for any COM object (Excel for example).
Usually I don't use this COM communication for production, because there are many vulnerabilities and some performance issues. I recommend you to use another workflow when possible.
But to your question. You can't release this COM object as you try. I recommend you to wrap Inventor.Application to some IDisposable object and quit them in dispose method, when you create your own instance.
static void Main(string[] args)
{
InventorTest();
//Waiting for dispose message
//Console.ReadKey();
}
private static void InventorTest()
{
using (var invProvider = new InventorDisposableProvider())
{
var invApp = invProvider.InventorApp;
invApp.Visible = true;
Console.Write("Quit Inventor? (y/n) ");
string quit = Console.ReadLine();
if (quit == "y")
{
invApp.Quit();
}
}
}
class InventorDisposableProvider : IDisposable
{
private Application invApp;
private bool startedByMe = false;
/// <summary>
/// Gets running or start new instance of Inventor
/// </summary>
public Application InventorApp
{
get
{
if (invApp == null) GetInventorApp();
return invApp;
}
}
/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
if (startedByMe && invApp != null)
{
invApp .Quit();
Console.WriteLine("Quit");
}
}
private void GetInventorApp()
{
string ProgId = "Inventor.Application";
try
{
invApp = (Inventor.Application)Marshal.GetActiveObject(ProgId);
startedByMe = false;
}
catch (Exception e1)
{
try
{
Type InventorType = Type.GetTypeFromProgID(ProgId);
invApp = (Inventor.Application)Activator.CreateInstance(InventorType);
startedByMe = true;
}
catch (Exception e2)
{
Console.WriteLine(e1);
Console.WriteLine(e2);
}
}
}
}
I don't know if this is the best solution but it is a good start point.
I found only one issue. When user quit the console application by cross. In this case you can see this article how to solve this case.
capture-console-exit-c-sharp
From a C# service, how I can check whether another app is dead or not?
I tried to use Process.Responding, it returns true but the app is died.
This is the code:
private List<string> getListStringGAppPath()
{
List<string> listGAppPaths = new List<string>();
Process[] processes = Process.GetProcessesByName("MyApp");
if (processes.Length > 0)
{
for (int i = 0; i < processes.Length; i++) {
listGAppPaths.Add(processes[i].Responding.ToString() + "######" + processes[i].MainModule.FileName);
//processes[i].Responding.ToString() always return True
}
return listGAppPaths;
}
else
{
return null;
}
}
When process dies, windows seems to toggles its state to Suspended, you can try checking its state first. Also here: Detecting process crash in .NET
You can check if the process is responding:
foreach (var process in System.Diagnostics.Process.GetProcesses())
{
Console.WriteLine("Process Name: {0}, Responding: {1}", process.ProcessName, process.Responding);
}
Similar to this answer:
Check status of process
You can use the methods in System.Diagnostics.Process to get process information.
GetProcessesByName(String)
Creates an array of new Process components and associates them with all the process resources on the local computer that share the specified process name.
GetProcessById(Int32)
Returns a new Process component, given the identifier of a process on the local computer.
GetProcesses()
Creates a new Process component for each process resource on the local computer.
If the process does not exist, then it must have died?
I made a program that opens COM6. The program starts when the user logs on.
If another user logs on, while the first user is still logged in, the program crazes because the COM is already open.
I found this code, which I thought could solve the problem. The code was meant to close all other application with the same name, but apparently, it does not work, when the other app is running under another user. Have anybody got any solution for this ?
void CloseAllButMe()
{
Process[] processes;
Process self = Process.GetCurrentProcess();
processes = Process.GetProcessesByName(self.ProcessName);
foreach (Process p in processes)
{
if (self.Id != p.Id) p.CloseMainWindow();
}
}
You can use the methods Process.Kill to stop a process. Calling Kill will immediately stop the process and could cause a loss of work.
Here is a code sample for killing Calculator:
public static void KillPaint()
{
System.Diagnostics.Process[] procs = null;
try
{
procs = Process.GetProcessesByName("calc");
Process mspaintProc = procs[0];
if (!mspaintProc.HasExited)
{
mspaintProc.Kill();
}
}
finally
{
if (procs != null)
{
foreach (Process p in procs)
{
p.Dispose();
}
}
}
}
This question already has answers here:
How to check if process is idle? C#
(2 answers)
Closed 4 years ago.
I have several programs that are already released. I want to create new program to read the released running programs - wheather they are idle or not for about 5 minutes. If they are in the idle condition, I want to terminate them.
IDLE means the user not use mouse and keyboard or no process in that program.
As an example, these are my several released programs:
pgrA.exe - Running - Idle 3 Minutes
pgrB.exe - Stopped
pgrC.exe - Running - Idle 7 Minutes
pgrD.exe - Running - not Idle
pgrE.exe - Running - Idle 11 Minutes
My program to terminate idle programs (on single PC):
IdleReader.exe (will terminate pgrC.exe and pgrE.exe)
that all running in 1 PC.
this some codes of the new program to terminate idle Program:
private void refresh_PrgList()
{
using (var con = new SqlConnection(ConfigurationManager.AppSettings["ConnectionStr"]))
using (var cmd = con.CreateCommand())
{
con.Open();
cmd.CommandText = "select ProgramID, ProgramName from MKTPrograms";
var reader = cmd.ExecuteReader();
var yy = false;
dgvPrgList.Rows.Clear();
while (reader.Read())
{
yy = false;
foreach (var xx in Process.GetProcesses())
if (xx.ProcessName.Replace(".exe", string.Empty).ToUpper() == reader[0].ToString().Replace(" ", string.Empty).ToUpper())
{
//----- I want to Detect the Idle Program here -----//
//--------------------------------------------------//
dgvPrgList.Rows.Add(new object[] { reader[0].ToString(), reader[0].ToString(), "Running", xx.StartTime, System.DateTime.Now - xx.StartTime });
yy = true;
}
if (yy == false)
dgvPrgList.Rows.Add(new object[] { reader[0].ToString(), reader[1].ToString(), "Stopped", "" });
}
for (int x = 1 ; x < dgvPrgList.Columns.Count ; x++ )
{
dgvPrgList.Columns[x-1].AutoSizeMode = DataGridViewAutoSizeColumnMode.DisplayedCells;
}
}
}
Thanks,
Sure, there are tons of ways to do that.
Look at the processing time (UserProcessorTime,TotalProcessorTime), store it and compare it; you'd get a good feeling about "idle" after some time.
Then just call Kill on the System.Diagnostics.Process class to terminate it.
In .net one can do:
var processName= "yourApp";
if (Process.GetProcessesByName(processName) == null)
Assert.Inconclusive("Skipped: {processName} is not running");
The above code is a unit test, and it can be used to get a specific process, you can find the name you are looking for in your resource monitor:
To see if an application is installed one would simulate the call to start that application, you'd do that like this:
/// <summary>
/// Determines whether the specified application executable is installed.
/// </summary>
/// <param name="name">The command line name.</param>
/// <returns><c>true</c> if the specified name is installed; otherwise, <c>false</c>.</returns>
public static bool IsInstalled(string name)
{
using (var key = Registry.ClassesRoot.OpenSubKey($"{name}\\shell\\open\\command"))
return key != null;
}
One can check if an application is having the right state by looking at the main window. you do this by getting the process as shown before
what I do to see if the application has started but the user is still at his login is:
if (MyAppProcess.MainWindowTitle.Contains("Login"))
return true;
I use:
if (!TCP.IsListing(login.ServerName, login.Port))
Assert.Inconclusive($"Skipped: TWS not accepting connection port {login.Port}");
To see if a method is accepting connections without actually creating a connection, no all applications like it when you just open a socket port, some crash, some corrupt.
The code that does that is listed below, only works locally, you can't do this remote. I use the host as my servers have more than 1 IP and failing to listen will cause my application to fire up a fallback instance and update the DNS server. Easy to create an application failover cluster without having to have enterprise licenses for all your multi-socket multi-core servers ;-)
public static bool IsListing(string hostUri, int portNumber, int millisecondTimeOut=500)
{
try
{
var info = new ProcessStartInfo() {
Arguments = "-a -p TCP",
CreateNoWindow=false,
FileName="netstat",
WindowStyle= ProcessWindowStyle.Hidden,
UseShellExecute = false,
RedirectStandardOutput = true
};
using (var p = Process.Start(info))
using (StreamReader reader = p.StandardOutput)
{
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
if (line.Contains($":{portNumber}"))
{
return true;
}
}
}
return false;
}
catch (SocketException)
{
return false;
}
}
You can see if a host (remote or local) can be reached using a ping. You can do that using:
public static async Task<bool> IsOnline(string hostUri)
{
try
{
object token = Guid.NewGuid().ToString();
using (var p = new Ping())
{
var result= await p.SendPingAsync(hostNameOrAddress: hostUri, timeout: 1000);
return result.Status == IPStatus.Success;
}
}
catch (Exception)
{
return false;
}
}
How can I kill some active processes by searching for their .exe filenames in C# .NET or C++?
Quick Answer:
foreach (var process in Process.GetProcessesByName("whatever"))
{
process.Kill();
}
(leave off .exe from process name)
My solution is to use Process.GetProcess() for listing all the processes.
By filtering them to contain the processes I want, I can then run Process.Kill() method to stop them:
var chromeDriverProcesses = Process.GetProcesses().
Where(pr => pr.ProcessName == "chromedriver"); // without '.exe'
foreach (var process in chromeDriverProcesses)
{
process.Kill();
}
Update:
In case if you want to do the same in an asynchronous way (using the C# 8 Async Enumerables), check this out:
const string processName = "chromedriver"; // without '.exe'
await Process.GetProcesses()
.Where(pr => pr.ProcessName == processName)
.ToAsyncEnumerable()
.ForEachAsync(p => p.Kill());
Note: using async methods doesn't always mean code will run faster.
The main benefit is that the foreground thread will be released while operating.
You can use Process.GetProcesses() to get the currently running processes, then Process.Kill() to kill a process.
If you have the process ID (PID) you can kill this process as follow:
Process processToKill = Process.GetProcessById(pid);
processToKill.Kill();
You can Kill a specific instance of MS Word.
foreach (var process in Process.GetProcessesByName("WINWORD"))
{
// Temp is a document which you need to kill.
if (process.MainWindowTitle.Contains("Temp"))
process.Kill();
}
Depending on how many processes there are to kill (e.g. when its hundreds like in my case), foreaching over all of them might take quite a while. (interesting sidenote: while Kill() was usually quite quick in .NET FW 4.8 , somehow in NET 6.0 Windows its a lot slower - seeing multiple Win32Exceptions in the debug/trace until the target process is finally done)
Anyway back to topic:
In case of an app shutdown, where u need to make sure every process is is gone, consider using the TAP library - particulary the Parallel shortcuts, hundreds of processes killed within a glimpse.
Usage example:
var procs = Process.GetProcessByName("mydirtyprocesses");
if (procs.Length == 0) return;
procs.AsParallel().ForAll(process =>
{
try
{
process.Kill();
// No process linked to the process comp (mostly because the process died in
// the short timespan between invoking GetProcess() and the effective
// initialization of the props/fields of the component. -OR- Process has
// already exited (when the exit happened after the process component has
// beenpopulated (difference is, in case 1 you cannot even get the Process
// ID from // the component, in case 2 you see data like Id and get the true
// for HasExited // - so always be prepared for that.
// catch (InvalidOperationException)
{
// Process is gone, no further action required
return;
}
// Ensuring process is gone (otherwise try again or fail or whatever)
if (!process.HasExited)
{
// Handle it
}
}
In this particular scenario just wrap it properly in try/catch , as with such a number of processes the probability for an exception is quite increased
static void Main()
{
string processName = Process.GetCurrentProcess().ProcessName;
int processId = Process.GetCurrentProcess().Id;
Process[] oProcesses = Process.GetProcessesByName(processName);
if (oProcesses.Length > 1)
{
if ((MessageBox.Show("Application is opened!", "",MessageBoxButtons.YesNo) == DialogResult.Yes)) ;
{
foreach (var process in Process.GetProcessesByName(processName))
{
if (process.Id != processId)
{
process.Kill();
}
}
}
}
else
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new frmLogin());
}
}
public void EndTask(string taskname)
{
string processName = taskname.Replace(".exe", "");
foreach (Process process in Process.GetProcessesByName(processName))
{
process.Kill();
}
}
//EndTask("notepad");
Summary: no matter if the name contains .exe, the process will end. You don't need to "leave off .exe from process name", It works 100%.