I personally think it is easier to do this with a call to CreateProcess
. You need to create a file handle, and pass that to CreateProcess
to use as stdout. There are a few details that you need to take care over, mostly concerning handle inheritance. You can get it all done with this function:
procedure ExecuteCommandAndAppendToFile(
const Executable: string;
const Arguments: string;
const WorkingDir: string;
const OutputFile: string
);
const
sa: TSecurityAttributes = (nLength: SizeOf(sa); bInheritHandle: True);
var
hstdout: THandle;
StartupInfo: TStartupInfo;
ProcessInfo: TProcessInformation;
cmd: string;
begin
hstdout := CreateFile(PChar(OutputFile), GENERIC_WRITE, 0, @sa, OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL, 0);
Win32Check(hstdout<>INVALID_HANDLE_VALUE);
try
//move to end of file since we wish to append
SetFilePointer(hstdout, 0, nil, FILE_END);
ZeroMemory(@StartupInfo, SizeOf(StartupInfo));
StartupInfo.cb := SizeOf(StartupInfo);
StartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
StartupInfo.wShowWindow := SW_HIDE;
StartupInfo.hStdOutput := hstdout;
StartupInfo.hStdError := hstdout;
cmd := Executable + ' ' + Arguments;
UniqueString(cmd);//not needed but let's be explicit
if not CreateProcess(nil, PChar(cmd), nil, nil, True,
CREATE_NO_WINDOW or NORMAL_PRIORITY_CLASS, nil, PChar(WorkingDir),
StartupInfo, ProcessInfo) then
begin
RaiseLastOSError;
end;
try
WaitForSingleObject(ProcessInfo.hProcess, INFINITE);
finally
CloseHandle(ProcessInfo.hProcess);
CloseHandle(ProcessInfo.hThread);
end;
finally
CloseHandle(hstdout);
end;
end;
This uses WaitForSingleObject
to wait for completion. That will block the thread that runs this code. You could move this code into a different thread if you have GUI that you wish not to block. Or you could use MsgWaitForMultipleObjects
. Or you could use your Sleep
based loop if you must (I don't much care for Sleep
but it's your choice).
So, you may wish to tweak bits of this, but the basics are all present here.