Recent

Author Topic: Piping Console Output into GUI Application  (Read 10930 times)

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 9521
  • FPC developer.
Re: Piping Console Output into GUI Application
« Reply #15 on: June 09, 2021, 10:54:20 pm »
My solution will only trap Pascal output.

If he means gtk errors etc, I don't know.

Maybe to make it more clear, if I start the application in a console "./libsockuse" or "sudo ./libsockuse" (this is at least required if I want to use it with root rights to set CAN configuration), it will show the (error) messages in that shell window. This is what I want to catch and show it in the application itself.

Those might not even be application errors, but kernel (module) errors redirected to the console.

Maybe directly interacting with sysconsole pty or so?

Anyway I suggest to rethink that. It sounds simple, but it probably isn't. (and by that I mean that it might be hard to do that from _any_ process, not just a FPC compiled one)

Fred vS

  • Hero Member
  • *****
  • Posts: 2387
    • StrumPract is the musicians best friend
Re: Piping Console Output into GUI Application
« Reply #16 on: June 09, 2021, 11:18:23 pm »
Hello.

Thanks to Warfley (sorry I did not re-find the post), this will redirect all message-console, including gtk errors etc, into a text file.

Code: Pascal  [Select][+][-]
  1. program errorlog;
  2.   uses
  3.    ... ,  Classes, BaseUnix;
  4.    var
  5.      fs: TFileStream;
  6.    begin
  7.      fs := TFileStream.Create('error.log', fmOpenReadWrite or fmCreate);
  8.      FpDup2(fs.Handle, StdErrorHandle);
  9.        ...  // all your program stuff
  10.      Application.run; // Inside application, catch fs data
  11.      fs.free;
  12.     end.

Maybe you could catch the 'error.log' data in the program himself (close fs, then read error.log).
« Last Edit: June 10, 2021, 04:23:48 pm by Fred vS »
I use Lazarus 2.0.6 32/64 and FPC 3.2.0 32/64 on Debian 10.2 64 bit, Windows 10, Windows 7 32/64, Windows XP 32,  FreeBSD 64 and Mac OS X Snow Leopard 32.
Widgetset: fpGUI, MSEgui, Win32, GTK2, Qt, Carbon.

https://github.com/fredvs
https://gitlab.com/fredvs

engkin

  • Hero Member
  • *****
  • Posts: 2924
Re: Piping Console Output into GUI Application
« Reply #17 on: June 10, 2021, 02:34:38 am »
Yes, I agree with Fred. I found a SO post that solved this problem using Dup2 and pipes. This way you don't need a "file". I tested the idea on an Android terminal. Seemed to work:
Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   Classes, SysUtils, BaseUnix
  7.   {$IFDEF UNIX}
  8.   {$IFDEF UseCThreads}
  9.   ,cthreads
  10.   {$ENDIF}
  11.   {$ENDIF}
  12.   { you can add units after this };
  13. {
  14. https://stackoverflow.com/questions/955962/how-to-buffer-stdout-in-memory-and-write-it-from-a-dedicated-thread
  15. }
  16. function Test(AHandle:cInt; var APipe: tfildes):cint;
  17. var
  18.   saved: cint;
  19. begin
  20.   Result:=-1;
  21.   saved:=FpDup(AHandle);
  22.   if FpPipe(APipe)<>0 then
  23.   begin
  24.     WriteLn('FpPipe failed.');
  25.     exit;
  26.   end;
  27.   FpDup2(APipe[1], AHandle);
  28.   FpClose(APipe[1]);
  29.   Result:=saved;
  30. end;
  31.  
  32.  
  33. const
  34.   MAX_LEN=40;
  35. type
  36.   TBuffer=array[0..MAX_LEN-1] of char;
  37. var
  38.   Pipe: tfildes=(0,0);
  39.   saved_, TestHandle: cint;
  40.   buffer:TBuffer;
  41. begin
  42.   buffer:=Default(TBuffer);
  43.  
  44.   TestHandle:=StdOutputHandle;
  45.   //TestHandle:=Test(StdErrorHandle);
  46.  
  47.   WriteLn('Test Before');
  48.   saved_:=Test(TestHandle, Pipe);
  49.   WriteLn('Test After'); //<---- this will be sent to the pipe
  50.   Flush(StdOut);
  51.  
  52.   fpread(Pipe[0], buffer[0], MAX_LEN); // read from pipe into buffer
  53.  
  54.   FpDup2(saved_, StdOutputHandle);  // reconnect stdout for testing
  55.  
  56.   WriteLn(Format('read: %s', [buffer]));
  57.   readLn;
  58. end.

