How to read from a process that hasn't closed? - c#

I'm making a simple program in C# that allows a client to connect to a server and run MS DOS commands on that server.
The program worked well, later on I decided I wanted to cmd.exe process to run whilst the client was connected to the server, this is where I'm stuck at. I wanted this so that I could
run commands like CD and change the working directory, previously this would've had no effect because the cmd.exe process was closed after the command was run.
It appears that I can only read from the StandardOutput and StandardError streams if the process has exited, is there any workaround for this?
Here's some of my code used in the program :
*Creates and returns cmd.exe process: *
private Process createDOS()
{
try
{
// Create a ProcessStartInfo class
ProcessStartInfo startInfo = new ProcessStartInfo("cmd","");
// Set up the values
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.RedirectStandardInput = true;
startInfo.CreateNoWindow = false;
// Create a cmd.exe process
Process process = new Process();
// Apply the start info and run
process.StartInfo = startInfo;
process.Start();
return process;
}
catch (Exception)
{
return null;
}
}
Method that formats the response into a string :
private string findResponse(StreamReader err,StreamReader outp)
{
string output = outp.ReadToEnd();
string error = err.ReadToEnd();
string ret = "";
// Add the output to the return field if the process actually output data
if (output.Length > 0)
{
ret = "OUTPUT : " + output;
}
// Then attempt to add data from the error stream
if (error.Length > 0)
{
// If output was read, add a newline character separating the two fields
if (ret.Length > 0) ret = ret + Environment.NewLine;
ret = ret + "ERROR : " + error;
}
// If there's no output, that means there's no error, which means the execution was silently succesful
if (ret.Length <= 0)
{
ret = "Command execution succesful!";
}
return ret;
}
Server listener block :
private void run()
{
while (true)
{
Stream stream = null;
StreamReader sr = null;
StreamWriter sw = null;
Socket socket = null;
Process proc = null;
try
{
socket = server.AcceptSocket();
stream = new NetworkStream(socket);
sr = new StreamReader(stream);
sw = new StreamWriter(stream);
String mode = sr.ReadLine();
sw.WriteLine("Serverside link connected");
sw.Flush();
// Create cmd process in WINPROCESS mode
if (mode == "WINPROCESS")
{
proc = createDOS();
}
String line;
while ((line = sr.ReadLine()) != null)
{
if (mode == "WINPROCESS")
{
proc.StandardInput.WriteLine(line);
proc.StandardInput.Flush();
// Response
sw.WriteLine(findResponse(proc.StandardError, proc.StandardOutput));
sw.Flush();
}
else
{
sw.WriteLine(exeDOS(line));
sw.Flush();
}
}
}
catch (Exception ex)
{
// Silence
}
finally
{
if (socket != null) socket.Close();
if (stream != null) stream.Close();
if (sw != null) sw.Close();
if (sr != null) sr.Close();
if (proc != null) proc.Close();
}
}
}
Any help would be appreciated, thanks!

You need to add a handler to the OutputDataReceived and ErrorDataReceived events:
process.StartInfo.RedirectStandardOutput = true;
process.OutputDataReceived +=
new DataReceivedEventHandler(process_OutputDataReceived);
process.StartInfo.RedirectStandardError = true;
process.ErrorDataReceived +=
new DataReceivedEventHandler(process_ErrorDataReceived);
if (process.Start())
{
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
}
}
void process_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
Console.WriteLine(e.Data);
}
void process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
Console.WriteLine(e.Data);
}

Related

C# print file, wait for close file - program hang up

