2009-07-17 12 views
25

Mam aplikację, która wywołuje inny proces w oknie poleceń, a proces ten aktualizuje statystyki, które wyświetlają się w oknie konsoli. Myślałem, że to dość prosta operacja, ale nie mogę tego zmusić do działania. Czy czegoś brakuje?ProcessInfo and RedirectStandardOutput

string assemblyLocation = Assembly.GetExecutingAssembly().Location; 

Process process = new Process 
{ 
    ProcessStart = 
    { 
     RedirectStandardOutput = true, 
     UseShellExecute = false, 
     WindowStyle = ProcessWindowStyle.Hidden, 
     Arguments = arg, 
     FileName = assemblyLocation.Substring(0, assemblyLocation.LastIndexOf("\\")) + "\\ffmpeg.exe", 
     CreateNoWindow = true 
    } 
}; 

process.Start(); 

Console.WriteLine(process.StandardOutput.ReadToEnd()); 

process.WaitForExit(); 

Idealnie chciałbym, aby zmiany w wynikach w tym procesie zostały trafione lub dane przychodzą do czytelnika, że ​​otrzymuję od niego zdarzenia.

Każda pomoc byłaby świetna, wydaje mi się, że to pytanie dla początkujących, ale wydaje się, że czegoś brakuje.

Odpowiedz

46

Doświadczyłem tego wcześniej. Czasami sposób, w jaki proces, który wywołujesz, wyprowadza na konsolę, nie jest zgodny z tym rodzajem przekierowania wyjścia. W tym przypadku miałem na tyle szczęścia, że ​​mogłem zmodyfikować proces zewnętrzny, aby obejść ten problem.

Możesz spróbować uruchomić swój kod na innym procesie, który wysyła do konsoli i sprawdzić, czy działa poprawnie. W tej chwili czyta mi prawo do mnie.

EDIT:

poszedłem i wyciągnął blok kodu mam używany do tego celu. Jest to w aplikacji WPF, która przekierowuje wyjście procesu do okna. Zwróć uwagę na powiązanie zdarzenia. Ponieważ to jest WPF, muszę wywołać moje wezwanie do zapisania danych. Ponieważ nie są zaniepokojeni blokowanie, ou powinny być w stanie po prostu zastąpić że z:

Console.WriteLine(e.Data); 

Mam nadzieję, że to pomaga!

private static void LaunchProcess() 
    { 
     Process build = new Process(); 
     build.StartInfo.WorkingDirectory = @"dir"; 
     build.StartInfo.Arguments = ""; 
     build.StartInfo.FileName = "my.exe"; 

     build.StartInfo.UseShellExecute = false; 
     build.StartInfo.RedirectStandardOutput = true; 
     build.StartInfo.RedirectStandardError = true; 
     build.StartInfo.CreateNoWindow = true; 
     build.ErrorDataReceived += build_ErrorDataReceived; 
     build.OutputDataReceived += build_ErrorDataReceived; 
     build.EnableRaisingEvents = true; 
     build.Start(); 
     build.BeginOutputReadLine(); 
     build.BeginErrorReadLine(); 
     build.WaitForExit(); 
    } 

    // write out info to the display window 
    static void build_ErrorDataReceived(object sender, DataReceivedEventArgs e) 
    { 
     string strMessage = e.Data; 
     if (richTextBox != null && !String.Empty(strMessage)) 
     { 
      App.Instance.Dispatcher.BeginInvoke(DispatcherPriority.Send, (ThreadStart)delegate() 
      { 
       Paragraph para = new Paragraph(new Run(strMessage)); 
       para.Margin = new Thickness(0); 
       para.Background = brushErrorBrush; 
       box.Document.Blocks.Add(para); 
      }); 
     } 
    } 
+0

masz na myśli inny wątek lub inny proces? mogę uruchomić proces z wiersza cmd, a wynik wygląda dobrze. –

+0

Miałem na myśli inny proces niż ten, który próbujesz. W przypadku, z jakim się spotkałem, kiedy przekierowałem standardowe wyjście, proces nie wyczyściłby jego bufora wyjściowego po uruchomieniu w ten sposób, a więc strumień byłby dostępny dopiero na końcu. To może, ale nie musi być twój problem. Zobacz mój przykład kodu, jak sobie z tym poradzić w moich własnych aplikacjach. – patjbs

+0

Mogę to zrobić z większością aplikacji konsolowych, ale nie z PowerShell. jakieś pomysły? –

21

Nie jestem pewien, co dokładnie problemu używasz do, ale jeśli szukasz działać na wyjściu tak szybko, jak to jest generowane, spróbować podpinania do OutputDataReceived przypadku procesu wywołującego tę funkcję. Można określić moduły obsługi, aby otrzymywać asynchronicznie dane wyjściowe z procesu. Z powodzeniem stosuję to podejście.