To test on Android:
Code: Text  [Select][+][-]
  1. adb push project1 "/data/local/tmp"
  2. adb shell chmod 755 /data/local/tmp/project1
  3. adb shell /data/local/tmp/project1

The output:
Quote
Test Before
read: Test After

On Linux, StdErrorHandle is hard coded:
Code: Pascal  [Select][+][-]
  1. const
  2.   UnusedHandle    = -1;
  3.   StdInputHandle  = 0;
  4.   StdOutputHandle = 1;
  5.   StdErrorHandle  = 2;

I believe it should work with kernel errors as well.

MarkMLl

  • Hero Member
  • *****
  • Posts: 3035
Re: Piping Console Output into GUI Application
« Reply #18 on: June 10, 2021, 08:52:59 am »
As a slight aside: Marco, am I correct in that the Lazarus IDE doesn't have an interactive shell-out facility?

Afaik indeed it doesn't. It was designed for systems that could run more tasks in parallel, so there was no need for functionality to suspend the IDE to run a shell like old DOS IDEs did.

Thanks for that, just checking I've not overlooked anything. I've got a vague recollection of a couple of cases where it would have been useful although I can't remember the exact details... and of course there's the "shell-like" facilities of "change current directory" and "set shell/environment variable".

MarkMLl
Turbo Pascal v1 on CCP/M-86, multitasking with LAN and graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Edson

  • Hero Member
  • *****
  • Posts: 1207
Re: Piping Console Output into GUI Application
« Reply #19 on: June 10, 2021, 08:10:41 pm »
Maybe I don't understand well the problem but in case you need to capture the output or error of a process , you can try my library: https://github.com/t-edson/UnTerminal

If you need to emulate a terminal, to control special inputs like "sudo" needs, I wrote an article about that: http://blogdetito.com/2016/12/04/el-inicio-de-un-terminal-con-linux-y-free-pascal/
Lazarus 2.0.10 - FPC 3.2.0 - x86_64-win64 on Windows 8

ThomasK

  • New Member
  • *
  • Posts: 40
Re: Piping Console Output into GUI Application
« Reply #20 on: June 25, 2021, 07:49:11 pm »
Yes, I agree with Fred. I found a SO post that solved this problem using Dup2 and pipes. This way you don't need a "file". I tested the idea on an Android terminal. Seemed to work:

I modified the code a little bit:
Code: Pascal  [Select][+][-]
  1. program test_pipe;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. {$Define std}
  6.  
  7. uses
  8.   Classes, SysUtils, BaseUnix
  9.   {$IFDEF UNIX}
  10.   {$IFDEF UseCThreads}
  11.   ,cthreads
  12.   {$ENDIF}
  13.   {$ENDIF}
  14.   { you can add units after this };
  15. {
  16. https://stackoverflow.com/questions/955962/how-to-buffer-stdout-in-memory-and-write-it-from-a-dedicated-thread
  17. }
  18. function Test(AHandle:cInt; var APipe: tfildes):cint;
  19. var
  20.   saved: cint;
  21. begin
  22.   Result:=-1;
  23.   saved:=FpDup(AHandle);
  24.   if FpPipe(APipe)<>0 then
  25.   begin
  26.     WriteLn('FpPipe failed.');
  27.     exit;
  28.   end;
  29.   FpDup2(APipe[1], AHandle);
  30.   FpClose(APipe[1]);
  31.   Result:=saved;
  32. end;
  33.  
  34.  
  35. const
  36.   MAX_LEN=40;
  37. type
  38.   TBuffer=array[0..MAX_LEN-1] of char;
  39. var
  40.   Pipe: tfildes=(0,0);
  41.   saved_, TestHandle: cint;
  42.   buffer:TBuffer;
  43. begin
  44.   buffer:=Default(TBuffer);
  45. {$ifdef std}
  46.   TestHandle:=StdOutputHandle;
  47. {$else}
  48.   TestHandle:=StdErrorHandle;
  49. {$endif}
  50.   WriteLn('Test Before');
  51.   saved_:=Test(TestHandle, Pipe);
  52.   WriteLn('Test After'); //<---- this will be sent to the pipe
  53. {$ifdef std}
  54.   Flush(StdOut);
  55. {$else}
  56.   Flush(StdErr);
  57. {$endif}
  58.  
  59.   fpread(Pipe[0], buffer[0], MAX_LEN); // read from pipe into buffer
  60. {$ifdef std}
  61.   FpDup2(saved_, StdOutputHandle);  // reconnect stdout for testing
  62. {$else}
  63.   FpDup2(saved_, StdErrorHandle);  // reconnect stderr for testing
  64. {$endif}
  65.  
  66.   WriteLn(Format('read from pipe: %s', [buffer]));
  67.   readLn;
  68. end.
  69.  

