Подтвердить что ты не робот

Получить 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);
            }
        }

или

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-функции без 7z.exe(возможно, с 7za.exe или с некоторой DLL). Спасибо всем. Чтобы ответить на вопрос, событие OuputDataRecieved отлично работает!

4b9b3361

Ответ 1

Взгляните на эту страницу, похоже, это решение для вас: http://msdn.microsoft.com/en-us/library/system.diagnostics.process.beginoutputreadline.aspx и http://msdn.microsoft. ком /EN-US/library/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();

Кстати, ls -R C:\перечисляет все файлы из корня C: рекурсивно. Это много файлов, и я уверен, что это не будет сделано, когда первые результаты появятся на экране. Существует вероятность того, что 7zip задержит вывод перед его отображением. Я не уверен, какие параметры вы даете процессу.

Ответ 2

Я не знаю, ищет ли кто-нибудь решение для этого, но для меня это появилось несколько раз, потому что я пишу инструмент в Unity в поддержку некоторых игр и из-за ограниченной функциональной совместимости некоторых системы с моно (например, PIA для чтения текста из Word, например), мне часто приходится писать исполняемые исполняемые файлы (иногда исполняемые Windows, иногда MacOS) OS и запускать их из 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();

Здесь, где я устанавливаю сервер сокетов:

/// <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;
}

Здесь мой обработчик сокетов для потока... обратите внимание, что вам придется создавать несколько потоков в некоторых случаях; почему у меня есть этот _builderCommThreads List там (я портировал его из кода в другом месте, где я делал что-то подобное, но вызывал несколько экземпляров подряд):

/// <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;
}

Конечно, вам нужно будет объявить некоторые вещи вверху:

private TcpListener _listener = null;
private IPAddress _ipAddress = null;
private List<Thread> _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();

/// <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);
    }
}

... Я использую это, чтобы запустить инструмент командной строки в Windows, который использует материал PIA для вывода текста из документа Word. Я попробовал PIA.dlls в Unity, но столкнулся с проблемами взаимодействия с моно. Я также использую его в MacOS для вызова сценариев оболочки, которые запускают дополнительные экземпляры Unity в пакетном режиме и запускают сценарии редактора в тех случаях, которые возвращаются к инструменту через это соединение сокета. Это здорово, потому что теперь я могу отправлять отзывы пользователю, отлаживать, отслеживать и отвечать на конкретные этапы процесса, и так далее, и так далее.

НТН

Ответ 3

Чтобы правильно обрабатывать вывод и/или перенаправление ошибок, вы также должны перенаправить ввод. Кажется, это особенность/ошибка во время выполнения внешнего приложения, которое вы запускаете, и из того, что я видел до сих пор, он больше нигде не упоминается.

Пример использования:

        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
    }

Ответ 4

Я использовал класс CmdProcessor, описанный здесь по нескольким проектам с большим успехом. Сначала он выглядит немного сложнее, но очень прост в использовании.

Ответ 5

Попробуйте это.

        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.

Теперь для обновления вывода в пользовательский интерфейс вы можете использовать timer с двумя строками

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

Это может помочь вам

Ответ 6

Проблема вызвана вызовом метода Process.WaitForExit. Что это делает, согласно документации, это:

Устанавливает период ожидания для завершения связанного процесса и блокирует текущий поток выполнения до тех пор, пока не истечет время или процесс не завершится. Чтобы избежать блокировки текущего потока, используйте событие Exited.

Итак, чтобы получить выходные данные процесса во время его работы, подключите обработчик события Process.Exited объекта Process, как показано ниже. Событие Exited может возникнуть, только если значение свойства EnableRaisingEvents равно true.

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


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

Сделав это, вы сможете получить выходные данные вашего процесса, пока он еще выполняется, через событие Process.OutputDataReceived, как вы это делаете в настоящее время. (PS - пример кода на этой странице события также допускает ошибку при использовании Process.WaitForExit.)

Еще одно замечание: вам нужно убедиться, что ваш объект Process не очищен, прежде чем ваш метод Exited может сработать. Если ваш процесс инициализирован в операторе использования, это может быть проблемой.