2010-07-24 11 views
6

Quello che mi piacerebbe fare è simile a ciò che Visual Studio fa nella sua finestra di output o altri editor nelle finestre degli strumenti: Avvia un altro processo B dal mio processo A e cattura il suo output stdout/stderr.Come catturare lo stdout da un altro processo in Win32 senza latenza?

Finora, ho funzionato con CreatePipe(), ma per qualche motivo, l'output di B non arriva a B quando viene scritto. Si comporta più come un buffer di qualche tipo che viene riempito e quando è pieno, tutti i contenuti dei buffer arrivano ad A contemporaneamente. Ho scritto il mio programma di test che emette qualcosa e fa subito un fflush(stdout). Quindi l'output arriva direttamente su A. Ma non posso cambiare il codice di tutti i processi B che mi piacerebbe usare in quel modo. Anche il tentativo di svuotare il tubo da A non funziona.

Come si suppone che funzioni?

Il mio codice di inizializzazione, così come codice di consumare:

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; 

Consumo:

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: Ho appena trovato questa domanda: Continuously read from STDOUT of external process in Ruby. È lo stesso problema, ma nel contesto di Ruby. Purtroppo la soluzione era usare una libreria Ruby che lo facesse funzionare. Come fa quella libreria a farlo? Qual è l'equivalente in Win32/C++?

+1

Il thread collegato è una soluzione * nix. Niente di simile in Win32. –

+0

Come fai a sapere che è collegato solo a unix? Notate anche che esistono programmi su Windows che possono farcela bene - quelli a sorgente chiusa:/ – marc40000

risposta

3

Non puoi farlo. Se l'output non è stato svuotato nel processo incriminato, in realtà non è stato scritto su stdout in primo luogo. Cioè, il sistema operativo non ha ancora ottenuto i dati dal processo di destinazione ancora.

Questo non è alcun tipo di latenza intrinseca con le pipe, è che i programmi che stai monitorando non sono ancora stati scritti nella pipe.

Si dovrebbe notare lo stesso identico comportamento per il prompt dei comandi quando si eseguono detti programmi, poiché il prompt dei comandi utilizza la stessa soluzione di pipa che si sta utilizzando. Se non lo fai, è perché i programmi in questione stanno rilevando che stanno scrivendo su un handle di file, piuttosto che su un handle di console, e stanno facendo un buffering aggiuntivo.

+0

Hmm, come lo rilevano? Posso aggirare questo? Questa libreria ruby ​​nel link all'altra domanda che ho postato sembra essere in grado di farlo. – marc40000

+0

@marc: 1. La libreria ruby ​​non era su Windows. 2. La libreria ruby ​​può/non può aver controllato un programma diverso. La necessità della libreria effettiva in Ruby è dovuta a una limitazione del ruby, non a una limitazione dell'applicazione controllata. Quindi una biblioteca è stata in grado di risolverlo. –

Problemi correlati