I wrote program that listens on a directory (GUI - WPF). When the new file show up in this directory is sent to the printer. The problem occurs when I try to save a large file to this directory. I have to wait until the file is closed, and then send it to the printer. I have a function that checks if the file is open. But when I use it in the whole GUI hangs. How do I use this function asynchronously?
protected void newfile(object fscreated, FileSystemEventArgs Eventocc)
{
try
{
string CreatedFileName = Eventocc.Name;
FileInfo createdFile = new FileInfo(CreatedFileName);
string extension = createdFile.Extension;
string eventoccured = Eventocc.ChangeType.ToString();
fsLastRaised = DateTime.Now;
this.Dispatcher.Invoke((Action)(() =>
{
String file = "";
file = watchingFolder + "\\" + CreatedFileName;
//printing
this.Dispatcher.Invoke((Action)(() =>
{
FileInfo info = new FileInfo(file);
while (!IsFileReady(info)) { }
var t = new Thread(() => printFile(file, extension)); //send to printer
t.Start();
}));
}));
}
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show("Error");
}
}
IsFileReady function:
public static bool IsFileReady(FileInfo file)
{
FileStream stream = null;
try
{
stream = file.Open(FileMode.Open, FileAccess.Read, FileShare.None);
}
catch (IOException)
{
//the file is unavailable because it is:
//still being written to
//or being processed by another thread
//or does not exist (has already been processed)
return true;
}
finally
{
if (stream != null)
stream.Close();
}
//file is not locked
return false;
}
And printfile
public void printFile(string filepath, string ext)
{
ProcessStartInfo info = new ProcessStartInfo();
info.Verb = "print";
info.FileName = filepath;
info.CreateNoWindow = true;
info.WindowStyle = ProcessWindowStyle.Hidden;
Process p = new Process();
p.StartInfo = info;
p.Start();
p.WaitForInputIdle();
System.Threading.Thread.Sleep(3000);
if (false == p.CloseMainWindow())
p.Kill();
}
}
How can I correct this code to work with large files without hangs up?
EDIT:
For check new file I use FileSystemWatcher
private void start(object sender, RoutedEventArgs e)
{
if (watchingFolder == null)
{
}
else
{
fs = new FileSystemWatcher(watchingFolder, "*.*");
fs.EnableRaisingEvents = true;
fs.IncludeSubdirectories = true;
fs.Created += new FileSystemEventHandler(newfile);
btnSatrt.IsEnabled = false;
btnStop.IsEnabled = true;
}
}
You're executing while (!IsFileReady(info)) { } through Dispatcher.Invoke, that executes the code on the UI thread so it will block for sure the app.
You aren't interacting at all with the UI, so the correct approach is to execute it asynchronously, via Tasks and awaits or with a background thread via the ThreadPool and not using at all Dispatcher.Invoke.
Try to execute all code in the newfile event handler on a background thread by starting a new task:
protected async void newfile(object fscreated, FileSystemEventArgs Eventocc)
{
try
{
await Task.Run(() =>
{
string CreatedFileName = Eventocc.Name;
FileInfo createdFile = new FileInfo(CreatedFileName);
string extension = createdFile.Extension;
string eventoccured = Eventocc.ChangeType.ToString();
fsLastRaised = DateTime.Now;
string file = watchingFolder + "\\" + CreatedFileName;
FileInfo info = new FileInfo(file);
while (!IsFileReady(info)) { }
printFile(file, extension);
});
}
catch (Exception ex)
{
System.Windows.Forms.MessageBox.Show("Error");
}
}
Use a BackgroundWorker instead of Dispatcher.Invoke.

Execute multiple command lines with output

