Recent

Author Topic: Redirect program-output to TMemo in real-time  (Read 24097 times)

KpjComp

  • Hero Member
  • *****
  • Posts: 680
Re: Redirect program-output to TMemo in real-time
« Reply #15 on: July 13, 2012, 01:43:08 pm »
Quote
If it helps, I could also add the Source!

That might help.

eny

  • Hero Member
  • *****
  • Posts: 1584
Re: Redirect program-output to TMemo in real-time
« Reply #16 on: July 13, 2012, 04:03:52 pm »
Try to do it with a separate thread; so you only update the memo when there actually is data available (see attachment for the class that you can use. Guarantees until the front door... ).

Example to run a command and capture the output (windows):
Code: [Select]
type
  TfrmMain1 = class(TForm)
  ...
  private
    procedure OnOutputAvailable(const pBuffer: PByteArray; const pCount: integer);
  ...

implementation

// Test button to start the threaded dir command
procedure TfrmMain1.Button1Click(Sender: TObject);
var th: TShellCommandRunnerThread;
begin
  th := TShellCommandRunnerThread.Create;
  th.CommandLine := 'cmd /c dir d:\temp /s/b';
  th.OnOutputAvailable := @OnOutputAvailable;
  th.Start;
end;

// Capture the output
procedure TfrmMain1.OnOutputAvailable(const pBuffer: PByteArray; const pCount: integer);
begin
  Memo1.Append(TShellCommandRunner.BufferToString(pBuffer,pCount));
end;
All posts based on: Win10 (Win64); Lazarus 1.8.0 'stable' (#56594 win64) unless specified otherwise...

Lazarus

  • New member
  • *
  • Posts: 26
Re: Redirect program-output to TMemo in real-time
« Reply #17 on: July 13, 2012, 04:42:54 pm »
@eny:
Thank you very much for your code example! I used your code in a new project, but it gives me just the error "File not found" :o -- but that's maybe because of the start command.

But anyway, is there way to get the program working without using an external class? It's not THAT important, but it might help me to give better support to my program.
And it's a little bit hard for me to understand the way how the program works, because I'm just a beginner of coding in Lazarus...

@KpjComp:
I've attached the Source now but the project has become more complex, so it could be harder to read the Source...
Anyway, the interesting Unit for you should be the "Main"-Unit.

I've also added the Minecraft-Server-File to the archive, it just requires Java to start  %).

The "Main"-Form will get the startparameters for the server from a file called "MinecraftServerManager_ServerFileInfo".

You can compile the program and, while debugging, you should easily click the "Start Server"-Button in the Main-Form to start the server. You'll see that the program hangs up  :-[.


Thank you for your help, you both!

The file is to big for a attachment, so I've uploaded the archive @Zippyshare (MinecraftServerManager.zip)

Greetings,
Lazarus

PS: By the way, I'm not a native English-speaker, I hope it's not too difficult for you to understand what I've written.

Lazarus

  • New member
  • *
  • Posts: 26
Re: Redirect program-output to TMemo in real-time
« Reply #18 on: July 14, 2012, 03:05:04 pm »
I have now added the ShellCommandRunner-Class of eny to my project instead of TProcess.
When I start the server by clicking "Start Server"-Button, the server starts, but no output is redirected :o.

And another question: How do I send input to TShellCommandRunnerThread?
Does TShellCommandRunnerThread.Write() do this?

Thanks for replies and greetings,
Lazarus

eny

  • Hero Member
  • *****
  • Posts: 1584
Re: Redirect program-output to TMemo in real-time
« Reply #19 on: July 14, 2012, 03:43:23 pm »
I have now added the ShellCommandRunner-Class of eny to my project instead of TProcess.
When I start the server by clicking "Start Server"-Button, the server starts, but no output is redirected :o.
Dit you add the OnOutputAvailable method?
If so, when you run the program just from the command line, does it generate output at all?
Is the output sent to the stdout or stderr?

Quote
And another question: How do I send input to TShellCommandRunnerThread?
Does TShellCommandRunnerThread.Write() do this?
It should  :)
All posts based on: Win10 (Win64); Lazarus 1.8.0 'stable' (#56594 win64) unless specified otherwise...

Lazarus

  • New member
  • *
  • Posts: 26
Re: Redirect program-output to TMemo in real-time
« Reply #20 on: July 14, 2012, 03:52:22 pm »
Dit you add the OnOutputAvailable method?
If so, when you run the program just from the command line, does it generate output at all?
Is the output sent to the stdout or stderr?
Yes, I added the OnOutputAvailiable procedure.
The program generates definitely output, with KpjComp's method the Memo receives the first 2 lines of the output after that it hangs up.

And how do I check wether the output is written to stdout or stderr  %) ?
That's my first project handling with output...

