从Process获取实时输出

我的项目有问题。 我想启动一个进程,7z.exe(控制台版)。 我尝试过三种不同的东西:

  • Process.StandardOutput.ReadToEnd();
  • OutputDataReceived和BeginOutputReadLine
  • 的StreamWriter

什么都行不通。 它始终“等待”过程结束以显示我想要的内容。 我没有任何代码可以放,只是如果你想要我的代码与其中一个列出的东西。 谢谢。

编辑:我的代码:

process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.CreateNoWindow = true; process.Start(); this.sr = process.StandardOutput; while (!sr.EndOfStream) { String s = sr.ReadLine(); if (s != "") { System.Console.WriteLine(DateTime.Now + " - " + s); } } 

要么

 process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardOutput = true; process.OutputDataReceived += new DataReceivedEventHandler(recieve); process.StartInfo.CreateNoWindow = true; process.Start(); process.BeginOutputReadLine(); process.WaitForExit(); public void recieve(object e, DataReceivedEventArgs outLine) { System.Console.WriteLine(DateTime.Now + " - " + outLine.Data); } 

要么

 process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardOutput = true; process.Start(); string output = p.StandardOutput.ReadToEnd(); process.WaitForExit(); 

“过程”是我的预制过程

好吧我知道为什么它不能正常工作:7z.exe是错误:它在控制台中显示百分比加载,并且仅在当前文件完成时才发送信息。 例如,在提取中,它工作得很好:)。 我将搜索另一种方法来使用没有7z.exe的7z函数(可能使用7za.exe或一些DLL)。 谢谢大家。 要回答这个问题,OuputDataRecieved事件运行正常!

看看这个页面,它看起来这是你的解决方案: http : //msdn.microsoft.com/en-us/library/system.diagnostics.process.beginoutputreadline.aspx和http://msdn.microsoft。 COM / EN-US /库/ system.diagnostics.process.standardoutput.aspx

[编辑]这是一个有效的例子:

  Process p = new Process(); p.StartInfo.RedirectStandardError = true; p.StartInfo.RedirectStandardOutput = true; p.StartInfo.UseShellExecute = false; p.StartInfo.CreateNoWindow = true; p.StartInfo.FileName = @"C:\Program Files (x86)\gnuwin32\bin\ls.exe"; p.StartInfo.Arguments = "-RC:\\"; p.OutputDataReceived += new DataReceivedEventHandler( (s, e) => { Console.WriteLine(e.Data); } ); p.ErrorDataReceived += new DataReceivedEventHandler((s, e) => { Console.WriteLine(e.Data); }); p.Start(); p.BeginOutputReadLine(); p.BeginErrorReadLine(); 

顺便说一句,ls -RC:\递归地列出来自C的根目录的所有文件。 这些是很多文件,我确信当第一个结果显示在屏幕上时它没有完成。 在显示输出之前,7zip有可能保持输出。 我不确定你给这个过程提供了什么样的参数。

我不知道是否还有人在寻找解决方案,但它已经多次出现,因为我在Unity中编写了一个支持某些游戏的工具,并且由于某些系统与单声道的互操作性有限(比如PIA从Word读取文本),我经常需要编写特定于操作系统(有时是Windows,有时是MacOS)的可执行文件,并从Process.Start()启动它们。

问题是,当您启动这样的可执行文件时,它将在另一个阻止您的主应用程序的线程中启动,从而导致挂起。 如果您希望在此期间为您的用户提供有用的反馈,而不是您各自的操作系统所形成的旋转图标,那么您就有点紧张了。 使用流将不起作用,因为线程仍然被阻塞,直到执行完成。

我遇到的解决方案对某些人来说可能看起来很极端,但我发现对我来说效果很好,就是使用套接字和multithreading在两个应用程序之间建立可靠的同步通信。 当然,这只适用于您同时创作这两个应用的情况。 如果没有,我想你运气不好。 …我想看看它是否适用于使用传统流方法的multithreading,所以如果有人想尝试并在这里发布结果会很棒。

无论如何,这是目前为我工作的解决方案:

在主要或调用应用程序中,我执行以下操作:

 ///  /// Handles the OK button click. ///  private void HandleOKButtonClick() { string executableFolder = ""; #if UNITY_EDITOR executableFolder = Path.Combine(Application.dataPath, "../../../../build/Include/Executables"); #else executableFolder = Path.Combine(Application.dataPath, "Include/Executables"); #endif EstablishSocketServer(); var proc = new Process { StartInfo = new ProcessStartInfo { FileName = Path.Combine(executableFolder, "WordConverter.exe"), Arguments = locationField.value + " " + _ipAddress.ToString() + " " + SOCKET_PORT.ToString(), UseShellExecute = false, RedirectStandardOutput = true, CreateNoWindow = true } }; proc.Start(); 

这是我建立套接字服务器的地方:

 ///  /// Establishes a socket server for communication with each chapter build script so we can get progress updates. ///  private void EstablishSocketServer() { //_dialog.SetMessage("Establishing socket connection for updates. \n"); TearDownSocketServer(); Thread currentThread; _ipAddress = Dns.GetHostEntry(Dns.GetHostName()).AddressList[0]; _listener = new TcpListener(_ipAddress, SOCKET_PORT); _listener.Start(); UnityEngine.Debug.Log("Server mounted, listening to port " + SOCKET_PORT); _builderCommThreads = new List(); for (int i = 0; i < 1; i++) { currentThread = new Thread(new ThreadStart(HandleIncomingSocketMessage)); _builderCommThreads.Add(currentThread); currentThread.Start(); } } ///  /// Tears down socket server. ///  private void TearDownSocketServer() { _builderCommThreads = null; _ipAddress = null; _listener = null; } 

