StandardOutput blocks when asked for user input - c#

I'm currently building a simple application, and it starts openvpn.exe. However, openvpn.exe asks for an username and password.
But, when this happens, my program does not read the string, it simply waits until the CMD is closed and then continues on with the code. So, it blocks until the window is closed.
Is there any way to circumvent this? My code is as follows:
void button_Connect_Click(object sender, EventArgs e)
{
var proc = new Process();
proc.StartInfo.FileName = #"C:\Program Files (x86)\OpenVPN\bin\openvpn.exe";
proc.StartInfo.Arguments = "--config config.ovpn --auto-proxy";
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.WorkingDirectory = #"C:\Program Files (x86)\OpenVPN\bin";
// set up output redirection
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardError = true;
// Input
proc.StartInfo.RedirectStandardInput = true;
// Other
proc.EnableRaisingEvents = true;
proc.StartInfo.CreateNoWindow = false;
// see below for output handler
proc.ErrorDataReceived += proc_DataReceived;
proc.OutputDataReceived += proc_DataReceived;
proc.Start();
myStreamWriter = proc.StandardInput;
proc.BeginErrorReadLine();
proc.BeginOutputReadLine();
proc.WaitForExit();
}
void proc_DataReceived(object sender, DataReceivedEventArgs e)
{
// output will be in string e.Data
if (e.Data != null)
{
string Data = e.Data.ToString();
if (Data.Contains("Enter Auth Username"))
{
this.myStreamWriter.WriteLine("myinput");
}
}
}

The proc_DataReceived is triggered on new line. Assuming the password is the only input that console app is asking for, you can just send it to your myStreamWriter right after starting the process. It will be buffered and consumed when necessary.

Related

.NET (Windows Forms) : Update Textbox with output from external script

I want to create a small C#-program which has multiple buttons which execute a Powershell script and put the results asynchronously into a textbox. I wanted to do it the following way:
private void Button_Click(object sender, RoutedEventArgs e)
{
var ps1File = #"SomeScript.ps1";
Process proc = new System.Diagnostics.Process();
proc.StartInfo.FileName = "powershell.exe";
proc.StartInfo.Arguments = $"-NoProfile -ExecutionPolicy unrestricted \"{ps1File}\"";
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.CreateNoWindow = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardError = true;
var err = "";
tbConsole.Text = "Loading printers ...";
proc.OutputDataReceived += (o, e2) =>
{
if (e2.Data == null) err = e2.Data;
else
{
if (e2.Data == null) err = e2.Data;
else tbConsoleError.AppendText(e2.Data);
}
};
proc.ErrorDataReceived += (o, e2) =>
{
if (e2.Data == null) err = e2.Data;
else tbConsoleError.AppendText(e2.Data);
};
proc.Start();
// and start asynchronous read
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
// wait until it's finished in your background worker thread
proc.WaitForExit();
tbConsole.AppendText("... finished");
}
Now when I hit the button the sript runs but while editing the textboxes it gives me an error that I cannot edit the textbox as it is managed by a different thread.
When I now try to edit my code to use "invoke" and delegate I find that my MainWindow (objet-reference: "this") does not have an "invoke"-method. What am I doing wrong?

How to get the the result of batch file to C#

I am currently struggling with bringing existing data in batch file to C# window form
The whole objective is getting a result lively from batch file to C# rich text box but I am keep getting failure of doing it.
The procedure works like click button->run batch file secretly->C# gets data lively->display in rich text box
I was successful to run a batch file but it runs in another new CMD causing hanging problem during debugging.
I would like to know whether anyone can wrote me a code to overcome such problem. Hope for the best answer
ProcessStartInfo cmd = new ProcessStartInfo();
Process process = new Process();
cmd.FileName = #"cmd";
cmd.UseShellExecute = false;
cmd.RedirectStandardError = true;
cmd.RedirectStandardInput = true;
cmd.RedirectStandardOutput = true;
cmd.CreateNoWindow = true;
process.EnableRaisingEvents = false;
process.StartInfo = cmd;
process.Start();
process.StandardInput.Write(#"cd C:\Users\%username%\Desktop\Claymore's Dual Ethereum+Decred_Siacoin_Lbry_Pascal AMD+NVIDIA GPU Miner v11.0" + Environment.NewLine);
process.StandardInput.Write(#"EthDcrMiner64.exe -allpools 1 -epool asia1.ethereum.miningpoolhub.com:20535 -ewal AJStudio.AJStudio001 -epsw x -esm 2" + Environment.NewLine);
process.StandardInput.Close();
string result = process.StandardOutput.ReadToEnd();
StringBuilder sb = new StringBuilder();
sb.Append("[result info]" + DateTime.Now + "\r\n");
sb.Append(result);
sb.Append("\r\n");
richTextBox1.Text = sb.ToString();
process.WaitForExit();
process.Close();
To get real-time feedback from Process, use OutputDataReceived and ErrorDataReceived events. Something like this:
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.OutputDataReceived += Process_OutputDataReceived;
process.ErrorDataReceived += Process_ErrorDataReceived;
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
private void Process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data == null) return;
log("ERROR: " + e.Data);
}
private void Process_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data == null) return;
log(e.Data);
}

Trying to capture console output of python program in c#

