Page 1 of 3

RunProgram() and get its output data

Posted: Fri Apr 28, 2006 2:59 pm
by Psychophanta
I want to show the external program output data while it is still running.
However i find no way to get output data from the external program before it ends and show it via a ListViewGadget for example. Why? because calling ReadProgramString/Data DOES WAIT until program is finished, and calling AvailableProgramOutput() will always return 0 until program is finished.
So then, how to do it???

Posted: Fri Apr 28, 2006 3:29 pm
by dmoc
I don't know :P

(crass attempt to update my forum rating :P )

Posted: Fri Apr 28, 2006 4:07 pm
by Trond
CODE: Piper.pb/piper.exe

Code: Select all

OpenConsole()
Repeat
  PrintN(Str(I))
  Delay(500)
  I + 1
ForEver
CODE: Reader.pb/reader.exe

Code: Select all

OpenWindow(0, 0, 0, 512, 384, "", #PB_Window_ScreenCentered | #PB_Window_SystemMenu)
CreateGadgetList(WindowID(0))
ListViewGadget(0, 10, 10, 492, 364)

Pid = RunProgram("piper.exe", "", "", #PB_Program_Open | #PB_Program_Read | #PB_Program_Hide)


Repeat
  Select WaitWindowEvent(10)
    Case #PB_Event_CloseWindow
      Break
  EndSelect
  If AvailableProgramOutput(Pid)
    String.s = ReadProgramString(Pid)
    AddGadgetItem(0, 0, String)
  EndIf
ForEver

KillProgram(Pid)
CloseProgram(Pid)

Posted: Fri Apr 28, 2006 5:05 pm
by Psychophanta
Your piper.exe is not a valid external program for the test.

To do it you can make a piper.exe with quickbasic, turboC or similar. You'll see that there is not possible to read the data until ext. program is finished. :cry:

Posted: Fri Apr 28, 2006 5:27 pm
by Trond
This is definetely a bug. I tested with this Pascal program (compiled with Delphi) and the test code above (but removed the Hide flag):

Code: Select all

program Project2;

{$APPTYPE CONSOLE}

uses
   SysUtils;

var
   I: Integer = 0;

begin
   repeat
      Sleep(500);
      WriteLn(I);
      Inc(I);
   until 1 = 0
end.
When I run it appears like "nothing" happens. My program reacts normally. The pascal program shows an empty console. I then wait for some time. Suddenly, the numbers from 0 to 33 dumps into the listview!

Of course, one could blame this on piper.exe. However... Now my program does not react to gui input any more! This cannot be anything else than a bug in PB as the command ReadProgramString() (which waits and locks up the gui if the stream is empty) should ONLY be called when there is data in the stream. There is no other command in my program that would lock up the gui as far as I can tell.

Well, I wait a bit longer. Now another bunch of numbers dumps into the listview (ca 30.) Then I go away to write this. When I get back to my program it is still locked up for a while. Then it reacts normally and the listview has now 145 items. I then write here again, now the program won't react again. Now it reacts. Now I got a bunch of new numbers and it stopped reacting.

Edit: I stopped the program with the debugger's kill command (it didn't react again) and piper.exe crashed with an unknown exception.
---------------------------
piper.exe - Programfeil
---------------------------
Unntaket Ukjent programunntak (0x0eedfade) oppstod i programmet på 0x7952bc3f.


---------------------------
OK
---------------------------
(The exception Unknown program exception (0x0eedfade) happened in the program at 0x7952bc3f.)

Posted: Fri Apr 28, 2006 9:03 pm
by Psychophanta
I think it is a bug, at least in the manual, because there are nothing talking about this behaviour.

Posted: Sat Apr 29, 2006 12:16 am
by Fred
Are you sure than the WriteLn() command really flush the pipe ?

Posted: Sat Apr 29, 2006 12:41 am
by freak
The ReadProgramString() waits until a newline is found before it returns (or at program end for the last line),
so if the program outputs text but no newline, AvailableProgramOutput() will
return nonzero, but ReadProgramString() will not return immediately.

It would be nice if you could provide the delphi code as executable so i can test
if the newline detection works properly, or if the output shows the above problem.

The reason that the command is line-based is for ease of use. Most console
output is line-based, so it makes more sense to read it in that manner as well.
To prevent this, ReadProgramData() can be used.

Posted: Sat Apr 29, 2006 10:44 am
by Trond
You can test using this PB code:

Code: Select all

Handle = GetStdHandle_(#STD_OUTPUT_HANDLE)

Repeat
  Delay(500)
  Str.s = Str(I) + #CRLF$
  WriteConsole_(Handle, @Str, Len(Str), 0, 0)
  I + 1
Until 1 = 0
Be sure to compile as console application to piper.exe.

Test program:

Code: Select all

OpenWindow(0, 0, 0, 512, 384, "", #PB_Window_ScreenCentered | #PB_Window_SystemMenu) 
CreateGadgetList(WindowID(0)) 
ListViewGadget(0, 10, 10, 492, 364) 

Pid = RunProgram("piper.exe", "", "", #PB_Program_Open | #PB_Program_Read) 

Repeat 
  Select WaitWindowEvent(10)
    Case #PB_Event_CloseWindow
      Break
  EndSelect
  If AvailableProgramOutput(Pid)
    String.s = ReadProgramString(Pid)
    AddGadgetItem(0, 0, String)
  EndIf
ForEver

KillProgram(Pid)
CloseProgram(Pid)
The PB program never receives anything.

Here is the compiled version of the delphi source above: http://d.turboupload.com/d/557577/Project2.exe.html

Posted: Sat Apr 29, 2006 10:23 pm
by Psychophanta
freak wrote:It would be nice if you could provide the delphi code as executable so i can test
if the newline detection works properly, or if the output shows the above problem.
http://www.penguinbyte.com/apps/pbwebst ... /PIPER.zip

You can use that program made in QuickBasic 4.5.
Pure MS-DOS.

EDIT: Note that always when ProgramRunning(Program) is <>0 , AvailableProgramOutput(Program) is =0 , and viceversa. This means that one of those both functions is a nonsense.

Posted: Sat Apr 29, 2006 10:27 pm
by Trond
freak wrote:The reason that the command is line-based is for ease of use. Most console
output is line-based, so it makes more sense to read it in that manner as well.
To prevent this, ReadProgramData() can be used.
1. Hmm, what does the Ln in WriteLn() mean?
2. ReadProgramData() does not fix the problem.

Posted: Sun Apr 30, 2006 3:42 pm
by Fred
AFAIK, WriteConsole() doesn't work trough a pipe, so if delphi uses this, it's not correct. PB does it 'the right way' with the PrintN() command. You can test it with: piper.exe >piped.txt in a cmd prompt and your file will be blank. Just replace WriteConsole_() with printn() and it will work.

Posted: Sun Apr 30, 2006 4:05 pm
by Trond
I tried with the delphi version and out.txt is not empty.

Edit: I also tested PB's quickbasic version and out.txt is not empty.

Edit: Also I find it not right that we should have to know about pipes and stuff when the command description in the help file is "Read the programs console output" and WriteConsole_() is the correct way to write to the console as far as I can tell.

Posted: Sun Apr 30, 2006 4:14 pm
by Fred
Trond wrote:I tried with the delphi version and out.txt is not empty.

Edit: I also tested PB's quickbasic version and out.txt is not empty.

Edit: Also I find it not right that we should have to know about pipes and stuff when the command description in the help file is "Read the programs console output" and WriteConsole_() is the correct way to write to the console as far as I can tell.
Could you put the exe for download ? Edit: just seen in the above post. Edit2: here the redirected file is empty.

About the WriteConsole_(), it doesn't work with pipes, period. You have to use WriteFile_() for this.

Posted: Sun Apr 30, 2006 4:32 pm
by Trond
Here the redirected file is not empty, it contains one successive number per line. However, when I try to get the output using PB I don't get a thing.