2014-01-02 3 views
2

У меня есть приложение C# A, которое вызывает другое приложение C# B, которое вызывает несколько экземпляров приложения C.Перенаправление стандартного вывода

Я хотел бы перенаправить вывод приложение C к выходу приложения А.

У меня уже есть рабочее перенаправление вывода приложения B в выход приложения A.

Теперь, из кода приложения B, я перенаправляю вывод каждого процесса, и я печатаю перенаправленный вывод на консоль. К сожалению, по какой-то причине ничего не печатается.

(Я в настоящее время тестирую его без использования приложения A - я использую только приложение B).

Вот код:

private void runSingleFile (string execFile, string commandArgs) 
{ 
     Process processToRun = new Process(); 
     processToRun .StartInfo.FileName = execFile; 
     processToRun .StartInfo.Arguments = commandArgs; 
     processToRun .StartInfo.UseShellExecute = false; 
     processToRun .StartInfo.RedirectStandardOutput = true; 
     processToRun .OutputDataReceived += outputRedirection; 
     processToRun.Start(); 
     Console.WriteLine(""); 
     Thread.Sleep(100); 
     processToRun.BeginOutputReadLine(); 
} 

private void outputRedirection(object sendingProcess, DataReceivedEventArgs outLine) 
{ 
     try 
     { 
      if (outLine.Data != null) 
       Console.WriteLine(outLine.Data); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine(ex.Message); 
      return; 
     } 
} 

Любые идеи?

+1

Некоторые приложения пишут StandardError вместо StandardOutput, попробовали ли вы перенаправить свой StandardError? –

+0

Я точно знаю, что приложение 'C' пишет в StandardOutput, я его написал ... В любом случае, я пробовал это сейчас - до сих пор ничего. – Idanis

ответ

1

Код, который вы опубликовали, работает. Учитывая тестовую программу

// ConsoleApplication2.exe 
static void Main(string[] args) 
{ 
    Console.WriteLine("Test1..."); 
    Console.WriteLine("Test2..."); 
    System.Threading.Thread.Sleep(100); 
    Console.WriteLine("Test3..."); 
} 

называется:

static void Main(string[] args) 
{ 
    runSingleFile("ConsoleApplication2.exe", ""); 
    runSingleFile("ConsoleApplication2.exe", ""); 
    runSingleFile("ConsoleApplication2.exe", ""); 
    Console.ReadLine(); 
} 

производит выход:

enter image description here

Очевидно что-то еще не работает в другой части кода (то, что вы не показали нас).

Наиболее вероятное объяснение, если приложение C работает правильно, заключается в том, что приложение B завершается до того, как все экземпляры C завершены. Возможно, вам придется добавить код, который заставляет B ждать всех экземпляров C для возврата.

Обратите внимание, что:.

static void Main(string[] args) 
{ 
    runSingleFile("ConsoleApplication2.exe", ""); 
    runSingleFile("ConsoleApplication2.exe", ""); 
    runSingleFile("ConsoleApplication2.exe", ""); 
    //Console.ReadLine(); // ** Don't wait! 
} 

завершается немедленно и не возвращает некоторые или все данные (особенно, если вы удалите Sleep вызов в runSingleFile

Рассмотрим:

static long processCount = 0;    //ADD 

static void runSingleFile(string execFile, string commandArgs) 
{ 
    Interlocked.Increment(ref processCount); //ADD 
    Process processToRun = new Process(); 
    processToRun.StartInfo.FileName = execFile; 
    processToRun.StartInfo.Arguments = commandArgs; 
    processToRun.StartInfo.UseShellExecute = false; 
    processToRun.StartInfo.RedirectStandardOutput = true; 
    processToRun.OutputDataReceived += outputRedirection; 
    processToRun.EnableRaisingEvents = true; //ADD 
    processToRun.Exited += processExited;  //ADD 
    processToRun.Start(); 
    Console.WriteLine(""); 
    processToRun.BeginOutputReadLine(); 
} 

static void processExited(object sender, EventArgs e) 
{ 
    Interlocked.Decrement(ref processCount); 
} 

с

static void Main(string[] args) 
{ 
    runSingleFile("ConsoleApplication2.exe", ""); 
    runSingleFile("ConsoleApplication2.exe", ""); 
    runSingleFile("ConsoleApplication2.exe", ""); 
    while (Interlocked.Read(ref processCount) > 0) 
    { 
     System.Threading.Thread.Sleep(100); 
    } 
} 

Приведенное выше приложение B ждет, пока не вернутся все порожденные процессы. Пример упрощен и, очевидно, может быть улучшен, но он демонстрирует проблему, я думаю, и предлагает метод решения.

Возможно, у вас может возникнуть соблазн использовать что-то более элегантное, например, WaitHandle.WaitAll(), но это создает проблему, когда Джош отметил, что ваши выходные события могут не срабатывать до тех пор, пока процесс не завершится - дескриптор процесса будет сигнализировать о его прекращении, но его опубликовано сообщения могут оставаться в очереди. Ожидание на событии Exited позволяет убрать это условие гонки, поскольку это событие всегда будет последним сообщением в очереди (AFAIK).

Также обратите внимание на использование здесь функций Interlocked - Console.WriteLine - это потокобезопасный, но другой доступ к переменной - нет. Поскольку в консольном приложении нет контекста синхронизации, события, вызванные порожденными процессами, обрабатываются потоками в threadpool (а не в основном потоке консольного приложения). Это вводит все проблемы, связанные с многопотоковой обработкой, которые должны управляться надлежащим образом.

1

Объект .net Process не позволяет легко «сделать правильную вещь» относительно правильной обработки ввода-вывода.

Эти страницы перечислены некоторые из проблем, которые должны быть рассмотрены:

Из кода образец, вот некоторые из предметов, которые вы хотите посмотреть:

  1. Вы захотите захватить как stderr, так и stdin (последний должен быть немедленно закрыт, если не используется).
  2. Вам также необходимо знать, что вы все равно можете получать события на своем outputRedirection обратном вызове после, после чего завершился дочерний процесс.
Смежные вопросы