I want to perform some command lines to display the result after each input.
Process p = new Process();
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "cmd.exe";
info.RedirectStandardInput = true;
info.UseShellExecute = false;
p.StartInfo = info;
p.Start();
using (StreamWriter sw = p.StandardInput)
{
if (sw.BaseStream.CanWrite)
{
sw.WriteLine("ftp");
//output
sw.WriteLine("open ftp.server.com");
//output
sw.WriteLine("username");
//output
sw.WriteLine("password");
//output
}
}
Help me to understand how to make the output result after each sw.WriteLine(...)?
Updated
It is not working with ftp. Why?
Initialization:
Test test = new Test();
test.start();
Console.ReadKey();
Class Test:
class Test
{
static StringBuilder StdOutput = new StringBuilder();
Process p = null;
Queue<string> cmdQueue = new Queue<string>();
public void start(){
cmdQueue = new Queue<string>();
cmdQueue.Enqueue("cd c:\\");
cmdQueue.Enqueue("dir");
cmdQueue.Enqueue("ftp");
cmdQueue.Enqueue("open us1.hostedftp.com");
cmdQueue.Enqueue("z3r9#ya.ru");
cmdQueue.Enqueue("123456");
cmdQueue.Enqueue("dir");
setupProcess();
startProcess();
}
private void setupProcess()
{
p = new Process();
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "cmd";
info.CreateNoWindow = true;
info.RedirectStandardOutput = true;
info.RedirectStandardInput = true;
info.UseShellExecute = false;
p.OutputDataReceived += new DataReceivedEventHandler(OutputDataHandler);
StdOutput = new StringBuilder();
p.StartInfo = info;
}
private async void startProcess()
{
p.Start();
p.BeginOutputReadLine();
using (StreamWriter sw = p.StandardInput)
{
if (sw.BaseStream.CanWrite)
{
while (cmdQueue.Count > 0)
{
string cmd = cmdQueue.Dequeue();
if (cmd != null & cmd != "")
{
await sw.WriteLineAsync(cmd);
Thread.Sleep(100);
//System.Console.WriteLine(StdOutput);
}
else
{
break;
}
}
Console.WriteLine(StdOutput);
}
p.WaitForExit();
}
}
private static void OutputDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (!String.IsNullOrEmpty(outLine.Data))
{
StdOutput.Append(Environment.NewLine + outLine.Data);
//System.Console.WriteLine(Environment.NewLine + outLine.Data);
}
}
}
I assume that you are actually asking about how to catch outputs from all the commands you want to have executed in the (one) process.
Here is a version of a solution I came up with a long time ago, when I was a rookie here..
The trick is to collect the output as is comes along by listening to events the Process will trigger whenever output gets created: OutputDataReceived and ErrorDataReceived. We need to run things async for this to work, so it will look a little more complicated than the usual examples, which only have one process executing one command..:
First a few variables:
Queue<string> cmdQueue = new Queue<string>();
static StringBuilder StdOutput = new StringBuilder();
static StringBuilder ErrOutput = new StringBuilder();
Process p = null;
Task processTask = null;
bool processIsRunning = false;
Here is a button click event that starts processing all commands from a multiline TextBox. Output gets collected in the two StringBuilders; when the queue is empty, I wait a little longer..:
private void button1_Click(object sender, EventArgs e)
{
cmdQueue = new Queue<string>(tb_commands.Lines.ToList());
setupProcess();
startProcessTask();
while (cmdQueue.Count > 0) Thread.Sleep(100);
Thread.Sleep(500);
tb_out.AppendText(StdOutput + "\r\n" + ErrOutput + "\r\n");
}
Here is the routine that set up the Process. Here we register two events that will notify us when there are lines in the output streams..:
private void setupProcess()
{
p = new Process();
ProcessStartInfo info = new ProcessStartInfo();
info.FileName = "cmd.exe";
info.CreateNoWindow = true;
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
info.RedirectStandardInput = true;
info.UseShellExecute = false;
p.OutputDataReceived += new DataReceivedEventHandler(OutputDataHandler);
p.ErrorDataReceived += new DataReceivedEventHandler(ErrorDataHandler);
StdOutput = new StringBuilder();
ErrOutput = new StringBuilder();
p.StartInfo = info;
}
After the setup we can start a Task that will start our Process asynchonously..:
private void startProcessTask()
{
var task = Task.Factory.StartNew(() => startProcess());
processTask = task;
}
..and finally here is the async method that after starting the Process and beginning with the asynchronous read operations on the redirected streams, keeps feeding it all lines from the command queue.
private async void startProcess()
{
try { p.Start(); processIsRunning = true; } catch
{
ErrOutput.Append("\r\nError starting cmd process.");
processIsRunning = false;
}
p.BeginOutputReadLine();
p.BeginErrorReadLine();
using (StreamWriter sw = p.StandardInput)
{
if (sw.BaseStream.CanWrite)
do
{
try
{
string cmd = cmdQueue.Dequeue();
if (cmd != null & cmd != "") await sw.WriteLineAsync(cmd);
} catch { }
} while (processIsRunning);
try { p.WaitForExit(); } catch { ErrOutput.Append("WaitForExit Error.\r\n"); }
}
}
The last pieces are the two events we have registered for reading the output from the two streams and adding them to the StringBuilders:
private static void OutputDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (!String.IsNullOrEmpty(outLine.Data))
{
StdOutput.Append(Environment.NewLine + outLine.Data);
}
}
private static void ErrorDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if (!String.IsNullOrEmpty(outLine.Data))
{
ErrOutput.Append(Environment.NewLine + outLine.Data);
}
}
Note that this works fine for all sorts of commands you can feed into the process, including FTP. Here I change my codepage, show the images I have before, log in to an FTP server, call up the help page, cd and dir, download an image, close the connection and check the images I have now..:
One Caveat: There must be something wrong in the way I wrote this, as VS keeps complaining about a System.InvalidOperationException and the exe file hogs ~10% cpu. Helping me out would be very much appreciated..

