Sources
Delphi Russian Knowledge Base
DRKB - это самая большая и удобная в использовании база знаний по Дельфи в рунете, составленная Виталием Невзоровым

Перехват вывода консоли

01.01.2007
unit consoleoutput; 
 
interface
 
uses
 
Controls, Windows, SysUtils, Forms;
 
function GetDosOutput(const CommandLine:string): string;
 
implementation
 
function GetDosOutput(const CommandLine:string): string;
var
  SA
: TSecurityAttributes;
  SI
: TStartupInfo;
  PI
: TProcessInformation;
 
StdOutPipeRead, StdOutPipeWrite: THandle;
 
WasOK: Boolean;
 
Buffer: array[0..255] of Char;
 
BytesRead: Cardinal;
 
WorkDir, Line: String;
begin
 
Application.ProcessMessages;
 
with SA do
 
begin
    nLength
:= SizeOf(SA);
    bInheritHandle
:= True;
    lpSecurityDescriptor
:= nil;
 
end;
 
// создаём пайп для перенаправления стандартного вывода
 
CreatePipe(StdOutPipeRead,  // дескриптор чтения
             
StdOutPipeWrite, // дескриптор записи
             @SA
,              // аттрибуты безопасности
             
0                // количество байт принятых для пайпа - 0 по умолчанию
             
);
 
try
   
// Создаём дочерний процесс, используя StdOutPipeWrite в качестве стандартного вывода,
   
// а так же проверяем, чтобы он не показывался на экране.
   
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); // стандартный ввод не перенаправляем
      hStdOutput
:= StdOutPipeWrite;
      hStdError
:= StdOutPipeWrite;
   
end;
 
   
// Запускаем компилятор из командной строки
   
WorkDir := ExtractFilePath(CommandLine);
   
WasOK := CreateProcess(nil, PChar(CommandLine), nil, nil, True, 0, nil, PChar(WorkDir), SI, PI);
 
   
// Теперь, когда дескриптор получен, для безопасности закрываем запись.
   
// Нам не нужно, чтобы произошло случайное чтение или запись.
   
CloseHandle(StdOutPipeWrite);
   
// если процесс может быть создан, то дескриптор, это его вывод
   
if not WasOK then
     
raise Exception.Create('Could not execute command line!')
   
else
     
try
       
// получаем весь вывод до тех пор, пока DOS-приложение не будет завершено
       
Line := '';
        repeat
         
// читаем блок символов (могут содержать возвраты каретки и переводы строки)
         
WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil);
 
         
// есть ли что-нибудь ещё для чтения?
         
if BytesRead > 0 then
         
begin
           
// завершаем буфер PChar-ом
           
Buffer[BytesRead] := #0;
           
// добавляем буфер в общий вывод
           
Line := Line + Buffer;
         
end;
       
until not WasOK or (BytesRead = 0);
       
// ждём, пока завершится консольное приложение
       
WaitForSingleObject(PI.hProcess, INFINITE);
     
finally
       
// Закрываем все оставшиеся дескрипторы
       
CloseHandle(PI.hThread);
       
CloseHandle(PI.hProcess);
     
end;
 
finally
      result
:=Line;
     
CloseHandle(StdOutPipeRead);
 
end;
end;
 
 
end.

Взято из https://forum.sources.ru


{
This function runs a program (console or batch) and adds its output
to Memo1
}
 
{....}
 
private
   
function RunCaptured(const _dirName, _exeName, _cmdLine: string): Boolean;
 
{....}
 
function TForm1.RunCaptured(const _dirName, _exeName, _cmdLine: string): Boolean;
var
  start
: TStartupInfo;
  procInfo
: TProcessInformation;
  tmpName
: string;
  tmp
: Windows.THandle;
  tmpSec
: TSecurityAttributes;
  res
: TStringList;
 
return: Cardinal;
begin
 
Result := False;
 
try
   
{ Set a temporary file }
    tmpName
:= 'Test.tmp';
   
