Page 1 of 2
Control a called routine through RunProgram()
Posted: Wed Oct 19, 2022 7:22 pm
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$)
Re: Control a called routine through RunProgram()
Posted: Wed Oct 19, 2022 10:28 pm
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.
Re: Control a called routine through RunProgram()
Posted: Thu Oct 20, 2022 10:38 am
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)
Re: Control a called routine through RunProgram()
Posted: Thu Oct 20, 2022 11:34 am
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.
Re: Control a called routine through RunProgram()
Posted: Thu Oct 20, 2022 11:44 am
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
Re: Control a called routine through RunProgram()
Posted: Thu Oct 20, 2022 11:47 am
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.
Re: Control a called routine through RunProgram()
Posted: Thu Oct 20, 2022 11:49 am
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
Re: Control a called routine through RunProgram()
Posted: Thu Oct 20, 2022 11:56 am
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?
Re: Control a called routine through RunProgram()
Posted: Thu Oct 20, 2022 1:32 pm
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 ...
Re: Control a called routine through RunProgram()
Posted: Thu Oct 20, 2022 2:06 pm
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...
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.
Re: Control a called routine through RunProgram()
Posted: Thu Oct 20, 2022 2:33 pm
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.
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.

Re: Control a called routine through RunProgram()
Posted: Thu Oct 20, 2022 4:11 pm
by Oso
Olli wrote: Thu Oct 20, 2022 2:06 pm
@Oso Sure I am wrong if I forget a '#' char...
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.

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.

Re: Control a called routine through RunProgram()
Posted: Thu Oct 20, 2022 5:41 pm
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 )
Re: Control a called routine through RunProgram()
Posted: Thu Oct 20, 2022 7:39 pm
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.
Re: Control a called routine through RunProgram()
Posted: Thu Oct 20, 2022 7:56 pm
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.