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?
Related
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?
Im running an exe through process in my c# program, i want the process to be completely invisible without the console of it popping up.
This is my code
Process process2 = new Process();
process2.StartInfo.FileName = "cmd.exe";
process2.StartInfo.UseShellExecute = false;
process2.StartInfo.CreateNoWindow = true;
process2.StartInfo.RedirectStandardOutput = true;
process2.StartInfo.RedirectStandardError = true;
process2.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process2 = Process.Start(path3);
even with this code the console window still opens and shows, any help will be appreciated :)
Using
UseShellExecute = false; CreateNoWindow = true;
Should hide the process although it depends on the path in which you are opening, if the process has a force show
Try the following:
private void RunCmd(string exePath, string arguments = null)
{
//create new instance
ProcessStartInfo startInfo = new ProcessStartInfo(exePath, arguments);
startInfo.Arguments = arguments; //arguments
startInfo.CreateNoWindow = true; //don't create a window
startInfo.RedirectStandardError = true; //redirect standard error
startInfo.RedirectStandardOutput = true; //redirect standard output
startInfo.RedirectStandardInput = false;
startInfo.UseShellExecute = false; //if true, uses 'ShellExecute'; if false, uses 'CreateProcess'
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.ErrorDialog = false;
//create new instance
using (Process p = new Process { StartInfo = startInfo, EnableRaisingEvents = true })
{
//subscribe to event and add event handler code
p.ErrorDataReceived += (sender, e) =>
{
if (!String.IsNullOrEmpty(e.Data))
{
//ToDo: add desired code
Debug.WriteLine("Error: " + e.Data);
}
};
//subscribe to event and add event handler code
p.OutputDataReceived += (sender, e) =>
{
if (!String.IsNullOrEmpty(e.Data))
{
//ToDo: add desired code
Debug.WriteLine("Output: " + e.Data);
}
};
p.Start(); //start
p.BeginErrorReadLine(); //begin async reading for standard error
p.BeginOutputReadLine(); //begin async reading for standard output
//waits until the process is finished before continuing
p.WaitForExit();
}
}
See also this post.
I fixed it by renaming the window instead ty for all your help
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;
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
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.