Anyway, thanks for the reply :)!

eny

  • Hero Member
  • *****
  • Posts: 1584
Re: Redirect program-output to TMemo in real-time
« Reply #21 on: July 14, 2012, 04:32:26 pm »
Please post your button click code and the code for handling the received data.
All posts based on: Win10 (Win64); Lazarus 1.8.0 'stable' (#56594 win64) unless specified otherwise...

Lazarus

  • New member
  • *
  • Posts: 26
Re: Redirect program-output to TMemo in real-time
« Reply #22 on: July 14, 2012, 05:03:13 pm »
ButtonClick-Procedre:
Code: [Select]
// Button: Start Server
procedure TMainWindow.StartButtonClick(Sender: TObject);
var
  FileInfo: TStringList;
begin
  // checking wether server already running
  if ServerStatusBar.Panels.Items[0].Text = 'Running' then
  begin
    ShowMessage('Server already running!' + sLineBreak + 'Stop server before restarting!');
    Exit;
  end;

  // import info about server-file
  FileInfo := TStringList.Create;
  try
    FileInfo.LoadFromFile('MinecraftServerManager_ServerFileInfo');
  except
    ShowMessage('Could not import ServerFileInfo!' + sLineBreak + 'Do NOT delete "MinecraftServerManager_ServerFileInfo"!');
    Halt;
  end;

  // defining Server-Process
  Server.CommandLine := 'java -Xmx' + FileInfo.Strings[1] + ' -Xms' + FileInfo.Strings[1] + ' -jar ' + FileInfo.Strings[0] + ' nogui';

  FileInfo.Free;

  // starting Server-Process
  ServerStatusBar.Panels.Items[0].Text := 'Running';
  Server.Start;
end;

ReceivingData-Procedure:
Code: [Select]
// Capture the output
procedure TMainWindow.OnOutputAvailable(const pBuffer: PByteArray; const pCount: integer);
begin
  ServerOutput.Append(TShellCommandRunner.BufferToString(pBuffer,pCount));
end;

eny

  • Hero Member
  • *****
  • Posts: 1584
Re: Redirect program-output to TMemo in real-time
« Reply #23 on: July 14, 2012, 05:17:27 pm »
Ah sorry, my question was incomplete.
I want to see the place where you initialize the server thread.
So where do you create the TShellCommandRunnerThread?
Please show the code where you have put:
Code: [Select]
  ...
  server := TShellCommandRunnerThread.Create;
  server.OnOutputAvailable := @OnOutputAvailable;
  ...
All posts based on: Win10 (Win64); Lazarus 1.8.0 'stable' (#56594 win64) unless specified otherwise...

Lazarus

  • New member
  • *
  • Posts: 26
Re: Redirect program-output to TMemo in real-time
« Reply #24 on: July 14, 2012, 05:44:05 pm »
The ShellCommanderThread is initialized in the Form's OnCreate-Procedure:
Code: [Select]
// OnCreate: Create Server-Process
procedure TMainWindow.FormCreate(Sender: TObject);
begin
  Server := TShellCommandRunnerThread.Create;
end;

Ah, I think I found my mistake: I forgot the
Code: [Select]
server.OnOutputAvailable := @OnOutputAvailable; line.
I'll add this now :o :-[

Lazarus

  • New member
  • *
  • Posts: 26
Re: Redirect program-output to TMemo in real-time
« Reply #25 on: July 14, 2012, 06:20:21 pm »
The program works now amazingly!
Sorry for my mistake! I'm to stupid to copy an very simple code  %)...

Thank you very, very much, eny!

Your class is really useful and helpful, did you wrote it by yourself :D?

Greetings and many thanks again,
Lazarus

eny

  • Hero Member
  • *****
  • Posts: 1584
Re: Redirect program-output to TMemo in real-time
« Reply #26 on: July 14, 2012, 07:06:56 pm »
The program works now amazingly!
Sorry for my mistake! I'm to stupid to copy an very simple code  %)...
In general don't start experimenting with code that is given to you and that you don't fully understand (i.e. leaving things out).
Just ask what the individual statements do when not clear :D.

Quote
Your class is really useful and helpful, did you wrote it by yourself :D?
Yes. Although most of the information can be found in the Wiki in code fragments.
The problem for people new to Lazarus or new to OO is always how to combine it and create a higher abstraction level.

A couple of suggestions:
  • Since the server thread frees itself automatically after closing, there is no real need to create it beforehand in the Form Create or define it as an object variable.
    You can just as easily use a local variable as a placeholder in the button click method.
  • You do not yet have a way to detect the ending of the program. When in method OnOutputAvailable the number of bytes = 0, then the program has ended. You can use that to do all handling you need in the main program.
  • if ServerStatusBar.Panels.Items[0].Text = 'Running' ...
    It's better to use a boolean variable, something like 'ServerIsRunning:boolean' that you set to true or false. That way you can code things like:
Code: [Select]
if ServerIsRunning then
begin
  ShowMessage('Server already running!' + sLineBreak + 'Stop server before restarting!');
  Exit;
end;

Have fun!
All posts based on: Win10 (Win64); Lazarus 1.8.0 'stable' (#56594 win64) unless specified otherwise...

wp

  • Hero Member
  • *****
  • Posts: 5453
Re: Redirect program-output to TMemo in real-time
« Reply #27 on: July 15, 2012, 12:32:23 pm »
Thank you for the marvellous ShellCommandRunnerThread. It solved detecting output from gnuplot called by my program.

Quote
Since the server thread frees itself automatically after closing, there is no real need to create it beforehand in the Form Create or define it as an object variable.

But what if the thread does not terminate for some reason? What happens if the thread is still running when the program is closed? To avoid any surprises I assigned the thread to a form variable, set that to nil in the thread's OnTerminate event, checked for nil in the form's OnCloseQuery event, and, if not nil, terminate the thread here manually. Is that really necessary?
« Last Edit: July 15, 2012, 04:54:34 pm by wp »
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10

eny

  • Hero Member
  • *****
  • Posts: 1584
Re: Redirect program-output to TMemo in real-time
« Reply #28 on: July 15, 2012, 01:12:00 pm »
But what if the thread does not terminate for some reason? What happens if the thread is still running when the program is closed? To avoid any surprises I assigned the thread to a form variable, set that to nil in the thread's OnTerminate event, checked for nil in the form's OnCloseQuery event, and, if not nil, terminate the thread here manually. Is that really necessary?
That's a good point.
In my simple example I assume the threaded/executed program would end before the main program (as was the case at the time I developed this).
It certainly is good to check that the thread has ended before the program ends.
In that case it would be good to include an additional check in the loop that reads data from the submitted process.

So in TShellCommandRunner.Execute add something like:
Code: [Select]
// While the process is running, wait until it is finished
while Process.Running and (not TerminationRequested) do
...
TerminationRequested is an additional new boolean property of the class that can be set by an external process i.e. the main form's OnClose event.
Here it starts to get tricky because you can only set this property while the thread is active/running.
So you'd have to introduce some sort of synchronizing between the main program and the thread itself (e.g. critical sections).

The easiest solution is probably not let the thread free it free itself.
Just wait for the terminate event and then free the thread or request termination when required.
All posts based on: Win10 (Win64); Lazarus 1.8.0 'stable' (#56594 win64) unless specified otherwise...

wp

  • Hero Member
  • *****
  • Posts: 5453
Re: Redirect program-output to TMemo in real-time
« Reply #29 on: July 15, 2012, 04:53:30 pm »
Quote
TerminationRequested is an additional new boolean property of the class that can be set by an external process i.e. the main form's OnClose event.

I think that's not needed - shouldn't it be sufficient to call ShellCommandRunnerThread.Terminate for stopping the hanging thread. There is one minor issue: the OnTerminate event handler is not called when the process is killed this way. Therefore my form variable identifying the thread is not set to nil automatically - of course I can set it to nil manually together with calling Terminate.
Lazarus trunk / fpc 3.0.4 / all 32-bit on Win-10