I am writing win32 app to invoke a command line utility GBAK.EXE ( 
interbase backup/restore).  I use CreateProcess + CreatePipe + ReadFile 
to capture the output of gbak.exe.
Everything works well with the code I wrote (see below). However, I 
encounter a 5-10 seconds on certain stage the code is executing for a 
database I want to backup (size is about 400M).  I expect the output is 
showing on screen smoothly but it doesn't:
procedure TForm5.Button1Click(Sender: TObject);
var C: TConsoleProcess;
begin
   C := TConsoleProcess.Create('c:\temp\gbak.exe', '-b -v -t -user 
sysdba -pas masterkey "localhost:c:\temp\db.fdb" "c:\temp\a.fbk"');
   try
     C.Execute;
     while not C.EOF do begin
       Memo1.Lines.Add(C.GetNextLine);
     end;
   finally
     C.Free;
   end;
end;
First I thought it was because nature of the backup process.  I then 
test on Dos prompt with the same command line parameters but this time 
the output showing smoothly without any delay.  Does anyone has the same 
experience as me?  Thank you.
type
   TConsoleProcess = class(TObject)
   private
     SI: TStartupInfo;
     PI: TProcessInformation;
     StdOutPipeRead, StdOutPipeWrite: THandle;
     FAppName: string;
     FCmdLine: string;
     FLine: string;
     FActive: Boolean;
   protected
     procedure StartProcess;
     procedure StopProcess;
   public
     constructor Create(const aAppName, aCmdLine: string);
     procedure BeforeDestruction; override;
     function Execute: Integer;
     function EOF: boolean;
     function GetNextLine: string;
   end;
procedure TConsoleProcess.BeforeDestruction;
begin
   inherited;
   StopProcess;
end;
function TConsoleProcess.EOF: boolean;
begin
   Result := not FActive and (FLine = '');
end;
function TConsoleProcess.Execute: Integer;
var SA: TSecurityAttributes;
     WasOK: boolean;
     iExit: DWord;
begin
   if FActive then
     raise Exception.Create('Service already start');
   StartProcess;
   with SA do begin
     nLength := SizeOf(SA);
     bInheritHandle := True;
     lpSecurityDescriptor := nil;
   end;
   // create pipe for standard output redirection
   CreatePipe(StdOutPipeRead,  // read handle
              StdOutPipeWrite, // write handle
              @SA,             // security attributes
              0                // number of bytes reserved for pipe - 0 
default
             );
   // Make child process use StdOutPipeWrite as standard out,
   // and make sure it does not show on screen.
   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 std 
input
     hStdOutput := StdOutPipeWrite;
     hStdError := StdOutPipeWrite;
   end;
   // launch the command line compiler
   WasOK := CreateProcess(PChar(FAppName), PChar(FCmdLine), nil, nil, 
True, 0, nil, nil, SI, PI);
   // Now that the handle has been inherited, close write to be safe.
   // We don't want to read or write to it accidentally.
   CloseHandle(StdOutPipeWrite);
   // if process could be created then handle its output
   if not WasOK then
     raise Exception.Create(SysErrorMessage(GetLastError))
   else begin
     GetExitCodeProcess(PI.hProcess, iExit);
     Result := iExit;
   end;
end;
function TConsoleProcess.GetNextLine: string;
var Buffer: array[0..255] of Char;
     BytesRead: Cardinal;
     iPos: integer;
     WasOK: boolean;
begin
   Result := '';
   if not EOF then begin
     iPos := Pos(#$0D#$0A, FLine);
     if iPos = 0 then begin
       repeat
         // read block of characters (might contain carriage returns and 
line feeds)
         WasOK := ReadFile(StdOutPipeRead, Buffer, SizeOf(Buffer) - 1, 
BytesRead, nil);
         // has anything been read?
         if WasOK and (BytesRead > 0) then begin
           // finish buffer to PChar
           Buffer[BytesRead] := #0;
           // combine the buffer with the rest of the last run
           FLine := FLine + Buffer;
         end else begin
           WaitForSingleObject(PI.hProcess, INFINITE);
           StopProcess;
         end;
       until (BytesRead > 0) or not FActive;
       iPos := Pos(#$0D#$0A, FLine);
     end;
     if iPos > 0 then begin
       Result := Copy(FLine, 1, iPos);
       Delete(FLine, 1, iPos + 1);
     end;
   end;
end;
procedure TConsoleProcess.StartProcess;
begin
   FActive := True;
end;
procedure TConsoleProcess.StopProcess;
begin
   if FActive then begin
     CloseHandle(PI.hThread);
     CloseHandle(PI.hProcess);
     CloseHandle(StdOutPipeRead);
     FActive := False;
   end;
end;
This is how I run the code:
-- 
Best regards,
Chau Chee Yang
E Stream Software Sdn Bhd
URL: www.sql.com.my
SQL Financial Accounting
> Hi,
> 
> I am writing win32 app to invoke a command line utility GBAK.EXE (
> interbase backup/restore).  I use CreateProcess + CreatePipe +
> ReadFile to capture the output of gbak.exe.
> 
> Everything works well with the code I wrote (see below). However, I
> encounter a 5-10 seconds on certain stage the code is executing for a
> database I want to backup (size is about 400M).  I expect the output
> is showing on screen smoothly but it doesn't:
Perhaps because its not sending new lines but maybe redrawing the same
one a few times .. at a guess
-- 
Liz the Brit
Delphi things I have released: http://www.xcalibur.co.uk/DelphiThings
That's the normal process for GBAK.  When writing out records for a table, 
there's no output until the end of the table is reached, so for a big table, 
you'll see a pause, then xxx records written.
Dan
20000 records written
40000 records written
60000 records written
...
230000 records written
However, the output lines of gbak.exe process created by CreateProcess 
doesn't behave like this.  I just wondering if the TConsoleProcess I 
wrote is on right track or I have missed something to make it works as 
smooth as gbak.exe run under cmd.exe?
Hmm, you must be using a different version of GBAK than I am.  Are you using 
Interbase or Firebird?
At any rate, I suspect there's some buffering going on in the I/O 
redirection that you don't see when writing directly to the console.
Dan
No. I am very sure I use the same version of GBAK.EXE for both 
CreateProcess and run from CMD.EXE.  I am using Firebird 1.5.3 superserver.
It is very easy to try out, just find a large database (400-500M). Then 
you run the GBAK.EXE on CMD.EXE and notice how to output shown.
The next step is using the TConsoleProcess I have written (in previous 
post) and notice the output. You will see the different.
Ok, you're using Firebird, so I assume the GBAK version you're using is 
different from the one that ships with Interbase.  I don't have Firebird 
installed, so I can't really investigate the issue further, but it may have 
to do with how Firebird handles writing updates (20000...30000...40000) to 
the console.
I believe Firebird implements the service API which has a backup routine 
that can be used instead of GBAK as long as you're creating a backup file 
locally on the server.
Dan