这是我的线程的套接字处理程序…请注意,在某些情况下,您必须创建多个线程; 这就是为什么我在那里有_builderCommThreads列表(我从其他地方的代码移植它,我在做类似的事情,但连续调用多个实例):

 ///  /// Handles the incoming socket message. ///  private void HandleIncomingSocketMessage() { if (_listener == null) return; while (true) { Socket soc = _listener.AcceptSocket(); //soc.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 10000); NetworkStream s = null; StreamReader sr = null; StreamWriter sw = null; bool reading = true; if (soc == null) break; UnityEngine.Debug.Log("Connected: " + soc.RemoteEndPoint); try { s = new NetworkStream(soc); sr = new StreamReader(s, Encoding.Unicode); sw = new StreamWriter(s, Encoding.Unicode); sw.AutoFlush = true; // enable automatic flushing while (reading == true) { string line = sr.ReadLine(); if (line != null) { //UnityEngine.Debug.Log("SOCKET MESSAGE: " + line); UnityEngine.Debug.Log(line); lock (_threadLock) { // Do stuff with your messages here } } } // } catch (Exception e) { if (s != null) s.Close(); if (soc != null) soc.Close(); UnityEngine.Debug.Log(e.Message); //return; } finally { // if (s != null) s.Close(); if (soc != null) soc.Close(); UnityEngine.Debug.Log("Disconnected: " + soc.RemoteEndPoint); } } return; } 

当然,你需要在顶部声明一些内容:

 private TcpListener _listener = null; private IPAddress _ipAddress = null; private List _builderCommThreads = null; private System.Object _threadLock = new System.Object(); 

…然后在调用的可执行文件中,设置另一端(在这种情况下我使用静态,你可以使用你想要的):

 private static TcpClient _client = null; private static Stream _s = null; private static StreamReader _sr = null; private static StreamWriter _sw = null; private static string _ipAddress = ""; private static int _port = 0; private static System.Object _threadLock = new System.Object(); ///  /// Main method. ///  ///  static void Main(string[] args) { try { if (args.Length == 3) { _ipAddress = args[1]; _port = Convert.ToInt32(args[2]); EstablishSocketClient(); } // Do stuff here if (args.Length == 3) Cleanup(); } catch (Exception exception) { // Handle stuff here if (args.Length == 3) Cleanup(); } } ///  /// Establishes the socket client. ///  private static void EstablishSocketClient() { _client = new TcpClient(_ipAddress, _port); try { _s = _client.GetStream(); _sr = new StreamReader(_s, Encoding.Unicode); _sw = new StreamWriter(_s, Encoding.Unicode); _sw.AutoFlush = true; } catch (Exception e) { Cleanup(); } } ///  /// Clean up this instance. ///  private static void Cleanup() { _s.Close(); _client.Close(); _client = null; _s = null; _sr = null; _sw = null; } ///  /// Logs a message for output. ///  ///  private static void Log(string message) { if (_sw != null) { _sw.WriteLine(message); } else { Console.Out.WriteLine(message); } } 

…我正在使用它在Windows上启动一个命令行工具,它使用PIA的东西从Word文档中提取文本。 我在Unity中尝试过PIA的.dll,但遇到了单声道的互操作问题。 我还在MacOS上使用它来调用shell脚本,这些脚本在批处理模式下启动其他Unity实例,并在那些通过此套接字连接回访该工具的实例中运行编辑器脚本。 这很棒,因为我现在可以向用户发送反馈,调试,监控并响应流程中的特定步骤,等等。

HTH

要正确处理输出和/或错误重定向,还必须重定向输入。 它似乎是你开始的外部应用程序的运行时的function/错误,从我到目前为止看到的,它没有在其他任何地方提到。

用法示例:

  Process p = new Process(...); p.StartInfo.UseShellExecute = false; p.StartInfo.RedirectStandardOutput = true; p.StartInfo.RedirectStandardError = true; p.StartInfo.RedirectStandardInput = true; // Is a MUST! p.EnableRaisingEvents = true; p.OutputDataReceived += OutputDataReceived; p.ErrorDataReceived += ErrorDataReceived; Process.Start(); p.BeginOutputReadLine(); p.BeginErrorReadLine(); p.WaitForExit(); p.OutputDataReceived -= OutputDataReceived; p.ErrorDataReceived -= ErrorDataReceived; 

  void OutputDataReceived(object sender, DataReceivedEventArgs e) { // Process line provided in e.Data } void ErrorDataReceived(object sender, DataReceivedEventArgs e) { // Process line provided in e.Data } 

我已经在几个项目中使用了这里描述的CmdProcessor类并取得了很大的成功。 起初看起来有点令人生畏,但很容易使用。

试试这个。

  Process notePad = new Process(); notePad.StartInfo.FileName = "7z.exe"; notePad.StartInfo.RedirectStandardOutput = true; notePad.StartInfo.UseShellExecute = false; notePad.Start(); StreamReader s = notePad.StandardOutput; String output= s.ReadToEnd(); notePad.WaitForExit(); 

让上面的内容成为一个thread

现在,要将输出更新为UI,您可以使用具有两行的timer

  Console.Clear(); Console.WriteLine(output); 

这可能对你有所帮助