FillChar(tmpSec, SizeOf(tmpSec), #0);
    tmpSec
.nLength := SizeOf(tmpSec);
    tmpSec
.bInheritHandle := True;
    tmp
:= Windows.CreateFile(PChar(tmpName),
           Generic_Write
, File_Share_Write,
           @tmpSec
, Create_Always, File_Attribute_Normal, 0);
   
try
     
FillChar(start, SizeOf(start), #0);
      start
.cb          := SizeOf(start);
      start
.hStdOutput  := tmp;
      start
.dwFlags     := StartF_UseStdHandles or StartF_UseShowWindow;
      start
.wShowWindow := SW_Minimize;
     
{ Start the program }
     
if CreateProcess(nil, PChar(_exeName + ' ' + _cmdLine), nil, nil, True,
                       
0, nil, PChar(_dirName), start, procInfo) then
     
begin
       
SetPriorityClass(procInfo.hProcess, Idle_Priority_Class);
       
WaitForSingleObject(procInfo.hProcess, Infinite);
       
GetExitCodeProcess(procInfo.hProcess, return);
       
Result := (return = 0);
       
CloseHandle(procInfo.hThread);
       
CloseHandle(procInfo.hProcess);
       
Windows.CloseHandle(tmp);
       
{ Add the output }
        res
:= TStringList.Create;
       
try
          res
.LoadFromFile(tmpName);
          Memo1
.Lines.AddStrings(res);
       
finally
          res
.Free;
       
end;
       
Windows.DeleteFile(PChar(tmpName));
     
end
     
else
     
begin
       
Application.MessageBox(PChar(SysErrorMessage(GetLastError())),
         
'RunCaptured Error', MB_OK);
     
end;
   
except
     
Windows.CloseHandle(tmp);
     
Windows.DeleteFile(PChar(tmpName));
     
raise;
   
end;
 
finally
 
end;
end;
 
 
// Example:
 
procedure TForm1
.Button1Click(Sender: TObject);
begin
 
RunCaptured('C:\', 'cmd.exe', '/c dir');
end;

Взято с сайта https://www.swissdelphicenter.ch/en/tipsindex.php

procedure RunDosInMemo(CmdLine: string; AMemo: TMemo);
const
 
ReadBuffer = 2400;
var
 
Security: TSecurityAttributes;
 
ReadPipe, WritePipe: THandle;
  start
: TStartUpInfo;
 
ProcessInfo: TProcessInformation;
 
Buffer: Pchar;
 
BytesRead: DWord;
 
Apprunning: DWord;
begin
 
Screen.Cursor := CrHourGlass;
  Form1
.Button1.Enabled := False;
 
with Security do
 
begin
    nlength
:= SizeOf(TSecurityAttributes);
    binherithandle
:= true;
    lpsecuritydescriptor
:= nil;
 
end;
 
if Createpipe(ReadPipe, WritePipe,
   
@Security, 0) then
 
begin
   
Buffer := AllocMem(ReadBuffer + 1);
   
FillChar(Start, Sizeof(Start), #0);
    start
.cb := SizeOf(start);
    start
.hStdOutput := WritePipe;
    start
.hStdInput := ReadPipe;
    start
.dwFlags := STARTF_USESTDHANDLES +
      STARTF_USESHOWWINDOW
;
    start
.wShowWindow := SW_HIDE;
 
   
if CreateProcess(nil,
     
PChar(CmdLine),
     
@Security,
     
@Security,
     
true,
      NORMAL_PRIORITY_CLASS
,
     
nil,
     
nil,
      start
,
     
ProcessInfo) then
   
begin
      repeat
       
Apprunning := WaitForSingleObject
         
(ProcessInfo.hProcess, 100);
       
ReadFile(ReadPipe, Buffer[0],
         
ReadBuffer, BytesRead, nil);
       
Buffer[BytesRead] := #0;
       
OemToAnsi(Buffer, Buffer);
       
AMemo.Text := AMemo.text + string(Buffer);
 
       
Application.ProcessMessages;
     
until (Apprunning <> WAIT_TIMEOUT);
   
end;
   
FreeMem(Buffer);
   
CloseHandle(ProcessInfo.hProcess);
   
CloseHandle(ProcessInfo.hThread);
   
CloseHandle(ReadPipe);
   
CloseHandle(WritePipe);
 
end;
 
Screen.Cursor := CrDefault;
  Form1
.Button1.Enabled := True;
end;
 
procedure TForm1
.Button1Click(Sender: TObject);
begin
  Memo1
.Clear;
 
RunDosInMemo('ping -t 192.168.28.200', Memo1);
end;

Автор: Song

Взято из https://forum.sources.ru


Hужно использовать пайпы (CreatePipe), и работать с ними как с обычным файлом.

const
  H_IN_READ
= 1;
  H_IN_WRITE
= 2;
  H_OUT_READ
= 3;
  H_OUT_WRITE
= 4;
  H_ERR_READ
= 5;
  H_ERR_WRITE
= 6;
 
type
 
TPipeHandles = array [1..6] of THandle;
var
  hPipes
: TPipeHandles;
 
ProcessInfo: TProcessInformation;
 
(************CREATE HIDDEN CONSOLE PROCESS************)
function CreateHiddenConsoleProcess(szChildName: string;
         
ProcPriority: DWORD; ThreadPriority: integer): Boolean;
label
  error
;
var
  fCreated
: Boolean;
  si
: TStartupInfo;
  sa
: TSecurityAttributes;
begin
 
// Initialize handles
  hPipes
[ H_IN_READ ] := INVALID_HANDLE_VALUE;
  hPipes
[ H_IN_WRITE ] := INVALID_HANDLE_VALUE;
  hPipes
[ H_OUT_READ ] := INVALID_HANDLE_VALUE;
  hPipes
[ H_OUT_WRITE ] := INVALID_HANDLE_VALUE;
  hPipes
[ H_ERR_READ ] := INVALID_HANDLE_VALUE;
  hPipes
[ H_ERR_WRITE ] := INVALID_HANDLE_VALUE;
 
ProcessInfo.hProcess := INVALID_HANDLE_VALUE;
 
ProcessInfo.hThread := INVALID_HANDLE_VALUE;
 
// Create pipes
 
// initialize security attributes for handle inheritance (for WinNT)
  sa
.nLength := sizeof(sa);
  sa
.bInheritHandle := TRUE;
  sa
.lpSecurityDescriptor := nil;
 
// create STDIN pipe
 
if not CreatePipe( hPipes[ H_IN_READ ], hPipes[ H_IN_WRITE ], @sa, 0 ) then
   
goto error;
 
// create STDOUT pipe
 
if not CreatePipe( hPipes[ H_OUT_READ ], hPipes[ H_OUT_WRITE ], @sa, 0 ) then
   
goto error;
 
// create STDERR pipe
 
if not CreatePipe( hPipes[ H_ERR_READ ], hPipes[ H_ERR_WRITE ], @sa, 0 ) then
   
goto error;
 
// process startup information
 
ZeroMemory(Pointer(@si), sizeof(si));
  si
.cb := sizeof(si);
  si
.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
  si
.wShowWindow := SW_HIDE;
 
// assign "other" sides of pipes
  si
.hStdInput := hPipes[ H_IN_READ ];
  si
.hStdOutput := hPipes[ H_OUT_WRITE ];
  si
.hStdError := hPipes[ H_ERR_WRITE ];
 
// Create a child process
 
try
    fCreated
:= CreateProcess( nil, PChar(szChildName), nil, nil, True,
   
ProcPriority, // CREATE_SUSPENDED,
   
nil, nil, si, ProcessInfo );
 
except
    fCreated
:= False;
 
end;
 
 
if not fCreated then
   
goto error;
 
 
Result := True;
 
CloseHandle(hPipes[ H_OUT_WRITE ]);
 
CloseHandle(hPipes[ H_ERR_WRITE ]);
 
// ResumeThread( pi.hThread );
 
SetThreadPriority(ProcessInfo.hThread, ThreadPriority);
 
CloseHandle( ProcessInfo.hThread );
 
Exit;
 
//-----------------------------------------------------
  error
:
   
ClosePipes( hPipes );
   
CloseHandle( ProcessInfo.hProcess );
   
CloseHandle( ProcessInfo.hThread );
   
ProcessInfo.hProcess := INVALID_HANDLE_VALUE;
   
ProcessInfo.hThread := INVALID_HANDLE_VALUE;
   
Result := False;
end;
https://delphiworld.narod.ru/

DelphiWorld 6.0


Это пример запуска консольных программ с передачей ей консольного ввода (как если бы он был введен с клавиатуры после запуска программы) и чтением консольного вывода. Таким способом можно запускать например стандартный виндовый ftp.exe (в невидимом окне) и тем самым отказаться от использования специализированных, зачастую глючных компонент.

function ExecuteFile(FileName, StdInput: string;
 
TimeOut: integer;
 
var StdOutput: string): boolean;
 
label
 
Error;
 
type
 
TPipeHandles = (IN_WRITE, IN_READ,
    OUT_WRITE
, OUT_READ,
    ERR_WRITE
, ERR_READ);
 
type
 
TPipeArray = array[TPipeHandles] of THandle;
 
var
  i
: integer;
  ph
: TPipeHandles;
  sa
: TSecurityAttributes;
 
Pipes: TPipeArray;
 
StartInf: TStartupInfo;
 
ProcInf: TProcessInformation;
 
Buf: array[0..1024] of byte;
 
TimeStart: TDateTime;
 
 
function ReadOutput: string;
 
var
    i
: integer;
    s
: string;
   
BytesRead: longint;
 
 
begin
   
Result := '';
    repeat
 
     
Buf[0] := 26;
     
WriteFile(Pipes[OUT_WRITE], Buf, 1, BytesRead, nil);
     
if ReadFile(Pipes[OUT_READ], Buf, 1024, BytesRead, nil) then
     
begin
       
if BytesRead > 0 then
       
begin
          buf
[BytesRead] := 0;
          s
:= StrPas(@Buf[0]);
          i
:= Pos(#26, s);
         
if i > 0 then
            s
:= copy(s, 1, i - 1);
         
Result := Result + s;
       
end;
     
end;
 
     
if BytesRead1024 then
       
break;
   
until false;
 
end;
 
begin
 
Result := false;
 
for ph := Low(TPipeHandles) to High(TPipeHandles) do
   
Pipes[ph] := INVALID_HANDLE_VALUE;
 
 
// Создаем пайпы
  sa
.nLength := sizeof(sa);
  sa
.bInheritHandle := TRUE;
  sa
.lpSecurityDescriptor := nil;
 
 
if not CreatePipe(Pipes[IN_READ], Pipes[IN_WRITE], @sa, 0) then
   
goto Error;
 
if not CreatePipe(Pipes[OUT_READ], Pipes[OUT_WRITE], @sa, 0) then
   
goto Error;
 
if not CreatePipe(Pipes[ERR_READ], Pipes[ERR_WRITE], @sa, 0) then
   
goto Error;
 
 
// Пишем StdIn
 
StrPCopy(@Buf[0], stdInput + ^Z);
 
WriteFile(Pipes[IN_WRITE], Buf, Length(stdInput), i, nil);
 
 
// Хендл записи в StdIn надо закрыть - иначе выполняемая программа
 
// может не прочитать или прочитать не весь StdIn.
 
 
CloseHandle(Pipes[IN_WRITE]);
 
 
Pipes[IN_WRITE] := INVALID_HANDLE_VALUE;
 
 
FillChar(StartInf, sizeof(TStartupInfo), 0);
 
StartInf.cb := sizeof(TStartupInfo);
 
StartInf.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
 
 
StartInf.wShowWindow := SW_SHOW; // SW_HIDE если надо запустить невидимо
 
 
StartInf.hStdInput := Pipes[IN_READ];
 
StartInf.hStdOutput := Pipes[OUT_WRITE];
 
StartInf.hStdError := Pipes[ERR_WRITE];
 
 
if not CreateProcess(nil, PChar(FileName), nil,
   
nil, True, NORMAL_PRIORITY_CLASS,
   
nil, nil, StartInf, ProcInf) then
   
goto Error;
 
 
TimeStart := Now;
 
  repeat
   
Application.ProcessMessages;
    i
:= WaitForSingleObject(ProcInf.hProcess, 100);
   
if i = WAIT_OBJECT_0 then
     
break;
   
if (Now - TimeStart) * SecsPerDay > TimeOut then
     
break;
 
until false;
 
 
if iWAIT_OBJECT_0 then
   
goto Error;
 
StdOutput := ReadOutput;
 
 
for ph := Low(TPipeHandles) to High(TPipeHandles) do
   
if Pipes[ph]INVALID_HANDLE_VALUE then
     
CloseHandle(Pipes[ph]);
 
 
CloseHandle(ProcInf.hProcess);
 
CloseHandle(ProcInf.hThread);
 
Result := true;
 
Exit;
 
 
Error:
 
 
if ProcInf.hProcessINVALID_HANDLE_VALUE then
 
 
begin
   
CloseHandle(ProcInf.hThread);
    i
:= WaitForSingleObject(ProcInf.hProcess, 1000);
   
CloseHandle(ProcInf.hProcess);
   
if iWAIT_OBJECT_0 then
 
   
begin
     
ProcInf.hProcess := OpenProcess(PROCESS_TERMINATE,
        FALSE
,
       
ProcInf.dwProcessId);
 
     
if ProcInf.hProcess 0 then
     
begin
       
TerminateProcess(ProcInf.hProcess, 0);
       
CloseHandle(ProcInf.hProcess);
     
end;
   
end;
 
end;
 
 
for ph := Low(TPipeHandles) to High(TPipeHandles) do
   
if Pipes[ph]INVALID_HANDLE_VALUE then
     
CloseHandle(Pipes[ph]);
 
end;

Автор: Алексей Бойко

Взято из https://forum.sources.ru