■No34642に返信(ゆきさんの記事)
> 調べたところデリゲート処理が必要らしいのですがどのように改定いうかわかりません。
たとえばこんな感じ。
Public Class Form1
Private WithEvents proc As Process
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
If Me.components Is Nothing Then
Me.components = New System.ComponentModel.Container()
End If
Button1.Text = "開始"
Button2.Text = "標準入力を渡す"
Button2.Enabled = False
'コマンド
TextBox1.Text = Environment.GetEnvironmentVariable("ComSpec")
'引数
TextBox2.Text = ""
'標準入力の内容
TextBox3.Text = "DIR /D ""C:\Program Files (x86)\"""
'標準出力 & 標準エラー出力
RichTextBox1.ReadOnly = True
RichTextBox1.BackColor = Color.Black
RichTextBox1.ForeColor = Color.White
RichTextBox1.ScrollBars = RichTextBoxScrollBars.Both
RichTextBox1.WordWrap = False
RichTextBox1.HideSelection = False
End Sub
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Button1.Enabled = False
TextBox1.ReadOnly = True
TextBox2.ReadOnly = True
RichTextBox1.Clear()
Button2.Enabled = True
proc = Process.Start(New ProcessStartInfo(TextBox1.Text, TextBox2.Text) With
{
.UseShellExecute = False,
.CreateNoWindow = True,
.RedirectStandardInput = True,
.RedirectStandardOutput = True,
.RedirectStandardError = True
})
Me.components.Add(proc)
proc.BeginOutputReadLine()
proc.BeginErrorReadLine()
Await Task.Run(AddressOf proc.WaitForExit)
proc.Close()
Me.components.Remove(proc)
TextBox1.ReadOnly = False
TextBox2.ReadOnly = False
Button2.Enabled = False
Button1.Enabled = True
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
If proc IsNot Nothing Then
proc.StandardInput.Write(TextBox3.Text & vbLf)
Else
Debug.WriteLine("既に閉じられている")
End If
End Sub
Private Sub proc_OutputDataReceived(sender As Object, e As DataReceivedEventArgs) Handles proc.OutputDataReceived
StandardOut(e.Data, isError:=False)
End Sub
Private Sub proc_ErrorDataReceived(sender As Object, e As DataReceivedEventArgs) Handles proc.ErrorDataReceived
StandardOut(e.Data, isError:=True)
End Sub
Private Sub StandardOut(message As String, isError As Boolean)
Invoke(
New MethodInvoker(
Sub()
Dim s = RichTextBox1.TextLength
Dim l = If(message?.Length, 0)
RichTextBox1.SelectionStart = s
RichTextBox1.SelectedText = If(message, "")
RichTextBox1.Select(s, l)
RichTextBox1.SelectionColor = If(isError, Color.Yellow, RichTextBox1.ForeColor)
RichTextBox1.SelectionBackColor = If(isError, Color.Red, RichTextBox1.BackColor)
RichTextBox1.SelectionStart = RichTextBox1.TextLength
RichTextBox1.SelectedText = vbCr
End Sub
)
)
End Sub
End Class
ただし、標準出力された内容の末尾が改行で無いと
OutputDataReceived イベントが発生しない点にご注意ください。
「TIME /T」の結果は改行が来るのですぐに表示されますが、
「TIME」は改行を伴わないため、次の応答が来るまでイベントが発生しません。
「PAUSE」コマンドなどとも相性が悪いですね。
■No34644に追記(魔界の仮面弁士の記事)
> ただし、標準出力された内容の末尾が改行で無いと> OutputDataReceived イベントが発生しない点にご注意ください。
Process.OutputDataReceived イベントの代わりに
StandardOutput.ReadAsync メソッドを使うようにしてみました。
これなら「PAUSE」や「TIME」にも対応できるかと思います。
Public Class Form1
Private proc As Process
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
If Me.components Is Nothing Then
Me.components = New System.ComponentModel.Container()
End If
Button1.Text = "開始"
Button2.Text = "標準入力を渡す"
Button2.Enabled = False
'コマンド
TextBox1.Text = Environment.GetEnvironmentVariable("ComSpec")
'引数
TextBox2.Text = ""
'標準入力の内容
TextBox3.Text = "DIR /D ""C:\Program Files (x86)\"""
'標準出力 & 標準エラー出力
RichTextBox1.ReadOnly = True
RichTextBox1.BackColor = Color.Black
RichTextBox1.ForeColor = Color.White
RichTextBox1.ScrollBars = RichTextBoxScrollBars.Both
RichTextBox1.WordWrap = False
RichTextBox1.HideSelection = False
End Sub
Private Async Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Button1.Enabled = False
TextBox1.ReadOnly = True
TextBox2.ReadOnly = True
RichTextBox1.Clear()
Button2.Enabled = True
proc = Process.Start(New ProcessStartInfo(TextBox1.Text, TextBox2.Text) With
{
.UseShellExecute = False,
.CreateNoWindow = True,
.RedirectStandardInput = True,
.RedirectStandardOutput = True,
.RedirectStandardError = True
})
Me.components.Add(proc)
Await Task.WhenAll(
ConsumeOutput(proc.StandardOutput, False),
ConsumeOutput(proc.StandardError, True),
Task.Run(AddressOf proc.WaitForExit)
)
proc.Close()
Me.components.Remove(proc)
TextBox1.ReadOnly = False
TextBox2.ReadOnly = False
Button2.Enabled = False
Button1.Enabled = True
End Sub
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
If proc IsNot Nothing Then
proc.StandardInput.Write(TextBox3.Text & vbLf)
Else
Debug.WriteLine("既に閉じられている")
End If
End Sub
Private Async Function ConsumeOutput(reader As System.IO.TextReader, isError As Boolean) As Task
Dim buffer(2047) As Char
Dim length As Integer = Await reader.ReadAsync(buffer, 0, buffer.Length)
Dim foreColor = If(isError, Color.Yellow, Color.White)
Dim backColor = If(isError, Color.Red, Color.Empty)
While length > 0
Dim message As New String(buffer, 0, length)
Invoke(
New MethodInvoker(
Sub()
Dim pos = RichTextBox1.TextLength
RichTextBox1.Select(pos, 0)
RichTextBox1.SelectedText = If(message, "")
RichTextBox1.Select(pos, length)
RichTextBox1.SelectionColor = foreColor
RichTextBox1.SelectionBackColor = backColor
RichTextBox1.SelectionStart = RichTextBox1.TextLength
End Sub
)
)
length = Await reader.ReadAsync(buffer, 0, buffer.Length)
End While
End Function
End Class