2010-07-24 11 views
6

Lo que me gustaría hacer es similar a lo que Visual Studio hace en su ventana de salida u otros editores en sus ventanas de herramientas: Comience otro proceso B desde mi proceso A y capture su salida stdout/stderr.Cómo capturar stdout de otro proceso en Win32 sin latencia?

Hasta ahora, lo tengo trabajando con CreatePipe(), pero por alguna razón, la salida de B no llega a B justo cuando se escribe. Se comporta más como un buffer de algún tipo se llena y cuando está lleno, todo el contenido de los buffers llega a A de una vez. Escribí mi propio programa de prueba que produce algo y hace un fflush(stdout) directamente después. Entonces la salida llega directamente a A. Pero no puedo cambiar el código de todos los procesos B que me gustaría usar de esa manera. Tratar de lavar la tubería desde A tampoco funciona.

¿Cómo se supone que funciona?

Mi código de inicialización, así como el consumo de código:

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 ... 
    } 
} 

Editar: Acabo de encontrar esta pregunta: Continuously read from STDOUT of external process in Ruby. Es el mismo problema, pero en el contexto de Ruby. Lamentablemente, la solución fue utilizar alguna biblioteca de Ruby que simplemente lo haga funcionar. ¿Cómo lo hace esa biblioteca? ¿Cuál es el equivalente en Win32/C++?

+1

El hilo enlazado es una solución * nix. Nada similar en Win32. –

+0

¿Cómo sabes que solo está relacionado con Unix? También tenga en cuenta que existen programas en Windows que pueden hacerlo bien - de código cerrado:/ – marc40000

Respuesta

3

No puede hacer eso. Si el resultado no se ha eliminado en el proceso ofensivo, en realidad no se ha escrito en stdout en primer lugar. Es decir, el sistema operativo aún no ha recibido los datos del proceso de destino.

Esto no es ningún tipo de latencia inherente con las tuberías, es que los programas que está monitoreando aún no lo han escrito en la tubería.

Debe observar el mismo comportamiento para el símbolo del sistema al ejecutar dichos programas, porque el símbolo del sistema usa la misma solución de tubería que está utilizando. Si no lo hace, es porque los programas en cuestión están detectando que están escribiendo en un identificador de archivo, en lugar de un identificador de consola, y haciendo un almacenamiento en memoria intermedia adicional.

+0

Hmm, ¿cómo lo detectan? ¿Puedo solucionar eso? Esa biblioteca de ruby ​​en el enlace a la otra pregunta que publiqué parece ser capaz de hacer eso. – marc40000

+0

@marc: 1. La biblioteca de ruby ​​no estaba en Windows. 2. La biblioteca Ruby puede/no haber estado controlando un programa diferente. La necesidad de la biblioteca real en Ruby se debe a una limitación del rubí, no una limitación de la aplicación controlada. Por lo tanto, una biblioteca fue capaz de arreglarlo. –

Cuestiones relacionadas