delphi pipe windows-shell shellexecute

Obtener resultados de una aplicación shell/dos en una aplicación Delphi



pipe windows-shell (2)

Como siempre, Zarco Gajic tiene una solución: capturar el resultado de una ventana DOS (comando / consola) . Esta es una copia de su artículo para referencia futura:

El ejemplo ejecuta ''chkdsk.exe c: /' y muestra el resultado en Memo1. Coloque un TMemo (Memo1) y un TButton (Button1) en su formulario. Coloque este código en el procedimiento de evento OnCLick para Button1 :

procedure RunDosInMemo(DosApp: string; AMemo:TMemo); const READ_BUFFER_SIZE = 2400; var Security: TSecurityAttributes; readableEndOfPipe, writeableEndOfPipe: THandle; start: TStartUpInfo; ProcessInfo: TProcessInformation; Buffer: PAnsiChar; BytesRead: DWORD; AppRunning: DWORD; begin Security.nLength := SizeOf(TSecurityAttributes); Security.bInheritHandle := True; Security.lpSecurityDescriptor := nil; if CreatePipe({var}readableEndOfPipe, {var}writeableEndOfPipe, @Security, 0) then begin Buffer := AllocMem(READ_BUFFER_SIZE+1); FillChar(Start, Sizeof(Start), #0); start.cb := SizeOf(start); // Set up members of the STARTUPINFO structure. // This structure specifies the STDIN and STDOUT handles for redirection. // - Redirect the output and error to the writeable end of our pipe. // - We must still supply a valid StdInput handle (because we used STARTF_USESTDHANDLES to swear that all three handles will be valid) start.dwFlags := start.dwFlags or STARTF_USESTDHANDLES; start.hStdInput := GetStdHandle(STD_INPUT_HANDLE); //we''re not redirecting stdInput; but we still have to give it a valid handle start.hStdOutput := writeableEndOfPipe; //we give the writeable end of the pipe to the child process; we read from the readable end start.hStdError := writeableEndOfPipe; //We can also choose to say that the wShowWindow member contains a value. //In our case we want to force the console window to be hidden. start.dwFlags := start.dwFlags + STARTF_USESHOWWINDOW; start.wShowWindow := SW_HIDE; // Don''t forget to set up members of the PROCESS_INFORMATION structure. ProcessInfo := Default(TProcessInformation); //WARNING: The unicode version of CreateProcess (CreateProcessW) can modify the command-line "DosApp" string. //Therefore "DosApp" cannot be a pointer to read-only memory, or an ACCESS_VIOLATION will occur. //We can ensure it''s not read-only with the RTL function: UniqueString UniqueString({var}DosApp); if CreateProcess(nil, PChar(DosApp), nil, nil, True, NORMAL_PRIORITY_CLASS, nil, nil, start, {var}ProcessInfo) then begin //Wait for the application to terminate, as it writes it''s output to the pipe. //WARNING: If the console app outputs more than 2400 bytes (ReadBuffer), //it will block on writing to the pipe and *never* close. repeat Apprunning := WaitForSingleObject(ProcessInfo.hProcess, 100); Application.ProcessMessages; until (Apprunning <> WAIT_TIMEOUT); //Read the contents of the pipe out of the readable end //WARNING: if the console app never writes anything to the StdOutput, then ReadFile will block and never return repeat BytesRead := 0; ReadFile(readableEndOfPipe, Buffer[0], READ_BUFFER_SIZE, {var}BytesRead, nil); Buffer[BytesRead]:= #0; OemToAnsi(Buffer,Buffer); AMemo.Text := AMemo.text + String(Buffer); until (BytesRead < READ_BUFFER_SIZE); end; FreeMem(Buffer); CloseHandle(ProcessInfo.hProcess); CloseHandle(ProcessInfo.hThread); CloseHandle(readableEndOfPipe); CloseHandle(writeableEndOfPipe); end; end; procedure TForm1.Button1Click(Sender: TObject); begin {button 1 code} RunDosInMemo(''chkdsk.exe c:/',Memo1); end;

Actualización: el ejemplo anterior lee la salida en un solo paso. Aquí hay otro ejemplo de DelphiDabbler muestra cómo se puede leer el resultado mientras el proceso aún se está ejecutando:

function GetDosOutput(CommandLine: string; Work: string = ''C:/'): string; var SA: TSecurityAttributes; SI: TStartupInfo; PI: TProcessInformation; StdOutPipeRead, StdOutPipeWrite: THandle; WasOK: Boolean; Buffer: array[0..255] of AnsiChar; BytesRead: Cardinal; WorkDir: string; Handle: Boolean; begin Result := ''''; with SA do begin nLength := SizeOf(SA); bInheritHandle := True; lpSecurityDescriptor := nil; end; CreatePipe(StdOutPipeRead, StdOutPipeWrite, @SA, 0); try with SI do begin FillChar(SI, SizeOf(SI), 0); cb := SizeOf(SI); dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES; wShowWindow := SW_HIDE; hStdInput := GetStdHandle(STD_INPUT_HANDLE); // don''t redirect stdin hStdOutput := StdOutPipeWrite; hStdError := StdOutPipeWrite; end; WorkDir := Work; Handle := CreateProcess(nil, PChar(''cmd.exe /C '' + CommandLine), nil, nil, True, 0, nil, PChar(WorkDir), SI, PI); CloseHandle(StdOutPipeWrite); if Handle then try repeat WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil); if BytesRead > 0 then begin Buffer[BytesRead] := #0; Result := Result + Buffer; end; until not WasOK or (BytesRead = 0); WaitForSingleObject(PI.hProcess, INFINITE); finally CloseHandle(PI.hThread); CloseHandle(PI.hProcess); end; finally CloseHandle(StdOutPipeRead); end; end;

Tengo una aplicación de línea de comandos codificada en delphi que debo llamar desde una aplicación de escritorio normal (también codificada en Delphi). En resumen, quiero llamar a la aplicación de línea de comandos y mostrar el texto que emite "en vivo" en un cuadro de lista.

Han pasado años desde que jugué con el shell, pero recuerdo claramente que para tomar el texto de una aplicación de línea de comandos, tengo que usar el símbolo de la tubería ">". Me gusta esto:

C: /mycmdapp.exe> ​​c: /result.txt

Esto tomará cualquier texto impreso en el shell (usando writeLn) y lo volcará en un archivo de texto llamado "result.txt".

Pero ... (y aquí viene el pickle), quiero un resultado en vivo en lugar de un archivo de atrasos. Un ejemplo típico es el propio compilador Delphi, que se las arregla para informar al IDE lo que está sucediendo. Si mi memoria me sirve correctamente, parece recordar que debo crear un canal de "canalización" (?) Y luego asignar el nombre de canal a la llamada de shell.

Intenté googlear esto pero honestamente no estaba seguro de cómo formularlo. Espero que alguien de la comunidad pueda señalarme en la dirección correcta.

Actualizado : Esta pregunta puede ser idéntica a ¿Cómo ejecuto un programa de línea de comandos en Delphi? . Algunas de las respuestas se ajustan a lo que estoy buscando, aunque el título y la pregunta en sí no son idénticos.


Probablemente ya tenga el código en su disco duro: la función Execute en la unidad JclSysUtils del JCL (Biblioteca de códigos JEDI) hace lo que necesita:

function Execute(const CommandLine: string; OutputLineCallback: TTextHandler; RawOutput: Boolean = False; AbortPtr: PBoolean = nil): Cardinal;

Puede proporcionarle un procedimiento de devolución de llamada:
TTextHandler = procedure(const Text: string) of object;