Execute method on 3rd party exe from my own console application

If I where to have the executable: (executable is not a c# application it contains unmanaged code but the code is similar)
// ConsoleApplication1.exe
class Program
{
static void Main()
{
while (true)
{
System.Console.WriteLine("Enter command");
var input = System.Console.ReadLine();
if (input == "a")
SomeMethodA();
else if (input == "b")
SomeMethodB();
else if (input == "exit")
break;
else
System.Console.WriteLine("invalid command");
}
}
private static void SomeMethodA()
{
System.Console.WriteLine("Executing method A");
}
private static void SomeMethodB()
{
System.Console.WriteLine("Executing method B");
}
}
Then how could I execute SomeMethodA() from c#?
this is what I have worked out so far
Process p = new Process();
var procStartInfo = new ProcessStartInfo(#"ConsoleApplication1.exe")
{
UseShellExecute = false,
RedirectStandardOutput = true,
};
p.StartInfo = procStartInfo;
p.Start();
StreamReader standardOutput = p.StandardOutput;
var line = string.Empty;
while ((line = standardOutput.ReadLine()) != null)
{
Console.WriteLine(line);
// here If I send a then ENTER I will execute method A!
}
If you are just wanting to pass "a" so SomeMethodA executes you could do this
Process p = new Process();
var procStartInfo = new ProcessStartInfo(#"ConsoleApplication1.exe")
{
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardInput = true, //New Line
};
p.StartInfo = procStartInfo;
p.Start();
StreamReader standardOutput = p.StandardOutput;
StreamWriter standardInput = p.StandardInput; //New Line
var line = string.Empty;
//We must write "a" to the other program before we wait for a answer or we will be waiting forever.
standardInput.WriteLine("a"); //New Line
while ((line = standardOutput.ReadLine()) != null)
{
Console.WriteLine(line);
//You can replace "a" with `Console.ReadLine()` if you want to pass on the console input instead of sending "a" every time.
standardInput.WriteLine("a"); //New Line
}
If you are wanting to bypass the input process all together that is a much harder problem (if the method was not private it would be easier) and I will delete my answer.
P.S. You should wrap your two stream reader's in using statements
using (StreamReader standardOutput = p.StandardOutput)
using (StreamWriter standardInput = p.StandardInput)
{
var line = string.Empty;
standardInput.WriteLine("a");
while ((line = standardOutput.ReadLine()) != null)
{
Console.WriteLine(line);
standardInput.WriteLine("a");
}
}

standardoutput pauses loop at end of stream C#

I am reading the output of a java application started using Process and reading stdError, stdOutout and using stdInput to send commands. Here is the relevant code:
int mem = Properties.Settings.Default.mem_max;
string locale = Properties.Settings.Default.location;
Process bukkit_jva = new Process();
bukkit_jva.StartInfo.FileName = "java";
//bukkit_jva.StartInfo.Arguments = "-Xmx" + mem + "M -Xms" + mem + "M -jar " + locale + "bukkit.jar";
bukkit_jva.StartInfo.Arguments = "-Xmx512M -Xms512M -jar C:\\bukkit\\bukkit.jar";
bukkit_jva.StartInfo.UseShellExecute = false;
bukkit_jva.StartInfo.CreateNoWindow = true;
bukkit_jva.StartInfo.RedirectStandardError = true;
bukkit_jva.StartInfo.RedirectStandardOutput = true;
bukkit_jva.StartInfo.RedirectStandardInput = true;
bukkit_jva.Start();
//start reading output
SetText(bukkit_jva.StandardOutput.ReadLine());
SetText(bukkit_jva.StandardOutput.ReadLine());
SetText(bukkit_jva.StandardOutput.ReadLine());
SetText(bukkit_jva.StandardOutput.ReadLine());
StreamReader err = bukkit_jva.StandardError;
StreamReader output = bukkit_jva.StandardOutput;
StreamWriter writer = bukkit_jva.StandardInput;
SetText(err.Peek().ToString());
while (false == false)
{
if (vars.input != null)
{
writer.WriteLine(vars.input);
vars.input = null;
}
SetText(output.ReadLine() + err.ReadLine());
}
}
SetText() adds the line to a list of lines.
My problem is that the java app sometimes returns a string even when there is no input, so I always need to check for a new line. but If I need to send a command, and there is no new output, It will not send.
I tried different If statements on the readline, but it would only return the first few lines then it would stop.
basicly it seems to pause the loop if there is no new line for it to read.
How could I either setup my read/write loop differently or get the loop to unpause?
Thanks,
Adam
try this:
static void Main(string[] args)
{
ProcessStartInfo psi = new ProcessStartInfo("echoApp.exe");
psi.RedirectStandardInput = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardError = true;
psi.UseShellExecute = false;
Process echoApp = new Process();
echoApp.ErrorDataReceived += new DataReceivedEventHandler(echoApp_ErrorDataReceived);
echoApp.OutputDataReceived += new DataReceivedEventHandler(echoApp_OutputDataReceived);
echoApp.StartInfo = psi;
echoApp.Start();
echoApp.BeginOutputReadLine();
echoApp.BeginErrorReadLine();
echoApp.StandardInput.AutoFlush = true;
string str = "";
while (str != "end")
{
str = Console.ReadLine();
echoApp.StandardInput.WriteLine(str);
}
echoApp.CancelOutputRead();
echoApp.CancelErrorRead();
echoApp.Close();
}
static void echoApp_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
Console.WriteLine("stdout: {0}", e.Data);
}
static void echoApp_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
Console.WriteLine("stderr: {0}", e.Data);
}
and the little echoApp...
//echoApp
static void Main(string[] args)
{
string str="";
while (str != "end")
{
str = Console.ReadLine();
Console.WriteLine(str);
}
}
Please note if you try to access the output after CancelOutputRead() and CancelErrorRead() you may find that occasionally you are missing some text. I found the flush only occurs after explicitly calling Close(). Disposing (with a using statement) doesn't help.
This symptom will most likely occur when calling the command processor (CMD.EXE) because it does not explicitly flush itself. So be careful not to try and access the output (written from your event handlers) unless you explicitly call Close() first.

