Subject | Re: [firebird-support] Capture the output of GBAK console process and display on your GUI application |
---|---|
Author | Salvatore Besso |
Post date | 2007-12-10T21:03:39Z |
hello Stefan,
doing some modifications. It is not too long, so I post them into this message:
---------------------------------------------------------------
uses
Classes;
function ExecRedirected(const CommandLine: string; List: TStrings): Integer;
procedure KillExecRedirected;
var
ProcessHandle: Cardinal; // Global variable
implementation
uses
Windows, SysUtils;
function ExecRedirected(const CommandLine: string; List: TStrings): Integer;
const
BlockSize = 2048;
var
Security: TSecurityAttributes;
ReadPipe, WritePipe: THandle;
StartInfo: TStartupInfo;
ProcessInfo: TProcessInformation;
Buffer: array[0..BlockSize + 1] of Char;
Ok: Boolean;
BytesRead: Cardinal;
I: Integer;
Line: string;
ExitCode: DWORD;
begin
Application.ProcessMessages;
Security.nLength := SizeOf(Security);
Security.bInheritHandle := True;
Security.lpSecurityDescriptor := nil;
{ Create pipe to redirect on standard output }
if CreatePipe(ReadPipe, WritePipe, @Security, 0) then
begin
try
{ We use WritePipe as standard output to child process
and we assure that it is not displayed on the screen }
FillChar(StartInfo, SizeOf(StartInfo), 0);
StartInfo.cb := SizeOf(StartInfo);
StartInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
StartInfo.wShowWindow := SW_HIDE;
{ Don't redirect standard input }
StartInfo.hStdInput := GetStdHandle(STD_INPUT_HANDLE);
StartInfo.hStdOutput := WritePipe;
StartInfo.hStdError := WritePipe;
{ Create process }
if CreateProcess(nil, PChar(CommandLine), nil, nil, True,
NORMAL_PRIORITY_CLASS, nil, nil, StartInfo, ProcessInfo) then
begin
try
ProcessHandle := ProcessInfo.hProcess;
{ Now that the handle has been inherited, let's close WritePipe as
a security measure. We don't want to accidentally read or write
on it }
CloseHandle(WritePipe);
{ If process has been created then let's handle its output }
Line := '';
{ Get all output until the console application terminates }
repeat
Application.ProcessMessages;
{ Read a block of characters (it might contain CRLF's) }
Ok := ReadFile(ReadPipe, Buffer, BlockSize, BytesRead, nil);
{ Has anything been read? }
if BytesRead > 0 then
begin
{ Complete PChar buffer }
Buffer[BytesRead] := #0;
{ Convert possible OEM characters into Ansi }
OemToAnsi(Buffer, Buffer);
{ Add what has been read to the local string variable }
Line := Line + Buffer;
{ Normalize CRLF's }
Line := AdjustLineBreaks(Line);
{ Divide what has been read every end of line and add it to the list }
repeat
I := Pos(#13#10, Line);
if I > 0 then
begin
List.Add(Copy(Line, 1, I - 1));
Delete(Line, 1, I + 1)
end
until I = 0
end
until not Ok or (BytesRead = 0);
{ Add any possible remnant to the list }
if Line <> '' then
List.Add(Line);
Line := '';
List.Add(Line);
{ Wait for console application to terminate (it should be already
terminated now) }
WaitForSingleObject(ProcessInfo.hProcess, INFINITE)
finally
{ Get process exit code }
if GetExitCodeProcess(ProcessInfo.hProcess, ExitCode) then
Result := ExitCode
else Result := 996;
{ Close all process' handles }
CloseHandle(ProcessInfo.hThread);
CloseHandle(ProcessInfo.hProcess);
ProcessHandle := 0
end
end
else Result := 997
finally
{ Close all remaining handles }
CloseHandle(ReadPipe)
end
end
else Result := 998
end;
procedure KillExecRedirected;
{ You can kill the above process using its handle }
begin
if ProcessHandle <> 0 then
try
TerminateProcess(ProcessHandle, 999)
finally
ProcessHandle := 0
end
end;
---------------------------------------------------------------------
This is a simple usage example of a backup operation
(ReportMemo is the TMemo component):
CommandLine := 'drive:\path\gbak.exe -B -T -V -USER SYSDBA -PAS masterkey C:\DB\MyDB.fdb Z:\Backups\MyDB.fbk';
ExitCode := ExecRedirected(CommandLine, ReportMemo.Lines);
The TStrings parameter must be NOT NIL!
On exit examine the exit code returned by the function.
I have never tried, but I suppose that you can also use UNC paths.
---------------------------------------------------------------------
Have fun :-)
Salvatore
> would you show us (or send me) the code that does it?it's a pleasure. It consists of only one function and one procedure. The main function was borrowed from some Jedi code
doing some modifications. It is not too long, so I post them into this message:
---------------------------------------------------------------
uses
Classes;
function ExecRedirected(const CommandLine: string; List: TStrings): Integer;
procedure KillExecRedirected;
var
ProcessHandle: Cardinal; // Global variable
implementation
uses
Windows, SysUtils;
function ExecRedirected(const CommandLine: string; List: TStrings): Integer;
const
BlockSize = 2048;
var
Security: TSecurityAttributes;
ReadPipe, WritePipe: THandle;
StartInfo: TStartupInfo;
ProcessInfo: TProcessInformation;
Buffer: array[0..BlockSize + 1] of Char;
Ok: Boolean;
BytesRead: Cardinal;
I: Integer;
Line: string;
ExitCode: DWORD;
begin
Application.ProcessMessages;
Security.nLength := SizeOf(Security);
Security.bInheritHandle := True;
Security.lpSecurityDescriptor := nil;
{ Create pipe to redirect on standard output }
if CreatePipe(ReadPipe, WritePipe, @Security, 0) then
begin
try
{ We use WritePipe as standard output to child process
and we assure that it is not displayed on the screen }
FillChar(StartInfo, SizeOf(StartInfo), 0);
StartInfo.cb := SizeOf(StartInfo);
StartInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
StartInfo.wShowWindow := SW_HIDE;
{ Don't redirect standard input }
StartInfo.hStdInput := GetStdHandle(STD_INPUT_HANDLE);
StartInfo.hStdOutput := WritePipe;
StartInfo.hStdError := WritePipe;
{ Create process }
if CreateProcess(nil, PChar(CommandLine), nil, nil, True,
NORMAL_PRIORITY_CLASS, nil, nil, StartInfo, ProcessInfo) then
begin
try
ProcessHandle := ProcessInfo.hProcess;
{ Now that the handle has been inherited, let's close WritePipe as
a security measure. We don't want to accidentally read or write
on it }
CloseHandle(WritePipe);
{ If process has been created then let's handle its output }
Line := '';
{ Get all output until the console application terminates }
repeat
Application.ProcessMessages;
{ Read a block of characters (it might contain CRLF's) }
Ok := ReadFile(ReadPipe, Buffer, BlockSize, BytesRead, nil);
{ Has anything been read? }
if BytesRead > 0 then
begin
{ Complete PChar buffer }
Buffer[BytesRead] := #0;
{ Convert possible OEM characters into Ansi }
OemToAnsi(Buffer, Buffer);
{ Add what has been read to the local string variable }
Line := Line + Buffer;
{ Normalize CRLF's }
Line := AdjustLineBreaks(Line);
{ Divide what has been read every end of line and add it to the list }
repeat
I := Pos(#13#10, Line);
if I > 0 then
begin
List.Add(Copy(Line, 1, I - 1));
Delete(Line, 1, I + 1)
end
until I = 0
end
until not Ok or (BytesRead = 0);
{ Add any possible remnant to the list }
if Line <> '' then
List.Add(Line);
Line := '';
List.Add(Line);
{ Wait for console application to terminate (it should be already
terminated now) }
WaitForSingleObject(ProcessInfo.hProcess, INFINITE)
finally
{ Get process exit code }
if GetExitCodeProcess(ProcessInfo.hProcess, ExitCode) then
Result := ExitCode
else Result := 996;
{ Close all process' handles }
CloseHandle(ProcessInfo.hThread);
CloseHandle(ProcessInfo.hProcess);
ProcessHandle := 0
end
end
else Result := 997
finally
{ Close all remaining handles }
CloseHandle(ReadPipe)
end
end
else Result := 998
end;
procedure KillExecRedirected;
{ You can kill the above process using its handle }
begin
if ProcessHandle <> 0 then
try
TerminateProcess(ProcessHandle, 999)
finally
ProcessHandle := 0
end
end;
---------------------------------------------------------------------
This is a simple usage example of a backup operation
(ReportMemo is the TMemo component):
CommandLine := 'drive:\path\gbak.exe -B -T -V -USER SYSDBA -PAS masterkey C:\DB\MyDB.fdb Z:\Backups\MyDB.fbk';
ExitCode := ExecRedirected(CommandLine, ReportMemo.Lines);
The TStrings parameter must be NOT NIL!
On exit examine the exit code returned by the function.
I have never tried, but I suppose that you can also use UNC paths.
---------------------------------------------------------------------
Have fun :-)
Salvatore