Control a called routine through RunProgram()

Just starting out? Need help? Post your questions and find answers here.
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Control a called routine through RunProgram()

Post by Oso »

I'm looking for a way to run a compiled PB executable from a calling routine, then control standard input/output from the calling routine. I do not know if this is the best way, or if there are other ways. I seem to remember reading about 'sendmessage' or similar, so maybe this isn't the only way. However, I have adapted it from an example of RunProgram() at https://www.purebasic.com/documentation ... ogram.html

It doesn't allow me to control the called routine subprog.pb. Instead, the called routine doesn't respond at all. subprog.pb works fine if I run it directly. It also works fine if I change the calling programme mainprog.pb to remove | #PB_Program_Write. The intention is to accept input and display it back, until the word end is entered. Therefore in mainprog.pb I'm trying to send end to the called routine. Instead subprog.pb sits there waiting, and because I've used the | #PB_Program_Write flag, it no longer accepts keyboard input from me.

subprog.pb

Code: Select all

OpenConsole()
PrintN("Sub-programme now running")
While exitflag = #False
  inpval.s = Input()
  PrintN("Entered : " + inpval.s)
  If inpval.s = "end"
    exitflag = #True
  EndIf
Wend
mainprog.pb

Code: Select all

  Compiler = RunProgram("j:\pbtest\subprog.exe", "", "", #PB_Program_Open | #PB_Program_Read | #PB_Program_Write)
  Output$ = ""
  If Compiler
    While ProgramRunning(Compiler)
      WriteProgramStringN(compiler,"end",#PB_Ascii)
      If AvailableProgramOutput(Compiler)
        Output$ + ReadProgramString(Compiler) + Chr(13)
      EndIf
    Wend
    Output$ + Chr(13) + Chr(13)
    Output$ + "Exitcode: " + Str(ProgramExitCode(Compiler))
    CloseProgram(Compiler) ; Close the connection to the program
  EndIf
  MessageRequester("Output", Output$)
infratec
Always Here
Always Here
Posts: 7587
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Control a called routine through RunProgram()

Post by infratec »

Since your subprog is a PB program and this is using unicode, you need #PB_Unicode:

Code: Select all

Compiler = RunProgram("subprog.exe", "", "", #PB_Program_Open | #PB_Program_Read | #PB_Program_Write)
Output$ = ""
If Compiler
  While ProgramRunning(Compiler)
    If AvailableProgramOutput(Compiler)
      Output$ + ReadProgramString(Compiler) + #LF$
      WriteProgramStringN(Compiler, "end", #PB_Unicode)
    EndIf
  Wend
  Output$ + #LF$ + #LF$
  Output$ + "Exitcode: " + Str(ProgramExitCode(Compiler))
  CloseProgram(Compiler) ; Close the connection to the program
EndIf
MessageRequester("Output", Output$)
But if you want to 'communicate', then you should use UDP on localhost, or with API pipes or messages.
Olli
Addict
Addict
Posts: 1200
Joined: Wed May 27, 2020 12:26 pm

Re: Control a called routine through RunProgram()

Post by Olli »

Why not use the unicode default format ?

Code: Select all

Compiler = RunProgram("subprog.exe", "", "", #PB_Program_Open | #PB_Program_Read | #PB_Program_Write | PB_Program_Unicode)
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: Control a called routine through RunProgram()

Post by Oso »

infratec wrote: Wed Oct 19, 2022 10:28 pm Since your subprog is a PB program and this is using unicode, you need #PB_Unicode:

Code: Select all

      WriteProgramStringN(Compiler, "end", #PB_Unicode)
But if you want to 'communicate', then you should use UDP on localhost, or with API pipes or messages.
Thanks very much infratec I don't think I would have thought of that one. Yes, it works perfectly now, so long as I only send the "end" string once, to deal with the timing factor, of subprog ending before the calling routine has recognised it.

Your other suggestions are of interest. To be honest, with the above input/output type of control, I just wanted to better understand it, but I'm keen to find out what else can be done, by way of greater control over a sub-process.

When you say UDP, you're referring presumably to using localhost network connections? I've developed routines with CreateNetworkServer() and SendNetworkString() and they work really well. The only thing is, this requirement is for quite a lot of sub-processes to be executed under the control of a calling programme.

Can you briefly point me in the right direction on API pipes and messages, just links would be great if you can, many thanks.
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: Control a called routine through RunProgram()

Post by Oso »

Olli wrote: Thu Oct 20, 2022 10:38 am Why not use the unicode default format

Code: Select all

Compiler = RunProgram("subprog.exe", "", "", #PB_Program_Open | #PB_Program_Read | #PB_Program_Write | PB_Program_Unicode)
Thanks Olli but that gives a strange result. It returns the output from subprog in Chinese glyphs. I'm not sure what's going on there. Incidentally there should be a # in #PB_Program_Unicode.
https://imgur.com/WJQzi8P
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: Control a called routine through RunProgram()

Post by Oso »

Oso wrote: Thu Oct 20, 2022 11:44 am Why not use the unicode default format

Code: Select all

Compiler = RunProgram("subprog.exe", "", "", #PB_Program_Open | #PB_Program_Read | #PB_Program_Write | #PB_Program_Unicode)
Thanks Olli but that gives a strange result. It returns the output from subprog in Chinese glyphs. I'm not sure what's going on there... https://imgur.com/WJQzi8P Incidentally there should be a # in #PB_Program_Unicode.
Last edited by Oso on Thu Oct 20, 2022 11:49 am, edited 1 time in total.
infratec
Always Here
Always Here
Posts: 7587
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Control a called routine through RunProgram()

Post by infratec »

As already mentioned, I prefer network communication via UDP and localhost.
This way is crossplatform. If needed you can change localhost to the LAN interface and control it from an other PC.

Here is a link to messages:
viewtopic.php?t=29781

Global shared memory: (german)
https://www.purebasic.fr/german/viewtopic.php?t=16659

Named pipes:
viewtopic.php?p=582259
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: Control a called routine through RunProgram()

Post by Oso »

infratec wrote: Thu Oct 20, 2022 11:49 am As already mentioned, I prefer network communication via UDP and localhost.
This way is crossplatform. If needed you can change localhost to the LAN interface and control it from an other PC.
I agree, it does work well and it's regardless of platform, sure. I just have to get my head around the idea. What is the advantage of UDP over TCP, just that it's raw data?
infratec
Always Here
Always Here
Posts: 7587
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Control a called routine through RunProgram()

Post by infratec »

UDP cause of the simplicity and on a LAN you will not loose packets.
TCP has a lot of overhead. If you want transmit one packet, many others are nedeed for handshakes.
The CPU load is less, the transfer is faster, you don't need to handle connect and disconnect ...
Olli
Addict
Addict
Posts: 1200
Joined: Wed May 27, 2020 12:26 pm

Re: Control a called routine through RunProgram()

Post by Olli »

infratec wrote: Thu Oct 20, 2022 1:32 pm UDP cause of the simplicity and on a LAN you will not loose packets.
TCP has a lot of overhead. If you want transmit one packet, many others are nedeed for handshakes.
The CPU load is less, the transfer is faster, you don't need to handle connect and disconnect ...
Interesting answer.

@Oso

Sure I am wrong if I forget a '#' char... :shock: :lol:
I often have communication problems because of infratec's overpowering aura. It interferes with my concentration. I crumble. Then I have to sleep for 15 days to heal, and then after that, it's better! I tried to protect myself with armor from the Middle Ages, but I broke three keyboards with my iron hands... In any case, I thank his professionalism very much: we learn a lot. I will still do a test in case it is a bug for the strange characters you mention. But one thing is certain in the description of infratec : the network channel is well suited to operate smoothly.
infratec
Always Here
Always Here
Posts: 7587
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Control a called routine through RunProgram()

Post by infratec »

Olli wrote: Thu Oct 20, 2022 2:06 pmoverpowering aura
Ahhh, now I know why I don't need a torch light when I go for a late walk with our dogs. :wink:
But I'm still learning and I also have to ask questions in 'coding questions' and I'm very glad that I get help or hints from other members.
It's on a give-and-take basis.

Maybe Fred should install an optionial cookie "headache tablet", then you can set the option on #True before you read my postings.
:mrgreen: :mrgreen: :mrgreen:
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: Control a called routine through RunProgram()

Post by Oso »

Olli wrote: Thu Oct 20, 2022 2:06 pm @Oso Sure I am wrong if I forget a '#' char... :shock: :lol:
I often have communication problems because of infratec's overpowering aura. It interferes with my concentration.
Olli, I'm sorry, I shouldn't have mentioned the missing '#', it was nothing. :cry: Apologies also for the apparent double-post, I'm not sure why but it seems that when I edited to correct it, it posted a second time, instead of updating the existing post. I blame the steam-driven connection here, which stops intermittently and then springs into life. I have unfortunately had to return to the military dictatorship and the internet speed is abominable. :x
Oso
Enthusiast
Enthusiast
Posts: 595
Joined: Wed Jul 20, 2022 10:09 am

Re: Control a called routine through RunProgram()

Post by Oso »

infratec wrote: Thu Oct 20, 2022 1:32 pm The CPU load is less, the transfer is faster, you don't need to handle connect and disconnect ...
How do I transmit and receive via UDP if there is no connection and disconnection? Are you saying I don't need the below?

Code: Select all

ConnectionIDn = OpenNetworkConnection("127.0.0.1", n, #PB_Network_UDP,  5000 ) 
infratec
Always Here
Always Here
Posts: 7587
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Control a called routine through RunProgram()

Post by infratec »

No,

I mean you don't need to handle:

Code: Select all

#PB_NetworkEvent_Connect
#PB_NetworkEvent_Disconnect
You should create an own thread for the server. Then it is independent from the main code.
With PostEvent() you can emulate a pressed button for example, or execute the #PB_Event_CloseWindow code.
infratec
Always Here
Always Here
Posts: 7587
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Control a called routine through RunProgram()

Post by infratec »

Small example:

Code: Select all

CompilerIf Not #PB_Compiler_Thread
  CompilerError "Please enable thread safe in compiler options"
CompilerEndIf

EnableExplicit


Structure ServerParameter_Structure
  Thread.i
  Exit.i
EndStructure


Procedure ServerThread(*Parameter.ServerParameter_Structure)
  
  Protected.i Server, Event, Length
  Protected Cmd$
  Protected *Buffer
  
  
  Server = CreateNetworkServer(#PB_Any, 5000, #PB_Network_UDP, "127.0.0.1")
  If Server
    *Buffer = AllocateMemory(2048)
    If *Buffer
      Repeat
        Event = NetworkServerEvent()
        Select Event
          Case #PB_NetworkEvent_None
            Delay(10)
            
          Case #PB_NetworkEvent_Data
            Length = ReceiveNetworkData(EventClient(), *Buffer, MemorySize(*Buffer))
            If Length
              Cmd$ = PeekS(*Buffer, Length, #PB_UTF8|#PB_ByteLength)
              
              Select Cmd$
                Case "exit"
                  PostEvent(#PB_Event_CloseWindow)
              EndSelect
            EndIf
            
        EndSelect
      Until *Parameter\Exit
      FreeMemory(*Buffer)
    EndIf
    CloseNetworkServer(Server)
  EndIf
  
EndProcedure




Define.i Exit, Event
Define ServerParameter.ServerParameter_Structure


OpenWindow(0, 0, 0, 400, 300, "Test", #PB_Window_MinimizeGadget|#PB_Window_ScreenCentered)

ServerParameter\Thread = CreateThread(@ServerThread(), @ServerParameter)

Repeat
  Event = WaitWindowEvent()
  Select Event
    Case #PB_Event_CloseWindow
      Exit = #True
  EndSelect
  
Until Exit

If IsThread(ServerParameter\Thread)
  ServerParameter\Exit = #True
  If WaitThread(ServerParameter\Thread, 1000) = 0
    ; should never been reached
    KillThread(ServerParameter\Thread)
  EndIf
EndIf

Code: Select all

EnableExplicit

Define.i Con

Con = OpenNetworkConnection("127.0.0.1", 5000, #PB_Network_UDP, 1000, "127.0.0.1")
If Con
  SendNetworkString(con, "exit")
  CloseNetworkConnection(Con)
EndIf
Your sended messages should not be longer then 1500 bytes.
Else it will be more complicated.
Post Reply