since KCC had some problems and he want to use the solution as a 'standard',
I rewrote it a bit.
Save it as ExternalProgram.pbi
Code: Select all
;
; ExternalProgram.pbi
;
; https://www.purebasic.fr/english/viewtopic.php?t=64876
;
; V 1.01
;
; - added a list of commands to send
; - fixed the output, command should be printed behind the prompt and not below
;
; V 1.00
;
; by infratec
;
CompilerIf #PB_Compiler_IsMainFile
EnableExplicit
CompilerEndIf
CompilerIf Not #PB_Compiler_Thread
MessageRequester("Info", "Enable Thread-Safe in compiler options!")
End
CompilerEndIf
CompilerIf Not Defined(Event_CustomValue, #PB_Enumeration)
Enumeration Event_CustomValue #PB_Event_FirstCustomValue
#ExternalProgram_Event_StdOut
#ExternalProgram_Event_Error
#ExternalProgram_Event_Exit
EndEnumeration
CompilerElse
Enumeration Event_CustomValue
#ExternalProgram_Event_StdOut
#ExternalProgram_Event_Error
#ExternalProgram_Event_Exit
EndEnumeration
CompilerEndIf
Prototype.s ExternalProgram_CPxxxToUnicode(*Buffer, Length.i)
Structure ExternalProgram_ParameterStructure
Program$
ProgramParameter$
ProgramWorkingDirectory$
ProgramReadWriteMode.i
ProgramExit$
Semaphore.i
Mutex.i
Thread.i
ProgramID.i
StdOut$
StdIn$
StdErr$
CodePageConverter.ExternalProgram_CPxxxToUnicode
Exit.i
EndStructure
Procedure.s ExternalProgram_CP437ToUnicode(*Buffer, Length.i)
DataSection
ExternalProgram_CP437Data:
Data.u $00C7, $00FC, $00E9, $00E2, $00E4, $00E0, $00E5, $00E7
Data.u $00EA, $00EB, $00E8, $00EF, $00EE, $00EC, $00C4, $00C5
Data.u $00C9, $00E6, $00C6, $00F4, $00F6, $00F2, $00FB, $00F9
Data.u $00FF, $00D6, $00DC, $00A2, $00A3, $00A5, $20A7, $0192
Data.u $00E1, $00ED, $00F3, $00FA, $00F1, $00D1, $00AA, $00BA
Data.u $00BF, $2310, $00AC, $00BD, $00BC, $00A1, $00AB, $00BB
Data.u $2591, $2592, $2593, $2502, $2524, $2561, $2562, $2556
Data.u $2555, $2563, $2551, $2557, $255D, $255C, $255B, $2510
Data.u $2514, $2534, $252C, $251C, $2500, $253C, $255E, $255F
Data.u $255A, $2554, $2569, $2566, $2560, $2550, $256C, $2567
Data.u $2568, $2564, $2565, $2559, $2558, $2552, $2553, $256B
Data.u $256A, $2518, $250C, $2588, $2584, $258C, $2590, $2580
Data.u $03B1, $00DF, $0393, $03C0, $03A3, $03C3, $00B5, $03C4
Data.u $03A6, $0398, $03A9, $03B4, $221E, $03C6, $03B5, $2229
Data.u $2261, $00B1, $2265, $2264, $2320, $2321, $00F7, $2248
Data.u $00B0, $2219, $00B7, $221A, $207F, $00B2, $25A0, $00A0
EndDataSection
Protected Result$, i.i, Byte.a
Length - 1
For i = 0 To Length
Byte = PeekA(*Buffer + i)
If Byte < $80
Result$ + Chr(Byte)
Else
Result$ + Chr(PeekU(?ExternalProgram_CP437Data + (Byte & $7F) << 1))
EndIf
Next i
ProcedureReturn Result$
EndProcedure
Procedure.s ExternalProgram_CP850ToUnicode(*Buffer, Length.i)
DataSection
ExternalProgram_CP850Data:
Data.u $00c7, $00fc, $00e9, $00e2, $00e4, $00e0, $00e5, $00e7
Data.u $00ea, $00eb, $00e8, $00ef, $00ee, $00ec, $00c4, $00c5
Data.u $00c9, $00e6, $00c6, $00f4, $00f6, $00f2, $00fb, $00f9
Data.u $00ff, $00d6, $00dc, $00f8, $00a3, $00d8, $00d7, $0192
Data.u $00e1, $00ed, $00f3, $00fa, $00f1, $00d1, $00aa, $00ba
Data.u $00bf, $00ae, $00ac, $00bd, $00bc, $00a1, $00ab, $00bb
Data.u $2591, $2592, $2593, $2502, $2524, $00c1, $00c2, $00c0
Data.u $00a9, $2563, $2551, $2557, $255d, $00a2, $00a5, $2510
Data.u $2514, $2534, $252c, $251c, $2500, $253c, $00e3, $00c3
Data.u $255a, $2554, $2569, $2566, $2560, $2550, $256c, $00a4
Data.u $00f0, $00d0, $00ca, $00cb, $00c8, $0131, $00cd, $00ce
Data.u $00cf, $2518, $250c, $2588, $2584, $00a6, $00cc, $2580
Data.u $00d3, $00df, $00d4, $00d2, $00f5, $00d5, $00b5, $00fe
Data.u $00de, $00da, $00db, $00d9, $00fd, $00dd, $00af, $00b4
Data.u $00ad, $00b1, $2017, $00be, $00b6, $00a7, $00f7, $00b8
Data.u $00b0, $00a8, $00b7, $00b9, $00b3, $00b2, $25a0, $00a0
EndDataSection
Protected Result$, i.i, Byte.a
Length - 1
For i = 0 To Length
Byte = PeekA(*Buffer + i)
If Byte < $80
Result$ + Chr(Byte)
Else
Result$ + Chr(PeekU(?ExternalProgram_CP850Data + (Byte & $7F) << 1))
EndIf
Next i
ProcedureReturn Result$
EndProcedure
Procedure.s ExternalProgram_CP852ToUnicode(*Buffer, Length.i)
DataSection
ExternalProgram_CP852Data:
Data.u $00C7, $00FC, $00E9, $00E2, $00E4, $016F, $0107, $00E7
Data.u $0142, $00EB, $0150, $0151, $00EE, $0179, $00C4, $0106
Data.u $00C9, $0139, $013A, $00F4, $00F6, $013D, $013E, $015A
Data.u $015B, $00D6, $00DC, $0164, $0165, $0141, $00D7, $010D
Data.u $00E1, $00ED, $00F3, $00FA, $0104, $0105, $017D, $017E
Data.u $0118, $0119, $00AC, $017A, $010C, $015F, $00AB, $00BB
Data.u $2591, $2592, $2593, $2502, $2524, $00C1, $00C2, $011A
Data.u $015E, $2563, $2551, $2557, $255D, $017B, $017C, $2510
Data.u $2514, $2534, $252C, $251C, $2500, $253C, $0102, $0103
Data.u $255A, $2554, $2569, $2566, $2560, $2550, $256C, $00A4
Data.u $0111, $0110, $010E, $00CB, $010F, $0147, $00CD, $00CE
Data.u $011B, $2518, $250C, $2588, $2584, $0162, $016E, $2580
Data.u $00D3, $00DF, $00D4, $0143, $0144, $0148, $0160, $0161
Data.u $0154, $00DA, $0155, $0170, $00FD, $00DD, $0163, $00B4
Data.u $00AD, $02DD, $02DB, $02C7, $02D8, $00A7, $00F7, $00B8
Data.u $00B0, $00A8, $02D9, $0171, $0158, $0159, $25A0, $00A0
EndDataSection
Protected Result$, i.i, Byte.a
Length - 1
For i = 0 To Length
Byte = PeekA(*Buffer + i)
If Byte < $80
Result$ + Chr(Byte)
Else
Result$ + Chr(PeekU(?ExternalProgram_CP852Data + (Byte & $7F) << 1))
EndIf
Next i
ProcedureReturn Result$
EndProcedure
Procedure.s ExternalProgram_CP863ToUnicode(*Buffer, Length.i)
DataSection
ExternalProgram_CP863Data:
Data.u $00c7, $00fc, $00e9, $00e2, $00c2, $00e0, $00b6, $00e7
Data.u $00ea, $00eb, $00e8, $00ef, $00ee, $2017, $00c0, $00a7
Data.u $00c9, $00c8, $00ca, $00f4, $00cb, $00cf, $00fb, $00f9
Data.u $00a4, $00d4, $00dc, $00a2, $00a3, $00d9, $00db, $0192
Data.u $00a6, $00b4, $00f3, $00fa, $00a8, $00b8, $00b3, $00af
Data.u $00ce, $2310, $00ac, $00bd, $00bc, $00be, $00ab, $00bb
Data.u $2591, $2592, $2593, $2502, $2524, $2561, $2562, $2556
Data.u $2555, $2563, $2551, $2557, $255d, $255c, $255b, $2510
Data.u $2514, $2534, $252c, $251c, $2500, $253c, $255e, $255f
Data.u $255a, $2554, $2569, $2566, $2560, $2550, $256c, $2567
Data.u $2568, $2564, $2565, $2559, $2558, $2552, $2553, $256b
Data.u $256a, $2518, $250c, $2588, $2584, $258c, $2590, $2580
Data.u $03b1, $00df, $0393, $03c0, $03a3, $03c3, $00b5, $03c4
Data.u $03a6, $0398, $03a9, $03b4, $221e, $03c6, $03b5, $2229
Data.u $2261, $00b1, $2265, $2264, $2320, $2321, $00f7, $2248
Data.u $00b0, $2219, $00b7, $221a, $207f, $00b2, $25a0, $00a0
EndDataSection
Protected Result$, i.i, Byte.a
Length - 1
For i = 0 To Length
Byte = PeekA(*Buffer + i)
If Byte < $80
Result$ + Chr(Byte)
Else
Result$ + Chr(PeekU(?ExternalProgram_CP863Data + (Byte & $7F) << 1))
EndIf
Next i
ProcedureReturn Result$
EndProcedure
Procedure.s ExternalProgram_CP1252ToUnicode(*Buffer, Length.i)
DataSection
ExternalProgram_CP1252Data:
Data.u $20AC, $0000, $201A, $0192, $201E, $2026, $2020, $2021
Data.u $02C6, $2030, $0160, $2039, $0152, $0000, $017D, $0000
Data.u $0000, $2018, $2019, $201C, $201D, $2022, $2013, $2014
Data.u $02DC, $2122, $0161, $203A, $0153, $0000, $017E, $0178
EndDataSection
Protected Result$, i.i, Byte.a
Length - 1
For i = 0 To Length
Byte = PeekA(*Buffer + i)
If Byte < $80 Or Byte > $9F
Result$ + Chr(Byte)
Else
Result$ + Chr(PeekU(?ExternalProgram_CP1252Data + (Byte & $7F) << 1))
EndIf
Next i
ProcedureReturn Result$
EndProcedure
Procedure ExternalProgram_Thread(*Parameter.ExternalProgram_ParameterStructure)
Protected *Buffer, ReadLen.i, WriteLen.i, Error$, Timeout.i, StringMode.i, PeekStringMode.i
*Buffer = AllocateMemory(1024, #PB_Memory_NoClear)
If *Buffer
Select *Parameter\ProgramReadWriteMode
Case #PB_Program_Ascii
StringMode = #PB_Ascii
PeekStringMode = #PB_Ascii
Case #PB_Program_Unicode
StringMode = #PB_Unicode
PeekStringMode = #PB_Unicode
Case #PB_Program_UTF8
StringMode = #PB_UTF8
PeekStringMode = #PB_UTF8|#PB_ByteLength
EndSelect
*Parameter\ProgramID = RunProgram(*Parameter\Program$, *Parameter\ProgramParameter$, *Parameter\ProgramWorkingDirectory$, #PB_Program_Open|#PB_Program_Read|#PB_Program_Write|#PB_Program_Error|#PB_Program_Hide|*Parameter\ProgramReadWriteMode)
If *Parameter\ProgramID
Repeat
While AvailableProgramOutput(*Parameter\ProgramID)
ReadLen = AvailableProgramOutput(*Parameter\ProgramID)
If ReadLen > MemorySize(*Buffer)
*Buffer = ReAllocateMemory(*Buffer, ReadLen, #PB_Memory_NoClear)
EndIf
ReadLen = ReadProgramData(*Parameter\ProgramID, *Buffer, ReadLen)
If ReadLen
If *Parameter\CodePageConverter
*Parameter\StdOut$ = *Parameter\CodePageConverter(*Buffer, ReadLen)
Else
*Parameter\StdOut$ = PeekS(*Buffer, ReadLen, PeekStringMode)
EndIf
PostEvent(#ExternalProgram_Event_StdOut)
WaitSemaphore(*Parameter\Semaphore)
EndIf
Wend
Error$ = ReadProgramError(*Parameter\ProgramID, StringMode)
If Len(Error$)
*Parameter\StdErr$ = Error$
PostEvent(#ExternalProgram_Event_Error)
WaitSemaphore(*Parameter\Semaphore)
EndIf
If TryLockMutex(*Parameter\Mutex)
If Len(*Parameter\StdIn$)
*Parameter\StdIn$ + #CRLF$
WriteLen = PokeS(*Buffer, *Parameter\StdIn$, -1, StringMode|#PB_String_NoZero)
*Parameter\StdIn$ = ""
SignalSemaphore(*Parameter\Semaphore)
UnlockMutex(*Parameter\Mutex)
WriteLen = WriteProgramData(*Parameter\ProgramID, *Buffer, WriteLen)
Delay(50)
Else
UnlockMutex(*Parameter\Mutex)
EndIf
EndIf
Delay(10)
Until *Parameter\Exit Or Not ProgramRunning(*Parameter\ProgramID)
If *Parameter\Exit
If ProgramRunning(*Parameter\ProgramID)
If Len(*Parameter\ProgramExit$)
*Parameter\ProgramExit$ + #CRLF$
WriteLen = PokeS(*Buffer, *Parameter\ProgramExit$, -1, StringMode|#PB_String_NoZero)
WriteProgramData(*Parameter\ProgramID, *Buffer, WriteLen)
Else
WriteProgramData(*Parameter\ProgramID, #PB_Program_Eof, 0)
EndIf
Timeout = 300
Repeat
Delay(10)
Timeout - 1
While AvailableProgramOutput(*Parameter\ProgramID)
ReadProgramData(*Parameter\ProgramID, *Buffer, 1)
Wend
Until Timeout = 0 Or Not ProgramRunning(*Parameter\ProgramID)
If Timeout = 0
Debug "Kill"
KillProgram(*Parameter\ProgramID)
EndIf
EndIf
EndIf
CloseProgram(*Parameter\ProgramID)
EndIf
FreeMemory(*Buffer)
EndIf
PostEvent(#ExternalProgram_Event_Exit)
EndProcedure
;- Example
CompilerIf #PB_Compiler_IsMainFile
Enumeration
#MainWindow
#EditorGadget
#StringGadget
#ButtonGadget
EndEnumeration
Define.i Event, Exit, i, j, LastLine
Define Help$
Define ThreadParameter.ExternalProgram_ParameterStructure
NewList CommandList$()
LoadFont(0, "Consolas", 10)
OpenWindow(#MainWindow, 0, 0, 600, 500, "Remote console", #PB_Window_SystemMenu|#PB_Window_SizeGadget|#PB_Window_ScreenCentered)
WindowBounds(#MainWindow, 600, 500, #PB_Ignore, #PB_Ignore)
EditorGadget(#EditorGadget, 10, 10, 580, 410)
SetGadgetFont(#EditorGadget, FontID(0))
StringGadget(#StringGadget, 10, 430, 580, 20, "")
ButtonGadget(#ButtonGadget, 200, 460, 200, 30, "Send to console")
ThreadParameter\Program$ = "cmd.exe"
ThreadParameter\ProgramReadWriteMode = #PB_Program_Ascii
ThreadParameter\ProgramExit$ = "exit"
ThreadParameter\CodePageConverter = @ExternalProgram_CP850ToUnicode()
ThreadParameter\Semaphore = CreateSemaphore()
ThreadParameter\Mutex = CreateMutex()
ThreadParameter\Thread = CreateThread(@ExternalProgram_Thread(), @ThreadParameter)
AddElement(CommandList$())
CommandList$() = "help"
AddElement(CommandList$())
CommandList$() = "dir"
ResetList(CommandList$())
Repeat
Event = WaitWindowEvent()
Select Event
Case #ExternalProgram_Event_StdOut
Help$ = ThreadParameter\StdOut$
SignalSemaphore(ThreadParameter\Semaphore)
If Right(Help$, 1) = ">"
If NextElement(CommandList$())
LockMutex(ThreadParameter\Mutex)
ThreadParameter\StdIn$ = CommandList$()
UnlockMutex(ThreadParameter\Mutex)
WaitSemaphore(ThreadParameter\Semaphore)
EndIf
EndIf
Help$ = RemoveString(Help$, #CR$)
Help$ = RTrim(Help$, #LF$)
i = CountString(Help$, #LF$)
For j = 0 To i
If j = 0
LastLine = CountGadgetItems(#EditorGadget) - 1
SetGadgetItemText(#EditorGadget, LastLine, GetGadgetItemText(#EditorGadget, LastLine) + StringField(Help$, j + 1, #LF$))
Else
AddGadgetItem(#EditorGadget, -1, StringField(Help$, j + 1, #LF$))
EndIf
Next j
Case #ExternalProgram_Event_Error
Debug ThreadParameter\StdErr$
SignalSemaphore(ThreadParameter\Semaphore)
Case #ExternalProgram_Event_Exit
Exit = #True
Case #PB_Event_Gadget
Select EventGadget()
Case #ButtonGadget
LockMutex(ThreadParameter\Mutex)
ThreadParameter\StdIn$ = GetGadgetText(#StringGadget)
UnlockMutex(ThreadParameter\Mutex)
WaitSemaphore(ThreadParameter\Semaphore)
EndSelect
Case #PB_Event_SizeWindow
ResizeGadget(#ButtonGadget, WindowWidth(#MainWindow) / 2 - 100, WindowHeight(#MainWindow) - 40, #PB_Ignore, #PB_Ignore)
ResizeGadget(#StringGadget, #PB_Ignore, WindowHeight(#MainWindow) - 70, WindowWidth(#MainWindow) - 20, #PB_Ignore)
ResizeGadget(#EditorGadget, #PB_Ignore, #PB_Ignore, WindowWidth(#MainWindow) - 20, WindowHeight(#MainWindow) - 90)
Case #PB_Event_CloseWindow
If IsThread(ThreadParameter\Thread)
ThreadParameter\Exit = #True
WaitThread(ThreadParameter\Thread)
EndIf
FreeMutex(ThreadParameter\Mutex)
FreeSemaphore(ThreadParameter\Semaphore)
Exit = #True
EndSelect
Until Exit
CompilerEndIf
Maybe it is useful for others too.
Bernd