3

I have written a custom console application which redirects output to/from the command I am running (in the screenshot I am running 'cmd.exe') to a richtextbox control. The font being used is 'Lucida Console' which is the same font being used by 'cmd.exe' itself.

The problem I face, is that certain characters are not being displayed properly, however they are the correct character for that command. At best guess, these are ANSI terminal characters which must be escaped / processed / whatever, but I am unsure. So this doesn't become an XY problem, I will be clear with my intentions:

I would like to display these characters in the richtextbox the same as they are displayed when running 'cmd.exe'. The language I am using is C# on .NET Framework 4.5 with no extra controls. I am redirecting the output/input from System.Diagnostics.Process.Start() and the RichTextBox control.

My Project enter image description here

CMD.EXE enter image description here

Relevant Code :

    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;
    }

And the hook (that matters)

processInterace.OnProcessOutput+=processInterace_OnProcessOutput;

UPDATE

So I decided to try a new approach to resolve this problem. Since ProcessInterface doesn't appear to give any real control over changing the encoding of the output recieved from the process, I decided to try using the raw Process interface as follows :

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";
        } ) );
    }
}

This results in a new problem. I am not getting an event notification on all new data -- only on new lines terminated in a carriage return. An example of the output is depicted in the image below :

enter image description here

When I alter form_Load in my recent example, you can see that this finally fixes the encoding issue, but a new problem exists where the last line is not being returned until a carriage return is sent from the console:

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" );

}

enter image description here

Kraang Prime
  • 9,981
  • 10
  • 58
  • 124
  • 1
    The AAAA's are happening because the RichTextBox doesn't know how to interpret those extended ASCII characters. – Robert Harvey Mar 31 '16 at 01:20
  • @RobertHarvey - I was considering a `String.Replace()` type hack, however this only depicts a small number of the chars, and doesn't really address the problem. I know these characters are displayable. You will actually get the 'AAAA's if you copy the output from `tree` (via cmd.exe) into anything else.(aka, notepad) – Kraang Prime Mar 31 '16 at 01:21
  • Related: http://stackoverflow.com/q/666385 – Robert Harvey Mar 31 '16 at 01:22
  • @RobertHarvey - possibly, however do you know the encoding to test ? I am thinking this may be more than an encoding issue, but more related to processing ANSI / Terminal specific output. – Kraang Prime Mar 31 '16 at 01:24
  • https://en.wikipedia.org/wiki/Code_page_437. You'll also need a fixed-width font. – Robert Harvey Mar 31 '16 at 01:25
  • I am using the same font as used in `cmd.exe` . Not sure how I would go about switching the richtextbox to display codepage 437. This isn't something in the list. – Kraang Prime Mar 31 '16 at 01:33
  • The solution is going to look something like this: http://stackoverflow.com/a/11952504. You have to convert the extended ASCII to a Unicode equivalent. – Robert Harvey Mar 31 '16 at 01:35
  • @RobertHarvey - ty, but this isn't the problem. I don't think. Switched up my code to convert as follows > `Encoding.GetEncoding(437).GetString(Encoding.GetEncoding(437).GetBytes(args.Content.Substring(lastInput.Length).ToCharArray()));` and still the same output – Kraang Prime Mar 31 '16 at 01:47
  • That doesn't look right. – Robert Harvey Mar 31 '16 at 01:48
  • Where are the characters coming from? A file? A string in memory? It would help if you showed us your relevant code. – Robert Harvey Mar 31 '16 at 01:49
  • Will paste the relevant code. Give me a minute to separate it from the project. – Kraang Prime Mar 31 '16 at 01:50
  • http://ftp.magicsoftware.com/www/help/uniPaaS/mergedProjects/MasteringeDeveloper/How_Do_I_Convert_Between_ANSI_and_Unicode_.htm – Robert Harvey Mar 31 '16 at 01:54
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/107799/discussion-between-sanuel-jackson-and-robert-harvey). – Kraang Prime Mar 31 '16 at 01:59
  • So I have narrowed this down. It is not a font issue or an issue that RichTextBox is incapable of handling it, the output being streamed from StdOut from `Process.Start()` (the process events) is only coming through in the System.Text.Encoding.Default format (which happens to be CP1252) and thus is corrupting the string making it impossible to 'convert' . That being said, how can I force the redirect strings to come through in a certain encoding ? – Kraang Prime Mar 31 '16 at 02:47
  • @RobertHarvey - take a look above, perhaps this smaller issue can be figured out now. Either we can set the output encoding in `ProcessInterface` somehow (i couldn't find a way), or we can force the data from `Process` to output all data, not just 'lines' ending in a carriage return. – Kraang Prime Mar 31 '16 at 05:10

1 Answers1

1

You can use the File class's control over encoding to your advantage.

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);
Tom Blodget
  • 20,260
  • 3
  • 39
  • 72
  • I actually have this resolved without reading from an external file. I was looking at this method as a potential workaround, however I figured out a way to get all the data. Will be posting a solution once i get this Caret thing figured out. The color is ... eh.... – Kraang Prime Apr 01 '16 at 00:39
  • The entire project (with this functional without piping to a file as you are doing here) can be found as the console code for a different question I have raised here > http://stackoverflow.com/questions/36350974/custom-caret-not-being-set-on-initialization-in-c-sharp . Since your solution is one way to do this, and to preserve this link, I am flagging this answer as correct. Could you please add this SO link ref in your answer as it is a much more thorough solution. Thanks – Kraang Prime Apr 01 '16 at 08:04