使用扩展 ASCII 在 WinForms RichTextBox 控件中创建树
Using extended ASCII to create a tree in a WinForms RichTextBox control
我编写了一个自定义控制台应用程序,它将输出 to/from 我是 运行 的命令(在屏幕截图中我是 运行 'cmd.exe')重定向到 richtextbox 控件.使用的字体是 'Lucida Console',这与 'cmd.exe' 本身使用的字体相同。
我面临的问题是某些字符没有正确显示,但它们是该命令的正确字符。最好的猜测是,这些是必须转义/处理/其他的 ANSI 终端字符,但我不确定。所以这不会成为XY问题,我会明确我的意图:
我想在 richtextbox 中显示这些字符,就像它们在 运行 'cmd.exe' 时显示的一样。我使用的语言是 .NET Framework 4.5 上的 C#,没有额外的控件。我正在从 System.Diagnostics.Process.Start() 和 RichTextBox 控件重定向 output/input。
我的项目
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;
}
还有钩子(很重要)
processInterace.OnProcessOutput+=processInterace_OnProcessOutput;
更新
所以我决定尝试一种新的方法来解决这个问题。由于 ProcessInterface
似乎无法真正控制更改从进程接收到的输出的编码,我决定尝试使用原始 Process
接口,如下所示:
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";
} ) );
}
}
这导致了一个新问题。我没有收到有关所有新数据的事件通知——仅收到以马车 return 终止的新行。输出示例如下图所示:
当我在最近的示例中更改 form_Load
时,您可以看到这最终解决了编码问题,但是存在一个新问题,即最后一行直到回车才被 returned return 从控制台发送:
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" );
}
您可以利用文件 class 对编码的控制来发挥自己的优势。
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);
我编写了一个自定义控制台应用程序,它将输出 to/from 我是 运行 的命令(在屏幕截图中我是 运行 'cmd.exe')重定向到 richtextbox 控件.使用的字体是 'Lucida Console',这与 'cmd.exe' 本身使用的字体相同。
我面临的问题是某些字符没有正确显示,但它们是该命令的正确字符。最好的猜测是,这些是必须转义/处理/其他的 ANSI 终端字符,但我不确定。所以这不会成为XY问题,我会明确我的意图:
我想在 richtextbox 中显示这些字符,就像它们在 运行 'cmd.exe' 时显示的一样。我使用的语言是 .NET Framework 4.5 上的 C#,没有额外的控件。我正在从 System.Diagnostics.Process.Start() 和 RichTextBox 控件重定向 output/input。
我的项目
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;
}
还有钩子(很重要)
processInterace.OnProcessOutput+=processInterace_OnProcessOutput;
更新
所以我决定尝试一种新的方法来解决这个问题。由于 ProcessInterface
似乎无法真正控制更改从进程接收到的输出的编码,我决定尝试使用原始 Process
接口,如下所示:
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";
} ) );
}
}
这导致了一个新问题。我没有收到有关所有新数据的事件通知——仅收到以马车 return 终止的新行。输出示例如下图所示:
当我在最近的示例中更改 form_Load
时,您可以看到这最终解决了编码问题,但是存在一个新问题,即最后一行直到回车才被 returned return 从控制台发送:
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" );
}
您可以利用文件 class 对编码的控制来发挥自己的优势。
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);