I'm starting a Python process using the C# Process class. The standard output is redirected as I want to capture it. I'm able to get the standard output properly except when it crashes. We are aware that it will crash. We need to inspect the standard output when it crashes. We are not getting exact stdout once crashed.
When I run that script in the console manually, I can see the exact output at the time of the crash, but when I redirect the stdout using the > operator, the output is not written to the file, if the process crashed. I suspect that the I/O buffers are not getting flushed, if the process crashes. Is this true? Thanks in advance.
var lastLineOfStdOut = null;
var p = new Process
{
StartInfo =
{
FileName = ... ,
Arguments = ... ,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
}
};
p.OutputDataReceived += (sender, e) =>
{
if (e.Data == null) return;
lastLineOfStdOut = e.Data;
Console.WriteLine(e.Data);
};
p.Start();
p.BeginOutputReadLine();
p.WaitForExit();
var exitCode = p.ExitCode; // Use this to figure out if it crashed
Related
I have some script files that would usually be edited and run through a ui, but the offer to run on the command line when using the syntax: program.exe scriptfile.fmw --userparameter "some parameter".
If I simply write
arguments = "program.exe scriptfile.fmw --userparameter \"some parameter\"";
Process process = Process.Start("cmd.exe", $"/K {arguments};
process.WaitForExit();
the commandline starts, calls the correct exe and runs the script.
Now I want to retrieve the response of the script after it ran to catch possible error messages.
As far as I can see this requires to use ProcessStartInfo and that's basically where my problem seems to start.
ProcessStartInfo startInfo = new ProcessStartInfo()
{
FileName = "cmd.exe",
Arguments = $"/K {arguments}",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true
};
Process process = Process.Start(startInfo);
process.BeginErrorReadLine();
process.WaitForExit();
The code above opens a commandline window, but never uses the arguments given.
It escapes me on how I should hand the argument line over to the cmd.exe - process.
I was playing around with RedirectStandardInput and its StreamWriter, but never managed to get anything written into the cmd-window.
The same goes for the RedirectStandardOutput, where I would like to gather the script response in the cmd-window as a string or string[], to parse for specific exit codes of the script.
I think this is what you want, have a look below :
Process process = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo()
{
FileName = "program.exe", //You must use your program directly without invoking through cmd.exe
Arguments = "scriptfile.fmw --userparameter \"some parameter\"", //Add parameters and arguments here needed by your application
UseShellExecute = false,
EnableRaisingEvents = true, //You are missing this
RedirectStandardOutput = true,
RedirectStandardError = true
};
process.ErrorDataReceived += process_ErrorDataReceived; //You should listen to its output error data by subscribing to this event
process.BeginErrorReadLine();
process.Start(startInfo);
process.WaitForExit(); // You may now avoid this
Then at here do anything with your received error data!
private static void process_ErrorDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
{
DoSomething(e.Data); // Handle your error data here
}
EDIT-(1) : Please try this solution and ping me if it works or if you need some extra help. Everyone in comments is suggesting that you must not use cmd.exe to invoke your program as it may causes debugging overhead, performance issue and you might not get error details as well.
Cheers!
I used this code to log the output from the exe in command prompt to RIchTextBox.
ProcessStartInfo psi = new ProcessStartInfo("adb.exe", "devices");
psi.UseShellExecute = false;
psi.RedirectStandardOutput = true;
psi.CreateNoWindow = true;
var proc = Process.Start(psi);
string s = proc.StandardOutput.ReadToEnd();
proc.WaitForExit();
richTextBox1.Text = s;
This is basically android command to get the list of connected devices. This works fines as it has just two lines.
If the output of the exe is continous data how it can be logged efficiently.
I replaced the command with adb.exe logcat but it hangs and nothing comes on the RichTextBox.
How can I log this coninous output on the RichetxtBox?
It's because you call ReadToEnd on the output stream; C# will keep reading it and reading it and only finish reading it when the stream closes. At that point your code carries on. Your task is more complex than you realize. To read incrementally you need something like:
ProcessStartInfo psi = new ProcessStartInfo
{
FileName = Properties.Settings.Default.CommandLineFfmpegPath,
Arguments = string.Format(
Properties.Settings.Default.CommandLineFfmpegArgs,
OutputPath
),
WindowStyle = ProcessWindowStyle.Hidden,
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardOutput = true
};
var proc = System.Diagnostics.Process.Start(psi);
proc.OutputDataReceived += proc_OutputDataReceived;
proc.ErrorDataReceived += proc_ErrorDataReceived;
proc.BeginOutputReadLine();
And you need an event handler that will be called every time there is some data (but it will need to make sure it doesn't cause a cross thread violation), something like:
void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (richTextbox1.InvokeRequired)
richTextbox1.Invoke((MethodInvoker) delegate {this.Text += e.Data;});
else
richTextbox1.Text += e.Data;
}
I'm not sure I'd use something as heavy as a richtextbox for this.. You probably aren't formatting (unless the console output is colored and youre gonna reinterpret the color codes) so a textbox would do fine.
Oh, and you probably don't want to jam your UI thread by WaitForExit either
When I redirect stdout, the new script's process window does not show all the printing (obviously, as it's redirected...).
Is there a way to both show the printings in the script window (be it PowerShell or Python) and also capture its output after the process ends?
var process = new Process {
StartInfo = {
FileName = path,
Arguments = arguments,
UseShellExecute = false,
RedirectStandardOutput = true, // I want to capture stdout
}
};
using (process) {
process.Start();
process.WaitForExit();
var stdout = await process.StandardOutput.ReadToEndAsync().ConfigureAwait(false);
// I do have the output in stdout now, but the script's window hasn't shown anything while it was running.
// It makes me sad :-(
}
I need some help. I have a external app (test.exe with some dll files). In cmd i have run command like this: test.exe parmeters and get a lot of data with some needed info.
I have write app which execute this external app and output is not fully as I exec it with cmd. It's just some firstly lines. I don't mind whats wrong. Please help
using(var process = new Process {
StartInfo = new ProcessStartInfo {
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
FileName = orPath,
Arguments = parmeters.ToString(),
}
}) {
process.Start();
process.WaitForExit();
string result = "";
string standard_output;
while ((standard_output = process.StandardOutput.ReadLine()) != null) {
if (standard_output.Contains("xx"))
result = standard_output.Substring(standard_output.Length - 15);
}
Without a concise-but-complete code example that reliably demonstrates the problem, it's hard to say for sure. But it doesn't surprise me that if you try to consume StandardOutput after you have already called WaitForExit(), that not all of the output has been buffered and is available.
Maybe try this instead:
process.Start();
string result = "";
string standard_output;
while ((standard_output = process.StandardOutput.ReadLine())
!= null)
{
if (standard_output.Contains("xx"))
result = standard_output.Substring(
standard_output.Length - 15);
}
Note that I've simply removed the call to WaitForExit(). Reading the StandardOutput TextReader until it returns null will have the same effect as waiting for the end of the process, assuming a normal process (i.e. where the stdout doesn't get closed until the process exits).
I want to start a child process (indeed the same, console app) with elevated privileges but with hidden window.
I do next:
var info = new ProcessStartInfo(Assembly.GetEntryAssembly().Location)
{
UseShellExecute = true, // !
Verb = "runas",
};
var process = new Process
{
StartInfo = info
};
process.Start();
and this works:
var identity = new WindowsPrincipal(WindowsIdentity.GetCurrent());
identity.IsInRole(WindowsBuiltInRole.Administrator); // returns true
But UseShellExecute = true creates a new window and I also I can't redirect output.
So when I do next:
var info = new ProcessStartInfo(Assembly.GetEntryAssembly().Location)
{
RedirectStandardError = true,
RedirectStandardOutput = true,
UseShellExecute = false, // !
Verb = "runas"
};
var process = new Process
{
EnableRaisingEvents = true,
StartInfo = info
};
DataReceivedEventHandler actionWrite = (sender, e) =>
{
Console.WriteLine(e.Data);
};
process.ErrorDataReceived += actionWrite;
process.OutputDataReceived += actionWrite;
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
This doesn't elevate privileges and code above returns false. Why??
ProcessStartInfo.Verb will only have an effect if the process is started by ShellExecuteEx(). Which requires UseShellExecute = true. Redirecting I/O and hiding the window can only work if the process is started by CreateProcess(). Which requires UseShellExecute = false.
Well, that's why it doesn't work. Not sure if forbidding to start a hidden process that bypasses UAC was intentional. Probably. Very probably.
Check this Q+A for the manifest you need to display the UAC elevation prompt.
In my case, it was ok to get the outputs once the elevated child process is done. Here's the solution I came up. It uses a temporary file :
var output = Path.GetTempFileName();
var process = Process.Start(new ProcessStartInfo
{
FileName = "cmd",
Arguments = "/c echo I'm an admin > " + output, // redirect to temp file
Verb = "runas", // UAC prompt
UseShellExecute = true,
});
process.WaitForExit();
string res = File.ReadAllText(output);
// do something with the output
File.Delete(output);
Check this answer.
This seems to provide a workaround. But I recommend to try other methods like Named Pipes when you have access to source code of the child process.