Lazarus (freepascal) 从 TpCess 读取大量输出



我正在使用 TProcess 和这个 freepascal wiki 页面的建议在 Lazarus 中读取大型进程输出数据。

wiki 页面建议创建一个循环来读取过程输出数据,如下所示:

// ... If you want to read output from an external process, this is the code you should adapt for production use.
while True do
  begin          
    MemStream.SetSize(BytesRead + 2024); // make sure we have room
    NumBytes := OurProcess.Output.Read((MemStream.Memory + BytesRead)^, READ_BYTES);
    if NumBytes > 0
    then begin
      Inc(BytesRead, NumBytes);
      Write('.') //Output progress to screen.
    end else 
      BREAK // Program has finished execution.
  end;
// "Then read the MemStream to do your job" 

wiki 页面还提到调用程序应从输出管道读取以防止其变满。

那么,有多少数据使输出管道充满?

为什么我们应该使用MemStream(TMemoryStream)而不是直接从上述循环中的OurProcess.Output流中读取(使用bytesAvailable等)?

我正在从一个进程中读取 80MB 的 wav 数据,我注意到 MemStreamOurProcess.Output流的数据量相同!内存使用量翻倍。因此,来自 wiki 的建议方法不能被认为是有效或优化的。还是我错过了什么?

Afaik 输出/输入流是管道的流形式,而不是内存流。你看到的值是从操作系统句柄检索的,而不是从分配给 FPC 应用本身的内存中检索的。

这就像您可以在不读取整个文件的情况下请求磁盘上文件的 .size 一样。

    procedure RunExternalAppInMemo(DosApp:String;AMemo:TMemo);
    const READ_BYTES = 2048;
    var
    aProcess: TProcess; //TProcess is crossplatform is best way
    MemStream: TMemoryStream;
    NumBytes: LongInt;
    BytesRead: LongInt;
    Lines: TStringList;
  begin
   // A temp Memorystream is used to buffer the output
   MemStream := TMemoryStream.Create;
   Lines :=TStringList.Create;
   BytesRead := 0;
     aProcess := TProcess.Create(nil);
     aProcess.CommandLine := DosApp;
     aprocess.ShowWindow := swoHIDE;
     AProcess.Options := AProcess.Options + [poUsePipes];
     aProcess.Execute;
     while aProcess.Running do
     begin
       // make sure we have room
       MemStream.SetSize(BytesRead + READ_BYTES);
       // try reading it
       NumBytes := aProcess.Output.Read((MemStream.Memory + BytesRead)^, READ_BYTES);
       if NumBytes > 0 // All read() calls will block, except the final one.
          then Inc(BytesRead, NumBytes)
       else
          BREAK // Program has finished execution.
     end;
     MemStream.SetSize(BytesRead);
     Lines.LoadFromStream(MemStream);
     AMemo.lines.AddStrings(Lines);
     aProcess.Free;
     Lines.Free;
     MemStream.Free;
  end;

我今天正在处理这个问题,我已经修改了 Georgescu 的答案,因为我希望 Memo 即时显示输出流

procedure RunExternalAppInMemo(DosApp:String;AMemo:TMemo);
    const READ_BYTES = 2048;
    var
    aProcess: TProcess; //TProcess is crossplatform is best way    
    NumBytes: LongInt;
    Buffer: array of byte;    
    
  begin
    // set the size of your buffer
     SetLength(Buffer,READ_BYTES);   
     aProcess := TProcess.Create(nil);
     aProcess.CommandLine := DosApp;
     aprocess.ShowWindow := swoHIDE;
     AProcess.Options := AProcess.Options + [poUsePipes];     
     aProcess.Execute;
     
     while aProcess.Running do
      begin
       // try reading it
       NumBytes := aProcess.Output.Read(Buffer[0], length(buffer)*sizeof(byte)); // I usually do it that way, so I can change Buffer size on if needed
       AProcess.Suspend; //I have no experience with pipes, but it seems way I won loose eny output?
       if NumBytes > 0 then // All read() calls will block, except the final one.
        begin     
            
          AMemo.Lines.Add(Pchar(Buffer);            
          application.ProcessMessages;
          AProcess.Resume; 
        end
       else
          BREAK; // Program has finished execution.         
     end;
     setlength(Buffer,0);
     aProcess.Free;     
  end;

相关内容

  • 没有找到相关文章

最新更新