我该如何重定向一个控制台程序的输出到一个文本框在一个线程安全的方式? [英] How do I redirect a console program's output to a text box in a thread safe way?

查看:123
本文介绍了我该如何重定向一个控制台程序的输出到一个文本框在一个线程安全的方式?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有麻烦控制台输出重定向到Windows窗体的文本框。问题是线程相关。我运行一个控制台应用程序以下列方式,

 私人无效RunConsoleApp()
{
流程PROC =新工艺();
proc.StartInfo.FileName =APP.EXE;
proc.StartInfo.Arguments =-a -b -c
proc.StartInfo.UseShellExecute = FALSE;

//设置输出重定向
proc.StartInfo.RedirectStandardOutput = TRUE;
proc.StartInfo.RedirectStandardError = TRUE;
proc.EnableRaisingEvents = TRUE;
proc.StartInfo.CreateNoWindow = TRUE;

//设置接收的数据处理程序
proc.ErrorDataReceived + = proc_DataReceived;
proc.OutputDataReceived + = proc_DataReceived;

proc.Start();
proc.BeginErrorReadLine();
proc.BeginOutputReadLine();
proc.WaitForExit();

如果(proc.ExitCode == 0)
{
out_txtbx.AppendText(成功。+ Environment.NewLine);
}
,否则
{
out_txtbx.AppendText(失败。+ Environment.NewLine);
}
}



,然后捕获与该输出处理器处理数据

  //处理的控制台进程
无效proc_DataReceived(对象发件人,DataReceivedEventArgs E)$ b收到日期$ b {
如果(e.Data!= NULL)如果
{
((e.Data.EndsWith(DONE))||(e.Data.EndsWith(失败。))||
(e.Data.StartsWith(RESET)))
{
//这个崩溃的应用程序,但据说是正确的方法
this.AppendText(e.Data + Environment.NewLine);

//这工作,但调试一直警告我呼叫
//不是线程安全的
//out_txtbx.AppendText(e.Data + Environment.NewLine) ;
}
}
}



控制台文本然后将其附加这样,

 委托无效AppendTextDelegate(字符串文本);添加文本控制台框
私人无效AppendText通过(字符串文本)
{


//线程安全的方法//如果来自一个名为使用委托不同的线程,
//否则只是追加文本直接
如果(this.out_txtbx.InvokeRequired)
{当执行
out_txtbx此行
//应用程序崩溃.Invoke(新AppendTextDelegate(this.AppendText),新的对象[] {文本});
}
,否则
{
this.out_txtbx.AppendText(文本);
}
}



从所有的文档和例子我已经看到了这似乎是正确的方法,但它崩溃时out_txtbx.Invoke被称为应用程序。



什么可能被打破,哪些可供选择的方法有没有做到这一点?






解决方案(由汉斯帕桑特指出)



现在的问题是,该应用程序是停留在一个致命拥抱为线的结果,

  proc.WaitForExit(); 

这行应该被删除,该方法应该是这样的,

 私人无效RunConsoleApp()
{
过程PROC =新工艺();
proc.StartInfo.FileName =APP.EXE;
proc.StartInfo.Arguments =-a -b -c
proc.StartInfo.UseShellExecute = FALSE;

//设置输出重定向
proc.StartInfo.RedirectStandardOutput = TRUE;
proc.StartInfo.RedirectStandardError = TRUE;
proc.EnableRaisingEvents = TRUE;
proc.StartInfo.CreateNoWindow = TRUE;

//设置接收的数据处理程序
proc.ErrorDataReceived + = proc_DataReceived;
proc.OutputDataReceived + = proc_DataReceived;

//配置过程中退出事件
proc.Exited + =新的EventHandler(ProcExited);

proc.Start();
proc.BeginErrorReadLine();
proc.BeginOutputReadLine();

//这块主线程,并在死锁
//结果的Process.Exited事件应该被用来避免这种情况。
//proc.WaitForExit();
}

和应提供的事件处理程序,

  ///<总结> 
///当控制台进程完成
///<采取的操作; /总结>
私人无效ProcExited(对象发件人,发送System.EventArgs)
{
过程PROC =(进程)发送者;

//等待一会儿,以允许处理的所有控制台输出,并追加
//追加前成功/失败消息。
Thread.sleep代码(40);

如果(proc.ExitCode == 0)
{
this.AppendText(成功。+ Environment.NewLine);
ExitBootloader();
}
,否则
{
this.AppendText(失败。+ Environment.NewLine);
}

proc.Close();
}


解决方案

  proc.WaitForExit(); 



这就是所谓的僵局。你的主线程被阻塞,等待进程退出。从采取基本职责照顾停止。像保持UI更新。并确保Control.Invoke()请求调度。这会阻止完成AppendText通过()方法。停靠用于退出的过程。从曾经获得过去WaitForExit()调用停止你的UI线程。 致命拥抱,又名僵局。



