Проблема перенаправления консоли в реальном времени

Прежде всего, я потратил довольно много времени, пытаясь найти ответ на свой вопрос, но пока ничего.

Здесь проблема. Я должен выполнить команду 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) 
Другие вопросы по тегам