C# 如何在读取另一进程的重定向输出流时获得正确的编码

C# 如何在读取另一进程的重定向输出流时获得正确的编码,c#,character-encoding,async-await,C#,Character Encoding,Async Await,我正在启动另一个进程(node.exe),以便捕获其输出并将其显示在我自己的Winforms窗口中。其思想是,如果节点服务器崩溃,我将能够自动重新启动进程。包含的代码只是测试代码,而不是最终代码,并且不会终止进程,因此如果运行它,则需要在关闭表单后手动终止节点进程 我的问题是,尽管我正确地重定向了输出和错误流,但是在普通控制台上没有显示一些有趣的字符。如何更改它以正确检测编码并正确显示 下面是一个输出示例(每个字符串的开头都有一些垃圾字符) 以下是启动流程并重定向输出的代码: using Sys

我正在启动另一个进程(node.exe),以便捕获其输出并将其显示在我自己的Winforms窗口中。其思想是,如果节点服务器崩溃,我将能够自动重新启动进程。包含的代码只是测试代码,而不是最终代码,并且不会终止进程,因此如果运行它,则需要在关闭表单后手动终止节点进程

我的问题是,尽管我正确地重定向了输出和错误流,但是在普通控制台上没有显示一些有趣的字符。如何更改它以正确检测编码并正确显示

下面是一个输出示例(每个字符串的开头都有一些垃圾字符)

以下是启动流程并重定向输出的代码:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;

namespace AHVMonitor
{
    enum Output
    {
        StandardOutput,
        StandardError
    }

    public sealed class ProcessWatcher
    {
        private ConcurrentQueue<string> logLines = new ConcurrentQueue<string>();
        private Process process;
        private string arguments = ConfigurationManager.AppSettings["Arguments"];
        private string filename = ConfigurationManager.AppSettings["Filename"];

        public IList<string> Log
        {
            get { return logLines.ToArray(); }
        }

        public async Task<bool> WatchAsync()
        {
            Func<Task<bool>> waitForProcess = async () =>
            {
                var result = false;
                process = new Process();
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.RedirectStandardError = true;
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.FileName = filename;
                process.StartInfo.Arguments = arguments;
                process.StartInfo.WorkingDirectory = Path.GetDirectoryName(filename);

                // Hide the (empty) console window, since we are redirecting the output.
                process.StartInfo.CreateNoWindow = true;

                process.Start();

                await TaskExtensions.ForAsync(0, 3, 3, async i =>
                {
                    switch (i)
                    {
                        case 0:
                            await RedirectStandardErrorOrOutputAsync(Output.StandardOutput);
                            break;
                        case 1:
                            await RedirectStandardErrorOrOutputAsync(Output.StandardError);
                            break;
                        case 2:
                            result = await Task.Run(() =>
                            {
                                try
                                {
                                    process.WaitForExit();
                                    return process.ExitCode == 0;
                                }
                                catch { return false; }
                                finally
                                {
                                    process.Dispose();
                                    process = null;
                                }
                            });
                            break;
                    }
                });
                return result;
            };
            return await waitForProcess();
        }

        private async Task RedirectStandardErrorOrOutputAsync(Output outputType)
        {
            using (var reader = new StreamReader(outputType == Output.StandardError ? process.StandardError.BaseStream : process.StandardOutput.BaseStream))
            {
                var line = string.Empty;

                while ((line = await reader.ReadLineAsync()) != null)
                    logLines.Enqueue(line);
            }
        }
    }
}

“垃圾”看起来像是缺少不可打印字符ESC(0x1B)。

您是否尝试从单个线程访问standardoutput并查看结果如何?还没有。我可以试一试,看看会发生什么。这段代码基于我很久以前编写的一些更复杂的代码,它包装了ffmpeg,并且它的输出来自多个线程,所以我怀疑线程是一个问题。你确定那些“垃圾值”不是来自
标准错误
?很确定这一定是一个文本编码问题。我刚刚从live server上的控制台窗口复制了此内容:[2014-11-26 11:14:20.858][INFO][default]-用户jasoncf1已登录。因此,这些字符似乎只有在复制流时才会出现。我曾尝试在创建StreamReader时显式指定编码,并为其传递“true”参数以检测编码,但没有成功。你是对的。(缺少的字符在这里没有很好地复制和粘贴。)我本想更新这个问题或自己回答它,但这确实是字符的来源。因为我没有在我的应用程序中设置颜色,所以解决方案只是在日志中找到这些字符时替换它们。
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Threading.Tasks;