How To: Execute command line in C#, get STD OUT results

How do I execute a command-line program from C# and get back the STD OUT results? Specifically, I want to execute DIFF on two files that are programmatically selected and write the results to a text box.
// Start the child process.
Process p = new Process();
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "YOURBATCHFILE.bat";
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();
Code is from MSDN.
Here's a quick sample:
//Create process
System.Diagnostics.Process pProcess = new System.Diagnostics.Process();
//strCommand is path and file name of command to run
pProcess.StartInfo.FileName = strCommand;
//strCommandParameters are parameters to pass to program
pProcess.StartInfo.Arguments = strCommandParameters;
pProcess.StartInfo.UseShellExecute = false;
//Set output of program to be written to process output stream
pProcess.StartInfo.RedirectStandardOutput = true;
//Optional
pProcess.StartInfo.WorkingDirectory = strWorkingDirectory;
//Start the process
pProcess.Start();
//Get program output
string strOutput = pProcess.StandardOutput.ReadToEnd();
//Wait for process to finish
pProcess.WaitForExit();
There one other parameter I found useful, which I use to eliminate the process window
pProcess.StartInfo.CreateNoWindow = true;
this helps to hide the black console window from user completely, if that is what you desire.
// usage
const string ToolFileName = "example.exe";
string output = RunExternalExe(ToolFileName);
public string RunExternalExe(string filename, string arguments = null)
{
var process = new Process();
process.StartInfo.FileName = filename;
if (!string.IsNullOrEmpty(arguments))
{
process.StartInfo.Arguments = arguments;
}
process.StartInfo.CreateNoWindow = true;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardOutput = true;
var stdOutput = new StringBuilder();
process.OutputDataReceived += (sender, args) => stdOutput.AppendLine(args.Data); // Use AppendLine rather than Append since args.Data is one line of output, not including the newline character.
string stdError = null;
try
{
process.Start();
process.BeginOutputReadLine();
stdError = process.StandardError.ReadToEnd();
process.WaitForExit();
}
catch (Exception e)
{
throw new Exception("OS error while executing " + Format(filename, arguments)+ ": " + e.Message, e);
}
if (process.ExitCode == 0)
{
return stdOutput.ToString();
}
else
{
var message = new StringBuilder();
if (!string.IsNullOrEmpty(stdError))
{
message.AppendLine(stdError);
}
if (stdOutput.Length != 0)
{
message.AppendLine("Std output:");
message.AppendLine(stdOutput.ToString());
}
throw new Exception(Format(filename, arguments) + " finished with exit code = " + process.ExitCode + ": " + message);
}
}
private string Format(string filename, string arguments)
{
return "'" + filename +
((string.IsNullOrEmpty(arguments)) ? string.Empty : " " + arguments) +
"'";
}
The accepted answer on this page has a weakness that is troublesome in rare situations. There are two file handles which programs write to by convention, stdout, and stderr.
If you just read a single file handle such as the answer from Ray, and the program you are starting writes enough output to stderr, it will fill up the output stderr buffer and block. Then your two processes are deadlocked. The buffer size may be 4K.
This is extremely rare on short-lived programs, but if you have a long running program which repeatedly outputs to stderr, it will happen eventually. This is tricky to debug and track down.
There are a couple good ways to deal with this.
One way is to execute cmd.exe instead of your program and use the /c argument to cmd.exe to invoke your program along with the "2>&1" argument to cmd.exe to tell it to merge stdout and stderr.
var p = new Process();
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = "/c mycmd.exe 2>&1";
Another way is to use a programming model which reads both handles at the same time.
var p = new Process();
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = #"/c dir \windows";
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardInput = false;
p.OutputDataReceived += (a, b) => Console.WriteLine(b.Data);
p.ErrorDataReceived += (a, b) => Console.WriteLine(b.Data);
p.Start();
p.BeginErrorReadLine();
p.BeginOutputReadLine();
p.WaitForExit();
System.Diagnostics.ProcessStartInfo psi =
new System.Diagnostics.ProcessStartInfo(#"program_to_call.exe");
psi.RedirectStandardOutput = true;
psi.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
psi.UseShellExecute = false;
System.Diagnostics.Process proc = System.Diagnostics.Process.Start(psi); ////
System.IO.StreamReader myOutput = proc.StandardOutput;
proc.WaitForExit(2000);
if (proc.HasExited)
{
string output = myOutput.ReadToEnd();
}
You will need to use ProcessStartInfo with RedirectStandardOutput enabled - then you can read the output stream. You might find it easier to use ">" to redirect the output to a file (via the OS), and then simply read the file.
[edit: like what Ray did: +1]
One-liner run command:
new Process() { StartInfo = new ProcessStartInfo("echo", "Hello, World") }.Start();
Read output of command in shortest amount of reable code:
var cliProcess = new Process() {
StartInfo = new ProcessStartInfo("echo", "Hello, World") {
UseShellExecute = false,
RedirectStandardOutput = true
}
};
cliProcess.Start();
string cliOut = cliProcess.StandardOutput.ReadToEnd();
cliProcess.WaitForExit();
cliProcess.Close();
In case you also need to execute some command in the cmd.exe, you can do the following:
// Start the child process.
Process p = new Process();
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = "/C vol";
p.Start();
// Read the output stream first and then wait.
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
Console.WriteLine(output);
This returns just the output of the command itself:
You can also use StandardInput instead of StartInfo.Arguments:
// Start the child process.
Process p = new Process();
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "cmd.exe";
p.Start();
// Read the output stream first and then wait.
p.StandardInput.WriteLine("vol");
p.StandardInput.WriteLine("exit");
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
Console.WriteLine(output);
The result looks like this:
Since the most answers here dont implement the using statemant for IDisposable and some other stuff wich I think could be nessecary I will add this answer.
For C# 8.0
// Start a process with the filename or path with filename e.g. "cmd". Please note the
//using statemant
using myProcess.StartInfo.FileName = "cmd";
// add the arguments - Note add "/c" if you want to carry out tge argument in cmd and
// terminate
myProcess.StartInfo.Arguments = "/c dir";
// Allows to raise events
myProcess.EnableRaisingEvents = true;
//hosted by the application itself to not open a black cmd window
myProcess.StartInfo.UseShellExecute = false;
myProcess.StartInfo.CreateNoWindow = true;
// Eventhander for data
myProcess.Exited += OnOutputDataRecived;
// Eventhandler for error
myProcess.ErrorDataReceived += OnErrorDataReceived;
// Eventhandler wich fires when exited
myProcess.Exited += OnExited;
// Starts the process
myProcess.Start();
//read the output before you wait for exit
myProcess.BeginOutputReadLine();
// wait for the finish - this will block (leave this out if you dont want to wait for
// it, so it runs without blocking)
process.WaitForExit();
// Handle the dataevent
private void OnOutputDataRecived(object sender, DataReceivedEventArgs e)
{
//do something with your data
Trace.WriteLine(e.Data);
}
//Handle the error
private void OnErrorDataReceived(object sender, DataReceivedEventArgs e)
{
Trace.WriteLine(e.Data);
//do something with your exception
throw new Exception();
}
// Handle Exited event and display process information.
private void OnExited(object sender, System.EventArgs e)
{
Trace.WriteLine("Process exited");
}
Here is small example:
using System;
using System.Diagnostics;
class Program
{
static void Main(string[] args)
{
var p = Process.Start(
new ProcessStartInfo("git", "branch --show-current")
{
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
WorkingDirectory = Environment.CurrentDirectory
}
);
p.WaitForExit();
string branchName =p.StandardOutput.ReadToEnd().TrimEnd();
string errorInfoIfAny =p.StandardError.ReadToEnd().TrimEnd();
if (errorInfoIfAny.Length != 0)
{
Console.WriteLine($"error: {errorInfoIfAny}");
}
else {
Console.WriteLine($"branch: {branchName}");
}
}
}
I believe this is shortest form.
Please notice that most of command line tools easily confuse standard output and standard error, sometimes it makes sense just to clue those together into single string.
Also p.ExitCode might be sometimes useful.
Example above serves for purpose of writing command line utility like tools if you want to do it by yourself. Please note that for cli automation it's also possible to use Cake Frosten and Cake Git extension.
You can launch any command line program using the Process class, and set the StandardOutput property of the Process instance with a stream reader you create (either based on a string or a memory location). After the process completes, you can then do whatever diff you need to on that stream.
This might be useful for someone if your attempting to query the local ARP cache on a PC/Server.
List<string[]> results = new List<string[]>();
using (Process p = new Process())
{
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.Arguments = "/c arp -a";
p.StartInfo.FileName = #"C:\Windows\System32\cmd.exe";
p.Start();
string line;
while ((line = p.StandardOutput.ReadLine()) != null)
{
if (line != "" && !line.Contains("Interface") && !line.Contains("Physical Address"))
{
var lineArr = line.Trim().Split(' ').Select(n => n).Where(n => !string.IsNullOrEmpty(n)).ToArray();
var arrResult = new string[]
{
lineArr[0],
lineArr[1],
lineArr[2]
};
results.Add(arrResult);
}
}
p.WaitForExit();
}
This may not be the best/easiest way, but may be an option:
When you execute from your code, add " > output.txt" and then read in the output.txt file.
There is a ProcessHelper Class in PublicDomain open source code which might interest you.
Julian's solution is tested working with some minor corrections. The following is an example that also used https://sourceforge.net/projects/bat-to-exe/ GenericConsole.cs and https://www.codeproject.com/Articles/19225/Bat-file-compiler program.txt for args part:
using System;
using System.Text; //StringBuilder
using System.Diagnostics;
using System.IO;
class Program
{
private static bool redirectStandardOutput = true;
private static string buildargument(string[] args)
{
StringBuilder arg = new StringBuilder();
for (int i = 0; i < args.Length; i++)
{
arg.Append("\"" + args[i] + "\" ");
}
return arg.ToString();
}
static void Main(string[] args)
{
Process prc = new Process();
prc.StartInfo = //new ProcessStartInfo("cmd.exe", String.Format("/c \"\"{0}\" {1}", Path.Combine(Environment.CurrentDirectory, "mapTargetIDToTargetNameA3.bat"), buildargument(args)));
//new ProcessStartInfo(Path.Combine(Environment.CurrentDirectory, "mapTargetIDToTargetNameA3.bat"), buildargument(args));
new ProcessStartInfo("mapTargetIDToTargetNameA3.bat");
prc.StartInfo.Arguments = buildargument(args);
prc.EnableRaisingEvents = true;
if (redirectStandardOutput == true)
{
prc.StartInfo.UseShellExecute = false;
}
else
{
prc.StartInfo.UseShellExecute = true;
}
prc.StartInfo.CreateNoWindow = true;
prc.OutputDataReceived += OnOutputDataRecived;
prc.ErrorDataReceived += OnErrorDataReceived;
//prc.Exited += OnExited;
prc.StartInfo.RedirectStandardOutput = redirectStandardOutput;
prc.StartInfo.RedirectStandardError = redirectStandardOutput;
try
{
prc.Start();
prc.BeginOutputReadLine();
prc.BeginErrorReadLine();
prc.WaitForExit();
}
catch (Exception e)
{
Console.WriteLine("OS error: " + e.Message);
}
prc.Close();
}
// Handle the dataevent
private static void OnOutputDataRecived(object sender, DataReceivedEventArgs e)
{
//do something with your data
Console.WriteLine(e.Data);
}
//Handle the error
private static void OnErrorDataReceived(object sender, DataReceivedEventArgs e)
{
Console.WriteLine(e.Data);
}
// Handle Exited event and display process information.
//private static void OnExited(object sender, System.EventArgs e)
//{
// var process = sender as Process;
// if (process != null)
// {
// Console.WriteLine("ExitCode: " + process.ExitCode);
// }
// else
// {
// Console.WriteLine("Process exited");
// }
//}
}
The code need to compile inside VS2007, using commandline csc.exe generated executable will not show console output correctly, or even crash with CLR20r3 error. Comment out the OnExited event process, the console output of the bat to exe will be more like the original bat console output.
Just for fun, here's my completed solution for getting PYTHON output - under a button click - with error reporting. Just add a button called "butPython" and a label called "llHello"...
private void butPython(object sender, EventArgs e)
{
llHello.Text = "Calling Python...";
this.Refresh();
Tuple<String,String> python = GoPython(#"C:\Users\BLAH\Desktop\Code\Python\BLAH.py");
llHello.Text = python.Item1; // Show result.
if (python.Item2.Length > 0) MessageBox.Show("Sorry, there was an error:" + Environment.NewLine + python.Item2);
}
public Tuple<String,String> GoPython(string pythonFile, string moreArgs = "")
{
ProcessStartInfo PSI = new ProcessStartInfo();
PSI.FileName = "py.exe";
PSI.Arguments = string.Format("\"{0}\" {1}", pythonFile, moreArgs);
PSI.CreateNoWindow = true;
PSI.UseShellExecute = false;
PSI.RedirectStandardError = true;
PSI.RedirectStandardOutput = true;
using (Process process = Process.Start(PSI))
using (StreamReader reader = process.StandardOutput)
{
string stderr = process.StandardError.ReadToEnd(); // Error(s)!!
string result = reader.ReadToEnd(); // What we want.
return new Tuple<String,String> (result,stderr);
}
}

Categories