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

时间:2014-01-07 03:18:00

标签: process stream freepascal lazarus

我正在使用TProcess和来自this freepascal wiki页面的建议阅读Lazarus中的大型流程输出数据。

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

// ... 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中建议的方法不能被视为有效或优化。或者有些东西我不见了?

3 个答案:

答案 0 :(得分:0)

Afaik输出/输入流是管道的流形式,而不是内存流。您看到的值是从OS句柄中检索的,而不是从分配给FPC应用程序的内存中检索的。

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

答案 1 :(得分:0)

    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;

答案 2 :(得分:0)

我今天正在处理这个问题,我修改了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;