Redirected batch file process output not running

2019-09-02 03:30发布

I'm attempting to redirect output from a batch file execution to the main window of our console application.

I'm calling the method to run the process like this:

this.runProcess("\\bar\foo\blah\", "myBatch1.bat", "bat");

The method being called is as follows:

public void runProcess(string aPath,string aName,string aFiletype)
{

  Console.WriteLine();
  Console.WriteLine();
  Console.WriteLine("Started: {0}",DateTime.Now.ToString("dd-MMM hh:mm:ss"));
  Console.WriteLine("Will try run this file {0} {1}",aPath,aName);
  Console.WriteLine("File type {0}",aFiletype);

  string stInfoFileName;
  string stInfoArgs;

  if(aFiletype == "bat")
  {
    stInfoFileName = @"cmd.exe";
    stInfoArgs = "//c " + aName;
  }
  else
  { //vbs
    stInfoFileName = @"cscript";
    stInfoArgs = "//B " + aName;
  }

  this.aProcess.StartInfo.FileName = stInfoFileName;
  this.aProcess.StartInfo.Arguments =  stInfoArgs;
  this.aProcess.StartInfo.WorkingDirectory = @aPath;
  this.aProcess.StartInfo.CreateNoWindow = true;
  this.aProcess.StartInfo.UseShellExecute = false;
  this.aProcess.StartInfo.RedirectStandardError = true;
  this.aProcess.StartInfo.RedirectStandardOutput = true;

  this.aProcess.Start();
  Console.WriteLine("<<<got to here");

  Console.WriteLine(this.aProcess.StandardOutput.ReadToEnd());
  Console.WriteLine(this.aProcess.StandardError.ReadToEnd());

  this.aProcess.WaitForExit(); //<-- Optional if you want program running until your script exit
  this.aProcess.Close();

  Console.WriteLine("Finished: {0}",DateTime.Now.ToString("dd-MMM hh:mm:ss"));
}

To try to figure out what is happening I've added extra calls to WriteLine.
"<<<got to here" gets written to the console then it just hangs and nothing further happens.

Suspect my mistake is something very trivial as my experience with this technology is limited.

What am I doing wrong?

3条回答
乱世女痞
2楼-- · 2019-09-02 03:53

Well, you're using ReadToEnd() - that's going to block until the process exits, basically.

This is an exceptionally bad idea when you're redirecting both Standard Output and Error - when the I/O buffer gets full, both of the applications are going to freeze.

Instead, you might want to use asynchronous I/O to read the output (and write it to console as needed - you'll need to make sure the Error and Output don't mess each other up, though). Or just redirect either one of those rather than both.

The easiest way to handle this is by using ErrorDataReceived and OutputDataReceived events:

aProcess.ErrorDataReceived += (s, e) => Console.WriteLine(e.Data);
aProcess.OutputDataReceived += (s, e) => Console.WriteLine(e.Data);

aProcess.BeginOutputReadLine();
aProcess.BeginErrorReadLine();

aProcess.WaitForExit();

Apart from actually working, this also means that the output is printed out as it comes, rather than when the process exits.

查看更多
迷人小祖宗
3楼-- · 2019-09-02 03:57

I amended the first option after if(aFiletype == "bat") and the bat files are running ok.

public void runProcess(string aPath,string aName,string aFiletype)
{
  aProcess = new Process();

  Console.WriteLine();
  Console.WriteLine();
  Console.WriteLine("Started: {0}",DateTime.Now.ToString("dd-MMM hh:mm:ss"));
  Console.WriteLine("Will try run this file {0} {1}",aPath,aName);
  Console.WriteLine("File type {0}",aFiletype);

  string stInfoFileName;
  string stInfoArgs;

  if(aFiletype == "bat")
  {
    stInfoFileName = (@aPath + @aName);
    stInfoArgs = string.Empty;
  }
  else
  { //vbs
    stInfoFileName = @"cscript";
    stInfoArgs = "//B " + aName;
  }

  this.aProcess.StartInfo.FileName = stInfoFileName;
  this.aProcess.StartInfo.Arguments =  stInfoArgs;

  this.aProcess.StartInfo.WorkingDirectory = @aPath;

  //new 18 june 2015//////
  if(aFiletype == "bat")
  {
    this.aProcess.StartInfo.CreateNoWindow = true;
    this.aProcess.StartInfo.UseShellExecute = false;
    this.aProcess.StartInfo.RedirectStandardError = true;     //<< HJ
    this.aProcess.StartInfo.RedirectStandardOutput = true;    //<< HJ
  }
  ////////////////////////

  this.aProcess.StartInfo.WindowStyle = ProcessWindowStyle.Normal; //.Hidden
  this.aProcess.Start();

  aProcessName = this.aProcess.ProcessName;
  if(aFiletype == "bat")
  {
    this.aProcess.ErrorDataReceived += (s,e) => Console.WriteLine(e.Data);
    this.aProcess.OutputDataReceived += (s,e) => Console.WriteLine(e.Data);
    this.aProcess.BeginOutputReadLine();
    this.aProcess.BeginErrorReadLine();
  }

  this.aProcess.WaitForExit();
  this.aProcess.Dispose();

Console.WriteLine("Process {0} closed: {1}", this.aProcessName, DateTime.Now.ToString("dd-MMM hh:mm:ss"));
}
查看更多
Luminary・发光体
4楼-- · 2019-09-02 04:03

Since you want the child's output in the existing console, you don't need any redirection. Just set UseShellExecute to false and don't set CreateNoWindow.

This code works for me:

using System;
using System.Diagnostics;

namespace ConsoleApplication1
{
    class Program
    {
        Process aProcess = new Process();

        public void runProcess(string aPath, string aName, string aFiletype)
        {
            Console.WriteLine();
            Console.WriteLine();
            Console.WriteLine("Started: {0}", DateTime.Now.ToString("dd-MMM hh:mm:ss"));
            Console.WriteLine("Will try run this file {0} {1}", aPath, aName);
            Console.WriteLine("File type {0}", aFiletype);

            string stInfoFileName;
            string stInfoArgs;

            if (aFiletype == "bat")
            {
                stInfoFileName = "cmd.exe";
                stInfoArgs = "/c " + aPath + aName;
            }
            else
            { //vbs
                stInfoFileName = "cscript";
                stInfoArgs = "/B " + aPath + aName;
            }

            this.aProcess.StartInfo.FileName = stInfoFileName;
            this.aProcess.StartInfo.Arguments = stInfoArgs;
            this.aProcess.StartInfo.WorkingDirectory = aPath;
            this.aProcess.StartInfo.UseShellExecute = false;

            this.aProcess.Start();

            Console.WriteLine("<<<got to here");

            this.aProcess.WaitForExit(); //<-- Optional if you want program running until your script exit
            this.aProcess.Close();

            Console.WriteLine("Finished: {0}", DateTime.Now.ToString("dd-MMM hh:mm:ss"));
        }

        static void Main(string[] args)
        {
            new Program().runProcess("c:\\working\\", "test.bat", "bat");
            Console.WriteLine("Exiting");
        }
    }
}

I took out the redirection and associated logic, and the line that set CreateNoWindow. I also added aPath to the command line so that it would work for UNC paths (paths with no drive letter) since they can't be set as the working directory.

查看更多
登录 后发表回答