i have a method that creates a Process calling a console app.
double myProcess()
{
double results;
Process process = new Process();
process.EnableRaisingEvents = true;
process.OutputDataReceived += new DataReceivedEventHandler(process_OutputDataReceived);
process.StartInfo.FileName = filename;
process.StartInfo.Arguments = argument;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.CreateNoWindow = true;
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
return results;
}
static void process_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
string stringResults = (string)e.Data;
.
.
do some work on stringResults...
.
.
results = stringResults;
}
my problem is,how do i send data from process_OutputDataReceived back to myProcess? I cannot use singleton as there are possibilities that this process will be executed in multiple thread.
You don't need a separate method for the OutputDataReceived handler; you can use an anonymous method to set the results variable directly:
process.OutputDataReceived += (sender, e) => results = e.Data;
(Also, should results be string or double?)
Edit: A couple of alternatives for when you need to do more work in the handler:
process.OutputDataReceived +=
(sender, e) =>
{
string stringResults = e.Data;
// do some work on stringResults...
results = stringResults;
}
// or
process.OutputDataReceived +=
delegate(object sender, DataReceivedEventArgs e)
{
string stringResults = e.Data;
// do some work on stringResults...
results = stringResults;
}
Related
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);
}
I have a crash in Event Viewer with this:
Exception Info: System.ArgumentOutOfRangeException
at System.Text.StringBuilder.ToString()
at XXX.BrainActions+<>c__DisplayClass11_0.<runApp>b__0(System.Object,
System.EventArgs)
The offending code is this, why is StringBuilder causing this exception?
Process process = new Process();
StringBuilder builder = new StringBuilder();
try
{
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.FileName = filename;
process.StartInfo.Arguments = arguments;
process.StartInfo.WorkingDirectory = Themes.getInstance().getScriptPath();
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.EnableRaisingEvents = true;
process.Exited += new EventHandler((object sender, System.EventArgs e) => {
string consoleOutput = builder.ToString();
if (!String.IsNullOrWhiteSpace(consoleOutput))
{
Log.INFO("XXX", String.Format("Console output: {0}", builder.ToString()));
}
processesRunning.Remove(process);
if (callbacks != null)
{
callbacks.ActionCompleted(action);
if (processesRunning.Count == 0)
{
callbacks.AllActionsCompleted();
}
}
});
process.Start();
process.PriorityClass = ProcessPriorityClass.High;
process.OutputDataReceived += (sender, args) => builder.Append(args.Data);
process.ErrorDataReceived += (sender, args) => builder.Append(args.Data);
process.BeginOutputReadLine();
process.BeginErrorReadLine();
processesRunning.Add(process);
}
catch (Exception e)
{
}
StringBuilder is used to basically get the output of the external process and log it to the console using Log4net which is basically wrapped in the Log() function. Any ideas? Note that this crash rarely happens I've only seen it once but once is a reason to fix it.
I created a GUI that calls a batch file and shows the command line output in a textbox after clicking a button. The batch file runs and the output is redirected properly for some time until ~80 lines in and the textbox output suddenly starts displaying the actual code in the batch file. Does this indicate a bug in the batch file or a problem with my script? I don't really know how to start debugging this problem.
I also notice that the batch file I'm calling from the GUI makes calls to other batch files. Could this be causing problems as well?
I should also mention that the batch file successfully runs from the command line.
private void buildProg_Click(object sender, EventArgs e)
{
using (Process process = new Process())
{
process.StartInfo.WorkingDirectory = some_directory;
process.StartInfo.FileName = "start.bat";
process.StartInfo.Arguments = "arg1 arg2";
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.OutputDataReceived += proc_OutputDataReceived;
process.EnableRaisingEvents = true;
process.Start();
process.BeginOutputReadLine();
}
}
private void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
this.Invoke((Action)(() =>
{
textBox1.AppendText(Environment.NewLine + e.Data);
}));
}
When I run it from the GUI the batch file seems to be tripping up at this part.
if exist %version_file_path% (
set /p _version= <%version_file_path%
) else (
echo %version_file_path% not found
pause
)
There's two things happening here;
1) The /p statement is asking for user input, pausing the command and waiting.
2) You're disposing the Process before the events are firing, meaning we wouldn't be able to simulate the input it needs to continue executing.
This pretty much solves it:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.FormClosing += Form1_FormClosing;
}
Process p;
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
p?.Dispose();
}
private void button1_Click(object sender, EventArgs e)
{
if (p != null)
p.Dispose();
p = new Process();
p.StartInfo.WorkingDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
p.StartInfo.FileName = "test.bat";
p.StartInfo.Arguments = "";
p.StartInfo.CreateNoWindow = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardInput = true;
p.OutputDataReceived += proc_OutputDataReceived;
p.Start();
p.BeginOutputReadLine();
}
private void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
this.Invoke((Action)(() =>
{
textBox1.AppendText(Environment.NewLine + e.Data);
}));
//can use either of these lines.
(sender as Process)?.StandardInput.WriteLine();
//p.StandardInput.WriteLine();
}
}
I am using iperf-2.0.5-2-win32 tool to find network bandwidth. I have written codes in c# which opens the cmd prompt, pass iperf parameters to start server side & client side. iperf-2.0.5-2-win32 exe will not open directly, need to open through cmd prompt only.
At present the output(Transfer rate & Bandwidth) is displaying on cmd prompt itself. I want these output to be displayed in textbox
I have tried StreamReader also. But it takes null, I have also tried OutputDataReceived Event, its also taking null.
Found few codes for ipconfig & ping.but those were not working with iperf codes.
button_click event(),
{
Process Client_proc = new Process();
ProcessStartInfo Client_command = new ProcessStartInfo("cmd.exe");
string ip = txtIP.Text;
Client_command.CreateNoWindow = true;
Client_command.WindowStyle = ProcessWindowStyle.Hidden;
Client_command.WorkingDirectory = #"E:\Iperf\RunEXE_Through_Application\iperf-2.0.5-2-win32";
Client_command.Arguments = "/c START iperf -c " + ip;
Client_proc.StartInfo = Client_command;
Client_command.RedirectStandardOutput = true;
Client_command.UseShellExecute = false;
Client_proc.OutputDataReceived += new DataReceivedEventHandler(Client_proc_OutputDataReceived);
Client_proc.Start();
Client_proc.BeginOutputReadLine();
Client_proc.WaitForExit();
}
void Client_proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
string newLine = e.Data.Trim() + Environment.NewLine;
MethodInvoker append = () => txtOutput.Text += newLine;
txtOutput.BeginInvoke(append);
}
}
Plz help me.Earlier responses are appreciated
Thanks
you use this complete code for your disposal
It is not perfect (some problems when using multiple streams )
public void RunProcess(string FileName, string Arguments, bool EventWhenExit )
{
process = new Process();
process.EnableRaisingEvents = true;
process.OutputDataReceived += new DataReceivedEventHandler(OnDataReceivedEvent);
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.LoadUserProfile = false;
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = FileName; // Gets or sets the application or document to start.
process.StartInfo.Arguments = Arguments;//Gets or sets the set of command-line arguments to use when starting the application
Thread.Sleep(1000);
if (EventWhenExit)
{
process.EnableRaisingEvents = true;
process.Exited += new EventHandler(myprocess_Exited);/*New line */
}
process.Start();
process.BeginOutputReadLine();
PID = process.Id;
}
private void myprocess_Exited(object sender, EventArgs e)
{
process.Refresh();
Thread.Sleep(2000);
onProcessEnd(this, "ENDOF " + Proc.ToString());
Console.WriteLine("Process exsiting ");
}
private void OnDataReceivedEvent(object sender, DataReceivedEventArgs e)
{
string OutputFromProcess = e.Data;
//fire event to event handler class for further use
onDataOutputFromProcess(this, OutputFromProcess, Proc.ToString());
}
than in your GUI layer you should bind to onDataOutputFromProcess event
there you should have something like
if (screenToPrint.InvokeRequired) //&& this.Visible)
{
try
{
this.Invoke(new Action<AppendToScreenParam>(AppendTextFullConfig), new object[] { append });
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
return;
}
else
{
screenToPrint.SelectionFont = font;
screenToPrint.SelectionColor = append.Color;
//screenToPrint.AppendText(append.Message);
string TextToPrint = string.Format("{0}\n", append.Message);
screenToPrint.AppendText(TextToPrint);
}
}
Maybe it is because iperf process is returning error. Subscribe the ErrorDataReceived event with Client_proc.ErrorDataReceived += Client_proc_ErrorDataReceived; and see the results. If command returns error, you can see the error message as output.
void Client_proc_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
this.txtOutput.BeginInvoke(new MethodInvoker(() => { this.txtOutput.Text = e.Data; }));
}
}
Below is my code so far, I am having an issue when I call Dispatcher.BeginInvoke, it does not process these messages at the correct time
Class Script:
public void Execute()
{
var process = new Process();
var startinfo = new ProcessStartInfo("cmd.exe", #"/C c:\test\my.bat");
startinfo.WorkingDirectory = "c:\\test";
startinfo.RedirectStandardOutput = true;
startinfo.RedirectStandardError = true;
startinfo.UseShellExecute = false;
startinfo.CreateNoWindow = true;
process.EnableRaisingEvents = true;
process.StartInfo = startinfo;
process.OutputDataReceived += (sender, args) => OutputDataReceived(args.Data);
process.ErrorDataReceived += (sender, args) => ErrorDataReceived(args.Data);
process.Exited += Exited;
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
process.WaitForExit();
int exitCode = process.ExitCode;
}
public void OutputDataReceived(string data)
{
Logging.Logger.Log("data received in script - " + data);
// throw event if we have a subscriber, else just return
if (OnScriptOutPut == null) return;
allFormattedOutPut += Environment.NewLine;
allFormattedOutPut += data;
allRawOutPut += data;
ScriptOutputEventArgs args = new ScriptOutputEventArgs(data);
OnScriptOutPut(this, args);
}
WPF Window calls the script class and subscribes to OnScriptOutPut event
The problem is below, UpdateOutPutTextBox only gets called after the script is finished, then all the updateoutputtextbox messages are processed all at once, they do not get processed when the begininvoke is called causing the screen to get updated at the end instead of when new output data is received.. Any help is appreciated!
private void btnRunScript_Click(object sender, RoutedEventArgs e)
{
Script script = new Script();
script.OnScriptOutPut += script_OnScriptOutPut;
script.Execute();
}
private void script_OnScriptOutPut(object sender, ScriptOutputEventArgs args)
{
Application.Current.Dispatcher.BeginInvoke(new Action(() => UpdateOutPutTextBox(args.Data)),System.Windows.Threading.DispatcherPriority.Send);
Logging.Logger.Log("data received in event ");
}
private void UpdateOutPutTextBox(string data)
{
Logging.Logger.Log("data received in update "+data);
tbOutput.Text += Environment.NewLine;
tbOutput.Text += data;
}
You are calling Execute on the UI thread and blocking the thread with WaitForExit. Then all of the BeginInvoke actions are getting queued up. Remove the call to WaitForExit. If you need to do something with the exit code, get the value in the Exited event handler.
I can not go through the whole code out there,
But looking into your query,
Dispatcher.BeginInvoke
BeginInvoke -> is like calling async , and async operations may take time depending on the conditions, use Invoke instead if you can, your code is alot ! reduse it if possible!