gpt4 book ai didi

delphi - 如何重定向 CreateProcess 执行的命令的大量输出?

转载 作者:行者123 更新时间:2023-12-03 15:09:11 28 4
gpt4 key购买 nike

我需要从命令行运行 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?

我见过这个post ,但它在实现输出文件重定向方面并不完整。我想我应该问“实现这个最有效的方法是什么?”

如果有人以前这样做过,请发布一个代码示例来说明我如何做到这一点。

TIA。

编辑:

根据 David Heffernan 的回答,这是我修改后的代码,确实可以正常工作:

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;

最佳答案

创建用于重定向的文件句柄。这就是你的 cmd 脚本的作用。这会重定向到名为 'MYDB.bak' 的文件。

因此,调用CreateFile来创建具有该名称的文件,并分配作为StartupInfo.hStdOutput返回的句柄。当外部进程完成时,在文件句柄上调用CloseHandle来关闭文件。您需要决定如何处理标准错误句柄。一种常见的选择是将其与标准输出合并。为 hStdOutputhStdError 分配相同的句柄。

您的代码分配标准句柄,但不要求外部进程使用它们。您需要在 StartupInfo.dwFlags 中包含 STARTF_USESTDHANDLES

CreateFile 的调用将如下所示:

StdOutFileHandle := CreateFile(
'MYDB.bak',
GENERIC_WRITE,
FILE_SHARE_READ,
nil,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
0
);

检查 CreateFile 返回的值是否不等于 INVALID_HANDLE_VALUE

正如我在上一个问题中提到的,您需要外部进程继承您传递给它的文件句柄。如果您不允许继承句柄,则外部进程无法使用您传递给它的句柄。因此,为 CreateProcessbInheritHandles 参数传递 True

默认情况下,CreateFile 创建的文件句柄不可继承。您可以传递使其可继承的安全属性。或者您可以在创建后明确设置它。后者看起来像这样:

Win32Check(SetHandleInformation(StdOutFileHandle, HANDLE_FLAG_INHERIT, 1));

前者的示例(在管道的上下文中)可以在我的回答中看到:How to redirect binary gbak output to a Delphi stream?

提到StdOutPipeWrite的代码全部需要删除。它目前无法工作,因为您没有初始化句柄。

您应该充分利用 try/finally 以确保即使面对异常也不会泄漏任何句柄。

最后,您的代码包含很多错误,但几乎没有错误检查。我建议您阅读并重新阅读 CreateProcess 的文档。另请仔细阅读 MSDN 上的此示例:http://msdn.microsoft.com/en-us/library/windows/desktop/ms682499.aspx 。虽然它使用管道,但原理是相同的。执行完全相同的操作,但使用调用 CreateProcess 返回的句柄代替管道。

关于delphi - 如何重定向 CreateProcess 执行的命令的大量输出?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19211958/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com