Wait properties windows dialog before continue script - c#

I had a piece of code to display the properties window of a file,
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = #"C:\Users\nyongrand\Desktop\Internet Download Manager.lnk";
psi.Verb = "properties";
Process process = Process.Start(psi);
process.WaitForExit(); //This give me exception, Object reference not set to an instance of an object.
what I want is to wait until the window properties is closed, because if my code closed the properties window will be closed as well, I need a solution between, my code is able to wait for the properties window closed, or my code can exit without closing the properties window.

The exception you're getting means that process is null at the time you try to call its WaitForExit member method. So the question you should be asking is why.
Start with the documentation for the overload of the Process.Start function that you're calling to see what it actually returns. Sure enough, it returns a Process object, but only under certain conditions:
Return Value
Type: System.Diagnostics.Process
A new Process component that is associated with the process resource, or null if no process resource is started (for example, if an existing process is reused).
And, from the "Remarks" section:
Note: If the address of the executable file to start is a URL, the process is not started and null is returned.
So, if an existing process is re-used, the Process.Start method will return null. And you cannot call methods on null.

Try replacing
Process process = Process.Start(psi);
with
Process process = new Process();
if(process.Start(psi))
{
process.WaitForExit();
}
else
{
//Do something here to handle your process failing to start
}
The problem you face with your code is that Process.Start() returns a Boolean. It's not a factory for Process objects.

Related

Batch script calling robocopy in Process won't terminate

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

How to stop a process after you run it?

