Capturing output of OS command in 'reatime' lazarus / 34-Windows ---------------------------------------------------------------------- fatmonk · 2018-03-27 14:01 I'm using the following to capture the output of a OS console command. My problem is that this code buffers all output until the command is complete before displaying it and I'd like to display the output in realtime, or at least line by line (while still being able to track when the command finishes execution if possible. I'm thinking that I might be able to do this by appending the last chunk of buffer to the TMemo (mOutput) within the while...do, but I just can't figure it out. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i procedure TmainForm.ActionButtonClick(Sender: TObject);const  READ_BYTES = 2048;var  OutputLines: TStringList;  MemStream: TMemoryStream;  OurProcess: TProcess;  NumBytes: LongInt;  BytesRead: LongInt;  userInput: String;begin   // A temp Memorystream is used to buffer the output  MemStream := TMemoryStream.Create;  BytesRead := 0;   OurProcess := TProcess.Create(nil);   OurProcess.Executable := 'netstat';  OurProcess.Parameters.Add('-a');   OurProcess.Options := [poUsePipes, poNoConsole];  OurProcess.Execute;   while True do    begin      // make sure we have room      MemStream.SetSize(BytesRead + READ_BYTES);       // try reading it      NumBytes := OurProcess.Output.Read((MemStream.Memory + BytesRead)^, READ_BYTES);    if NumBytes > 0 // All read() calls will block, except the final one.        then begin          Inc(BytesRead, NumBytes);        end else          BREAK // Program has finished execution.    end;   MemStream.SetSize(BytesRead);   OutputLines := TStringList.Create;  OutputLines.LoadFromStream(MemStream);   mOutput.Lines.AddStrings(OutputLines);  OutputLines.Free;  OurProcess.Free;  MemStream.Free; end; Am I on the right track? Is there a better way to do this? (Note, netstat is just used as an example here.. this could be any command that takes a while to run but outputs its progress on screen sequentially). Thanks, FM [EDIT: Removed spurious end; and reformatted to make levels clearer] ---------------------------------------------------------------------- taazz · 2018-03-27 14:56 you have one end to many the code to add the lines to the control (moutput.... etc)  is executed outside the loop that reads output from the process. also change the "while true" to "while ourProcess.Running" or something along those lines. ---------------------------------------------------------------------- fatmonk · 2018-03-27 15:13 The extra end is because I have cut out a bunch of distracting code... I'll see if I can correct that so that the question makes more sense... The code actually works fine with commands that return quickly - the output is correctly displayed in the TMemo. In fact it works with comands that take a long time to return as well but the application hangs while it is waiting for the command to complete before displaying anything. I've changed the while true - it makes no difference in terms of the problem I'm asking about, but does seem a more correct way of doing things. -FM ---------------------------------------------------------------------- taazz · 2018-03-27 15:20 Quote from: fatmonk on March 27, 2018, 05:13:41 pmThe extra end is because I have cut out a bunch of distracting code... I'll see if I can correct that so that the question makes more sense... The code actually works fine with commands that return quickly - the output is correctly displayed in the TMemo. In fact it works with comands that take a long time to return as well but the application hangs while it is waiting for the command to complete before displaying anything. I've changed the while true - it makes no difference in terms of the problem I'm asking about, but does seem a more correct way of doing things. -FM As I said, pay close attention to where you do form updates. Your code updates the control on the form only after the while loop has finished. you need to put it inside the loop and call the form.repaint as well. ---------------------------------------------------------------------- Thaddy · 2018-03-27 15:32 In this case I would use a - ahum - timer for display, since the network throughput is probably much higher than the refresh rate of the monitor and either way human eyes are too slow to keep up with either. But indeed, your loop needs to post a message from within. If you do not want to loose information you will need to buffer... no other way. ---------------------------------------------------------------------- fatmonk · 2018-03-27 15:58 @taazz As per my original question, I thought that writing to the TMemo from within the while...do was one way to do this /but/ I cannot clear and rewrite the whole of the TMemo content each time as it already contains previous output which needs to be kept. As each 'chunk' of output is collected in the buffer MemStream I would like to append that new chunk to the end of the TMemo content. This would obviously need to be within the while...do loop but I cannot figure out how to get just the last chunk of what was read into the MemStream. As you can see in the code I am using at the moment, the whole of MemStream is currently loaded into a TSTringList called OutputLines. I then use .AddLines() from TMemo to append these to mOutput. Rather than loading the whole of MemStream into the TMemo at the end of the process, I'd liek to do it line by line within the while...do loop. So.. what you are suggesting is, I believe, exactly what I want to do but I can't figure out how to do it (without overwriting the previous content of the TMemo). -FM ---------------------------------------------------------------------- fatmonk · 2018-03-27 16:16 OK, slightly different approach... Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i       PreviousOutput:=mOutput.Lines       OutputLines := TStringList.Create;       OurProcess.Options := [poUsePipes, poNoConsole];      OurProcess.Execute;       while OurProcess.Running do      begin        // make sure we have room        MemStream.SetSize(BytesRead + READ_BYTES);         // try reading it      NumBytes := OurProcess.Output.Read((MemStream.Memory + BytesRead)^, READ_BYTES);        if NumBytes > 0 // All read() calls will block, except the final one.          then            begin            Inc(BytesRead, NumBytes);               MemStream.SetSize(BytesRead);               OutputLines.LoadFromStream(MemStream);               mOutput.Lines:=PreviousOutput;              mOutput.Lines.AddStrings(OutputLines);            end        else        BREAK // Program has finished execution.      end;  But I get a runtime SIGSEGV crash which seems to be to do with trying to set the content of the TMemo to the PreviousOutput. (PreviousOutput is a TStrings). -FM ---------------------------------------------------------------------- taazz · 2018-03-27 16:22 1) lines.addstrings does not clear the existing data it only appends the new you do not have to back up the existing. 2) previousoutput is a pointer to the existing lines not a backup it is a waste of cpu cycles to assign it to back to the lines. 3)after each succesfull addstrings call you need to clear the memstream otherwise you will keep on appending the same data over and over again. ---------------------------------------------------------------------- fatmonk · 2018-03-27 17:05 1) I understand that that is what lines.addstrings does - I am appending the output from the new command to the output from the previous command - because... 3) I do not want to clear the memstream in case I miss some data (as per Thaddy's comment). I want to buffer all of the new command's output incrementally so as not to miss anything. 2) I always trip up on this one... how do I get the content rather than a pointer? -FM ---------------------------------------------------------------------- taazz · 2018-03-27 23:35 Quote from: fatmonk on March 27, 2018, 07:05:07 pm1) I understand that that is what lines.addstrings does - I am appending the output from the new command to the output from the previous command - because... 3) I do not want to clear the memstream in case I miss some data (as per Thaddy's comment). I want to buffer all of the new command's output incrementally so as not to miss anything. use a static sized buffer something along the lines of Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var  .........  vBuffer : array[0..read_bytes] of byte;begin   .......    NumBytes := OurProcess.Output.Read(vBuffer[0], READ_BYTES);   vbuffer[numbytes] := 0;//make sure that the string is null terminated.   memStream.write(vbuffer[0],numbytes);   OutputLines.Text := string(pchar(@vBuffer[0]));//break the string in to lines.   memo1.lines.addstrings(outputlines);//append those lines to the memo   oputputlines.clear; // clear the inserted lines from the next insert. you get the point I hope. Quote from: fatmonk on March 27, 2018, 07:05:07 pm2) I always trip up on this one... how do I get the content rather than a pointer? simple you create a tstringlist and call its addstrings method eg Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i var  vPreviousOutput :TStringlist; begin  vPreviousOutput :=TStringlist.Create;   try    PreviousOutput.assign(mOutput.Lines);  ......  finally    vPreviousOutput.free;  end; When you do something like memo1.Lines := stringlist; you actually call the properties setter method that it autocalls the memo1.lines.assign(stringlist); ---------------------------------------------------------------------- Edson · 2018-03-28 02:55 Maybe you can check https://github.com/t-edson/UnTerminal That library can capture the output of a process usign events in four different ways. ---------------------------------------------------------------------- ASerge · 2018-03-28 12:29 Quote from: fatmonk on March 27, 2018, 04:01:41 pmCode: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   OurProcess.Executable := 'netstat';  OurProcess.Parameters.Add('-a');netstat has problems with real-time redirection. Use additional utilities like https://www.codeproject.com/Articles/16163/Real-Time-Console-Output- Redirection or program switches that do not cause a delay: -na. To check the operation of your program in real time, use "cmd /c dir c:\  /s". This is a long operation, and if your program sees output immediately, then it works correctly.