如何在读取另一个进程的重定向输出流时获得正确的编码
本文关键字:编码 输出流 重定向 读取 另一个 进程 | 更新日期: 2023-09-27 18:32:13
我正在启动另一个进程(node.exe),以便捕获其输出并将其显示在我自己的Winforms窗口中。这个想法是,如果节点服务器崩溃,我将能够自动重新启动该过程。包含的代码只是测试代码,而不是最终代码,它不会终止进程,因此如果您运行它,则需要在关闭窗体后手动终止节点进程。
我的问题是,尽管我正确地重定向了输出和错误流,但仍有一些有趣的字符不会显示在普通控制台上。如何更改它以正确检测编码并正确显示?
下面是一个输出示例(每个字符串的开头都有一些垃圾字符)。
[32m[2014-11-26 08:24:21.525] [INFO] console - [39mExpress server listening on port 8080
下面是启动进程并重定向输出的代码:
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);
}
}
}
}
要使该代码正常工作,您需要为我的 3 个任务上的 ForAsync 提供这两个扩展。(包装一个不是我写的 ForEachAsync 实现。
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
}
}
使用"ProcessWatcher"(仅包含一个按钮和一个文本框)的表单的代码如下:
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();
}
}
}
该"垃圾"看起来像用于设置颜色的转义码,缺少不可打印的字符 ESC (0x1B)。