delphi - How to redirect large amount of output from command executed by CreateProcess? -
i need run sqlite backup command command line. don't want use "cmd /c". command is:
sqlite3.exe mydb.db .dump > mydb.bak
i not find example on shows how this.
code have far, collected various posts this, incomplete:
function startprocess(const acommandline: string; ashowwindow: boolean = true; awaitforfinish: boolean = false): integer; var commandline: string; startupinfo: tstartupinfo; processinformation: tprocessinformation; stdoutpiperead, stdoutpipewrite: thandle; handle: boolean; begin result := 0; fillchar(startupinfo, sizeof(tstartupinfo), 0); fillchar(processinformation, sizeof(tprocessinformation), 0); startupinfo.cb := sizeof(tstartupinfo); startupinfo.hstdinput := getstdhandle(std_input_handle); startupinfo.hstdoutput := stdoutpipewrite; startupinfo.hstderror := stdoutpipewrite; if not(ashowwindow) begin startupinfo.dwflags := startf_useshowwindow; startupinfo.wshowwindow := sw_shownormal; end; commandline := acommandline; uniquestring(commandline); handle := createprocess(nil, pchar(commandline), nil, nil, false, create_new_process_group + normal_priority_class, nil, nil, startupinfo, processinformation); closehandle(stdoutpipewrite); if handle result := processinformation.dwprocessid; if awaitforfinish waitforsingleobject(processinformation.hprocess, infinite); closehandle(processinformation.hprocess); closehandle(processinformation.hthread); end;
since output dump command large, i'm not sure how capture output stdout , redirect it. redirect what? copy con? or tfilestream.write?
i've seen post, incomplete regard implementing redirection output file. guess should ask "what efficient way implement this?"
if has done before, please post code sample illustrating how can it.
tia.
edit:
based on david heffernan's answer, here revised code indeed works properly:
function startprocesswithredirectedoutput(const acommandline: string; const aoutputfile: string; ashowwindow: boolean = true; awaitforfinish: boolean = false): integer; var commandline: string; startupinfo: tstartupinfo; processinformation: tprocessinformation; stdoutfilehandle: thandle; processresult: boolean; begin result := 0; stdoutfilehandle := createfile(pchar(aoutputfile), generic_write, file_share_read, nil, create_always, file_attribute_normal, 0); win32check(stdoutfilehandle <> invalid_handle_value); win32check(sethandleinformation(stdoutfilehandle, handle_flag_inherit, 1)); try fillchar(startupinfo, sizeof(tstartupinfo), 0); fillchar(processinformation, sizeof(tprocessinformation), 0); startupinfo.cb := sizeof(tstartupinfo); startupinfo.dwflags := startupinfo.dwflags or startf_usestdhandles; startupinfo.hstdinput := getstdhandle(std_input_handle); startupinfo.hstdoutput := stdoutfilehandle; startupinfo.hstderror := stdoutfilehandle; if not(ashowwindow) begin startupinfo.dwflags := startupinfo.dwflags or startf_useshowwindow; startupinfo.wshowwindow := sw_hide; end; commandline := acommandline; uniquestring(commandline); processresult := win32check(createprocess(nil, pchar(commandline), nil, nil, true, create_new_process_group + normal_priority_class, nil, nil, startupinfo, processinformation)); if processresult begin try result := processinformation.dwprocessid; if awaitforfinish waitforsingleobject(processinformation.hprocess, infinite); if processinformation.hprocess <> invalid_handle_value closehandle(processinformation.hprocess); if processinformation.hthread <> invalid_handle_value closehandle(processinformation.hthread); end; end; closehandle(stdoutfilehandle); end; end; procedure tfadmin.dodbbackup(adbbackupfile: string); var b, p, q: string; begin b := extractfilepath(paramstr(0)) + 'ppdb.bak'; p := extractfilepath(paramstr(0)) + 'sqlite3.exe'; q := extractfilepath(paramstr(0)) + 'ppdb.db .dump'; fmain.uniconnection1.close; try startprocesswithredirectedoutput(p + ' ' + q, b, true, true); fmain.uniconnection1.open; end; zipmaster1.fspecargs.add(b); zipmaster1.zipfilename := adbbackupfile; zipmaster1.add; deletefile(b); showmessage('backup complete!'); end;
create file handle redirection. that's cmd script does. redirects file named 'mydb.bak'
.
so, call createfile
create file name, , assign handle returned startupinfo.hstdoutput
. when external process has finished, call closehandle
on file handle close file. you'll need decide standard error handle. 1 common choice merge standard output. assign same handle both hstdoutput
, hstderror
.
your code assigning standard handles, not asking external process uses them. need include startf_usestdhandles
in startupinfo.dwflags
.
the call createfile
this:
stdoutfilehandle := createfile( 'mydb.bak', generic_write, file_share_read, nil, create_always, file_attribute_normal, 0 );
check value returned createfile
not equal invalid_handle_value
.
as mentioned in previous question, need external process inherit file handle pass it. if don't allow inheritance of handles external process cannot use handle pass it. pass true
binherithandles
parameter of createprocess
.
the file handle created createfile
not, default inheritable. can either pass security attributes make inheritable. or can set explicitly after have created. latter looks this:
win32check(sethandleinformation(stdoutfilehandle, handle_flag_inherit, 1));
examples of former (in context of pipes) can seen in answer here: how redirect binary gbak output delphi stream?
the code mentions stdoutpipewrite
needs deleted. cannot work @ moment because not initializing handle.
you should make use of try/finally
ensure don't leak handles in face of exceptions.
finally, code contains lot of errors, , little error checking. suggest read , re-read documentation createprocess
. have read of example on msdn: http://msdn.microsoft.com/en-us/library/windows/desktop/ms682499.aspx. although uses pipes, principals same. same, instead of pipes use handle returned call createprocess
.
Comments
Post a Comment