Как перенаправить большое количество вывода из команды, выполняемой CreateProcess?
Мне нужно запустить команду резервного копирования sqlite из командной строки. Я не хочу использовать "CMD / C". Команда:
sqlite3.exe MYDB.db .dump> MYDB.bak
Я не мог найти пример на SO, который показывает, как это сделать.
Код, который я до сих пор собирал из различных SO-сообщений, выглядит следующим образом, но он очень неполный:
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) then
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 then
Result := ProcessInformation.dwProcessId;
if AWaitForFinish then
WaitForSingleObject(ProcessInformation.hProcess, INFINITE);
CloseHandle(ProcessInformation.hProcess);
CloseHandle(ProcessInformation.hThread);
end;
Поскольку вывод команды dump очень велик, я не уверен, как перехватить вывод из stdout и затем перенаправить его. Перенаправить это на что? КОПИЯ КОН? или в TFileStream.Write?
Я видел этоПочта, но его неполное в отношении реализации перенаправления в выходной файл. Думаю, мне следует спросить: «Каков наиболее эффективный способ реализовать это?»
Если кто-то делал это раньше, пожалуйста, опубликуйте пример кода, иллюстрирующий, как я могу это сделать.
ТИА.
РЕДАКТИРОВАТЬ:
Основываясь на ответе Дэвида Хеффернана, вот мой пересмотренный код, который действительно работает правильно:
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) then
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 then
begin
try
Result := ProcessInformation.dwProcessId;
if AWaitForFinish then
WaitForSingleObject(ProcessInformation.hProcess, INFINITE);
finally
if ProcessInformation.hProcess <> INVALID_HANDLE_VALUE then
CloseHandle(ProcessInformation.hProcess);
if ProcessInformation.hThread <> INVALID_HANDLE_VALUE then
CloseHandle(ProcessInformation.hThread);
end;
end;
finally
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);
finally
fMain.UniConnection1.Open;
end;
ZipMaster1.FSpecArgs.Add(b);
ZipMaster1.ZipFileName := ADBBackupFile;
ZipMaster1.Add;
DeleteFile(b);
ShowMessage('Backup complete!');
end;