2012-01-10 2 views
17

У меня проблема в моем проекте. Я хотел бы запустить процесс, 7z.exe (консольная версия). я пробовал три разные вещи:Получить Live-выход из процесса

  • Process.StandardOutput.ReadToEnd();
  • OutputDataReceived & BeginOutputReadLine
  • StreamWriter

Ничего не работает. Он всегда «ждет», чтобы конец процесса отображал то, что я хочу. У меня нет кода для ввода, просто если вы хотите, чтобы мой код с одним из перечисленных вещей вверх. Благодарю.

Edit: Мой код:

 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 событие отлично работает!

+0

Любая причина, по которой вы не используете загрузку DLL/SDK из 7zip, что позволяет значительно улучшить управление, чем любая консольная техника? – Yahia

+0

Это поможет увидеть код, который вы пробовали с процессом, например, при создании процесса – MethodMan

+0

Так как 7z.exe охватывает все функции, которые я хотел. – Extaze

ответ

16

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

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

+0

Посмотрите мой код:/ – Extaze

+0

Все, что я могу найти, вам следует использовать событие OutputDataReceived, на данный момент у меня нет времени проверить его, может быть, позже. (http://harbertc.wordpress.com/2006/05/16/reading-text-from-a-process-executed-programmatically-in-c/) –

+0

Я пробовал все, но проблема была 7z.exe. Я решил взять 7z-библиотеку для C# (SevenZipSharp), и проблема была решена. Спасибо;) – Extaze

0

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

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

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

+1

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

+0

Как сказал Михель, для получения «окончательного» результата – Extaze

1

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

+0

Не работает ... Он ждет, пока процесс перестанет показывать мне информацию ... – Extaze

4

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

Вот где я установить сокет-сервер:

/// <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 список там (я портировал из кода в других местах, где я делал что-то подобное, но называя несколько экземпляров подряд):

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

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

НТН

4

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

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

 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 
    } 
+1

Перенаправление стандартного ввода устраняет проблему для меня. –

Смежные вопросы