How to redirect binary gbak output to a Delphi stream? -
i want firebird backup tool gbak write output delphi stream (with no intermediate file). there command line parameter write stdout rather file. use execute
method in jedi's jclsysutils
launch gbak , process output.
it looks this:
procedure dobackup; var lbackupabortflag: boolean; lbackupstream: tstringstream; begin lbackupabortflag := false; lbackupstream := tstringstream.create; try execute('"c:\path to\gbak.exe" -b -t -v -user sysdba -pas "pw" <db> stdout', lbackupstream.writestring, // should process stdout (backup) somememo.lines.append, // should process stderr (log) true, // backup "raw" false, // log not @lbackupabortflag); lbackupstream.savetofile('c:\path to\output.fbk'); lbackupstream.free; end; end;
the problem output file way small contain actual backup. still see elements of file's content. tried different stream types, doesn't seem make difference. going wrong here?
update
to clear: other solutions welcome well. of all, need reliable. that's why went jedi in first place, not reinvent such thing. then, nice, if not complicated.
my first answer effective when wish merge stdout , stderr. however, if need keep these separate, approach no use. , can see, closer reading of question, , comments, wish keep 2 output streams separate.
now, not straightforward extend first answer cover this. problem code there uses blocking i/o. , if need service 2 pipes, there obvious conflict. commonly used solution in windows asynchronous i/o, known in windows world overlapped i/o. however, asynchronous i/o more complex implement blocking i/o.
so, i'm going propose alternative approach still uses blocking i/o. if want service multiple pipes, , want use blocking i/o obvious conclusion need 1 thread each pipe. easy implement – easier asynchronous option. can use identical code move blocking read loops threads. example, re-worked in way, looks this:
{$apptype console} uses sysutils, classes, windows; type tprocessoutputpipe = class private frd: thandle; fwr: thandle; public constructor create; destructor destroy; override; property rd: thandle read frd; property wr: thandle read fwr; procedure closewritepipe; end; constructor tprocessoutputpipe.create; const pipesecurityattributes: tsecurityattributes = ( nlength: sizeof(tsecurityattributes); binherithandle: true ); begin inherited; win32check(createpipe(frd, fwr, @pipesecurityattributes, 0)); win32check(sethandleinformation(frd, handle_flag_inherit, 0));//don't inherit read handle of pipe end; destructor tprocessoutputpipe.destroy; begin closehandle(frd); if fwr<>0 closehandle(fwr); inherited; end; procedure tprocessoutputpipe.closewritepipe; begin closehandle(fwr); fwr := 0; end; type treadpipethread = class(tthread) private fpipehandle: thandle; fstream: tstream; protected procedure execute; override; public constructor create(pipehandle: thandle; stream: tstream); end; constructor treadpipethread.create(pipehandle: thandle; stream: tstream); begin inherited create(false); fpipehandle := pipehandle; fstream := stream; end; procedure treadpipethread.execute; var buffer: array [0..4096-1] of byte; bytesread: dword; begin while readfile(fpipehandle, buffer, sizeof(buffer), bytesread, nil) , (bytesread<>0) begin fstream.writebuffer(buffer, bytesread); end; end; function readoutputfromexternalprocess(const applicationname, commandline: string; stdout, stderr: tstream): dword; var stdoutpipe, stderrpipe: tprocessoutputpipe; stdoutthread, stderrthread: treadpipethread; startupinfo: tstartupinfo; processinfo: tprocessinformation; lpapplicationname: pchar; modfiablecommandline: string; begin if applicationname='' lpapplicationname := nil else lpapplicationname := pchar(applicationname); modfiablecommandline := commandline; uniquestring(modfiablecommandline); stdoutpipe := nil; stderrpipe := nil; stdoutthread := nil; stderrthread := nil; try stdoutpipe := tprocessoutputpipe.create; stderrpipe := tprocessoutputpipe.create; zeromemory(@startupinfo, sizeof(startupinfo)); startupinfo.cb := sizeof(startupinfo); startupinfo.dwflags := startf_useshowwindow or startf_usestdhandles; startupinfo.wshowwindow := sw_hide; startupinfo.hstdoutput := stdoutpipe.wr; startupinfo.hstderror := stderrpipe.wr; win32check(createprocess(lpapplicationname, pchar(modfiablecommandline), nil, nil, true, create_no_window or normal_priority_class, nil, nil, startupinfo, processinfo)); stdoutpipe.closewritepipe;//so process able terminate stderrpipe.closewritepipe;//so process able terminate stdoutthread := treadpipethread.create(stdoutpipe.rd, stdout); stderrthread := treadpipethread.create(stderrpipe.rd, stderr); stdoutthread.waitfor; stderrthread.waitfor; win32check(waitforsingleobject(processinfo.hprocess, infinite)=wait_object_0); win32check(getexitcodeprocess(processinfo.hprocess, result)); stderrthread.free; stdoutthread.free; stderrpipe.free; stdoutpipe.free; end; end; procedure test; var stdout, stderr: tfilestream; exitcode: dword; begin stdout := tfilestream.create('c:\desktop\stdout.txt', fmcreate); try stderr := tfilestream.create('c:\desktop\stderr.txt', fmcreate); try exitcode := readoutputfromexternalprocess('', 'cmd /c dir /s c:\windows\system32', stdout, stderr); stderr.free; end; stdout.free; end; end; begin test; end.
if wish add support cancelling, add in call terminateprocess
when user cancelled. bring halt, , function return exit code supplied terminateprocess
. i'm hesitant right suggest cancellation framework you, think code in answer pretty close meeting requirements.
Comments
Post a Comment