2010-07-24 10 views
6

Was ich tun möchte ist ähnlich wie Visual Studio in seinem Ausgabefenster oder anderen Editoren in ihren Werkzeugfenstern: Starten Sie einen anderen Prozess B von meinem Prozess A und erfassen Sie seine Ausgabe von stdout/stderr.Wie kann man stdout von einem anderen Prozess in Win32 ohne Latenz erfassen?

Bisher habe ich es mit CreatePipe() arbeiten, aber aus irgendeinem Grund, die Ausgabe von B kommt nicht auf B richtig, wenn es geschrieben wird. Es verhält sich eher wie ein Puffer, der gefüllt wird, und wenn er voll ist, kommt der gesamte Pufferinhalt auf einmal bei A an. Ich habe mein eigenes Testprogramm geschrieben, das etwas ausgibt und direkt danach eine fflush(stdout) macht. Dann kommt die Ausgabe direkt bei A an. Aber ich kann den Code aller B-Prozesse, die ich auf diese Weise verwenden möchte, nicht ändern. Der Versuch, das Rohr von A zu spülen, funktioniert auch nicht.

Wie soll das funktionieren?

Meine Initialisierungscode sowie aufwändige Code:

sa.nLength = sizeof(SECURITY_ATTRIBUTES); 
sa.bInheritHandle = TRUE; 
sa.lpSecurityDescriptor = NULL; 

err = CreatePipe(&hChildStdoutRd, &hChildStdoutWr, &sa, stdouthistory); 
if (err == 0) 
    return 1; 
err = DuplicateHandle(GetCurrentProcess(), hChildStdoutRd, 
         GetCurrentProcess(), &hChildStdoutRdDup , 0, 
         FALSE, 
         DUPLICATE_SAME_ACCESS); 
if (err == 0) 
    return 3; 
CloseHandle(hChildStdoutRd); 

DWORD a, b, c; 
a = PIPE_READMODE_BYTE | PIPE_NOWAIT; 
b = 0; 
c = 0; 
SetNamedPipeHandleState(hChildStdoutRdDup, &a, &b, &c); 

err = CreatePipe(&hChildStdinRd, &hChildStdinWr, &sa, stdinhistory); 
if (err == 0) 
    return 1; 
err = DuplicateHandle(GetCurrentProcess(), hChildStdinWr, 
         GetCurrentProcess(), &hChildStdinWrDup , 0, 
         FALSE, 
         DUPLICATE_SAME_ACCESS); 
if (err == 0) 
    return 4; 
CloseHandle(hChildStdinWr); 

a = PIPE_READMODE_BYTE | PIPE_NOWAIT; 
b = 0; 
c = 0; 

ZeroMemory(&si,sizeof(STARTUPINFO)); 
si.cb = sizeof(STARTUPINFO); 
si.dwFlags = STARTF_USESTDHANDLES; 
si.wShowWindow = SW_SHOW; 

si.hStdOutput = hChildStdoutWr; 
si.hStdError = hChildStdoutWr; 
si.hStdInput = hChildStdinRd; 

ZeroMemory(&pi, sizeof(PROCESS_INFORMATION)); 

err = CreateProcess(0, this->cmdline, 0, 0, true, CREATE_NO_WINDOW, 0, 0, &si, &pi); 
if (err == 0) 
    return 4; 

Verbrauch:

DWORD avail; 
unsigned int ofs = 0; 
if (PeekNamedPipe(hChildStdoutRdDup, NULL, 0, NULL, &avail, NULL)) 
{ 
    if (avail != 0) 
    { 
     int err = ReadFile(hChildStdoutRdDup, s + ofs, slen, &threadbuffern, 0); 
          // Consume ... 
    } 
} 

Edit: Ich habe gerade diese Frage: Continuously read from STDOUT of external process in Ruby. Es ist das gleiche Problem, aber im Kontext von Ruby. Leider war die Lösung, eine Ruby-Bibliothek zu verwenden, die es einfach funktioniert. Wie macht die Bibliothek das? Was ist das Äquivalent in Win32/C++?

+1

Der verknüpfte Thread ist eine * nix-Lösung. Nichts ähnliches in Win32. –

+0

Woher wissen Sie, dass es nur mit Unix verwandt ist? Beachten Sie auch, dass es Programme auf Windows gibt, die es gut können - closed source:/ – marc40000

Antwort

3

Sie können das nicht tun. Wenn die Ausgabe in dem fehlerhaften Prozess nicht geleert wurde, wurde sie nicht wirklich in stdout geschrieben. Das heißt, das Betriebssystem hat die Daten aus dem Zielprozess noch nicht einmal abgerufen.

Dies ist keine Art von inhärenter Latenz mit Pipes, es ist, dass die Programme, die Sie überwachen, es tatsächlich noch nicht in die Pipe geschrieben haben.

Sie sollten das exakt gleiche Verhalten für die Eingabeaufforderung beim Ausführen der Programme beachten, da die Eingabeaufforderung die gleiche Rohrlösung verwendet, die Sie verwenden. Wenn Sie dies nicht tun, liegt es daran, dass die fraglichen Programme feststellen, dass sie in ein Dateihandle statt in ein Konsolenhandle schreiben und zusätzliche Pufferung durchführen.

+0

Hmm, wie erkennen sie das? Kann ich das umgehen? Diese Ruby-Bibliothek im Link zu der anderen Frage, die ich gepostet habe, scheint das zu können. – marc40000

+0

@marc: 1. Die Ruby-Bibliothek war nicht auf Windows. 2. Die Ruby-Bibliothek hat möglicherweise ein anderes Programm nicht gesteuert. Die Notwendigkeit für die eigentliche Bibliothek in Ruby ist auf eine Beschränkung von Ruby zurückzuführen, keine Einschränkung der kontrollierten Anwendung. So konnte eine Bibliothek es beheben. –