7

I'm working on a custom IDE in C# for a scripting language, and I have a problem.

I'm trying to start the compiler process (pawncc.exe) and pass arguments to it. I've done that, and now I have a problem. When I want to display the output from the compiler application, it only displays some parts of it. It should output this (got this from the command prompt):

Pawn compiler 3.2.3664                  Copyright (c) 1997-2006, ITB CompuPhase

newGM.pwn(0) : fatal error 100: cannot read from file: "includes/main_include.inc"

Compilation aborted.
1 Error.

But it doesn't. It outputs this (in the application, using the same command/arguments):

Pawn compiler 3.2.3664          Copyright (c) 1997-2006, ITB CompuPhase


1 Error.

I just don't get it! It's a really weird thing. It might be something simple but I've been looking at it, and researching for hours now! Here's my code:

        public Form3(string path)
        {
            InitializeComponent();

            this._path = path;

            Process myProcess = new Process();
            ProcessStartInfo startInfo = new ProcessStartInfo("pawncc.exe");
            startInfo.CreateNoWindow = true;
            startInfo.UseShellExecute = false;
            startInfo.RedirectStandardOutput = true;
            startInfo.Arguments = path + " -r -d2";
            myProcess.StartInfo = startInfo;
            myProcess.Start();

            while (true)
            {
                string myString;
                byte[] buffer = new byte[512];
                var ar = myProcess.StandardOutput.BaseStream.BeginRead(buffer, 0, 512, null, null);
                ar.AsyncWaitHandle.WaitOne();
                var bytesRead = myProcess.StandardOutput.BaseStream.EndRead(ar);
                if (bytesRead > 0)
                {
                    myString = Encoding.ASCII.GetString(buffer, 0, bytesRead);
                }
                else
                {
                    myProcess.WaitForExit();
                    break;
                }
                richTextBox1.Text = myString;

            }

        }

!!EDIT:

It does the same thing with this code:

        public Form3(string path)
        {
            InitializeComponent();

            this._path = path;

            Process myProcess = new Process();
            ProcessStartInfo startInfo = new ProcessStartInfo("pawncc.exe");
            startInfo.CreateNoWindow = true;
            startInfo.UseShellExecute = false;
            startInfo.RedirectStandardOutput = true;
            startInfo.RedirectStandardError = true;
            startInfo.Arguments = path + " -r -d2";
            myProcess.StartInfo = startInfo;
            myProcess.Start();

            using (StreamReader reader = myProcess.StandardOutput)
            {
                string result = reader.ReadToEnd();
                richTextBox1.Text = result;
            }
        }
Quin Schurman
  • 118
  • 1
  • 6
  • I just asked a [similar question](http://stackoverflow.com/q/14094771/250725) a couple of days ago. The short version of the answer is that handling the data events (`myProcess.OutputDataReceived` and `myProcess.ErrorDataReceived `) are far for reliable than using the synchronous read methods. – psubsee2003 Jan 02 '13 at 01:29

3 Answers3

6

You need to redirect the standard error stream as well:

startInfo.RedirectStandardError = true;

Edit: I just reviewed the code and discovered that you are only readonly the StandardOutput stream.

I generally monitor the process for both the standard and error output streams using the DataReceived events on the process and adding the results into a stringbuilder, then storing the StringBuilder content in the UI element:

    private static System.Text.StringBuilder m_sbText;

    public Form3(string path)
    {
        InitializeComponent();

        this._path = path;

        Process myProcess = new Process();
        ProcessStartInfo startInfo = new ProcessStartInfo("pawncc.exe");
        startInfo.CreateNoWindow = true;
        startInfo.UseShellExecute = false;
        startInfo.RedirectStandardOutput = true;
        startInfo.RedirectStandardError = true;
        startInfo.Arguments = path + " -r -d2";
        myProcess.StartInfo = startInfo;

        m_sbText = new System.Text.StringBuilder(1000);

        myProcess.OutputDataReceived += ProcessDataHandler;
        myProcess.ErrorDataReceived += ProcessDataHandler;

        myProcess.Start();

        myProcess.BeginOutputReadLine();
        myProcess.BeginErrorReadLine();

        while (!myProcess.HasExited)
        {
            System.Threading.Thread.Sleep(500);
            System.Windows.Forms.Application.DoEvents();
        }
        RichTextBox1.Text = m_sbText.ToString();
    }

    private static void ProcessDataHandler(object sendingProcess, DataReceivedEventArgs outLine)
    {
        // Collect the net view command output. 
        if (!String.IsNullOrEmpty(outLine.Data))
        {
            // Add the text to the collected output.
            m_sbText.AppendLine(outLine.Data);
        }
    }

There are obviously variations on this, but this should get you started.

competent_tech
  • 44,465
  • 11
  • 90
  • 113
  • @QuinSchurman: I just realized that you were only monitoring the StandardOutput stream. I have updated the answer to show using the process events for receiving the incoming data, which I think is much easier to use. – competent_tech Jan 02 '13 at 01:26
  • Thanks so much! It worked. Marked as answer. Also, thankyou for the quick response. – Quin Schurman Jan 02 '13 at 01:30
  • So I've used this code - it works nicely, but I would like to expand on this by splitting up the output and sorting them into a list. For example, I could put errors with line numbers into a list in the UI, making the output readable easier. Better example: Change this `newGM.pwn(0) : fatal error 100: cannot read from file: "includes/main_include.inc"` to this (In the tab of newGM, a docked list at the bottom.) `Errors: %i. error id 100: (FATAL!) file "includes/main_include.inc" is missing or unreadable.` – Quin Schurman Jan 02 '13 at 22:11
  • There are two calls to `Process.Start`. – Lii Mar 30 '15 at 16:47
1

I've noticed some sporadic issues when dealing with the raw output/error streams from spawned processes in the past, hence why I usually deal with captured output via eventing:

Process myProcess = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo("pawncc.exe");
startInfo.CreateNoWindow = true;
startInfo.UseShellExecute = false;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardError = true;
startInfo.Arguments = path + " -r -d2";
myProcess.EnableRaisingEvents = true;
myProcess.OutputDataReceived += OnOutputDataReceived;
myProcess.ErrorDataReceived += OnErrorDataReceived;
myProcess.StartInfo = startInfo;
myProcess.Start();
myProcess.BeginOutputReadLine();
myProcess.BeginErrorReadLine(); 
JerKimball
  • 16,584
  • 3
  • 43
  • 55
  • This doesn't explain why some output is retrieved but not the error. – Jeremy Thompson Jan 02 '13 at 01:20
  • @JeremyThompson True, but I've noticed some sporadic issues when dealing with the raw output/error streams from spawned processes (processii?) in the past, hence the suggestion (and the inclusion of the StandardError items) – JerKimball Jan 02 '13 at 01:24
  • +1 fair call, possibly add this in your answer `I've noticed some sporadic issues when dealing with the raw output/error streams from spawned processes in the past, hence my suggestion to hook up the StandardError events.` – Jeremy Thompson Jan 02 '13 at 01:27
1

I dont have the pawnCC application so I cant try but it appears they restrict the verbosity of debugging information to external applications - apart from the command prompt.

Can you try spawning the pawncc.exe via cmd:

"cmd.exe \c CommandParameterToLaunchPawnCCwithArguments"
Jeremy Thompson
  • 61,933
  • 36
  • 195
  • 321