when the compiler switch is set for 'std' it works.

Code: Bash  [Select][+][-]
  1. pi@RasPiHW:~/FreePascal/Projekte/Konsolenapps $ ./test_pipe
  2. Test Before
  3. read from pipe: Test After
  4.  
Finished as pogrammed with <Enter>

But when stderr should be used, the program stucks.
Code: Bash  [Select][+][-]
  1. pi@RasPiHW:~/FreePascal/Projekte/Konsolenapps $ ./test_pipe
  2. Test Before
  3. Test After
  4.  
  5.  
  6.  
  7. ^C
  8. pi@RasPiHW:~/FreePascal/Projekte/Konsolenapps $
  9.  
It has to be terminated with Ctrl-c.

Any ideas?
Started Pascal on a Siemens 4004/151 in 1977. TurboPascal 1.0 in 1984 on PC-Dos.

engkin

  • Hero Member
  • *****
  • Posts: 2924
Re: Piping Console Output into GUI Application
« Reply #21 on: June 25, 2021, 09:09:48 pm »
It is waiting for you to hit enter (or somehow pass it if you use Android through ADB). The code for testing is more like:
Code: Pascal  [Select][+][-]
  1. program Project1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. uses
  6.   Classes, SysUtils, BaseUnix
  7.   {$IFDEF UNIX}
  8.   {$IFDEF UseCThreads}
  9.   ,cthreads
  10.   {$ENDIF}
  11.   {$ENDIF}
  12.   { you can add units after this };
  13. {
  14. https://stackoverflow.com/questions/955962/how-to-buffer-stdout-in-memory-and-write-it-from-a-dedicated-thread
  15. }
  16. function Test(AHandle:cInt; var APipe: tfildes):cint;
  17. var
  18.   saved: cint;
  19. begin
  20.   Result:=-1;
  21.   saved:=FpDup(AHandle);
  22.   if FpPipe(APipe)<>0 then
  23.   begin
  24.     WriteLn('FpPipe failed.');
  25.     exit;
  26.   end;
  27.   FpDup2(APipe[1], AHandle);
  28.   FpClose(APipe[1]);
  29.   Result:=saved;
  30. end;
  31.  
  32. procedure Test_StdText(var f:Text);
  33. const
  34.   MAX_LEN=40;
  35. type
  36.   TBuffer=array[0..MAX_LEN-1] of char;
  37. var
  38.   Pipe: tfildes=(0,0);
  39.   saved_, TestHandle: cint;
  40.   buffer:TBuffer;
  41. begin
  42.   buffer:=Default(TBuffer);
  43.   TestHandle:=TextRec(f).Handle;
  44.  
  45.   WriteLn(f,'Test Before');
  46.   saved_:=Test(TestHandle, Pipe);
  47.   WriteLn(f,'Test After'); //<---- this will be sent to the pipe
  48.   Flush(f);
  49.  
  50.   fpread(Pipe[0], buffer[0], MAX_LEN); // read from pipe into buffer
  51.  
  52.   FpDup2(saved_, TextRec(f).Handle);  // reconnect stdout for testing
  53.  
  54.   WriteLn(Format('read: %s', [buffer]));
  55.   //readLn;
  56. end;
  57.  
  58. begin
  59.   WriteLn('StdOut:');
  60.   Test_StdText(StdOut);
  61.  
  62.   WriteLn('StdErr:');
  63.   Test_StdText(StdErr);
  64. end.

Pressing Ctrl+C is ending the local process.

 

TinyPortal © 2005-2018