Catching runtime exceptions and outputs from python script in c# - c#

I'm writing a WCF service which runs python scripts.
For That I've been using the following code:
ProcessStartInfo start = new ProccessStartInfo();
start.FileName = "my/full/path/to/python.exe";
start.Arguments = string.Format("{0} {1}", script, args);
start.UseShellExecute = false;
start.CreateNoWindow = true;
start.RedirectStandardOutput = true;
start.RedirectStandardError = true;
Process p = new Process();
p.StartInfo = start;
p.Start();
p.WaitForExit();
string stderr = Process.StandardError.ReadToEnd();
string stdout = Process.StandardOutput.ReadToEnd();
Now what I've noticed (After a lot of testing) is that the Process object gets the Standard Error/Output or catches an exception if the error is related to "Compilation" errors, like if I have an undeclared variable used or stuff like that, but runtime exceptions and prints are not being caught or able to be read in the C# scope.
I've tried to run either the whole "python.exe pythonCommand.py args" as they are sent from the C# code or just send what was in the ProcessStartInfo.Arguments in the Command Line Prompt and it returned exceptions and prints in both cases, but still when I run it via the C# Process object I get no exceptions thrown at me to catch and no output or error what so ever.
I've found nothing about this which makes me feel kind of stupid (hopefully I am and this has a simple solution), and I would really appreciate if someone that has stumbled upon this case could help me out.
Thanks,
Matt

Well, to answer your comment here:
But the problem is both the Exception not being thrown from the the
python nor an output to be read. The Standard Output of the process
(and the Standard Error as well) are always empty – Matt Star
You are capturing the output wrong. This will allow you to capture the output and should show any exceptions thrown.
using System;
using System.Diagnostics;
namespace InteractWithConsoleApp
{
class Program
{
static void Main(string[] args)
{
ProcessStartInfo cmdStartInfo = new ProcessStartInfo();
cmdStartInfo.FileName = #"C:\Windows\System32\cmd.exe";
cmdStartInfo.RedirectStandardOutput = true;
cmdStartInfo.RedirectStandardError = true;
cmdStartInfo.RedirectStandardInput = true;
cmdStartInfo.UseShellExecute = false;
cmdStartInfo.CreateNoWindow = true;
Process cmdProcess = new Process();
cmdProcess.StartInfo = cmdStartInfo;
cmdProcess.ErrorDataReceived += cmd_Error;
cmdProcess.OutputDataReceived += cmd_DataReceived;
cmdProcess.EnableRaisingEvents = true;
cmdProcess.Start();
cmdProcess.BeginOutputReadLine();
cmdProcess.BeginErrorReadLine();
cmdProcess.StandardInput.WriteLine("ping www.bing.com"); //Execute ping bing.com
cmdProcess.StandardInput.WriteLine("exit"); //Execute exit.
cmdProcess.WaitForExit();
}
static void cmd_DataReceived(object sender, DataReceivedEventArgs e)
{
Console.WriteLine("Output from other process");
Console.WriteLine(e.Data);
}
static void cmd_Error(object sender, DataReceivedEventArgs e)
{
Console.WriteLine("Error from other process");
Console.WriteLine(e.Data);
}
}
}
I copied the code from this post: How to parse command line output from c#?
I have used this method many times and it works perfect.
Hope this helps.

Related

C# - Output command prompt in real time to a text box [duplicate]

