Проблема перенаправления консоли в реальном времени
Прежде всего, я потратил довольно много времени, пытаясь найти ответ на свой вопрос, но пока ничего.
Здесь проблема. Я должен выполнить команду cli с аргументами и перенаправить вывод (REALTIME) в текстовое поле. Команда будет выглядеть так: smcli - n name -f "script.scr"
Поскольку smcli не находится в системной переменной PATH, мне сначала нужно перейти к нужному каталогу.
Итак, я заканчиваю тем, что запускаю полную команду, которая выглядит следующим образом: cmd /c cd "c:\Program Files(x86)\bla\" && smcli - n name -f "script.scr"
Я помещу код в одну секунду, но, в принципе, все работает отлично, я попадаю в правильный каталог, запускаю команду, она запускает скрипт, все хорошо.
Я пытался перенаправить вывод в другую форму, но тогда он больше не работал; в любом случае, это был бы бонусный вопрос.
Дело в том, что когда скрипт запускается, он говорит, шаг за шагом, что он делает, и отправляет эту информацию на выход (то есть, восстанавливая имя, восстанавливая конфигурацию диска, ...)
Как я уже сказал, я получаю все эти результаты.... как только ВСЕ закончено (т.е. после 5 минут очевидного замораживания). НЕ перенаправляя вывод в текстовое поле (т.е. оставляя окно консоли открытым), я получаю вывод в режиме реального времени.
Итак, вот код (его немного, извините):
Private Sub ReadMDcfgToolStripButton_Click(sender As Object, e As EventArgs) Handles ReadMDcfgToolStripButton.Click
Dim exitcode As Int32
smcli = Environment.GetEnvironmentVariable("ProgramFiles") & "\Dell\MD Storage Manager\client\" 'SMcli.exe"
Dim gotosmclidir As String = "cd """ & smcli & """"
Dim smclicommand As String
smclicommand = "smcli -n " & mdname & " -c ""save storagearray configuration file=\""" & saveFileDialog2.FileName & "\"" allconfig"";"
cmd = gotosmclidir & " && " & smclicommand
exitcode = RunCommandCom(cmd, "", False)
End Sub
Private Function RunCommandCom(command As String, arguments As String, permanent As Boolean) As Int32
' Usage:
'RunCommandCom("DIR", "/W", true)
'For the multiple command on one line the key are the & | && and || command connectors
'•A & B -> execute command A, then execute command B.
'•A | B -> execute command A, and redirect all it's output into the input of command B.
'•A && B -> execute command A, evaluate the errorlevel after running Command A, and if the exit code (errorlevel) is 0, only then execute command B.
'•A || B -> execute Command A, evalutate the exit code of this command and if it's anything but 0, only then execute command B.
TextBox1.Text = "Communication in progress..." & vbCrLf
exitcodelbl.Text = ""
CmdCloseBtn.Enabled = False
ToolStrip1.Enabled = False
CmdOutputPanel.Visible = True
Dim p As Process = New Process()
Dim pi As ProcessStartInfo = New ProcessStartInfo()
pi.FileName = "cmd.exe"
pi.Arguments = " " + If(permanent = True, "/K", "/C") + " " + command + " " + arguments
MsgBox(pi.Arguments)
pi.CreateNoWindow = True
pi.UseShellExecute = False
pi.RedirectStandardOutput = True
pi.RedirectStandardError = True
pi.CreateNoWindow = True
p.StartInfo = pi
AddHandler p.OutputDataReceived, AddressOf GotData
p.Start()
p.BeginOutputReadLine()
Do Until p.HasExited
Loop
exitcodelbl.Text = p.ExitCode
Select Case p.ExitCode
Case 0
exitcodelbl.Text += " - Command completed successfully."
Case 1
exitcodelbl.Text += " - Could not communicate with the Array."
Case 9
exitcodelbl.Text += " - Could not communicate with the Array."
Case 14
exitcodelbl.Text += " - Could not communicate with the Array."
Case Else
exitcodelbl.Text += " - Unknown code."
End Select
Return p.ExitCode
End Function
Sub GotData(ByVal sender As Object, ByVal e As DataReceivedEventArgs)
UpdateTextBox(e.Data)
End Sub
Private Delegate Sub UpdateTextBoxDelegate(ByVal Text As String)
Private Sub UpdateTextBox(ByVal Tex As String)
If Me.InvokeRequired Then
Dim del As New UpdateTextBoxDelegate(AddressOf UpdateTextBox)
Dim args As Object() = {Tex}
Me.Invoke(del, args)
Else
TextBox1.Text &= Tex & Environment.NewLine
End If
End Sub
Помимо самой команды, все остальное приходит из исследований, и они просто собраны вместе, способ выполнения команды с одной стороны и перенаправление с другой стороны.
Я полагаю, что проблема с моей командой (похоже, что эта часть ОЧЕНЬ разумна).
Либо я могу сделать это по-другому (опять же, это работает, но не в режиме реального времени), или я пропустил что-то тривиальное...?
Спасибо за вашу помощь.
После tinstaafl вот обновленный код:
Private Sub MainForm_Shown(sender As Object, e As EventArgs) Handles MyBase.Shown
copyrightlbl.Focus()
wait(1000)
Await RunCommandCom("", "192.168.219.152", False) '172.16.1.55
CmdCloseBtn.Enabled = True
End Sub
Private Async Function RunCommandCom(command As String, arguments As String, permanent As Boolean) As Task(Of Int32)
TextBox1.Text = "Communication in progress..." & vbCrLf
exitcodelbl.Text = ""
CmdCloseBtn.Enabled = False
ToolStrip1.Enabled = False
CmdOutputPanel.Visible = True
Dim p As Process = New Process()
With p.StartInfo
.WorkingDirectory = Environment.GetEnvironmentVariable("ProgramFiles") & "\Dell\MD Storage Manager\client\" 'Directory for SMcli.exe"
.FileName = "ping.exe"
.Arguments = command + " " + arguments
.CreateNoWindow = True
.UseShellExecute = False
.RedirectStandardOutput = True
.RedirectStandardError = True
End With
p.Start()
Do Until p.HasExited
TextBox1.Text &= Await p.StandardOutput.ReadLineAsync
Loop
exitcodelbl.Text = p.ExitCode
Return p.ExitCode
End Function
1 ответ
Вместо использования делегата попробуйте прочитать вывод непосредственно в текстовое поле. StandardOutput
Свойство Process - это поток, который вы можете использовать для чтения метода ReadLineAsync. Примерно так должно работать:
pi.CreateNoWindow = True
pi.UseShellExecute = False
pi.RedirectStandardOutput = True
pi.RedirectStandardError = True
p.StartInfo = pi
p.Start()
Do Until p.HasExited
TextBox1.Text &= Await p.StandardOutput.ReadLineAsync
Loop
Intellisense предложит вам выполнить подпрограмму Async, но при этом вы должны получить желаемый результат.
Private Async Function RunCommandCom(command As String, arguments As String, permanent As Boolean) As Task(Of Int32)
Await RunCommandCom("", "192.168.219.152", False)