I want to create simple Powershell-based Forms application which:
- start CMD application (for eg ping) and wait for exit
- redirect output of it in asynchronous way
- update the TextBox.Text dynamically when CMD process send new output ( ping google.com -t )
- if the user input is required (wait for specific key), allow for it
- don't block UI
Basics:
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.RedirectStandardInput = $true
$pinfo.UseShellExecute = $false
$pinfo.CreateNoWindow = $true
Standard methods of assign $process.StandardOutput /reader.ReadToEnd() to TextBox.Text won't work because they just wait for process to finish and big amount of text is send to TextBox.Text when process has exited. That solution prevents user from interaction with onscreen messages. I found solution but it works for c# console app:
Code: Select all
using System;
using System.Diagnostics;
namespace ConsoleApp1 {
internal class Program {
public static void Main() {
const string exe = @"ping.exe";
const string arg = "google.com -t";
var psi = new ProcessStartInfo {
UseShellExecute = false,
RedirectStandardError = true,
RedirectStandardOutput = true,
RedirectStandardInput = true,
WindowStyle = ProcessWindowStyle.Hidden,
CreateNoWindow = true,
FileName = exe,
Arguments = string.Join( " ", arg )
};
//using (var process = Process.Start(psi))
//{
var process = Process.Start(psi);
var err = "";
process.OutputDataReceived += (o, e) =>
{
if (e.Data == null) err = e.Data;
else Console.WriteLine(e.Data);
};
process.BeginOutputReadLine();
process.ErrorDataReceived += (o, e) =>
{
if (e.Data == null) err = e.Data;
else Console.WriteLine(e.Data);
};
process.BeginErrorReadLine();
process.WaitForExit();
Console.ReadKey();
}
}
}
But now, I would like to make it work for Powershell-based Forms. Fist step would be to convert OutputDataReceived => e.Data to Powershell then put the rest of the code to $button1_Click:
Code: Select all
$button1_Click = {
#TODO: Place custom script here
$pinfo = New-Object -TypeName System.Diagnostics.ProcessStartInfo
$pinfo.FileName = "ping.exe"
$pinfo.RedirectStandardError = $true
$pinfo.RedirectStandardOutput = $true
$pinfo.RedirectStandardInput = $true
$pinfo.UseShellExecute = $false
$pinfo.CreateNoWindow = $true
$pinfo.Arguments = "google.com"
$process = New-Object System.Diagnostics.Process
$process.StartInfo = $pinfo
#Setup Out Listener
$outEvent = Register-ObjectEvent -InputObj $process -Event "OutputDataReceived" -Action {
param ([System.Object]$sender, [System.Diagnostics.DataReceivedEventHandler]$e)
if (! [String]::IsNullOrEmpty($EventArgs.Data)) { $Event.MessageData.AppendLine($EventArgs.Data) }
if ($e.Data -ne $null) {
Write-Host $EventArgs.Data + $e.Data
$textbox1.Text += $EventArgs.Data + $e.Data
}
}
$process.Start() | Out-Null
$process.BeginOutputReadLine();
$process.WaitForExit()
}
Couple of questions:
- does using "BeginOutputReadLine()" is even possible since Powershell is STA/Console App/ModalForm ?
- should I try to use JobTracker ? I can't get async output from the backgroundJob object, any tips? AFAIK, BckgroundJobs can't be used for interactive process :/
- how about separate runspace for process and capture output? It might be overkill but if it do the job, it's fine.
Any kind of feedback is welcome.