I'm developing an Windows Forms application that requires me to call a separate program to perform a task. The program is a console application and I need to redirect standard output from the console to a TextBox in my program.
I have no problem executing the program from my application, but I don't know how to redirect the output to my application. I need to capture output while the program is running using events.
The console program isn't meant to stop running until my application stops and the text changes constantly at random intervals. What I'm attempting to do is simply hook output from the console to trigger an event handler which can then be used to update the TextBox.
I am using C# to code the program and using the .NET framework for development. The original application is not a .NET program.
EDIT:
Here's example code of what I'm trying to do. In my final app, I'll replace Console.WriteLine with code to update the TextBox. I tried to set a breakpoint in my event handler, and it isn't even reached.
void Method()
{
var p = new Process();
var path = #"C:\ConsoleApp.exe";
p.StartInfo.FileName = path;
p.StartInfo.UseShellExecute = false;
p.OutputDataReceived += p_OutputDataReceived;
p.Start();
}
static void p_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
Console.WriteLine(">>> {0}", e.Data);
}
This works for me:
void RunWithRedirect(string cmdPath)
{
var proc = new Process();
proc.StartInfo.FileName = cmdPath;
// set up output redirection
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardError = true;
proc.EnableRaisingEvents = true;
proc.StartInfo.CreateNoWindow = true;
// see below for output handler
proc.ErrorDataReceived += proc_DataReceived;
proc.OutputDataReceived += proc_DataReceived;
proc.Start();
proc.BeginErrorReadLine();
proc.BeginOutputReadLine();
proc.WaitForExit();
}
void proc_DataReceived(object sender, DataReceivedEventArgs e)
{
// output will be in string e.Data
}
You can use the following code
MemoryStream mem = new MemoryStream(1000);
StreamWriter writer = new StreamWriter(mem);
Console.SetOut(writer);
Assembly assembly = Assembly.LoadFrom(#"C:\ConsoleApp.exe");
assembly.EntryPoint.Invoke(null, null);
writer.Close();
string s = Encoding.Default.GetString(mem.ToArray());
mem.Close();
I've added a number of helper methods to the O2 Platform (Open Source project) which allow you easily script an interaction with another process via the console output and input (see http://code.google.com/p/o2platform/source/browse/trunk/O2_Scripts/APIs/Windows/CmdExe/CmdExeAPI.cs)
Also useful for you might be the API that allows the viewing of the console output of the current process (in an existing control or popup window). See this blog post for more details: http://o2platform.wordpress.com/2011/11/26/api_consoleout-cs-inprocess-capture-of-the-console-output/ (this blog also contains details of how to consume the console output of new processes)
Thanks to Marc Maxham for his answer that save me time !
As Jon of All Trades notice it, UseShellExecute must be set to false in order to redirect IO streams, otherwise the Start() call throws an InvalidOperationException.
Here is my modification of the code where txtOut is a WPF readonly Textbox
void RunWithRedirect(string cmdargs)
{
var proc = new Process()
{
StartInfo = new ProcessStartInfo("cmd.exe", "/k " + cmdargs)
{
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
},
EnableRaisingEvents = true
};
// see below for output handler
proc.ErrorDataReceived += proc_DataReceived;
proc.OutputDataReceived += proc_DataReceived;
proc.Start();
proc.BeginErrorReadLine();
proc.BeginOutputReadLine();
proc.WaitForExit();
}
void proc_DataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
Dispatcher.BeginInvoke(new Action( () => txtOut.Text += (Environment.NewLine + e.Data) ));
}

Process exits when Startinfo.RedirectStandardOutput is set true

I am trying to develop a console application which performs tfs merge.Command to execute is:
tf merge $/CodeBase/ALPHA $/CodeBase/BETA /recursive /version:C4~C6
When there is a conflict in the merge operation , an error is raised first and a resolve conflict window appeas where i resolve conflicts . After i resolve conflicts , final output is displayed.
c# code is as follows for console application which performs tf merge:
Process proc = new Process();
proc.StartInfo.FileName = "tf.exe";
proc.StartInfo.Arguments = " merge $/CodeBase/ALPHA $/CodeBase/BETA /version:C4~C6";
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardError = true;
proc.Start();
proc.WaitForExit();
string line1 = proc.StandardOutput.ReadLine();
string line2 = proc.StandardError.ReadToEnd();
Problem :
The process exits abrupty showing only the initial error message . Ideally it should wait until i resolve all the conflicts and the process will wait until then.
But since i am setting proc.StartInfo.RedirectStandardError = true; ,application is not waiting for resolve conflict window (not even starting it). But if i comment this line ,it is working correctly , but i am not able to read final output.
Help me please.
EDIT : 7/23
I believe i am doing asynchronous read operation (may be that could be problem). Is there a way by which i can perform synchronous read???
You have to attach eventhandlers
proc.ErrorDataReceived += (s, e) => ErrorLine(e.Data);
proc.OutputDataReceived += (s, e) => OutputLine(e.Data);
void ErrorLine(string text)
{
Console.ForegroundColor = ConsoleColor.White;
Console.BackgroundColor = ConsoleColor.DarkRed;
Console.Error.WriteLine(text);
Console.ResetColor();
}
void OutputLine(string text)
{
Console.Error.WriteLine(text);
}
Problem is with TFS which executes with '/noprompt' if it detects a stdout redirection.
Problem was solved by setting environment variable TFS_IGNORESTDOUTREDIRECT=1.

Problems getting desired output from Process.Start()

