Handle an external program via stdin/out/error

Share your advanced PureBasic knowledge/code with the community.
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5342
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: Handle an external program via stdin/out/error

Post by Kwai chang caine »

Hello INFRATEC

I know you have create a splendid code to manage directly the PYTHON Dll :wink:
But i search to working in REPL mode (Like with "cmd.exe") with the "python.exe" is it possible ?

I don't understand why your jewel code not works with PYTHON :oops:
That works when i send one shot order "C:\Python37\python.exe -V" i have the "3.7.0" version answer but PYTHON close immediately after answering :|
And if i send ""C:\Python37\python.exe" for connecting with PYTHON (like in the windows console) the prompt of PYTHON not appears and your code is locked :|

Code: Select all

; https://www.purebasic.fr/english/viewtopic.php?p=579279#p579279

CompilerIf Not #PB_Compiler_Thread
 MessageRequester("Info", "Enable Thread-Safe in compiler options!")
 End
CompilerEndIf

EnableExplicit

Enumeration
 #Form0
 #EditorCommande
 #StringCommande
 #BoutonLancer
 #BoutonEffacer
 #Timer
EndEnumeration

Enumeration #PB_Event_FirstCustomValue
 #Own_Event_FromProg
 #Own_Event_Exit
EndEnumeration

Structure ThreadParameterStructure
 Mutex.i
 Semaphore.i
 Thread.i
 ProgramID.i
 FromProg$
 ToProg$
 Exit.i
EndStructure

Procedure HandleIO(*ThreadParameter.ThreadParameterStructure)
 
 Protected ReadLen.i, WriteLen.i, Error$
 Protected *Buffer
 
 
 *Buffer = AllocateMemory(10000)
 
 If *Buffer
  
  ; WORKS => *ThreadParameter\ProgramID = RunProgram("cmd.exe","", "", #PB_Program_Open|#PB_Program_Read|#PB_Program_Write|#PB_Program_Error|#PB_Program_Hide)
  *ThreadParameter\ProgramID = RunProgram("C:\python.exe","", "", #PB_Program_Open|#PB_Program_Read|#PB_Program_Write|#PB_Program_Error|#PB_Program_Hide) ; NOT WORKS
  
  Delay(1000)
  If *ThreadParameter\ProgramID
   
   Repeat
    
    ReadLen = AvailableProgramOutput(*ThreadParameter\ProgramID)
    If ReadLen
     
     ReadLen = ReadProgramData(*ThreadParameter\ProgramID, *Buffer, ReadLen)
     If ReadLen
      Debug ReadLen
      *ThreadParameter\FromProg$ = PeekS(*Buffer, ReadLen, #PB_UTF8|#PB_ByteLength)
      PostEvent(#Own_Event_FromProg)
      WaitSemaphore(*ThreadParameter\Semaphore)
          Debug "FromProg printed"
     EndIf
     
    EndIf
    
    Error$ = ReadProgramError(*ThreadParameter\ProgramID, #PB_UTF8)
    If Len(Error$)
     *ThreadParameter\FromProg$ = Error$
     PostEvent(#Own_Event_FromProg)
     WaitSemaphore(*ThreadParameter\Semaphore)
      Debug "Error printed"
    EndIf
    
    If TryLockMutex(*ThreadParameter\Mutex)
     If Len(*ThreadParameter\ToProg$)
       Debug "ToProg found: " + *ThreadParameter\ToProg$
      *ThreadParameter\ToProg$ + #CRLF$
      WriteLen = PokeS(*Buffer, *ThreadParameter\ToProg$, -1, #PB_UTF8)
      *ThreadParameter\ToProg$ = ""
      SignalSemaphore(*ThreadParameter\Semaphore)
      UnlockMutex(*ThreadParameter\Mutex)
      WriteLen = WriteProgramData(*ThreadParameter\ProgramID, *Buffer, WriteLen)
     Else
      UnlockMutex(*ThreadParameter\Mutex)
     EndIf  
    EndIf
    
    Delay(10)
    
   Until *ThreadParameter\Exit
   
   CloseProgram(*ThreadParameter\ProgramID)
  EndIf 
  
  FreeMemory(*Buffer)
  
 EndIf
 
 PostEvent(#Own_Event_Exit)
 
EndProcedure

Define.i Event, Exit
Define Help$
Define ThreadParameter.ThreadParameterStructure
NewList TabloCommandeList.s()

OpenWindow(#Form0, 379, 176, 608, 542, "Remote console", #PB_Window_SystemMenu|#PB_Window_SizeGadget|#PB_Window_TitleBar)
EditorGadget(#EditorCommande, 5, 15, 593, 447)
StringGadget(#StringCommande, 6, 469, 594, 23, "")
ButtonGadget(#BoutonLancer, 11, 503, 201, 33, "Envoyer commande")
ButtonGadget(#BoutonEffacer, 211, 503, 201, 33, "Effacer")

ThreadParameter\Mutex = CreateMutex()
ThreadParameter\Semaphore = CreateSemaphore()
ThreadParameter\Thread = CreateThread(@HandleIO(), @ThreadParameter)

Repeat
 
 Event = WaitWindowEvent()
 
 Select Event
   
  Case #Own_Event_FromProg
   Help$ = ThreadParameter\FromProg$
   SignalSemaphore(ThreadParameter\Semaphore)
   AddGadgetItem(#EditorCommande, -1, Help$)
  
  Case #Own_Event_Exit
   Exit = #True
   
  Case #PB_Event_Gadget
   
   Select EventGadget()
    Case #BoutonLancer
     ThreadParameter\ToProg$ = GetGadgetText(#StringCommande)
     
    Case #BoutonEffacer
     SetGadgetText(#EditorCommande, "")
     
   EndSelect
   
  Case #PB_Event_CloseWindow
   Exit = #True
   
 EndSelect
 
Until Exit

If IsThread(ThreadParameter\Thread)
 ThreadParameter\Exit = #True
 If WaitThread(ThreadParameter\Thread, 3000) = 0
  KillThread(ThreadParameter\Thread)  ; should never happen
 EndIf
EndIf

FreeSemaphore(ThreadParameter\Semaphore)
FreeMutex(ThreadParameter\Mutex)
ImageThe happiness is a road...
Not a destination
infratec
Always Here
Always Here
Posts: 6817
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

Re: Handle an external program via stdin/out/error

Post by infratec »

Python makes several things ...

1. It uses stderr as regular output
2. It usese a buffered stream

Since PB handles stderr not like stdout (see my bug report)
you need to redirect stderr to stdout.

But look at my answer to your pipe question :wink:
Post Reply