您不能阻止你的主线程。使用Process.Exited事件来代替。


I am having trouble redirecting console output to a windows forms text box. The problem is thread related. I am running a console app in the following way,

private void RunConsoleApp()
{
    Process proc = new Process();
    proc.StartInfo.FileName = "app.exe";
    proc.StartInfo.Arguments = "-a -b -c";
    proc.StartInfo.UseShellExecute = false;

    // set up output redirection
    proc.StartInfo.RedirectStandardOutput = true;
    proc.StartInfo.RedirectStandardError = true;    
    proc.EnableRaisingEvents = true;
    proc.StartInfo.CreateNoWindow = true;

    // Set the data received handlers
    proc.ErrorDataReceived += proc_DataReceived;
    proc.OutputDataReceived += proc_DataReceived;

    proc.Start();
    proc.BeginErrorReadLine();
    proc.BeginOutputReadLine();
    proc.WaitForExit();

    if (proc.ExitCode == 0)
    {
        out_txtbx.AppendText("Success." + Environment.NewLine);
    }
    else
    {
        out_txtbx.AppendText("Failed." + Environment.NewLine);
    }
}

and then capture and process the data with this output handler,

// Handle the date received by the console process
void proc_DataReceived(object sender, DataReceivedEventArgs e)
{
    if (e.Data != null)
    {
        if ((e.Data.EndsWith("DONE.")) || (e.Data.EndsWith("FAILED.")) ||
            (e.Data.StartsWith("RESET")))
        {
            // This crashes the application, but is supposedly the correct method
            this.AppendText(e.Data + Environment.NewLine);

            // This works, but the debugger keeps warning me that the call
            // is not thread safe
            //out_txtbx.AppendText(e.Data + Environment.NewLine);
        }
    }
}

The console text is then appended like this,

delegate void AppendTextDelegate(string text);

// Thread-safe method of appending text to the console box
private void AppendText(string text)
{
    // Use a delegate if called from a different thread,
    // else just append the text directly
    if (this.out_txtbx.InvokeRequired)
    {
        // Application crashes when this line is executed
        out_txtbx.Invoke(new AppendTextDelegate(this.AppendText), new object[] { text });
    }
    else
    {
        this.out_txtbx.AppendText(text);
    }
}

From all the documentation and examples I have seen this appears to be the correct method, except that it is crashing the application when out_txtbx.Invoke is called.

What could be broken and what alternative ways are there to do this?


Solution (as pointed out by Hans Passant)

The problem is that the app is stuck in a "deadly embrace" as a result of the line,

proc.WaitForExit();

That line should be removed and the method should look like this,

private void RunConsoleApp()
{
    Process proc = new Process();
    proc.StartInfo.FileName = "app.exe";
    proc.StartInfo.Arguments = "-a -b -c";
    proc.StartInfo.UseShellExecute = false;

    // set up output redirection
    proc.StartInfo.RedirectStandardOutput = true;
    proc.StartInfo.RedirectStandardError = true;    
    proc.EnableRaisingEvents = true;
    proc.StartInfo.CreateNoWindow = true;

    // Set the data received handlers
    proc.ErrorDataReceived += proc_DataReceived;
    proc.OutputDataReceived += proc_DataReceived;

    // Configure the process exited event
    proc.Exited += new EventHandler(ProcExited);

    proc.Start();
    proc.BeginErrorReadLine();
    proc.BeginOutputReadLine();

    // This blocks the main thread and results in "deadly embrace"
    // The Process.Exited event should be used to avoid this.
    //proc.WaitForExit();
}

and an event handler should be provided,

/// <summary>
/// Actions to take when console process completes
/// </summary>
private void ProcExited(object sender, System.EventArgs e)
{
    Process proc = (Process)sender;

    // Wait a short while to allow all console output to be processed and appended
    // before appending the success/fail message.
    Thread.Sleep(40);

    if (proc.ExitCode == 0)
    {
        this.AppendText("Success." + Environment.NewLine);
        ExitBootloader();
    }
    else
    {
        this.AppendText("Failed." + Environment.NewLine);
    }

    proc.Close();
}

解决方案

proc.WaitForExit();

It is called deadlock. Your main thread is blocked, waiting for the process to exit. That stops it from taking care of essential duties. Like keeping the UI updated. And making sure that Control.Invoke() requests are dispatched. That stops the AppendText() method from completing. Which stops the process for exiting. Which stops your UI thread from ever getting past the WaitForExit() call. "Deadly embrace", aka deadlock.

You cannot block your main thread. Use the Process.Exited event instead.

这篇关于我该如何重定向一个控制台程序的输出到一个文本框在一个线程安全的方式?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