I am working on an application that calls several command line applications to do some post processing on some video files.
Right now I am trying to use Comskip to identify the commercial breaks in a video recording from my cable card tuner. This runs just fine, but I am having problems getting the screen output that I need.
String stdout = null;
using (var process = new Process())
{
var start = new ProcessStartInfo(comskip, cmdLine);
start.WindowStyle = ProcessWindowStyle.Normal;
start.CreateNoWindow = true;
start.UseShellExecute = false;
start.RedirectStandardOutput = true;
process.StartInfo = start;
process.Start();
process.WaitForExit();
stdout = process.StandardOutput.ReadToEnd();
}
I'm expecting stdout to grab what is displayed on the screen the same as when the application is launched manually (screen shot below) which is a continuous feed of what the application is doing, and mixed in the output are lines that give a % progress, which I want to use to update a progress bar
But running the above code only gives me:
The commandline used was:
"C:\Users\Chris\Google Drive\Tools\ComSkip\comskip.exe" "C:\Users\Chris\Desktop\ComSkip Tuning Files\Modern Family.wtv" "--ini=C:\Users\Chris\Desktop\ComSkip Tuning Files\comskip_ModernFamily.ini"
Setting ini file to C:\Users\Chris\Desktop\ComSkip Tuning Files\comskip_ModernFamily.ini as per commandline
Using C:\Users\Chris\Desktop\ComSkip Tuning Files\comskip_ModernFamily.ini for initiation values.
I also tried redirecting the StandardError stream and grabbing process.StandardError.ReadToEnd(); but the process appears to hang if I run with these options.
Am I missing something to capture what I'm hoping for, or is it possible that the output stream for this application is going somewhere else that is not accessible?
You must set following:
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.UseShellExecute = false;
process.OutputDataReceived += new DataReceivedEventHandler(ReadOutput);
process.ErrorDataReceived += new DataReceivedEventHandler(ErrorOutput);
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
and catch the output in ReadOutput and ErrorOutput
private static void ErrorOutput(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
stdout = "Error: " + e.Data;
}
}
private static void ReadOutput(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
stdout = e.Data;
}
}
See the docs on RedirectStandardOutput. Waiting for the child process to end before reading the output can cause a hang.
It particular, the example says not to do what you have done:
Process p = new Process();
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "Write500Lines.exe";
p.Start();
// Do not wait for the child process to exit before
// reading to the end of its redirected stream.
// p.WaitForExit();
// Read the output stream first and then wait.
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
You should use the events OutputDataReceived and possibly ErrorDataReceived and update the progress bar in the handler.

Command prompt output being read as empty string

I'm trying to execute command prompt commands and read the output in C#. This is my code:
ProcessStartInfo cmdInfo = new ProcessStartInfo("cmd.exe", "/c " + command);
cmdInfo.CreateNoWindow = true;
cmdInfo.RedirectStandardOutput = true;
cmdInfo.UseShellExecute = false;
Process cmd = new Process();
cmd.StartInfo = cmdInfo;
cmd.Start();
string result = cmd.StandardOutput.ReadToEnd();
cmd.WaitForExit();
cmd.Close();
return result;
It works most of the time, but sometimes result="" when that's impossible for the command I'm using (for example, route add should give output on success or failure). Any ideas? I was wondering if maybe I'd created a race condition between the process and the ReadToEnd call?
Not all output is written to StandardOutput; many applications will instead write to StandardError if something goes wrong. You would have to read from both to get all of the output.
As long as the application never blocks for input, it should be safe to call ReadToEnd() on both output streams to get all of the output. A safer option, however, is to hook up an event to the OutputDataReceived and ErrorDataReceived events. You can attach lambda expression to these that close over local variables to make things pretty simple:
var output = new StringBuilder();
var error = new StringBuilder();
cmd.OutputDataReceived += (o, e) => output.Append(e.Data);
cmd.ErrorDataReceived += (o, e) => error.Append(e.Data);
cmd.Start();
cmd.BeginOutputReadLine();
cmd.BeginErrorReadLine();
cmd.WaitForExit();

Executing "java -version" programmatically places the output in StandardError instead of StandardOutput

I am facing very strange behavior as I am trying to programmatically detect the java version on my PC. I am using the following C# code:
ProcessStartInfo procStartInfo = new ProcessStartInfo("cmd", "/c java -version");
procStartInfo.RedirectStandardOutput = true;
procStartInfo.RedirectStandardError = true;
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = true;
Process proc = new Process();
proc.StartInfo = procStartInfo;
proc.Start();
// Get the output into a string
output = proc.StandardOutput.ReadToEnd();
if (output == "")
output = proc.StandardError.ReadToEnd();
return output;
This displays the correct information but the result is found in proc.StandardError.ReadToEnd() instead of proc.StandardOutput.ReadToEnd() as in any other command (i.e. 'java -help').
Even more weird, if I run "java -showversion" which should output both java version and then help information I get the help info in the StandardOutput and the version info is in the StandardError.
This doesn't make sense to me.
I tested this on 2 Windows 7 x64 machines and 1 XP x32. Same thing everywhere. It's really weird.
Looks likes this (i.e. version being printed on standard out) is an old issue that sun (oracle) has never fixed in order to avoid breaking legacy systems: http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4380614. On linux, java -version > foo will create an empty file while java -version 2> foo will create a file containing the version.
ProcessStartInfo procStartInfo = new ProcessStartInfo("cmd", "/c java -version");
procStartInfo.RedirectStandardOutput = true;
procStartInfo.RedirectStandardError = true;
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = true;
Process proc = new Process();
proc.StartInfo = procStartInfo;
proc.EnableRaisingEvents = true;
// create event and wait for data receive
proc.OutputDataReceived += new DataReceivedEventHandler(proc_OutputDataReceived);
proc.ErrorDataReceived += new DataReceivedEventHandler(proc_ErrorDataReceived);
proc.Start();
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
proc.WaitForExit();
static void proc_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
string s = e.Data;
}
static void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
string s = e.Data;
}
This should do the trick:
java -version 2>&1 | more
"more" is not required, unless you want to redirect it to file for example.
Works both on *NIX and Win.

Categories