C# 使用扩展ASCII在WinForms RichTextBox控件中创建树

C# 使用扩展ASCII在WinForms RichTextBox控件中创建树,c#,console,richtextbox,C#,Console,Richtextbox,我已经编写了一个自定义控制台应用程序,它将我正在运行的命令(在我正在运行的“cmd.exe”屏幕截图中)的输出重定向到richtextbox控件。使用的字体是“Lucida Console”,它与“cmd.exe”本身使用的字体相同 我面临的问题是,某些字符没有正确显示,但它们是该命令的正确字符。最好的猜测是,这些是ANSI终端字符,必须转义/处理/无论什么,但我不确定。所以这不会成为XY的问题,我会清楚我的意图: 我希望在richtextbox中显示这些字符,与运行“cmd.exe”时显示的字

我已经编写了一个自定义控制台应用程序,它将我正在运行的命令(在我正在运行的“cmd.exe”屏幕截图中)的输出重定向到richtextbox控件。使用的字体是“Lucida Console”,它与“cmd.exe”本身使用的字体相同

我面临的问题是,某些字符没有正确显示,但它们是该命令的正确字符。最好的猜测是,这些是ANSI终端字符,必须转义/处理/无论什么,但我不确定。所以这不会成为XY的问题,我会清楚我的意图:

我希望在richtextbox中显示这些字符,与运行“cmd.exe”时显示的字符相同。我使用的语言是C#on.NETFramework4.5,没有额外的控件。我正在重定向System.Diagnostics.Process.Start()和RichTextBox控件的输出/输入

我的项目

CMD.EXE

相关代码:

    void processInterace_OnProcessOutput( object sender, ProcessEventArgs args ) {
        //  Write the output

        string outp = Encoding.GetEncoding(437).GetString(Encoding.GetEncoding(437).GetBytes(args.Content.Substring(lastInput.Length).ToCharArray()));

        WriteOutput( outp, ForeColor );
        lastInput="";

        //  Fire the output event.
        FireConsoleOutputEvent( args.Content );
    }

    public void WriteOutput( string output, Color color ) {
        if ( string.IsNullOrEmpty( lastInput )==false&&
            ( output==lastInput||output.Replace( "\r\n", "" )==lastInput ) )
            return;

        if ( !this.IsHandleCreated )
            return;

        Invoke( (Action)( () => {
            //  Write the output.
            richTextBoxConsole.Focus();
            richTextBoxConsole.SelectionColor=color;
            richTextBoxConsole.SelectedText+=output;
            inputStart=richTextBoxConsole.SelectionStart;
        } ) );
    }

    public void StartProcess( string fileName, string arguments ) {
        //  Are we showing diagnostics?
        if ( ShowDiagnostics ) {
            WriteOutput( "Preparing to run "+fileName, DiagnosticsColor );
            if ( !string.IsNullOrEmpty( arguments ) )
                WriteOutput( " with arguments "+arguments+"."+Environment.NewLine, DiagnosticsColor );
            else
                WriteOutput( "."+Environment.NewLine, DiagnosticsColor );
        }

        //  Start the process.
        processInterace.StartProcess( fileName, arguments );

        //  If we enable input, make the control not read only.
        if ( IsInputEnabled )
            richTextBoxConsole.ReadOnly=false;
    }
还有钩子(这很重要)

更新

所以我决定尝试一种新的方法来解决这个问题。由于
ProcessInterface
似乎无法真正控制更改从进程接收的输出的编码,因此我决定尝试使用原始
进程
接口,如下所示:

public partial class Form1 : Form {
    Process process { get; set; }
    ProcessStartInfo startinfo { get; set; }

