从进程重定向 StandardOutput

Redirect StandardOutput from a process

我正在使用 C++/CLI winforms 如何将标准输出重定向到文本框? 我关注了这个视频,但是没有用 https://www.youtube.com/watch?v=BDTCviA-5M8

我可以在控制台中看到输出 window,但是直到进程完成 GUI 冻结!

ProcessStartInfo ^psi = gcnew ProcessStartInfo("D://ffmpeg.exe", "-y -i D://1.avi D://process.mp4");        
psi->WindowStyle = ProcessWindowStyle::Hidden;
psi->UseShellExecute = false;
psi->RedirectStandardOutput = true;
process->StartInfo = psi;

process->Start();
String^ details = process->StandardOutput->ReadToEnd();
textBox1->Text = details; 
Console::WriteLine(details); 

我哪里错了?

您对标准输出的读取有问题。由于您的代码中只涉及一个线程,因此没有其他选择可以让所有事情连续发生。如果您想查看长 运行 命令工具的 progress/output,您需要使用 Process 实例中的 OutputDataReceived 事件。

以下代码使用该技术来实现您的目标:

ref class ProcessForm:Form
{
    TextBox ^textBox1 = gcnew TextBox();
    Button ^button = gcnew Button();

public:
    ProcessForm() {
        this->Width = 325;
        this->Height = 450;
        textBox1->Top = 20;
        textBox1->Width = 300;
        textBox1->Height = 400;
        textBox1->Multiline = true;

        button->Text = "Start";
        button->Click += gcnew System::EventHandler(this, &ProcessForm::OnClick);

        this->Controls->Add(textBox1);
        this->Controls->Add(button);
    }

    void ProcessForm::OnClick(System::Object ^sender, System::EventArgs ^e)
    {
        Process ^process = gcnew Process();
        // "D://ffmpeg.exe", "-y -i D://1.avi D://process.mp4"
        ProcessStartInfo ^psi = gcnew ProcessStartInfo("cmd.exe", "/c dir \*.exe /s");
        psi->WindowStyle = ProcessWindowStyle::Hidden;
        psi->UseShellExecute = false;
        psi->RedirectStandardOutput = true;
        process->StartInfo = psi;

        // handle incoming data from the standard output stream
        process->OutputDataReceived += gcnew DataReceivedEventHandler(this, &ProcessForm::OnOutputDataReceived);

        process->Start();
        // start reading from the standard ouput stream on a different thread
        process->BeginOutputReadLine();

        // prevent starting again ...
        process->Exited += gcnew System::EventHandler(this, &ProcessForm::OnExited);
        this->button->Enabled = false;
    }

    delegate void UpdateHandler(System::String ^text);

    // output the stuff on screen
    void ProcessForm::Update(System::String ^text)
    {
        textBox1->Text += text;
        Console::WriteLine(text);
    }

    // is invoked on a seperate thread if the process writes 
    // to the console
    void  ProcessForm::OnOutputDataReceived(System::Object ^sender, System::Diagnostics::DataReceivedEventArgs ^e)
    {
        // handle switching to the UI thread
        if (textBox1->InvokeRequired)
        {
            UpdateHandler^ handler = gcnew UpdateHandler(this, &ProcessForm::Update);
            textBox1->Invoke(handler, e->Data);
        }
        else 
        {
            Update(e->Data);
        }
    }

    // if we're done enable the button again
    void ProcessForm::OnExited(System::Object ^sender, System::EventArgs ^e)
    {
        this->button->Enabled = true;
    }

};

注意需要使用 Invoke,因为事件方法是从非 UI 线程调用的。