It has come time for me to get into inter-process communication. I want to have one program, "parent", that is managing a bunch of others running as seperate "child" processes. From the freeprocess module, as I understand it, I can read and write to a child process by attaching a TProcess to it and treating the TProcess like a stream. My question is how does one prepare the child program to be communicated with in the first place? As far as I can tell, I am supposed to open a TPipeStream in the child and read from/write to that, and if another program is listening/writing to the pipe then that is how communication takes place. Is this correct?
Inter-process communication
BlitzMax Forums/BlitzMax Beginners Area/Inter-process communication TProcess is not attached to the child process ... it actually is the child process or better executes it and takes care that it isn't not just freed again.
From the host, you use TPipeStream to communicate (TProcess holds one in process.pipe ...)
In the child you use StdInStream / StdOutStream
From the host, you use TPipeStream to communicate (TProcess holds one in process.pipe ...)
In the child you use StdInStream / StdOutStream
Okay, thanks for the clarification. One other thing, if I may; I noticed in FreeProcess that there is a std pipe and an err pipe to communicate through. On the child side how do you direct which pipe to write to? If I have an error in the child program then I definitely want to be able to control the stream message out so it goes to err pipe not std pipe, for example.
Never checked the error pipe. Perhaps that are system signals (you would need to check that) which BM does not allow you to send. You would need to write some own functionality for this purpose.
Or you are right in the way that it is possible to create a TPipeStream in the client without having a valid TProcess to use those 2 streams ...
So far I've always used the StdIn and Out so I can't say, sorry.
Or you are right in the way that it is possible to create a TPipeStream in the client without having a valid TProcess to use those 2 streams ...
So far I've always used the StdIn and Out so I can't say, sorry.
Alrighty, after some messing around I have arrived at this, but can someone tell me where I am screwing it up? Messages should be exchanged through pipes and appear in the complimentary text fields upon entry.
Child exe
Parent exe
Obviously the child.exe has to be compiled first and then wait in the same folder as the parent.exe so parent.exe will be able to find and run it.
Child exe
SuperStrict Framework pub.freeprocess Import brl.standardio Import brl.win32maxgui Import brl.eventqueue Local labtext1:String = "Enter a message here :" Local labtext2:String = "Message from parent :" Local window:TGadget = CreateWindow("Child Process", 310, 100, 300, 55, Null, 1|32) ActivateGadget(window) Local canvas:TGadget = CreateCanvas(0, 0, GadgetWidth(window), GadgetHeight(window), window) Local sg1:TGadget = CreateTextField(130, 5, GadgetWidth(window) - 135, 16, canvas) Local clab1:TGadget = CreateLabel(labtext1, 5, 5, Len(labtext1)*16, 16, canvas) Local sg2:TGadget = CreateTextField(130, 30, GadgetWidth(window) - 135, 16, canvas) Local clab2:TGadget = CreateLabel(labtext2, 5, 30, Len(labtext2)*16, 16, canvas) Local button:TGadget = CreateButton("", 0, 0, 0, 0, canvas ,BUTTON_OK) HideGadget button Local instrm:Int, outstrm:Int Global pipe:TPipeStream = TPipeStream.Create(instrm, outstrm) Repeat PollEvent() Select EventID() Case EVENT_GADGETACTION Select EventSource() Case button If TextFieldText(sg1) <> "" Then WriteStdIO(TextFieldText(sg1)) SetGadgetText(sg1,"") End If ActivateGadget(sg1) End Select End Select Local incoming:String = ReadStdIO() If incoming <> "" Then SetGadgetText(sg2, incoming) Until EventID() = EVENT_WINDOWCLOSE And EventSource() = window End Function WriteStdIO(text:String) pipe.WriteLine(text) End Function Function ReadStdIO:String() Local inc:String = pipe.ReadLine() If inc Then Return inc Else Return "" End Function
Parent exe
SuperStrict Framework pub.freeprocess Import brl.standardio Import brl.win32maxgui Import brl.eventqueue Local labtext1:String = "Enter a message here :" Local labtext2:String = "Message from child:" Local window:TGadget = CreateWindow("Parent Process", 0, 100, 300, 55, Null, 1|32) ActivateGadget(window) Local canvas:TGadget = CreateCanvas(0, 0, GadgetWidth(window), GadgetHeight(window), window) Local sg1:TGadget = CreateTextField(130, 5, GadgetWidth(window) - 135, 16, canvas) Local clab1:TGadget = CreateLabel(labtext1, 5, 5, Len(labtext1)*16, 16, canvas) Local sg2:TGadget = CreateTextField(130, 30, GadgetWidth(window) - 135, 16, canvas) Local clab2:TGadget = CreateLabel(labtext2, 5, 30, Len(labtext2)*16, 16, canvas) Local button:TGadget = CreateButton("", 0, 0, 0, 0, canvas ,BUTTON_OK) HideGadget button Local instrm:Int, outstrm:Int Global process:TProcess = CreateProcess("childtest", 0) Repeat PollEvent() Select EventID() Case EVENT_GADGETACTION Select EventSource() Case button If TextFieldText(sg1) <> "" Then WriteStdIO(TextFieldText(sg1)) SetGadgetText(sg1,"") End If ActivateGadget(sg1) End Select End Select Local incoming:String = ReadStdIO() If incoming <> "" Then SetGadgetText(sg2, incoming) Until EventID() = EVENT_WINDOWCLOSE And EventSource() = window terminateprocess(process) End Function WriteStdIO(text:String) process.pipe.WriteLine(text) End Function Function ReadStdIO:String() Local inc:String = process.pipe.ReadLine() If inc Then Return inc Else Return "" End Function
Obviously the child.exe has to be compiled first and then wait in the same folder as the parent.exe so parent.exe will be able to find and run it.
I'm playing with inter-process comunication and I'm blocked with ReadStdIN() function.
Have a look at the following example (master+client)
Master
Client 1 (proc_client) - this work perfectly
client 2 (proc_client with MaxGUI)
The problem is in the second client that uses MaxGUI - I think ReadStdIN() locks the program (and so MaxGUI processes) to wait some inputs: is there a workaround?
Have a look at the following example (master+client)
Master
Import maxgui.drivers Local window:tgadget , buttonstart:tgadget , buttonStop:tgadget,buttonSend:tgadget window = CreateWindow("Test Master" , 100 , 100 , 300 , 200) buttonStart = CreateButton("Start" , 20 , 20 , 70 , 30 , window) buttonStop = CreateButton("Stop" , 20 , 60 , 70 , 30 , window) buttonSend = CreateButton("Send" , 20 , 100 , 70 , 30 , window) DisableGadget buttonStop DisableGadget buttonSend Local pro:tprocess Local listeN:Int=0 While True WaitEvent If listen = 1 If pro rec$ = pro.pipe.ReadLine() If rec Print "Received: ["+ rec+"]" If Instr(rec,"EXIT")>0 Print "Terminate..." pro.terminate() pro = Null End End If End If rec = "" End If End If Select EventID() Case event_gadgetAction Select EventSource() Case buttonSend If pro pro.pipe.WriteLine "Hallo" End If Case buttonStart pro=CreateProcess("Proc_child2.debug.exe") DisableGadget buttonStart EnableGadget buttonStop EnableGadget buttonSend listen=1 Case buttonStop DisableGadget buttonStop EnableGadget buttonStart If pro pro.pipe.WriteLine "EXIT" End If End Select Case EVENT_WINDOWCLOSE End End Select Wend End
Client 1 (proc_client) - this work perfectly
Local msg$ While Not AppTerminate() msg$ = ReadStdin() If msg Notify "MESSAGE:[" + msg+"]" Print "RECEIVED" If msg = "EXIT" Print "EXIT" msg="" End If Wend End
client 2 (proc_client with MaxGUI)
Import maxgui.drivers Local window:tgadget , buttonstart:tgadget , buttonStop:tgadget , buttonSend:tgadget Local txt:tgadget Local msg$ window = CreateWindow("Test Client" , 100 , 100 , 300 , 200) buttonStop = CreateButton("Stop" , 20 , 20 , 70 , 30 , window) buttonSend = CreateButton("Send" , 20 , 60 , 70 , 30 , window) txt = CreateTextField(20 , 110 , 100 , 20 , window) CreateTimer 5 While True WaitEvent Select EventID() Case event_timerTick msg$ = ReadStdin() If msg Notify "MESSAGE:" + msg Print "RECEIVED" If msg = "EXIT" Print "EXIT" Exit End If msg="" End If Case event_gadgetAction Select EventSource() Case buttonSend Print TextFieldText(txt) Case buttonStop Print "EXIT" End Select Case EVENT_WINDOWCLOSE Print "EXIT" End End Select Wend End
The problem is in the second client that uses MaxGUI - I think ReadStdIN() locks the program (and so MaxGUI processes) to wait some inputs: is there a workaround?
degac, when trying out your examples, for me both clients block at attempting ReadStdin(). I found an explanation about the blocking here: Link
That behaviour is exactly why I want to use non-blocking pipes instead...
That behaviour is exactly why I want to use non-blocking pipes instead...
you should use plain text to comunicate processes. Just use readline and writeline. convert everything to strings. Standard IO streams work in a 4096 kbytes buffer and has a sort of interruption on CR characters. Also, I think non blocking pipes requiere multiple threading...
ok. thanks, I will try.
You can send update values to the client(s).
master.bmx
proc.bmx
master.bmx
SuperStrict Framework pub.freeprocess Import brl.eventqueue Import maxgui.drivers Import brl.timer Local Wnd_Main:TGadget = CreateWindow("Host", 100, 100, 300, 250, Null, WINDOW_TITLEBAR | WINDOW_STATUS) Local Btn_Start:TGadget = CreateButton("Start", 20, 20, 70, 30, Wnd_Main) Local Btn_Stop:TGadget = CreateButton("Stop", 20, 60, 70, 30, Wnd_Main) DisableGadget Btn_Stop Local Btn_Send:TGadget = CreateButton("Send", 20, 100, 70, 30, Wnd_Main) DisableGadget Btn_Send Local Txf_SendMessage:TGadget = CreateTextField(20, 140, 200, 20, Wnd_Main) Local proc:TProcess, Listen:Int = 0, procmsg:String CreateTimer 60 Repeat Select WaitEvent() Case EVENT_TIMERTICK If Listen = 1 If proc.Status() And proc.pipe.ReadAvail() procmsg = proc.pipe.ReadLine().Replace(Chr(13), "").Replace(Chr(0), "") If procmsg SetStatusText Wnd_Main, CurrentTime() + "[" + procmsg + "]" If procmsg = "EXIT" Print "Terminate..." proc.Terminate() proc = Null End End If End If End If proc.pipe.WriteLine("~~") End If Case EVENT_GADGETACTION Select EventSource() Case Btn_Send If proc Then proc.pipe.WriteLine(TextFieldText(Txf_SendMessage)) Case Btn_Start proc = CreateProcess("proc.exe") DisableGadget Btn_Start EnableGadget Btn_Stop EnableGadget Btn_Send SetStatusText Wnd_Main, "Process Started" Listen = 1 Case Btn_Stop DisableGadget Btn_Send DisableGadget Btn_Stop EnableGadget Btn_Start SetStatusText Wnd_Main, "Process Stopped" Listen = 0 proc.Terminate() proc = Null End Select Case EVENT_WINDOWCLOSE End End Select Forever End
proc.bmx
SuperStrict Framework brl.standardio Import brl.eventqueue Import maxgui.drivers import brl.timer Local Wnd_Main:TGadget = CreateWindow("Client", 400, 100, 300, 250, Null, WINDOW_TITLEBAR | WINDOW_STATUS) Local Btn_Stop:TGadget = CreateButton("Stop", 20, 20, 70, 30, Wnd_Main) Local Btn_Send:TGadget = CreateButton("Send", 20, 60, 70, 30, Wnd_Main) Local Txf_SendMessage:TGadget = CreateTextField(20, 140, 200, 20, Wnd_Main) CreateTimer 60 Repeat Select WaitEvent() Case EVENT_TIMERTICK Local stdmsg:String = StandardIOStream.ReadLine() 'ReadStdin() If stdmsg If stdmsg <> "~~" SetStatusText Wnd_Main, CurrentTime() + "[" + stdmsg + "]" 'Print "RECEIVED" EndIf If stdmsg = "EXIT" Print "EXIT" Exit End If End If Case EVENT_GADGETACTION Select EventSource() Case Btn_Send Print TextFieldText(Txf_SendMessage) Case Btn_Stop Print "EXIT" End Select Case EVENT_WINDOWCLOSE Print "EXIT" End End Select Forever End