Handle an external program via stdin/out/error
-
- Enthusiast
- Posts: 276
- Joined: Fri Apr 25, 2003 5:06 pm
- Location: Gummersbach - Germany
- Contact:
Re: Handle an external program via stdin/out/error
Hi infratec,
nice code example, but how can I check in the window event loop if a program called from cmd has ended and exited with error code: 0?
nice code example, but how can I check in the window event loop if a program called from cmd has ended and exited with error code: 0?
The Human Code Machine / Masters' Design Group
Re: Handle an external program via stdin/out/error
You have to check if the command prompt is 'printed' and then you can use
To find out the exitcode of the program.
Since you run cmd, this is needed, because else you get only the exit code of cmd.exe
Or you can write a batch file which includes the echo command.
Then you can also use cmd /c to execute it.
Code: Select all
echo %errorlevel%
Since you run cmd, this is needed, because else you get only the exit code of cmd.exe
Or you can write a batch file which includes the echo command.
Then you can also use cmd /c to execute it.
-
- Enthusiast
- Posts: 276
- Joined: Fri Apr 25, 2003 5:06 pm
- Location: Gummersbach - Germany
- Contact:
Re: Handle an external program via stdin/out/error
Thx a lot for your help! Using your various code examples and hints I got my java tool running from a hidden cmd console now. Here's an excerpt:
Code: Select all
DOS = RunProgram(ComSpec.s,"","",#PB_Program_Open | #PB_Program_Read | #PB_Program_Write | #PB_Program_Error )
If DOS
Repeat
Delay(10)
ReadLen = AvailableProgramOutput(DOS)
Debug ReadLen
Until ReadLen
If ReadLen > MemorySize(*Buffer)
*Buffer = ReAllocateMemory(*Buffer, ReadLen, #PB_Memory_NoClear)
EndIf
ReadLen = ReadProgramData(DOS, *Buffer, ReadLen)
Output.s = ExternalProgram_CP850ToUnicode(*Buffer, ReadLen)
Output.s = RemoveString(Output.s, #CR$)
Output.s = RTrim(Output.s, #LF$)
i = CountString(Output.s, #LF$)
Prompt.s=StringField(Output.s, i + 1, #LF$)
Debug "Prompt: " + Prompt.s
StartTime.q = ElapsedMilliseconds()
WriteProgramStringN(DOS, JavaParameter.s)
ExitCode.i=0
Output.s=""
Repeat
ReadLen = AvailableProgramOutput(DOS)
If ReadLen > MemorySize(*Buffer)
*Buffer = ReAllocateMemory(*Buffer, ReadLen, #PB_Memory_NoClear)
EndIf
If ReadLen
ReadLen = ReadProgramData(DOS, *Buffer, ReadLen)
Output.s = Output.s + ExternalProgram_CP850ToUnicode(*Buffer, ReadLen)
If FindString(Output.s, Prompt.s)
ExitCode.i = #True
EndIf
EndIf
Until ExitCode.i
The Human Code Machine / Masters' Design Group
- Kwai chang caine
- Always Here
- Posts: 5342
- Joined: Sun Nov 05, 2006 11:42 pm
- Location: Lyon - France
Re: Handle an external program via stdin/out/error
Hello at all
I use this nice code of INFRATEC, and it work perfectly
So i try now to send automaticaly a list of commands named "TabloCommandes()"
When the console have answering, the next command is sended
I understand nothing to SEMAPHORE and i have not found how i can test if the SEMAPHORE is locked or not
So i had the idea to adding a timer, but apparently there are when even a problem of synchronisation
If someone can help me
Have a good day
I use this nice code of INFRATEC, and it work perfectly
So i try now to send automaticaly a list of commands named "TabloCommandes()"
When the console have answering, the next command is sended
I understand nothing to SEMAPHORE and i have not found how i can test if the SEMAPHORE is locked or not
So i had the idea to adding a timer, but apparently there are when even a problem of synchronisation
If someone can help me
Have a good day
Code: Select all
CompilerIf Not #PB_Compiler_Thread
MessageRequester("Info", "Enable Thread-Safe in compiler options!")
End
CompilerEndIf
Enumeration
#Form0
#EditorCommande
#StringCommande
#BoutonLancer
#BoutonEffacer
#Timer
EndEnumeration
Enumeration #PB_Event_FirstCustomValue
#Own_Event_FromProg
#Own_Event_Exit
EndEnumeration
Structure ThreadParameterStructure
Semaphore.i
Thread.i
ProgramID.i
FromProg$
ToProg$
Exit.i
EndStructure
Global Dim TabloCommandes.s(10)
xx+1:TabloCommandes(xx) = "HELP"
xx+1:TabloCommandes(xx) = "DIR"
xx+1:TabloCommandes(xx) = "CD D:\"
xx+1:TabloCommandes(xx) = "D:"
xx+1:TabloCommandes(xx) = "DIR"
ReDim TabloCommandes(xx)
Procedure HandleIO(*ThreadParameter.ThreadParameterStructure)
Protected ReadLen.i, WriteLen.i, Error$
*Buffer = AllocateMemory(10000)
If *Buffer
*ThreadParameter\ProgramID = RunProgram("cmd.exe", "", "", #PB_Program_Open|#PB_Program_Read|#PB_Program_Write|#PB_Program_Error|#PB_Program_UTF8|#PB_Program_Hide)
Repeat
If *ThreadParameter\ProgramID
ReadLen = AvailableProgramOutput(*ThreadParameter\ProgramID)
If ReadLen
ReadLen = ReadProgramData(*ThreadParameter\ProgramID, *Buffer, ReadLen)
If ReadLen
*ThreadParameter\FromProg$ = PeekS(*Buffer, ReadLen, #PB_UTF8)
PostEvent(#Own_Event_FromProg)
WaitSemaphore(*ThreadParameter\Semaphore)
EndIf
EndIf
Error$ = ReadProgramError(*ThreadParameter\ProgramID, #PB_UTF8)
If Len(Error$)
*ThreadParameter\FromProg$ = Error$
PostEvent(#Own_Event_FromProg)
WaitSemaphore(*ThreadParameter\Semaphore)
EndIf
If Len(*ThreadParameter\ToProg$)
*ThreadParameter\ToProg$ + #CRLF$
WriteLen = PokeS(*Buffer, *ThreadParameter\ToProg$, -1, #PB_UTF8)
*ThreadParameter\ToProg$ = ""
WriteLen = WriteProgramData(*ThreadParameter\ProgramID, *Buffer, WriteLen)
SignalSemaphore(*ThreadParameter\Semaphore)
EndIf
Delay(10)
FillMemory(*Buffer, 10000)
EndIf
Until *ThreadParameter\Exit ;Or Not ProgramRunning(*ThreadParameter\ProgramID)
CloseProgram(*ThreadParameter\ProgramID)
FreeMemory(*Buffer)
EndIf
PostEvent(#Own_Event_Exit)
EndProcedure
Define Event.i, Exit.i
Define ThreadParameter.ThreadParameterStructure
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")
AddWindowTimer(#Form0, #Timer, 1000)
ThreadParameter\Semaphore = CreateSemaphore()
ThreadParameter\Thread = CreateThread(@HandleIO(), @ThreadParameter)
Repeat
Event = WaitWindowEvent()
Select Event
Case #Own_Event_FromProg
AddGadgetItem(#EditorCommande, -1, ThreadParameter\FromProg$)
SignalSemaphore(ThreadParameter\Semaphore)
Case #Own_Event_Exit
Exit = #True
Case #PB_Event_Timer
If EventTimer() = #Timer
If LigneCommande <= ArraySize(TabloCommandes())
LigneCommande + 1
ThreadParameter\ToProg$ = TabloCommandes(LigneCommande)
WaitSemaphore(ThreadParameter\Semaphore)
EndIf
EndIf
Case #PB_Event_Gadget
Select EventGadget()
Case #BoutonLancer
LigneCommande = 0
Case #BoutonEffacer
SetGadgetText(#EditorCommande, "")
;
EndSelect
Case #PB_Event_CloseWindow
If IsThread(ThreadParameter\Thread)
CloseProgram(ThreadParameter\ProgramID)
ThreadParameter\ProgramID = 0
EndIf
FreeSemaphore(ThreadParameter\Semaphore)
Exit = #True
EndSelect
Until Exit
The happiness is a road...
Not a destination
Not a destination
Re: Handle an external program via stdin/out/error
You need an additional Mutex.
Is not the right way, because you try to access this variable inside of the thread.
So you need to block the concurrent access.
And
Not tested!
Code: Select all
If LigneCommande <= ArraySize(TabloCommandes())
LigneCommande + 1
ThreadParameter\ToProg$ = TabloCommandes(LigneCommande)
WaitSemaphore(ThreadParameter\Semaphore)
EndIf
So you need to block the concurrent access.
Code: Select all
If EventTimer() = #Timer
If TryLockMutex(ThreadParameter\Mutex)
If LigneCommande <= ArraySize(TabloCommandes())
LigneCommande + 1
ThreadParameter\ToProg$ = TabloCommandes(LigneCommande)
UnlockMutex(ThreadParameter\Mutex)
EndIf
Else
; try it again and don't bölock the main loop
PostEvent(#PB_Event_Timer, #Form0, #Timer)
EndIf
EndIf
Code: Select all
If TryLockMutex(*ThreadParameter\Mutex)
If Len(*ThreadParameter\ToProg$)
*ThreadParameter\ToProg$ + #CRLF$
WriteLen = PokeS(*Buffer, *ThreadParameter\ToProg$, -1, #PB_UTF8)
*ThreadParameter\ToProg$ = ""
UnlockMutex(*ThreadParameter\Mutex)
WriteLen = WriteProgramData(*ThreadParameter\ProgramID, *Buffer, WriteLen)
SignalSemaphore(*ThreadParameter\Semaphore)
Else
UnlockMutex(*ThreadParameter\Mutex)
EndIf
EndIf
- Kwai chang caine
- Always Here
- Posts: 5342
- Joined: Sun Nov 05, 2006 11:42 pm
- Location: Lyon - France
Re: Handle an external program via stdin/out/error
Hello INFRATEC
Thanks for your example, i try it immediately and say to you the result
Thanks for your example, i try it immediately and say to you the result
Last edited by Kwai chang caine on Sun Jan 09, 2022 9:11 pm, edited 1 time in total.
The happiness is a road...
Not a destination
Not a destination
- Kwai chang caine
- Always Here
- Posts: 5342
- Joined: Sun Nov 05, 2006 11:42 pm
- Location: Lyon - France
Re: Handle an external program via stdin/out/error
That works very nice !!!
Thanks a lot MASTER for your like usualy golden help
The timer is the best solution for send commands one by one automatically, or it's possible and perhaps even more simple to do that only with the mutex, Semaphore, etc ???
Thanks a lot MASTER for your like usualy golden help
The timer is the best solution for send commands one by one automatically, or it's possible and perhaps even more simple to do that only with the mutex, Semaphore, etc ???
The happiness is a road...
Not a destination
Not a destination
Re: Handle an external program via stdin/out/error
Why not using a List() and send the next if the answer arrived.
Re: Handle an external program via stdin/out/error
Since you had some bugs inside, the list version wa not running as it should.
Here is a working version:
The main fault was a missing #PB_ByteLength:
Here is a working version:
Code: Select all
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
*ThreadParameter\ProgramID = RunProgram("cmd.exe", "", "", #PB_Program_Open|#PB_Program_Read|#PB_Program_Write|#PB_Program_Error|#PB_Program_UTF8|#PB_Program_Hide)
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")
AddElement(TabloCommandeList())
TabloCommandeList() = "HELP"
AddElement(TabloCommandeList())
TabloCommandeList() = "DIR"
AddElement(TabloCommandeList())
TabloCommandeList() = "CD H:\"
AddElement(TabloCommandeList())
TabloCommandeList() = "H:"
AddElement(TabloCommandeList())
TabloCommandeList() = "DIR"
ResetList(TabloCommandeList())
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$)
If Right(Help$, 1) = ">"
Debug "prompt found"
If NextElement(TabloCommandeList())
LockMutex(ThreadParameter\Mutex)
ThreadParameter\ToProg$ = TabloCommandeList()
UnlockMutex(ThreadParameter\Mutex)
WaitSemaphore(ThreadParameter\Semaphore)
EndIf
EndIf
Case #Own_Event_Exit
Exit = #True
Case #PB_Event_Gadget
Select EventGadget()
Case #BoutonLancer
LastElement(TabloCommandeList())
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)
Code: Select all
*ThreadParameter\FromProg$ = PeekS(*Buffer, ReadLen, #PB_UTF8|#PB_ByteLength)
Last edited by infratec on Mon Jan 10, 2022 2:28 pm, edited 3 times in total.
Re: Handle an external program via stdin/out/error
This topic title really appeals to me, but I have no idea how to work with this source or what can be done with it.
Can someone give a simple outline of how to use this code ?
Thanks.
Can someone give a simple outline of how to use this code ?
Thanks.
Yeah I know, but keep in mind ... Leonardo da Vinci was also an autodidact.
Re: Handle an external program via stdin/out/error
In general it should allow to receive and send data to a console program.
In the code of the first topic is also an example how to use it. (Inside of the CompilerIf)
It uses cmd.exe as console program and it is able to receive and send data to it.
Also a codepage translation is included.
In the code of the first topic is also an example how to use it. (Inside of the CompilerIf)
It uses cmd.exe as console program and it is able to receive and send data to it.
Also a codepage translation is included.
- Kwai chang caine
- Always Here
- Posts: 5342
- Joined: Sun Nov 05, 2006 11:42 pm
- Location: Lyon - France
Re: Handle an external program via stdin/out/error
Your new code with List() works perfectly too
Surely not need to increment an index of array, you have right it's more simple
Your code is smart, you send the new order only if the answer is received
The line is an additional security, because i have see that works without her
Again thanks for your merveillous help
I don't understand what is the benefit to use a list() rather than an array() ?
Surely not need to increment an index of array, you have right it's more simple
Your code is smart, you send the new order only if the answer is received
The line
Code: Select all
If Right(Help$, 1) = ">"
Again thanks for your merveillous help
The happiness is a road...
Not a destination
Not a destination
Re: Handle an external program via stdin/out/error
No, it is not an additional security.
The timer is removed.
So you need to know when you can send the next command.
You can send a command if the prompt is shown.
With this version you don't loose any time if the list is executed.
The timer is removed.
So you need to know when you can send the next command.
You can send a command if the prompt is shown.
With this version you don't loose any time if the list is executed.
Re: Handle an external program via stdin/out/error
infratec thank you.
Just tried it under Win-XP and it works with that included example. Nice.
As the terminal is also a console program (I suppose, yeah my level of coding... low)... yeah, I remember you didn't recommend it (on using alsa library), but now I'm just curious...
Yeah I know, but keep in mind ... Leonardo da Vinci was also an autodidact.
- Kwai chang caine
- Always Here
- Posts: 5342
- Joined: Sun Nov 05, 2006 11:42 pm
- Location: Lyon - France
Re: Handle an external program via stdin/out/error
It's exactely what i want , faster you die (French expression)
Ok but is it impossible the answer writing a ">" the loop see it at a certain time and this is not the prompt ?
The happiness is a road...
Not a destination
Not a destination