加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 大数据 > 正文

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

发布时间:2020-12-15 09:32:26 所属栏目:大数据 来源:网络整理
导读:我需要从命令行运行sqlite backup命令.我不想使用“cmd / c”.命令是: sqlite3.exe MYDB.db .dump MYDB.bak 我在SO上找不到任何显示如何执行此操作的示例. 到目前为止,从各种SO帖子收集的代码是这样的,但是非常不完整: function StartProcess(const AComma
我需要从命令行运行sqlite backup命令.我不想使用“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,False,CREATE_NEW_PROCESS_GROUP + NORMAL_PRIORITY_CLASS,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捕获输出然后重定向它.将它重定向到什么? COPY CON?还是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,CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL,0);
  Win32Check(StdOutFileHandle <> INVALID_HANDLE_VALUE);

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

  try
    FillChar(StartupInfo,0);
    FillChar(ProcessInformation,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,True,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);
  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关闭文件.您需要决定如何处理标准错误句柄.一种常见的选择是将其与标准输出合并.为hStdOutput和hStdError分配相同的句柄.

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

对CreateFile的调用如下所示:

StdOutFileHandle := CreateFile(
  'MYDB.bak',0
);

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

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

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

Win32Check(SetHandleInformation(StdOutFileHandle,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返回的句柄.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读