namespace AHVMonitor
{
    enum Output
    {
        StandardOutput,
        StandardError
    }

    public sealed class ProcessWatcher
    {
        private ConcurrentQueue<string> logLines = new ConcurrentQueue<string>();
        private Process process;
        private string arguments = ConfigurationManager.AppSettings["Arguments"];
        private string filename = ConfigurationManager.AppSettings["Filename"];

        public IList<string> Log
        {
            get { return logLines.ToArray(); }
        }

        public async Task<bool> WatchAsync()
        {
            Func<Task<bool>> waitForProcess = async () =>
            {
                var result = false;
                process = new Process();
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.RedirectStandardError = true;
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.FileName = filename;
                process.StartInfo.Arguments = arguments;
                process.StartInfo.WorkingDirectory = Path.GetDirectoryName(filename);

                // Hide the (empty) console window, since we are redirecting the output.
                process.StartInfo.CreateNoWindow = true;

                process.Start();

                await TaskExtensions.ForAsync(0, 3, 3, async i =>
                {
                    switch (i)
                    {
                        case 0:
                            await RedirectStandardErrorOrOutputAsync(Output.StandardOutput);
                            break;
                        case 1:
                            await RedirectStandardErrorOrOutputAsync(Output.StandardError);
                            break;
                        case 2:
                            result = await Task.Run(() =>
                            {
                                try
                                {
                                    process.WaitForExit();
                                    return process.ExitCode == 0;
                                }
                                catch { return false; }
                                finally
                                {
                                    process.Dispose();
                                    process = null;
                                }
                            });
                            break;
                    }
                });
                return result;
            };
            return await waitForProcess();
        }

        private async Task RedirectStandardErrorOrOutputAsync(Output outputType)
        {
            using (var reader = new StreamReader(outputType == Output.StandardError ? process.StandardError.BaseStream : process.StandardOutput.BaseStream))
            {
                var line = string.Empty;

                while ((line = await reader.ReadLineAsync()) != null)
                    logLines.Enqueue(line);
            }
        }
    }
}
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace AHVMonitor
{
    public static class TaskExtensions
    {
        #region IEnumerable<T>.ForEachAsync and IEnumerable<T>.ForAsync

        /// <summary>A ForEachAsync implementation. Based on a sample in an article by Stephen Toub,
        /// <a href="http://blogs.msdn.com/b/pfxteam/archive/2012/03/05/10278165.aspx">
        /// Implementing a simple ForEachAsync, part 2</a>.</summary>
        public static Task ForEachAsync<T>(this IEnumerable<T> source, int maxDegreeOfParallelism, Func<T, Task> body)
        {
            return Task.WhenAll(
                from partition in Partitioner.Create(source).GetPartitions(maxDegreeOfParallelism)
                select Task.Run(async () =>
                {
                    using (partition)
                        while (partition.MoveNext())
                            await body(partition.Current);
                }));
        }

        /// <summary>An asynchronous ForAsync implementation.</summary>
        /// <remarks>It simply creates an <b>Enumerable.Range</b> and wraps <b>ForEachAsync</b>.</remarks>
        public static Task ForAsync(int fromInclusive, int toExclusive, int maxDegreeOfParallelism, Func<int, Task> body)
        {
            return Enumerable.Range(
                fromInclusive, toExclusive).
                ForEachAsync(maxDegreeOfParallelism, async i => await body(i));
        }

        #endregion
    }
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace AHVMonitor
{
    public partial class WatcherForm : Form
    {
        private ProcessWatcher watcher = new ProcessWatcher();

        public WatcherForm()
        {
            InitializeComponent();
        }

        private void WatcherForm_Load(object sender, EventArgs e)
        {
            LogAsync();
        }

        private async void LogAsync()
        {
            while (true)
            {
                await Task.Delay(TimeSpan.FromSeconds(1D));
                var lines = watcher.Log;
                logTextBox.Lines = lines.ToArray();
                logTextBox.SelectionStart = logTextBox.TextLength;
                logTextBox.ScrollToCaret();
            }
        }

        private async void startButton_Click(object sender, EventArgs e)
        {
            await watcher.WatchAsync();
        }
    }
}