    public Form1() {
        InitializeComponent();
        process = new Process();
        startinfo = new ProcessStartInfo() {
            RedirectStandardError = true,
            RedirectStandardInput = true,
            RedirectStandardOutput = true,
            StandardErrorEncoding = System.Text.Encoding.GetEncoding(437),
            StandardOutputEncoding = System.Text.Encoding.GetEncoding(437),
            UseShellExecute = false,
            ErrorDialog = false,
            CreateNoWindow = true,
            WorkingDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)
            //WindowStyle=System.Diagnostics.ProcessWindowStyle.Normal
        };
    }

    void process_ErrorDataReceived( object sender, DataReceivedEventArgs e ) {
        Invoke( (Action)( () => {
            cb.Text+=e.Data+"\n";
        } ) );
    }

    void process_Exited( object sender, EventArgs e ) {
        this.Text = "Exited";
    }


    private void Form1_Load( object sender, EventArgs e ) {
        process.StartInfo.Arguments = "/K tree";
        startinfo.FileName="cmd.exe";
        process=new Process {
            StartInfo = startinfo
        };
        process.OutputDataReceived+=process_OutputDataReceived;
        process.ErrorDataReceived+=process_ErrorDataReceived;
        process.Exited+=process_Exited;
        process.EnableRaisingEvents=true;
        process.Start();
        process.BeginErrorReadLine();
        process.BeginOutputReadLine();

    }

    void process_OutputDataReceived( object sender, DataReceivedEventArgs e ) {
        Invoke( (Action)( () => {
            cb.Text+=e.Data + "\n";
        } ) );
    }
}
这导致了一个新问题。我没有收到关于所有新数据的事件通知——只收到在回车中终止的新行的事件通知。下图描述了输出的示例:

在我最近的示例中,当我更改
form_Load
时,您可以看到这最终解决了编码问题,但存在一个新问题,即在控制台发送回车之前,最后一行不会返回:

private async void Form1_Load( object sender, EventArgs e ) {
    process.StartInfo.Arguments = "";
    startinfo.FileName="cmd.exe";
    process=new Process {
        StartInfo = startinfo
    };
    process.OutputDataReceived+=process_OutputDataReceived;
    process.ErrorDataReceived+=process_ErrorDataReceived;
    process.Exited+=process_Exited;
    process.EnableRaisingEvents=true;
    process.Start();
    process.BeginErrorReadLine();
    process.BeginOutputReadLine();

    await process.StandardInput.WriteLineAsync( @"tree d:\sqlite" );

}

您可以利用File类对编码的控制来发挥优势

var tempOutPath = Path.GetTempFileName();
var chcp = Process.Start("cmd.exe", $@" /c chcp >""{tempOutPath}""");
chcp.WaitForExit();
var encoding = Int32.Parse(File.ReadAllText(tempOutPath, Encoding.GetEncoding(437)).Replace("Active code page:", ""));
var tree = Process.Start("cmd.exe", $@" /c tree ""C:\Program Files\WinCDEmu"" >""{tempOutPath}""");
tree.WaitForExit();
var result = File.ReadAllText(tempOutPath, Encoding.GetEncoding(encoding));
File.Delete(tempOutPath);

AAAA之所以出现,是因为RichTextBox不知道如何解释这些扩展的ASCII字符。@RobertHarvey-我正在考虑使用
String.Replace()
类型hack,但这只描述了少量字符,并不能真正解决问题。我知道这些字符是可以显示的。如果您将
树的输出(通过cmd.exe)复制到任何其他文件(又名记事本)中,您实际上会得到'AAAA',相关:@RobertHarvey-可能,但是您知道要测试的编码吗?我认为这可能不仅仅是一个编码问题,而是与处理特定于ANSI/终端的输出有关。。你还需要一个固定宽度的字体。我实际上已经解决了这个问题,而不需要从外部文件读取。我将此方法视为一种潜在的解决方法,但我找到了一种获取所有数据的方法。一旦我弄明白这个插入符号,我会发布一个解决方案。颜色是。。。呃……整个项目(没有像您在这里所做的那样通过管道连接到文件)可以作为控制台代码找到,这是我在这里提出的另一个问题>。由于您的解决方案是实现此目的的一种方法,并且为了保留此链接,我将此答案标记为正确。你能不能在你的答案中添加这个SO链接参考,因为这是一个更彻底的解决方案。谢谢
var tempOutPath = Path.GetTempFileName();
var chcp = Process.Start("cmd.exe", $@" /c chcp >""{tempOutPath}""");
chcp.WaitForExit();
var encoding = Int32.Parse(File.ReadAllText(tempOutPath, Encoding.GetEncoding(437)).Replace("Active code page:", ""));
var tree = Process.Start("cmd.exe", $@" /c tree ""C:\Program Files\WinCDEmu"" >""{tempOutPath}""");
tree.WaitForExit();
var result = File.ReadAllText(tempOutPath, Encoding.GetEncoding(encoding));
File.Delete(tempOutPath);