从Process获取实时输出

Ext*_*aze 21 c# redirect process live

我的项目有问题.我想启动一个进程,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);
            }
        }
Run Code Online (Sandbox Code Playgroud)

要么

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);
}
Run Code Online (Sandbox Code Playgroud)

要么

process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
string output = p.StandardOutput.ReadToEnd();
process.WaitForExit();
Run Code Online (Sandbox Code Playgroud)

"过程"是我的预制过程

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

Mic*_*gem 23

看看这个页面,它看起来这是你的解决方案:http://msdn.microsoft.com/en-us/library/system.diagnostics.process.beginoutputreadline.aspxhttp://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 = "-R C:\\";

        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();
Run Code Online (Sandbox Code Playgroud)

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

  • `p.BeginErrorReadLine();` 是需要的,否则错误处理程序不会被调用 (2认同)

use*_*599 6

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

用法示例:

        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;
Run Code Online (Sandbox Code Playgroud)

...

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

    void ErrorDataReceived(object sender, DataReceivedEventArgs e)
    {
        // Process line provided in e.Data
    }
Run Code Online (Sandbox Code Playgroud)

  • 标准输入的重定向为我解决了这个问题。 (2认同)

Bor*_*att 6

该问题是由调用Process.WaitForExit方法引起的。根据文档,它的作用是:

设置等待关联进程退出的时间段,并阻塞当前执行线程,直到时间过去或进程退出。为了避免阻塞当前线程,请使用 Exited 事件

因此,为了防止线程阻塞直到进程退出,请连接Process 对象的Process.Exited事件处理程序,如下所示。仅当 EnableRaisingEvents 属性的值为 true 时,Exited 事件才会发生。

    process.EnableRaisingEvents = true;
    process.Exited += Proc_Exited;


    private void Proc_Exited(object sender, EventArgs e)
    {
        // Code to handle process exit
    }
Run Code Online (Sandbox Code Playgroud)

通过这种方式,您将能够在流程仍在运行时通过Process.OutputDataReceived事件获取流程的输出,就像您当前所做的那样。(PS - 该事件页面上的代码示例也错误地使用了 Process.WaitForExit。)

另一个注意事项是,您需要确保在 Exited 方法触发之前您的 Process 对象不会被清理。如果您的进程是在 using 语句中初始化的,这可能是一个问题。


小智 5

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

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

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

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

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

/// <summary>
/// Handles the OK button click.
/// </summary>
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();
Run Code Online (Sandbox Code Playgroud)

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

/// <summary>
/// Establishes a socket server for communication with each chapter build script so we can get progress updates.
/// </summary>
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<Thread>();

    for (int i = 0; i < 1; i++) {
        currentThread = new Thread(new ThreadStart(HandleIncomingSocketMessage));
        _builderCommThreads.Add(currentThread);
        currentThread.Start();
    }
}

/// <summary>
/// Tears down socket server.
/// </summary>
private void TearDownSocketServer() {
    _builderCommThreads = null;

    _ipAddress = null;
    _listener = null;
}
Run Code Online (Sandbox Code Playgroud)

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

/// <summary>
/// Handles the incoming socket message.
/// </summary>
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;
}
Run Code Online (Sandbox Code Playgroud)

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

private TcpListener _listener = null;
private IPAddress _ipAddress = null;
private List<Thread> _builderCommThreads = null;
private System.Object _threadLock = new System.Object();
Run Code Online (Sandbox Code Playgroud)

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

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

/// <summary>
/// Main method.
/// </summary>
/// <param name="args"></param>
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();
    }
}

/// <summary>
/// Establishes the socket client.
/// </summary>
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();
    }
}

/// <summary>
/// Clean up this instance.
/// </summary>
private static void Cleanup() {
    _s.Close();
    _client.Close();

    _client = null;
    _s = null;
    _sr = null;
    _sw = null;
}

/// <summary>
/// Logs a message for output.
/// </summary>
/// <param name="message"></param>
private static void Log(string message) {
    if (_sw != null) {
        _sw.WriteLine(message);
    } else {
        Console.Out.WriteLine(message);
    }
}
Run Code Online (Sandbox Code Playgroud)

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

HTH