首页  编辑  

重定义控制台程序的输入输出

Tags: /超级猛料/Console.控制台程序/   Date Created:

重定义控制台程序的输入输出

unit RedCon;

(*

simple yet working console i/o redirection

(c) 2002 bhoc@pentagroup.ch

freeware

there are two Data events (OnStdOut and OnStdErr) that return a string;

two other events just signal that the program is running or that it has ended.

the SendData() method will submit a string to an open application such as cmd.exe.

sample:

procedure TForm1.Button1Click(Sender: TObject);

begin

 fCon := TRedirectedConsole.Create(Edit1.Text);

 fCon.OnStdOut := OnConStdOut;

 fCon.OnStdErr := OnConStdErr;

 fCon.OnRun := OnConRun;

 fCon.OnEnd := OnConEnd;

 fCon.Run;

end;

procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);

begin

 fCon.Free;

end;

procedure TForm1.OnConStdOut(Sender: TObject; s: String);

begin

 memo1.Lines.Append(s);

end;

procedure TForm1.OnConStdErr(Sender: TObject; s: String);

begin

 memo2.Lines.Append(s);

end;

procedure TForm1.OnConRun(Sender: TOBject);

begin

 Application.ProcessMessages;

 Sleep(10);

end;

procedure TForm1.OnConEnd(Sender: TOBject);

begin

 MessageBox(Application.Handle, 'Program has ended', 'Program Ended', MB_OK or MB_ICONINFORMATION or MB_SETFOREGROUND);

end;

procedure TForm1.Button2Click(Sender: TObject);

begin

 fCon.SendData(edit2.Text + #13#10);

end;

*)

interface

uses windows;

type

 TOnData = procedure(Sender: TObject; Data: String) of object;

 TOnRun = procedure(Sender: TObject) of object;

 TRedirectedConsole = Class(TObject)

 private

   fStdInRead, fStdInWrite: THandle;

   fStdOutRead, fStdOutWrite: THandle;

   fStdErrRead, fStdErrWrite: THandle;

   fSA: TSecurityAttributes;

   fPI: TProcessInformation;

   fSI: TStartupInfo;

   fCmdLine: String;

   fOnStdOut, fOnStdErr: TOnData;

   fOnRun, fOnEnd: TOnRun;

   fIsRunning: Boolean;

   fHidden: boolean;

   fTerminate: boolean;

   function ReadHandle(h: THandle; var s: string): integer;

 protected

 public

   constructor Create(CommandLine: String);

   destructor Destroy; override;

   procedure Run;

   procedure SendData(s: String);

   property OnStdOut: TOnData read fOnStdOut write fOnStdOut;

   property OnStdErr: TOnData read fOnStdErr write fOnStdErr;

   property OnRun: TOnRun read fOnRun write fOnRun;

   property OnEnd: TOnRun read fOnEnd write fOnEnd;

   property IsRunning: boolean read fIsRunning;

   property HideWindow: boolean read fHidden write fHidden;

 end;

implementation

const BufSize = 1024;

constructor TRedirectedConsole.Create(CommandLine: String);

begin

 inherited Create;

 fCmdLine := CommandLine;

 fIsRunning := False;

 fHidden := True;

 FillChar(fSA, SizeOf(fSA), 0);

 fSA.nLength := SizeOf(fSA);

 fSA.lpSecurityDescriptor := nil;

 fSA.bInheritHandle := True;

 CreatePipe(fStdInRead, fStdInWrite, @fSA, BufSize);

 CreatePipe(fStdOutRead, fStdOutWrite, @fSA, BufSize);

 CreatePipe(fStdErrRead, fStdErrWrite, @fSA, BufSize);

end;

destructor TRedirectedConsole.Destroy;

begin

 if fIsRunning then

 begin

   fTerminate := True;

 end;

 CloseHandle(fStdInWrite);

 CloseHandle(fStdOutRead);

 CloseHandle(fStdErrRead);

 inherited;

end;

function TRedirectedConsole.ReadHandle(h: THandle; var s: String): integer;

var

 BytesWaiting: Cardinal;

 Buf: Array[1..BufSize] of char;

{$IFDEF VER100}

 BytesRead: Integer;

{$ELSE}

 BytesRead: Cardinal;

{$ENDIF}

begin

 Result := 0;

 PeekNamedPipe(h, nil, 0, nil, @BytesWaiting, nil);

 if BytesWaiting > 0 then

 begin

   if BytesWaiting > BufSize then

     BytesWaiting := BufSize;

   ReadFile(h, Buf[1], BytesWaiting, BytesRead, nil);

   s := Copy(Buf, 1, BytesRead);

   Result := BytesRead;

 end;

end;

procedure TRedirectedConsole.SendData(s: String);

var

{$IFDEF VER100}

 BytesWritten: Integer;

{$ELSE}

 BytesWritten: Cardinal;

{$ENDIF}

begin

 if fIsRunning then

 begin

   WriteFile(fStdInWrite, s[1], Length(s), BytesWritten, nil);

 end;

end;

procedure TRedirectedConsole.Run;

var

 s: String;

begin

 fTerminate := False;

 FillChar(fSI, SizeOf(fSI), 0);

 fSI.cb := SizeOf(fSI);

 if fHidden then

   fSI.wShowWindow := SW_HIDE

 else

   fSI.wShowWindow := SW_SHOWDEFAULT;

 fSI.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;

 fSI.hStdInput := fStdInRead;

 fSI.hStdOutput := fStdOutWrite;

 fSI.hStdError := fStdErrWrite;

 if CreateProcess(nil, PChar(fCmdLine), nil, nil, True, NORMAL_PRIORITY_CLASS, nil, nil, fSI, fPI) then

 begin

   fIsRunning := True;

   CloseHandle(fStdOutWrite);

   CloseHandle(fStdErrWrite);

   CloseHandle(fStdInRead);

   CloseHandle(fPI.hThread);

   While WaitForSingleObject(fPI.hProcess, 10) = WAIT_TIMEOUT do

   begin

     if fTerminate then

     begin

       TerminateProcess(fPi.hProcess, 0);

     end;

     if ReadHandle(fStdOutRead, s) > 0 then

       if Assigned(fOnStdOut) then

         fOnStdOut(Self, s);

     if ReadHandle(fStdErrRead, s) > 0 then

       if Assigned(fOnStdErr) then

         fOnStdErr(Self, s);

     if Assigned(fOnRun) then

       fOnRun(Self);

   end;

   if ReadHandle(fStdOutRead, s) > 0 then

     if Assigned(fOnStdOut) then

       fOnStdOut(Self, s);

   if ReadHandle(fStdErrRead, s) > 0 then

     if Assigned(fOnStdErr) then

       fOnStdErr(Self, s);

   CloseHandle(fPI.hProcess);

   fIsRunning := False;

   if Assigned(fOnEnd) then

     fOnEnd(Self);

 end;

end;

end.