You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

异步获取可执行文件命令行日志失败:StringBuilder始终为空

背景

我写了一段异步运行可执行文件的C#代码,该可执行文件通常通过命令行运行,具体代码如下:

namespace Feather
{
    internal class Helper
    {
        private static StringBuilder output;
        private static Process cmd;

        public static void RunLogic(string args)
        {
            output = new StringBuilder();
            cmd = new Process();

            try
            {
                cmd.StartInfo.FileName = "Cotton.exe";
                cmd.StartInfo.Arguments = args;
                cmd.StartInfo.UseShellExecute = false;
                cmd.StartInfo.CreateNoWindow = true;
                cmd.StartInfo.RedirectStandardOutput = true;
                cmd.StartInfo.RedirectStandardInput = true;

                cmd.EnableRaisingEvents = true;
                cmd.OutputDataReceived +=
                   new DataReceivedEventHandler(cmd_OutputDataReceived);
                cmd.Exited += new EventHandler(cmd_Exited);

                cmd.Start();
            }

            catch (Exception ex)
            {
                RhinoApp.WriteLine("Error on process start: {0}", ex.Message);
            }
        }

        private static void cmd_Exited(object sender, EventArgs e)
        {
            try
            {
                if (!string.IsNullOrEmpty(output.ToString()))
                {
                    RhinoApp.WriteLine("Process output: {0}", output.ToString());
                }

                RhinoApp.WriteLine("Process finished.");
                cmd.Dispose();
            }

            catch (Exception ex)
            {
                RhinoApp.WriteLine("Error on process exit: {0}", ex.Message);
            }
        }

        private static void cmd_OutputDataReceived(object sender, DataReceivedEventArgs e)
        {
            try
            {
                RhinoApp.WriteLine("cmd_OutputDataReceived is called.");

                if (!String.IsNullOrEmpty(e.Data))
                {
                    output.Append(e.Data + Environment.NewLine);
                }
            }
            catch (Exception ex)
            {
                RhinoApp.WriteLine("Error on process data-receive: {0}", ex.Message);
            }
        }
    }
}

问题

代码里的private static StringBuilder output;始终为空,output.Append(e.Data + Environment.NewLine);没法往里面加内容,我找不到原因。请问怎么获取这个可执行文件的所有命令行日志?有没有简便的实现方式?


解决方案

1. 修复原代码的核心问题

你的代码漏了关键一步:启动异步读取标准输出的操作。在cmd.Start();之后必须调用cmd.BeginOutputReadLine();,否则OutputDataReceived事件永远不会触发,自然无法收集到输出内容。

另外,多数命令行程序会把错误信息输出到标准错误流(StandardError),而非标准输出流,你当前代码未捕获这部分内容,需要补充处理:

// 在StartInfo配置中添加
cmd.StartInfo.RedirectStandardError = true;
// 绑定错误流事件
cmd.ErrorDataReceived += cmd_ErrorDataReceived;

// 启动进程后,同时启动错误流的异步读取
cmd.Start();
cmd.BeginOutputReadLine();
cmd.BeginErrorReadLine();

新增错误流处理方法:

private static void cmd_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
    try
    {
        if (!string.IsNullOrEmpty(e.Data))
        {
            output.Append($"Error: {e.Data}{Environment.NewLine}");
            RhinoApp.WriteLine($"Process error: {e.Data}");
        }
    }
    catch (Exception ex)
    {
        RhinoApp.WriteLine("Error on process error-receive: {0}", ex.Message);
    }
}

同时注意:静态成员output和cmd存在线程安全问题,多次调用RunLogic会导致数据覆盖或异常,建议改为非静态实现,每次调用创建独立实例。

2. 简便实现方式(异步/await模式)

如果不需要实时处理输出,现代C#的异步/await模式能大幅简化代码,同时完整捕获所有输出和错误信息:

using System.Diagnostics;
using System.Text;

namespace Feather
{
    internal class Helper
    {
        public static async Task RunLogicAsync(string args)
        {
            var processStartInfo = new ProcessStartInfo
            {
                FileName = "Cotton.exe",
                Arguments = args,
                UseShellExecute = false,
                CreateNoWindow = true,
                RedirectStandardOutput = true,
                RedirectStandardError = true,
                StandardOutputEncoding = Encoding.UTF8,
                StandardErrorEncoding = Encoding.UTF8
            };

            using var process = new Process { StartInfo = processStartInfo };

            try
            {
                process.Start();

                // 异步读取输出和错误流
                var outputTask = process.StandardOutput.ReadToEndAsync();
                var errorTask = process.StandardError.ReadToEndAsync();

                await process.WaitForExitAsync();

                string output = await outputTask;
                string error = await errorTask;

                if (!string.IsNullOrEmpty(output))
                {
                    RhinoApp.WriteLine("Process output:\n{0}", output);
                }

                if (!string.IsNullOrEmpty(error))
                {
                    RhinoApp.WriteLine("Process error:\n{0}", error);
                }

                RhinoApp.WriteLine("Process finished.");
            }
            catch (Exception ex)
            {
                RhinoApp.WriteLine("Error: {0}", ex.Message);
            }
        }
    }
}

这种方式无需处理事件,代码简洁且避免了静态成员的线程安全问题。

内容的提问来源于stack exchange,提问作者Megidd

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.12 18:32:08