5

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

我看过这篇文章,但在实现到输出文件的重定向方面它是不完整的。我想我应该问“实现这一点的最有效方法是什么?”

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

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;
4

2 回答 2

5

为重定向创建一个文件句柄。这就是你的 cmd 脚本所做的。重定向到一个名为'MYDB.bak'.

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

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

调用CreateFile将如下所示:

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

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

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

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

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

前者的示例(在管道的上下文中)可以在我的回答中看到:如何将二进制 gbak 输出重定向到 Delphi 流?

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

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

最后,您的代码包含很多错误,并且很少进行错误检查。我建议您阅读并重新阅读CreateProcess. 还可以在 MSDN 上阅读此示例:http: //msdn.microsoft.com/en-us/library/windows/desktop/ms682499.aspx。尽管它使用管道,但原理是相同的。执行完全相同的操作,但使用调用返回的句柄而不是管道CreateProcess

于 2013-10-06T18:34:18.540 回答
3

对于一个更完整的答案,一个也说明输入重定向的答案,我发布了我的代码。感谢 David Heffernan 的指导,没有他的指导,这是不可能的。

该代码涉及 SQLite 数据库的备份和恢复,方法是使用 CreateProcess 调用 Sqlite3.exe 可执行文件。显然输入和输出需要重定向到/从这个命令,下面的代码说明了如何实现这一点:

function StartProcessWithRedirectedOutput(const ACommandLine: string; const AOutputFile: string;
  AShowWindow: boolean = True; AWaitForFinish: boolean = False): Integer;
var
  CommandLine: string;
  StartupInfo: TStartupInfo;
  ProcessInformation: TProcessInformation;
  StdOutFileHandle: THandle;
begin
  Result := 0;

  StdOutFileHandle := CreateFile(PChar(AOutputFile), GENERIC_WRITE, FILE_SHARE_READ, nil, CREATE_ALWAYS,
    FILE_ATTRIBUTE_NORMAL, 0);
  Win32Check(StdOutFileHandle <> INVALID_HANDLE_VALUE);
  try
    Win32Check(SetHandleInformation(StdOutFileHandle, HANDLE_FLAG_INHERIT, 1));
    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);

    Win32Check(CreateProcess(nil, PChar(CommandLine), nil, nil, True,
      CREATE_NEW_PROCESS_GROUP + NORMAL_PRIORITY_CLASS, nil, nil, StartupInfo, ProcessInformation));

    try
      Result := ProcessInformation.dwProcessId;

      if AWaitForFinish then
        WaitForSingleObject(ProcessInformation.hProcess, INFINITE);

    finally
      CloseHandle(ProcessInformation.hProcess);
      CloseHandle(ProcessInformation.hThread);
    end;

  finally
    CloseHandle(StdOutFileHandle);
  end;
end;

function StartProcessWithRedirectedInput(const ACommandLine: string; const AInputFile: string;
  AShowWindow: boolean = True; AWaitForFinish: boolean = False): Integer;
var
  CommandLine: string;
  StartupInfo: TStartupInfo;
  ProcessInformation: TProcessInformation;
  StdInFileHandle: THandle;
begin
  Result := 0;

  StdInFileHandle := CreateFile(PChar(AInputFile), GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING,
    FILE_ATTRIBUTE_NORMAL, 0);
  Win32Check(StdInFileHandle <> INVALID_HANDLE_VALUE);

  try
    Win32Check(SetHandleInformation(StdInFileHandle, HANDLE_FLAG_INHERIT, 1));
    FillChar(StartupInfo, SizeOf(TStartupInfo), 0);
    FillChar(ProcessInformation, SizeOf(TProcessInformation), 0);

    StartupInfo.cb := SizeOf(TStartupInfo);
    StartupInfo.dwFlags := StartupInfo.dwFlags or STARTF_USESTDHANDLES;
    StartupInfo.hStdInput := StdInFileHandle;
    StartupInfo.hStdOutput := GetStdHandle(STD_OUTPUT_HANDLE);
    StartupInfo.hStdError := GetStdHandle(STD_OUTPUT_HANDLE);

    if not(AShowWindow) then
    begin
      StartupInfo.dwFlags := StartupInfo.dwFlags or STARTF_USESHOWWINDOW;
      StartupInfo.wShowWindow := SW_HIDE;
    end;

    CommandLine := ACommandLine;
    UniqueString(CommandLine);

    Win32Check(CreateProcess(nil, PChar(CommandLine), nil, nil, True,
      CREATE_NEW_PROCESS_GROUP + NORMAL_PRIORITY_CLASS, nil, nil, StartupInfo, ProcessInformation));

    try
      Result := ProcessInformation.dwProcessId;

      if AWaitForFinish then
        WaitForSingleObject(ProcessInformation.hProcess, INFINITE);

    finally
      CloseHandle(ProcessInformation.hProcess);
      CloseHandle(ProcessInformation.hThread);
    end;

  finally
    CloseHandle(StdInFileHandle);
  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;

procedure TfAdmin.DoDBRestore(ADBBackupFile: String);
var
  b, p, q, q2, r: String;
begin
  b := ExtractFilePath(ParamStr(0)) + 'PPDB.bak';
  p := '"' + ExtractFilePath(ParamStr(0)) + 'sqlite3.exe"';
  q := '"' + ExtractFilePath(ParamStr(0)) + 'PPDB.db"';

  ZipMaster1.ExtrBaseDir := ExtractFilePath(ParamStr(0));
  ZipMaster1.ExtrOptions := [ExtrOverWrite];
  ZipMaster1.ZipFileName := ADBBackupFile;
  ZipMaster1.Extract;

  fMain.UniConnection1.Close;
  try
    q2 := StringReplace(q, '"', '', [rfReplaceAll]);
    r := ChangeFileExt(q2, '.db$');
    if FileExists(r) then
      DeleteFile(r);
    if not RenameFile(q2, r) then
      RaiseLastOSError;
    StartProcessWithRedirectedInput(p + ' ' + q, b, True, True);
  finally
    fMain.UniConnection1.Open;
  end;

  DeleteFile(b);

  ShowMessage('Restore complete!');

end;
于 2013-10-06T21:01:13.403 回答