ProcessStartInfo info = new ProcessStartInfo(...) 
info.UseShellExecute = false; 
info.RedirectStandardOutput = true; 
info.RedirectStandardError = true; 

Process p = Process.Start(info); 
p.OutputDataReceived += p_OutputDataReceived; 
p.ErrorDataReceived += p_ErrorDataReceived; 

p.BeginOutputReadLine(); 
p.BeginErrorReadLine(); 
p.WaitForExit(); 

..

void p_OutputDataReceived(object sender, DataReceivedEventArgs e) 
{ 
    Console.WriteLine("Received from standard out: " + e.Data); 
} 

void p_ErrorDataReceived(object sender, DataReceivedEventArgs e) 
{ 
    Console.WriteLine("Received from standard error: " + e.Data); 
} 

zobaczyć proces OutputDataReceived imprez off, aby uzyskać więcej informacji.

+0

Musisz 'Start()' proces przed 'WaitForExit()' – abatishchev

+0

@abatishchev: Tak, i robię to. O co ci chodzi? –

+0

Przepraszamy! Moja nieuwaga. Zmiksowany z 'Process p = Process.Start (info);' – abatishchev

10

Korzystanie z wyrażeń lambda itp:

var info = new ProcessStartInfo(path) 
{ 
    RedirectStandardError = true, 
    RedirectStandardOutput = true, 
    UseShellExecute = false, 
    Verb = "runas", 
}; 

var process = new Process 
{ 
    EnableRaisingEvents = true, 
    StartInfo = info 
}; 

Action<object, DataReceivedEventArgs> actionWrite = (sender, e) => 
{ 
    Console.WriteLine(e.Data); 
}; 

process.ErrorDataReceived += (sender, e) => actionWrite(sender, e); 
process.OutputDataReceived += (sender, e) => actionWrite(sender, e); 

process.Start(); 
process.BeginOutputReadLine(); 
process.BeginErrorReadLine(); 
process.WaitForExit(); 
+0

Tylko uwaga na ten temat: Czasownik "runas" działa tylko wtedy, gdy UseShellExecute = true, Verb = "runas". Jeśli UseShellExecute = true nie można przekierować danych wyjściowych. Zobacz więcej w http://kiquenet.wordpress.com/2014/08/22/uac-run-as-administrator-elevated-process/ – Kiquenet

4

ciekawe, że nie potrafi czytać ze standardowego wyjścia i standardowego błędu w tym samym czasie:

zarówno jeśli przekierowanie standardowego wyjścia i standardowego błędu i następnie spróbuj przeczytać oba, dla przykładu przy użyciu następującego kodu C#.

[C#] Wyjście

ciąg = p.StandardOutput.ReadToEnd();

string error = p.StandardError.ReadToEnd();

str.WaitForExit();

W takim przypadku, jeśli proces potomny zapisuje dowolny tekst do standardowego błędu, zablokuje proces , ponieważ proces macierzysty nie może odczytać ze standardowego błędu, dopóki nie zakończy odczytu ze standardowego wyjścia. Jednak proces nadrzędny nie odczyta ze standardowego wyjścia , dopóki proces się nie zakończy. Zalecanym rozwiązaniem tej sytuacji jest utworzenie dwóch wątków , aby aplikacja mogła odczytać dane wyjściowe każdego strumienia w osobnym wątku.

http://msdn.microsoft.com/en-us/library/system.diagnostics.processstartinfo.redirectstandardoutput(v=vs.71).aspx

+0

Nieprawda, zobacz akceptowaną odpowiedź. –

+2

Nie jest prawdą, że ** nie ma ** możliwości odczytu obu strumieni, tylko że nie jest bezpiecznie używać tylko metod synchronicznych w obu strumieniach w tym samym czasie. Chcę tylko wyjaśnić, że @ jeremy-mcgee, mówiąc "nie prawda", nie powoduje, że ** podane ** informacje w odpowiedzi są nieprawidłowe. – Aardvark

0

Sprawdź, czy wyjście spodziewasz nie jest wysyłany do wyjścia StandardError zamiast wyjścia StandardOutput

1

kod płynącej pracował w VS2010

void OnOutputDataReceived(object sender, DataReceivedEventArgs e) 
    { 
     if (String.IsNullOrEmpty(e.Data) == false) 
     { 
      new Thread(() => 
      { 
       this.Dispatcher.Invoke(new Action(() => 
       { 
        // Add you code here 
       })); 
      }).Start(); 
     } 
    } 
+0

Co to jest Dispatcher? Co to jest**? Co to jest wydarzenie OnOutputDataReceived? Co to jest typ DataReceivedEventArgs? Jaki jest pełny kontekst Twojego kodu źródłowego? – Kiquenet