This is the code I used to run the following exe program. How can I end this 3 process after I had run it?
Process.Start(#"C:\Bot-shortcut\DIE1.exe");
Process.Start(#"C:\Bot-shortcut\DIE2.exe");
Process.Start(#"C:\Bot-shortcut\DIE3.exe");
First, store the process object returned when you start the process.
If you want it to close normally, as though someone had clicked the close icon, then use CloseMainWindow. This simulates clicking the close icon so that the process can shut down normally. This is always preferable to killing the process, which can corrupt data.
If you want it to die instantly then use Kill. Note that this can corrupt data; the process might have been writing to a file when you killed it.
You have to get the process by Name and then stop it.
Here is the code snippet from MSDN:
Process[] myProcesses;
// Returns array containing all instances of Notepad.
myProcesses = Process.GetProcessesByName("Notepad");
foreach (Process myProcess in myProcesses)
{
myProcess.CloseMainWindow();
}
The Process.kill() will also stop the process but without any prompt.
Find the details in This article.
You can end your process using Kill
Process myProcess = new Process(#"C:\Bot-shortcut\DIE1.exe");
myProcess.Start();
//After Some Codes
myProcess.Kill();
Process.Start returns the process instance which you have started.
Store that in variable and use Process.Kill method to kill that process once you are done with it.
Process process = Process.Start(#"C:\Bot-shortcut\DIE1.exe");
process.Kill();
Store the process objects as variables:
var proc1 =Process.Start(#"C:\Bot-shortcut\DIE1.exe");
var proc2 = Process.Start(#"C:\Bot-shortcut\DIE2.exe");
var proc3 = Process.Start(#"C:\Bot-shortcut\DIE3.exe");
And wait for them to exit:
proc1.WaitForExit();
proc2.WaitForExit();
proc3.WaitForExit();
Or kill them:
proc1.Kill();
proc2.Kill();
proc3.Kill();

Killing external process when application dies

I've been working on a small piece that calls an external executable (ffmpeg in my case)
And then I wrote a test and used test runner in a debug mode, now if I stop debugging (terminate) it still runs ffmpeg. I tried to kill the process in the finalizer and with IDisposable - it still runs. How can I make sure that the process never will be left like that, and if the caller dies or fails or gets stopped by any means, the ffmpeg executable guaranteed to be killed.
I run the process as usual (nothing fancy)
var processInfo = new ProcessStartInfo(ffmpegPath)
{
UseShellExecute = false,
Arguments = arguments,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
};
using (var ffmpegProc = new Process())
{
ffmpegProc.EnableRaisingEvents = true;
ffmpegProc.StartInfo = processInfo;
ffmpegProc.Start();
ffmpegProc.WaitForExit();
}
You should use JobObject
With JobObject you can add child processes. So if the main process is killed or closed the os will terminate all child processes.
source: http://www.xtremevbtalk.com/showpost.php?p=1335552&postcount=22
Another solution is to pass to child object the parent PID.
Any child have to check for parent PID existence and if not found kill itself
There is nothing you can do when your process is terminated. There is no code run in your process after someone kills it with TerminateProcess call.
In case of more graceful cases (i.e. unhandled exception) you may have global handler that does something close to what you want.

Understanding how to control stdout using System.Diagnostics.Process

I see several questions about how to launch processes and push data into stdin, but not how to control where their output goes.
First here is my current code, run from a console mode C# application:
// Prepare the process to run
ProcessStartInfo start = new ProcessStartInfo();
// Enter in the command line arguments, everything you would enter after the executable name itself
start.Arguments = " -";
// Enter the executable to run, including the complete path
start.FileName = "doxygen.exe";
// Do you want to show a console window?
start.WindowStyle = ProcessWindowStyle.Normal;
start.CreateNoWindow = false;
start.RedirectStandardInput = true;
start.UseShellExecute = false;
// Run the external process & wait for it to finish
using (Process proc = Process.Start(start))
{
//doxygenProperties is just a dictionary
foreach (string key in doxygenProperties.Keys)
proc.StandardInput.WriteLine(key+" = "+doxygenProperties[key]);
proc.StandardInput.Close();
proc.WaitForExit();
// Retrieve the app's exit code
int exitCode = proc.ExitCode;
}
What happens when I run this is I do not see any new window (though I think I should) and all of doxygen.exe's stdout is printed to my app's console window.
What I would like to happen is one of two things:
Doxygen is launched in a visible window, and I can see its stdout in that window, not in my app's window.
Doxygen is launched in a hidden window, and it's stdout is written to a log file.
How can I achieve these?
In addition, why am I not getting a separate window for the spawned process, and why is the spawned process writing output to my window not its own?
One thing that you can do is use RedirectStandardOutput and instead of using WaitForExit you can use ReadToEnd
ProcessStartInfo start = new ProcessStartInfo();
start.RedirectStandardOutput = true;
//make other adjustments to start
Process p = new Process();
p.StartInfo = start;
p.Start();
string output = p.StandardOutput.ReadToEnd();
and then you can use string output at your leisure
If you want to get output in real-time the p.StandardOutput property has methods that allow you to get the output asynchronously. I don't know all the details to it offhand, I've only used it once before, but there's plenty of literature out there if you search for it.
Also be careful when redirecting both StandardOutput and StandardError at the same time, If they're long enough, it is possible for that to cause deadlocks.
You need to do two things:
1) Indicate that you want the standard output of the process to be directed to your app by setting the RedirectStandardOuput property to true in the process.
2) BEFORE the call to WaitForExit, start capturing the output:
string sOutput = p.StandardOutput.ReadToEnd();
If you do not start reading the output before calling wait for exit, you can encounter a deadlock.
However, it is important to know that standard output will only capture output information, not anything written to the standard error stream of the app.
In order to capture both streams of information, you can hook the process's OutputDataReceived and ErrorDataReceived events and write the event data directly into a log file or store it in a class property for use after the process has completed.

Getting a Process to terminate

I have a process object setup like the following:
Process p = new Process();
p.StartInfo.FileName = command;
p.StartInfo.UseShellExecute = true;
p.StartInfo.Arguments = String.Format(
commandArguments,
destinationLocation,
sourceLocation,
sourceDirName,
(string.IsNullOrEmpty(revisionNotes.Text)) ? "" : revisionNotes.Text);
(where undefined values are supplied externally to this code and are valid). The process in question launches and properly executes with p.Start(); but i need to catch it on termination. The console window flashes up briefly and goes away which would seem to indicate that the process is done, but none of the relevant events are fired (OutputDataRecieved, Exited, etc) and it's like the process never ends. (I'm trying to execute a lua script with some parameters if that's relevant). Can someone help me get this process to stop correctly?
WaitForExit
Have you set the EnableRaisingEvents property of the process to True? You won't catch the Exited event without it.

Categories