Different reference returned from Process.Start - c#

ProcessStartInfo psi = new ProcessStartInfo(BatchFile)
Process p = Process.Start(psi)
Why p.ID is different than process id visible in WindowsTaskManager
(BatchFile is path to external program with appropriate parameters)

I would assume that it's because p.ID is the id of the process that's running the batch file rather than the id of the process started by the batch file.
You can start the executable directly by Process.Start by using the correct overload

I assume BatchFile is some kind of cmd or bat file that runs other processes one by one.
So in Windows Task Manager you actually see ids of those processes that are run by batch file.
Examples
If I do this
var p = Process.Start("notepad.exe");
p.Id will match to the PID from Task Manager.
However, if I do this:
var p = Process.Start("test.cmd"); // test.cmd has notepad.exe call inside
p.Id will be different from PID shown in the Task Manager.

A process ID is only meaningful while the process is alive. The first thing to check is .HasExited - if this is true, ignore the process ID; it no longer has any meaning.
There are a number of ways you can start something and have no process left even though you can apparently see it still on screen:
if it is a script/bat/cmd that spawns something and exits (remember: you are watching the script, not the "something")
if the exe does some inter-exe voodoo internally - for example, most of the office apps and internet explorer do this; if there is an existing process, it forwards the args to that process to handle, and exits immediately

Related

How to kill process by specific Id in c#

I have multiple processes that running at the same time of my C# program and all of this processes have the same name with different ID and I want to kill specific process of my program by their specific ID or rename them and kill it by their specific name.
Process.GetProcessById(Int32)
Process.Kill()
Edit:
Pay attention to Notes and Remarks sections of the documentation, specially for the Kill method.
If you have the id you can do it like this (where id is an int32):
Process process = Process.GetProcessById(id);
process.Kill();
Or if you are using LinQ you can do it like this:
Process.GetProcesses()
.Where(x => x.Id.Equals(id))
.FirstOrDefault()
.Kill();

Git for windows seems to create sub-processes that never die

I use the following code to call git from C#:
var pInfo = new ProcessStartInfo
{
FileName = "git",
Arguments = "checkout master",
UseShellExecute = false
};
using var p = Process.Start(pInfo);
Console.WriteLine(p.Id);
p.WaitForExit();
The process ID printed is, lets say 3709. When my program ends, I look at the Task Manager and I see a git.exe process still running with a different ID, say 8865. This process remains running indefinitely.
It seems to me that the git process started by my program spawns a second git process that never exits. Is there any way for me to prevent this behavior? As it is, every time I run my program, there is yet another git.exe process left behind indefinitely, adding up to a lot over time, until I reboot.
(Alternatively, is there any other way to invoke git other than by running the executable?)

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
}
}

How to run Two process one after another using C#?

I have a application which will open a two microsoft ppsx file one after another. for that i have used process object to run the files. mention bellow
Process process = Process.Start(#"E:\test\test.ppsx");
I need to run the files in such a way that after finishing the first file ,second file should run automatically. can some one suggest me how can achieve that.
You can use WaitForExit method to wait to end process (Something like this):
var process1 = Process.Start(...);
process1.WaitForExit();
var process2 = Process.Start(...);
or subscribe into a Process.Exited event and execute another process after the first one. Check this for your reference.
You can use Process.WaitForExit method.
Instructs the Process component to wait indefinitely for the
associated process to exit.
Also check Process.Exited event.
Occurs when a process exits.
Process process = Process.Start(#"E:\test\test.ppsx");
process.WaitForExit();
Use Process.WaitForExit()
class Program
{
static void Main(string[] args)
{
Task.Run(() =>
{
Process.Start(#"c:\temp\presentation1.pptx").WaitForExit();
}).ContinueWith(o =>
{
Process.Start(#"c:\temp\presentation2.pptx").WaitForExit();
});
Console.ReadKey();
}
}
You should get all the ppsx files from the test directory in the E drive in an array and process on the array of ppsx files according to your requirements.
string[] files = Directory.GetFiles("your path");
loop through the array and pass each file path to the Process constructor and as lexeRoy said you can WaitForExit.

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