william_ahk wrote: ↑23 Oct 2022, 04:07But yeah I do intend to read the stdout in real time too. My apologies for not describing it well. I'm trying to leverage the power of Python interactive console with AHK. Is there any other way? I probably have to resort to using ControlSend to cmd.exe
Code: Select all
#SingleInstance force
ListLines 0
KeyHistory 0
SendMode "Input" ; Recommended for new scripts due to its superior speed and reliability.
SetWorkingDir A_ScriptDir ; Ensures a consistent starting directory.
f3::Exitapp
StrBuf(str, encoding)
{
; Calculate required size and allocate a buffer.
buf := Buffer(StrPut(str, encoding))
; Copy or convert the string.
StrPut(str, buf, encoding)
return buf
}
class ChildProcess {
static counter:=0
__New(CmdLine, onStdOutCallback, includeStdErr:=false, WorkingDir:="") { ;from zig : std.ChildProcess.exec() : https://github.com/ziglang/zig/blob/4624c818991f161fc6a7021119e4d071b6e40e6c/lib/std/child_process.zig#L373
ChildProcess.counter++
pipe_path:="\\.\pipe\ahk-childprocess-" DllCall("GetCurrentProcessId") "-" ChildProcess.counter
saAttr:=Buffer(24)
NumPut("Uint",saAttr.Size,saAttr,0) ;nLength
NumPut("Ptr",0,saAttr,8) ;lpSecurityDescriptor
NumPut("Int",1,saAttr,16) ;bInheritHandle
;https://learn.microsoft.com/en-us/windows/win32/procthread/creating-a-child-process-with-redirected-input-and-output
DllCall("CreatePipe"
,"Ptr*",&g_hChildStd_IN_Rd:=0
,"Ptr*",&g_hChildStd_IN_Wr:=0
,"Ptr",saAttr
,"Uint",0
)
bool:=DllCall("SetHandleInformation"
,"Ptr",g_hChildStd_IN_Wr
,"Uint",1 ;HANDLE_FLAG_INHERIT
,"Uint",0
)
g_hChildStd_OUT_Rd:=DllCall("CreateNamedPipe"
,"Str",pipe_path
,"Uint",0x40000001 ;0x00000001 | 0x40000000 ;PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED
,"Uint",0 ;0x00000000 ;PIPE_TYPE_BYTE
,"Uint",1
,"Uint",4096
,"Uint",4096
,"Uint",0
,"Ptr",saAttr
)
g_hChildStd_OUT_Wr:=DllCall("CreateFile"
,"Str",pipe_path
,"Uint",0x40000000 ;GENERIC_WRITE
,"Uint",0
,"Ptr",saAttr
,"Uint",3 ;OPEN_EXISTING
,"Uint",0x80 ;FILE_ATTRIBUTE_NORMAL
,"Ptr",0
)
bool:=DllCall("SetHandleInformation"
,"Ptr",g_hChildStd_OUT_Rd
,"Uint",1 ;HANDLE_FLAG_INHERIT
,"Uint",0
)
P8 := (A_PtrSize==8)
, SI:=Buffer(P8 ? 104 : 68, 0) ; STARTUPINFO structure
, NumPut("UInt", P8 ? 104 : 68, SI) ; size of STARTUPINFO
, NumPut("UInt", STARTF_USESTDHANDLES:=0x100, SI, P8 ? 60 : 44) ; dwFlags
, NumPut("Ptr", g_hChildStd_IN_Rd, SI, P8 ? 80 : 56) ; hStdInput
, NumPut("Ptr", g_hChildStd_OUT_Wr, SI, P8 ? 88 : 60) ; hStdOutput
, (includeStdErr && NumPut("Ptr", g_hChildStd_OUT_Wr, SI, P8 ? 96 : 64)) ; hStdError
, PI:=Buffer(P8 ? 24 : 16) ; PROCESS_INFORMATION structure
bool:=DllCall("CreateProcess"
,"Ptr",0
,"Str",CmdLine
,"Ptr",0 ;lpProcessAttributes: *SECURITY_ATTRIBUTES
,"Int",0 ;lpThreadAttributes: *SECURITY_ATTRIBUTES
,"Int",True
,"Uint",0x08000400 ;dwCreationFlags: CREATE_NO_WINDOW=0x08000000, CREATE_UNICODE_ENVIRONMENT=0x00000400
,"Int",0 ;lpEnvironment
,"Ptr",WorkingDir ? StrPtr(WorkingDir) : 0
,"Ptr",SI
,"Ptr",PI
)
; bool:=DllCall("CloseHandle", "Ptr",NumGet(PI,0,"Ptr")) ;piProcInfo.hProcess
; bool:=DllCall("CloseHandle", "Ptr",NumGet(PI,P8 ? 8 : 4,"Ptr")) ;piProcInfo.hThread
bool:=DllCall("CloseHandle", "Ptr",g_hChildStd_OUT_Wr) ;THIS IS MUST, THIS IS IN FACT SO NECESSARY THAT WaitForSingleObject WILL ALWAYS HANG/BE WAITING
bool:=DllCall("CloseHandle", "Ptr",g_hChildStd_IN_Rd)
this.g_hChildStd_IN_Wr := g_hChildStd_IN_Wr
this.g_hChildStd_OUT_Rd:=g_hChildStd_OUT_Rd
this.onDataCallback := onDataCallback
this.overlapped:=Buffer(32, 0)
this.bump_amt:=512
while (true) {
this.next_buf:=Buffer(this.bump_amt)
bool:=DllCall("ReadFile"
,"Ptr",g_hChildStd_OUT_Rd
,"Ptr",this.next_buf
,"Uint",this.next_buf.Size
,"Uint*",&read_bytes:=0
,"Ptr",this.overlapped
)
if (bool == 1) {
this.bump_amt+=read_bytes
this.onDataCallback.Call(StrGet(this.next_buf,read_bytes,"UTF-8"))
} else {
switch (A_LastError) {
case 997: ;IO_PENDING
break
case 109: ;BROKEN_PIPE: The pipe has been ended.
; break outer1
return
default:
MsgBox "h89fh2398h4`nA_LastError: " A_LastError
}
}
}
this.checkForStr()
this.timer := this.checkForStr.Bind(this)
SetTimer this.timer, 10
}
checkForStr() {
status:=DllCall("WaitForSingleObject"
,"Ptr",this.g_hChildStd_OUT_Rd
,"Uint",0
)
if (status == 258) {
return
} else if (status == 0) {
bool:=DllCall("GetOverlappedResult"
,"Ptr",this.g_hChildStd_OUT_Rd
,"Ptr",this.overlapped
,"Uint*", &Overlapped_read_bytes:=0
,"Int",0
)
this.bump_amt+=Overlapped_read_bytes
this.onDataCallback.Call(StrGet(this.next_buf,Overlapped_read_bytes,"UTF-8"))
while (true) {
this.next_buf:=Buffer(this.bump_amt)
bool:=DllCall("ReadFile"
,"Ptr",this.g_hChildStd_OUT_Rd
,"Ptr",this.next_buf
,"Uint",this.next_buf.Size
,"Uint*",&read_bytes:=0
,"Ptr",this.overlapped
)
if (bool == 1) {
this.bump_amt+=read_bytes
this.onDataCallback.Call(StrGet(this.next_buf,read_bytes,"UTF-8"))
} else {
switch (A_LastError) {
case 997: ;IO_PENDING
break
case 109: ;BROKEN_PIPE: The pipe has been ended.
; break outer1
SetTimer this.timer, 0
return
default:
MsgBox "h89fh2398h4`nA_LastError: " A_LastError
}
}
}
} else {
MsgBox "no way: " status
}
}
noMoreStdIn() {
bool:=DllCall("CloseHandle", "Ptr",this.g_hChildStd_IN_Wr)
}
writeToStdIn(str) {
; Calculate required size and allocate a buffer.
wowBuf := Buffer(StrPut(str, "UTF-8"))
; Copy or convert the string.
StrPut(str, wowBuf, "UTF-8")
DllCall("WriteFile"
,"Ptr",this.g_hChildStd_IN_Wr
,"Ptr",wowBuf
,"Uint",wowBuf.Size - 1
,"Ptr*",&byteswritten:=0
,"Ptr",0
)
}
}
finalStr:=""
onDataCallback(str) {
global finalStr
finalStr.=str
Tooltip finalStr
}
; -i is what took me so much time to figure out because no one had it: everyone was running python -u on SCRIPTS.py, not on STDIN
pythonProcess := ChildProcess("python -u -i", onDataCallback)
pythonProcess.writeToStdIn("print(2**20)`n")
Sleep 500
pythonProcess.writeToStdIn("print(2**19)`n")
pythonProcess.noMoreStdIn()
Code: Select all
const child_process = require('child_process')
const pythonProcess = child_process.spawn('python')
pythonProcess.stdout.on('data', (data) => {
process.stdout.write(data.toString())
})
pythonProcess.stderr.on('data', (data) => {
process.stdout.write(data.toString())
})
pythonProcess.on('close', (code) => {
process.stdout.write(`child process exited with code ${code}`)
})
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
async function main() {
pythonProcess.stdin.write("print(2**20)\n")
await sleep(1000)
pythonProcess.stdin.write("print(2**19)\n")
// ONLY works when you end(), and it comes all at once, instead of in real time
pythonProcess.stdin.end()
}
main()
Code: Select all
const child_process = require('child_process')
const pythonProcess = child_process.spawn('python', ['-u', '-i'], {
stdio: 'overlapped',
})
pythonProcess.stderr.pipe(pythonProcess.stdout)
pythonProcess.stdout.on('data', (data) => {
process.stdout.write(data.toString())
})
if (true) { //you can comment this out to only get stdout
pythonProcess.stderr.on('data', (data) => {
process.stdout.write(data.toString())
})
}
pythonProcess.on('close', (code) => {
process.stdout.write(`child process exited with code ${code}`)
})
function sleep(ms) {
return new Promise(resolve => setTimeout(resolve, ms))
}
async function main() {
pythonProcess.stdin.write("print(2**20)\n")
await sleep(1000)
pythonProcess.stdin.write("print(2**19)\n")
// doesn't need end()
}
main()