如何将二进制gbak输出重定向到Delphi流?

如何将二进制gbak输出重定向到Delphi流?,第1张

概述我希望Firebird备份工具gbak将其输出写入Delphi流(没有中间文件).有一个命令行参数可以写入stdout而不是文件.然后我在JEDI的JclSysUtils中使用Execute方法来启动gbak并处理该输出. 它看起来像这样: procedure DoBackup;var LBackupAbortFlag: Boolean; LBackupStream: TStringSt 我希望Firebird备份工具gbak将其输出写入Delphi流(没有中间文件).有一个命令行参数可以写入stdout而不是文件.然后我在JEDI的JclSysUtils中使用Execute方法来启动gbak并处理该输出.

它看起来像这样:

procedure dobackup;var  LBackupAbortFlag: Boolean;  LBackupStream: TStringStream;begin  LBackupAbortFlag := False;  LBackupStream := TStringStream.Create;  try    Execute('"C:\path to\gbak.exe" -b -t -v -user SYSDBA -pas "pw" <db> stdout',LBackupStream.WriteString,// Should process stdout (backup)      SomeMemo.lines.Append,// Should process stderr (log)      True,// Backup is "raw"      False,// Log is not      @LBackupAbortFlag);    LBackupStream.Savetofile('C:\path to\output.fbk');  finally    LBackupStream.Free;  end;end;

问题是输出文件太小而无法包含实际备份.我仍然看到文件内容的元素.我尝试了不同的流类型,但这似乎没有什么区别.这可能会出错?

更新

需要明确的是:其他解决方案也是受欢迎的.最重要的是,我需要一些可靠的东西.这就是为什么我首先选择JEDI,而不是重新发明这样的事情.那么,如果它不会太复杂,那就太好了.

解决方法 当您希望合并stdout和stderr时,我的第一个答案是有效的.但是,如果您需要将这些分开,那么这种方法是没有用的.我现在可以通过仔细阅读您的问题和您的评论,看到您确实希望将两个输出流分开.

现在,扩展我的第一个答案以涵盖这一点并非完全直截了当.问题是那里的代码使用阻塞I / O.如果你需要维修两个管道,那就有明显的冲突. windows中常用的解决方案是异步I / O,在windows世界中称为重叠I / O.但是,异步I / O的实现要比阻塞I / O复杂得多.

所以,我将提出一种仍然使用阻塞I / O的替代方法.如果我们想要服务多个管道,并且我们想要使用阻塞I / O,那么显而易见的结论是每个管道需要一个线程.这很容易实现 – 比异步选项容易得多.我们可以使用几乎相同的代码,但将阻塞读取循环移动到线程中.我的例子,以这种方式重新工作,现在看起来像这样:

{$APPTYPE CONSolE}uses  SysUtils,Classes,windows;type  TProcessOutputPipe = class  private    Frd: THandle;    Fwr: THandle;  public    constructor Create;    destructor Destroy; overrIDe;    property rd: THandle read Frd;    property wr: THandle read Fwr;    procedure CloseWritePipe;  end;constructor TProcessOutputPipe.Create;const  PipeSecurityAttributes: TSecurityAttributes = (    nLength: SizeOf(TSecurityAttributes);    binheritHandle: True  );begin  inherited;  Win32Check(CreatePipe(Frd,Fwr,@PipeSecurityAttributes,0));  Win32Check(SetHandleinformation(Frd,HANDLE_FLAG_inherit,0));//don't inherit read handle of pipeend;destructor TProcessOutputPipe.Destroy;begin  CloseHandle(Frd);  if Fwr<>0 then    CloseHandle(Fwr);  inherited;end;procedure TProcessOutputPipe.CloseWritePipe;begin  CloseHandle(Fwr);  Fwr := 0;end;type  TReadPipeThread = class(TThread)  private    FPipeHandle: THandle;    FStream: TStream;  protected    procedure Execute; overrIDe;  public    constructor Create(PipeHandle: THandle; Stream: TStream);  end;constructor TReadPipeThread.Create(PipeHandle: THandle; Stream: TStream);begin  inherited Create(False);  FPipeHandle := PipeHandle;  FStream := Stream;end;procedure TReadPipeThread.Execute;var  Buffer: array [0..4096-1] of Byte;  BytesRead: DWORD;begin  while Readfile(FPipeHandle,Buffer,SizeOf(Buffer),BytesRead,nil) and (BytesRead<>0) do begin    FStream.WriteBuffer(Buffer,BytesRead);  end;end;function ReadOutputFromExternalProcess(const Applicationname,Commandline: string; stdout,stderr: TStream): DWORD;var  stdoutPipe,stderrPipe: TProcessOutputPipe;  stdoutThread,stderrThread: TReadPipeThread;  StartupInfo: TStartupInfo;  ProcessInfo: TProcessinformation;  lpApplicationname: PChar;  ModfiableCommandline: string;begin  if Applicationname='' then    lpApplicationname := nil  else    lpApplicationname := PChar(Applicationname);  ModfiableCommandline := Commandline;  UniqueString(ModfiableCommandline);  stdoutPipe := nil;  stderrPipe := nil;  stdoutThread := nil;  stderrThread := nil;  try    stdoutPipe := TProcessOutputPipe.Create;    stderrPipe := TProcessOutputPipe.Create;    ZeroMemory(@StartupInfo,SizeOf(StartupInfo));    StartupInfo.cb := SizeOf(StartupInfo);    StartupInfo.DWFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;    StartupInfo.wShowWindow := SW_HIDE;    StartupInfo.hStdOutput := stdoutPipe.wr;    StartupInfo.hStdError := stderrPipe.wr;    Win32Check(CreateProcess(lpApplicationname,PChar(ModfiableCommandline),nil,True,CREATE_NO_WINDOW or norMAL_PRIORITY_CLASS,StartupInfo,ProcessInfo));    stdoutPipe.CloseWritePipe;//so that the process is able to terminate    stderrPipe.CloseWritePipe;//so that the process is able to terminate    stdoutThread := TReadPipeThread.Create(stdoutPipe.rd,stdout);    stderrThread := TReadPipeThread.Create(stderrPipe.rd,stderr);    stdoutThread.WaitFor;    stderrThread.WaitFor;    Win32Check(WaitForSingleObject(ProcessInfo.hProcess,INFINITE)=WAIT_OBJECT_0);    Win32Check(GetExitCodeProcess(ProcessInfo.hProcess,Result));  finally    stderrThread.Free;    stdoutThread.Free;    stderrPipe.Free;    stdoutPipe.Free;  end;end;procedure Test;var  stdout,stderr: TfileStream;  ExitCode: DWORD;begin  stdout := TfileStream.Create('C:\Desktop\stdout.txt',fmCreate);  try    stderr := TfileStream.Create('C:\Desktop\stderr.txt',fmCreate);    try      ExitCode := ReadOutputFromExternalProcess('','cmd /c dir /s C:\windows\system32',stdout,stderr);    finally      stderr.Free;    end;  finally    stdout.Free;  end;end;begin  Test;end.

如果您希望添加对取消的支持,那么您只需在用户取消时添加对TerminateProcess的调用.这将使一切停止,函数将返回您提供给TerminateProcess的退出代码.我现在犹豫为你建议一个取消框架,但我认为这个答案中的代码现在非常接近满足你的要求.

总结

以上是内存溢出为你收集整理的如何将二进制gbak输出重定向到Delphi流?全部内容,希望文章能够帮你解决如何将二进制gbak输出重定向到Delphi流?所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1277819.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-09
下一篇 2022-06-09

发表评论

登录后才能评论

评论列表(0条)

保存