I am trying to retrieve console output of a python script by running that script in c# console application. but I don't know what I am doing wrong as I am not able to read any output. Please help me.
This is my code sample:
Process proc = new Process();
try
{
proc.StartInfo.FileName = "C:\\Program Files\\Python36\\python.exe";
proc.StartInfo.Arguments = "\"E:/Database/Python Scripts/TestFile.py\" \"E:/Database/Testing.db\"";
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.CreateNoWindow = true;
proc.EnableRaisingEvents = true;
proc.Start();
while (!proc.StandardOutput.EndOfStream)
{
string line = proc.StandardOutput.ReadLine();
Console.WriteLine("line:" + line);
}
Console.WriteLine("The End");
Console.ReadLine();
}
catch(Exception ex)
{
Console.WriteLine("Error:{0}, Detail: {1}", ex.Message, ex.StackTrace);
Console.ReadLine();
}
finally
{
//Console.ReadLine();
proc.Close();
proc.Dispose();
}
First you need to set proc.StartInfo.RedirectStandardOutput = false; to true
then you need to listen for the output event
proc.StartInfo.FileName = "C:\\Program Files\\Python36\\python.exe";
proc.StartInfo.Arguments = "\"E:/Database/Python Scripts/TestFile.py\" \"E:/Database/Testing.db\"";
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.CreateNoWindow = true;
proc.EnableRaisingEvents = true;
proc.OutputDataReceived+= ProcessOutputHandler;
proc.Start();
proc.BeginErrorReadLine();
proc.BeginOutputReadLine();
private void ProcessOutputHandler(object sender, DataReceivedEventArgs e)
{
//get the data using e.Data
}
You can do the same thing for the ErrorOutput Since they are seperate streams you can get. To listen for error just add an event listener for proc.ErrorDataReceived+= ProcessErrorOutputHandler;

C# Async StandardInput Redirection

I am creating WinForm application which starts a process with redirection. I am using Async method:
proc = new Process();
proc.StartInfo.FileName = commandLocation + procesName;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.Arguments = arguments;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardInput = true;
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.WorkingDirectory = workingDirectory;
proc.StartInfo.CreateNoWindow = true;
proc.OutputDataReceived += (sender, args) => AppendLine(args.Data);
proc.ErrorDataReceived += (sender, args) => AppendLine(args.Data);
proc.Start();
proc.StandardInput.WriteLine(inputText);
proc.BeginOutputReadLine();
while (!proc.HasExited) //Instead of WaitForExit
{
Thread.Sleep(100); //Because of CPU usage
Application.DoEvents();
}
Here is AppendLine method for displaying output to richtextbox:
private void AppendLine(string line)
{
if (richTextBoxOutput.InvokeRequired)
{
Action act = () =>
{
this.richTextBoxOutput.AppendText(line + Environment.NewLine);
};
this.BeginInvoke(act);
}
else
{
richTextBoxOutput.AppendText(line + Environment.NewLine);
}
}
Everything works well. But when I run a console application that needs input (for example Console.ReadLine()), my application crashes link (not responding). How can I send input to the redirected console application throughout its run?

In C# How do you read from DOS program you executed if the program is constantly outputting?

I have found many examples of coding on how to execute cmd.exe and execute a command, and execute even nslookup and interact, but the problem I am having is with a particular dos program that when it starts, it does not stop "outputting". here is some code and I will put a comment and the errors I get from C#
Here is how I have it setup in a more advanced way so I can receive output from the program on events
public void StartApplication(string appNameAndPath)
{
StreamReader outputStream;
Process p = new Process();
p.StartInfo.FileName = appNameAndPath;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = false;//for now just so I can see it
p.Start();
//here is my advanced example
if(advanced == true)
{
outputStream = p.StandardOutput;
DoReadOutPut();
}
else
{//here is a simple example
while (p.StandardOutput.ReadLine() != null) //this hangs here until the application exists
{
txt += (p.StandardOutput.ReadLine());
}
}
}
void DoReadOutput()
{
outputStream.BaseStream.BeginRead( readOutputBuffer, 0, readOutputBuffer.Length, new AsyncCallback( OnReadOutputCompleted ), null );
//this does sometimes fire but only with 0 bytes, on other dos programs it would say Memory read not allowed
}
void OnReadOutputCompleted( IAsyncResult result )
{
int cbRead = outputStream.BaseStream.EndRead( result );
ProcessOutput( readOutputBuffer, cbRead );
DoReadOutput();
}
private void ProcessOutput(byte[] buffer, int cbRead)
{
string text = p.StartInfo.StandardOutputEncoding.GetString(buffer, 0, 10000); //this is where it hangs until the program exits or is not writing anymore
this.Invoke((Action)delegate
{
SetTextBoxValue(text);//im doing this because im on another thread otherwise textBox1.Text - text"
});
}
I do not want to have to use API and GetText and create an engine to ReadLastLine, can anyone help me with this? I suppose you would want an example exe, creating a C# application that while(true){Console.WriteLine("bla");} would suffice as the example exe but not the exe I am having trouble with. The exe takes over the dos window and has an "old school interface"
async/await can help here....
await Exec(yourExe,parameters);
Task Exec(string exe,string args)
{
var tcs = new TaskCompletionSource<object>();
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = exe;
psi.UseShellExecute = false;
psi.RedirectStandardOutput = true;
psi.Arguments = args;
var proc = Process.Start(psi);
proc.OutputDataReceived += (s, e) =>
{
this.Invoke((Action) (()=>richTextBox1.AppendText(e.Data + Environment.NewLine)));
};
proc.Exited += (s, e) => tcs.SetResult(null);
proc.EnableRaisingEvents = true;
proc.BeginOutputReadLine();
return tcs.Task;
}
You need to handle callback events to read streams:
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
Process proc = new Process();
proc.StartInfo = startInfo;
proc.ErrorDataReceived += new DataReceivedEventHandler(DataReceiveHandler);
proc.OutputDataReceived += new DataReceivedEventHandler(DataReceiveHandler);
proc.Start();
proc.BeginErrorReadLine();
proc.BeginOutputReadLine();
proc.WaitForExit();
Code borrowed from this post

Categories

Resources