Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Capturing output from a CreateProcess encounter some delay

54 views
Skip to first unread message

Chau Chee Yang

unread,
Dec 30, 2006, 3:08:57 AM12/30/06
to
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:

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

Liz

unread,
Dec 30, 2006, 4:10:14 AM12/30/06
to
Chau Chee Yang wrote:

> 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

Dan Palley

unread,
Dec 30, 2006, 4:18:16 PM12/30/06
to
"Chau Chee Yang" <c...@sql.com.my> wrote in message
news:45961e7a$1...@newsgroups.borland.com...

> 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:

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


Chau Chee Yang

unread,
Dec 31, 2006, 9:13:29 AM12/31/06
to
But that doesn't happen if I execute gbak.exe cmd.exe shell. The records
written increased smoothly:

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?

Dan Palley

unread,
Jan 2, 2007, 12:12:04 PM1/2/07
to
"Chau Chee Yang" <c...@sql.com.my> wrote in message
news:4597c57c$1...@newsgroups.borland.com...

> Dan Palley wrote:
>> "Chau Chee Yang" <c...@sql.com.my> wrote in message
>> news:45961e7a$1...@newsgroups.borland.com...
>>> 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:
>>
>> 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
> But that doesn't happen if I execute gbak.exe cmd.exe shell. The records
> written increased smoothly:
>
> 20000 records written
> 40000 records written
> 60000 records written
> ...
>
> 230000 records written

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


Chau Chee Yang

unread,
Jan 2, 2007, 8:26:34 PM1/2/07
to

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.

Dan Palley

unread,
Jan 3, 2007, 12:29:41 PM1/3/07
to
"Chau Chee Yang" <c...@sql.com.my> wrote in message
news:459b...@newsgroups.borland.com...

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


0 new messages