2

I have implemented the following code (adapted from the tutorial) in order to run a command prompt window, run a program and read the output. The code is called from a ButtonClick event handler, which is nested in a User Control.

I was under the impression that this would allow the rest of my program to function whilst the external process runs, due to the fact that the methods are 'asynchronous'. However, this does not appear to be the case, as my UI will freeze while the operation is running. I should add that the output received when the cmd process ends is correct.

Sorry to dump a load of code like this, just not sure what else to do at this point!

Any assistance would be greatly appreciated.

public static void runExternalProcess()
{
    StringBuilder output = new StringBuilder();

    Process cmd = new Process();
    cmd.StartInfo.FileName = "cmd.exe";          
    cmd.StartInfo.UseShellExecute = false;
    cmd.StartInfo.CreateNoWindow = true;
    cmd.StartInfo.RedirectStandardOutput = true;

    cmd.OutputDataReceived += new DataReceivedEventHandler(outputEventHandler);        
    cmd.StartInfo.RedirectStandardInput = true;        
    cmd.Start();
    cmd.BeginOutputReadLine();      

    StreamWriter sortStreamWriter = cmd.StandardInput; 
    StreamWriter sw = cmd.StandardInput;

    if (sw.BaseStream.CanWrite)
    {
        sw.WriteLine("ping www.google.com");
    }

    sw.Close();

    cmd.WaitForExit();

    MessageBox.Show(output.ToString());

    cmd.Close();
}

private static void outputEventHandler(object sendingProcess, DataReceivedEventArgs e)
{
    if (!String.IsNullOrEmpty(e.Data))
    {
        output.Append(e.Data + Environment.NewLine);
    }
}
abatishchev
  • 98,240
  • 88
  • 296
  • 433
Mr. Spice
  • 1,552
  • 1
  • 15
  • 15

3 Answers3

5

Your problem is here:

cmd.WaitForExit();

This is a blocking call.

If you want to respond to the process exiting without blocking then you need to add a handler for the Exited event.

Matt Burland
  • 44,552
  • 18
  • 99
  • 171
5

How about registering for the Exited event and showing the MessageBox there:

StringBuilder output = new StringBuilder();
Process cmd = new Process();

public void RunExternalPing()
{
   cmd.StartInfo.FileName = "cmd.exe";
   cmd.StartInfo.UseShellExecute = false;
   cmd.StartInfo.CreateNoWindow = true;
   cmd.StartInfo.RedirectStandardOutput = true;
   cmd.StartInfo.RedirectStandardInput = true;

   cmd.EnableRaisingEvents = true;
   cmd.OutputDataReceived += 
      new DataReceivedEventHandler(cmd_OutputDataReceived);
   cmd.Exited += new EventHandler(cmd_Exited);

   cmd.Start();
   cmd.BeginOutputReadLine();
   StreamWriter sw = cmd.StandardInput;
   sw.WriteLine("ping www.google.com");
   sw.Close();
}

void cmd_Exited(object sender, EventArgs e)
{
   MessageBox.Show(output.ToString());
   cmd.Dispose();
}

private void cmd_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
   if (!String.IsNullOrEmpty(e.Data))
   {
      output.Append(e.Data + Environment.NewLine);
   }
}

From MSDN:

There are two ways of being notified when the associated process exits: synchronously and asynchronously. Synchronous notification relies on calling the WaitForExit method to pause the processing of your application until the associated component exits. Asynchronous notification relies on the Exited event. In either case, EnableRaisingEvents must be set to true for the Process component to receive notification that the process has exited.

SwDevMan81
  • 48,814
  • 22
  • 151
  • 184
  • I've added in the code above, and removed the watiforexit() call, but now the function doesnt show the message box, any ideas? – Mr. Spice Apr 12 '12 at 16:12
  • Yes sorry was being slow (and then edited the comment). The message box now does not show at all though. Would you have any idea as to why? – Mr. Spice Apr 12 '12 at 16:18
  • Is the command window still open? It wont show until the process ends (the command window is closed). – SwDevMan81 Apr 12 '12 at 16:21
  • No the window opens for a few seconds (while the command runs) and then closes, but no messagebox from the exitedEventHandler – Mr. Spice Apr 12 '12 at 16:23
  • Ah I see, after closing the stream writer, I also called `cmd.close()`. I take it this line is not necessary if the process closes itself? – Mr. Spice Apr 12 '12 at 16:34
  • No it wont, you should `Dispose` of the `Process` when you are done with it. So I would move the `cmd` object outside the local function and call `cmd.Dispose` in the `cmd_Exited` function – SwDevMan81 Apr 12 '12 at 16:43
2

All this code is linear, if you don't want to freeze the thread you're in, you should create a new thread and perform a callback when that thread is finished.

Check out BackgroundWorker.

Matthew
  • 24,703
  • 9
  • 76
  • 110