将控制台输出重定向到独立程序的文本框

49
我正在开发一个 Windows Forms 应用程序,需要调用另一个程序来执行任务。该程序是控制台应用程序,我需要将控制台的标准输出重定向到我的程序中的一个文本框。
我可以从我的应用程序中执行该程序,但不知道如何将输出重定向到我的应用程序中。我需要在程序运行时使用事件来捕获输出。
控制台程序不会停止运行,直到我的应用程序停止,并且文本在随机时间间隔内不断更改。我的目标是仅仅挂接控制台输出以触发事件处理程序,然后用它来更新文本框。
我使用 C# 编写程序,并使用 .NET 框架进行开发。原始应用程序不是 .NET 程序。
编辑: 以下是我正在尝试的示例代码。在我的最终应用程序中,我将用更新文本框的代码替换 Console.WriteLine。我尝试在事件处理程序中设置断点,但没有达到该断点。
    void Method()
    {
        var p = new Process();
        var path = @"C:\ConsoleApp.exe";

        p.StartInfo.FileName = path;
        p.StartInfo.UseShellExecute = false;
        p.OutputDataReceived += p_OutputDataReceived;

        p.Start();
    }

    static void p_OutputDataReceived(object sender, DataReceivedEventArgs e)
    {
        Console.WriteLine(">>> {0}", e.Data);
    }

InputDataReceived在哪里? - Dmitri Nesteruk
4个回答

72

这对我有效:

void RunWithRedirect(string cmdPath)
{
    var proc = new Process();
    proc.StartInfo.FileName = cmdPath;

    // set up output redirection
    proc.StartInfo.RedirectStandardOutput = true;
    proc.StartInfo.RedirectStandardError = true;    
    proc.EnableRaisingEvents = true;
    proc.StartInfo.CreateNoWindow = true;
    // see below for output handler
    proc.ErrorDataReceived += proc_DataReceived;
    proc.OutputDataReceived += proc_DataReceived;

    proc.Start();

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

    proc.WaitForExit();
}

void proc_DataReceived(object sender, DataReceivedEventArgs e)
{
    // output will be in string e.Data
}

5
有时候(程序退出时)e.Data会变成null,所以你需要检查一下是否e.Data != null! - mmmmmmmm
第一次看到这种东西是在EditPlus中。现在我可以在我的.NET程序中做到这一点。谢谢! - Alex Essilfie
3
我们是否需要将UseShellExecute=false以重定向输出? - user565869
1
谢谢Mark,但是使用这个作为可执行文件的编译Ruby脚本并仅输出命令结果并不完全按预期工作。首先,正如Jon of All Trades所提到的,您需要设置proc.StartInfo.UseShellExecute = false; ...然后在程序完成后,您可以一次性获取通过BeginOutputReadLine()发送的所有数据。我有一种感觉,ReadLine字面上想要行而不是文本,因此,如果可执行文件发送的回车符/换行符代码与环境不匹配,则它将无法捕获OutputDataReceived中的任何数据,直到应用程序完成。 - John Suit
如果您在生产中使用此代码,请记住 Process 是可释放的,还要记得分离事件处理程序(例如proc.ErrorDataReceived -= proc_DataReceived)。 - Moo

5
您可以使用以下代码。
        MemoryStream mem = new MemoryStream(1000);
        StreamWriter writer = new StreamWriter(mem);
        Console.SetOut(writer);

        Assembly assembly = Assembly.LoadFrom(@"C:\ConsoleApp.exe");
        assembly.EntryPoint.Invoke(null, null);
        writer.Close();

        string s = Encoding.Default.GetString(mem.ToArray());
        mem.Close();

9
如果"C:\ConsoleApp.exe"不是一个.NET应用程序,它将无法运行! - mmmmmmmm

1

我已经为O2平台(开源项目)添加了许多辅助方法,使您可以轻松地通过控制台输出和输入脚本与另一个进程进行交互(请参见http://code.google.com/p/o2platform/source/browse/trunk/O2_Scripts/APIs/Windows/CmdExe/CmdExeAPI.cs)。

此外,对您有用的可能是允许查看当前进程的控制台输出的API(在现有控件或弹出窗口中)。有关更多详细信息,请参阅此博客文章:http://o2platform.wordpress.com/2011/11/26/api_consoleout-cs-inprocess-capture-of-the-console-output/(此博客还包含有关如何使用新进程的控制台输出的详细信息)


0

感谢Marc Maxham的答案,节省了我的时间!

正如All Trades的Jon所指出的那样,为了重定向IO流,必须将UseShellExecute设置为false,否则Start()调用会抛出InvalidOperationException

这是我修改后的代码,其中txtOut是一个WPF只读文本框。

void RunWithRedirect(string cmdargs)
{
    var proc = new Process()
    {
        StartInfo = new ProcessStartInfo("cmd.exe", "/k " + cmdargs)
        {
            RedirectStandardOutput = true,
            RedirectStandardError = true,
            CreateNoWindow = true
        },
        EnableRaisingEvents = true
    };

    // see below for output handler
    proc.ErrorDataReceived += proc_DataReceived;
    proc.OutputDataReceived += proc_DataReceived;
    proc.Start();

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

    proc.WaitForExit();
}

void proc_DataReceived(object sender, DataReceivedEventArgs e)
{
    if (e.Data != null)
        Dispatcher.BeginInvoke(new Action( () => txtOut.Text += (Environment.NewLine + e.Data) ));
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接