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, 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
)
read_handle:=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
)
write_handle:=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",read_handle
,"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", write_handle, SI, P8 ? 88 : 60) ; hStdOutput
, NumPut("Ptr", write_handle, 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",write_handle) ;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.read_handle:=read_handle
}
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
)
}
stopAndGetStr() {
bool:=DllCall("CloseHandle", "Ptr",this.g_hChildStd_IN_Wr)
read_handle:=this.read_handle
overlapped:=Buffer(32, 0)
bump_amt:=512
finalStr:=""
outer1:
while (true) {
while (true) {
next_buf:=Buffer(bump_amt)
bool:=DllCall("ReadFile"
,"Ptr",read_handle
,"Ptr",next_buf
,"Uint",next_buf.Size
,"Uint*",&read_bytes:=0
,"Ptr",overlapped
)
if (bool == 1) {
bump_amt+=read_bytes
finalStr.=StrGet(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
default:
MsgBox "h89fh2398h4`nA_LastError: " A_LastError
}
}
}
; status:=DllCall("WaitForSingleObject"
; ,"Ptr",read_handle
; ,"Uint",4294967295 ;INFINITE ;If dwMilliseconds is INFINITE, the function will return only when the object is signaled.
; )
while (true) { ;this is faster but maybe more expensive???
status:=DllCall("WaitForSingleObject"
,"Ptr",read_handle
,"Uint",0
)
; ToolTip status
if (status == 258) {
DllCall("Sleep", "Uint",0)
continue
} else if (status == 0) {
break
} else {
MsgBox "no way: " status
}
}
bool:=DllCall("GetOverlappedResult"
,"Ptr",read_handle
,"Ptr",overlapped
,"Uint*", &Overlapped_read_bytes:=0
,"Int",0
)
bump_amt+=Overlapped_read_bytes
finalStr.=StrGet(next_buf,Overlapped_read_bytes,"UTF-8")
}
return finalStr
}
}
pythonProcess := ChildProcess("python")
pythonProcess.writeToStdIn("print(2**20)`n")
sleep 500
; this is "continuously write stdin"
pythonProcess.writeToStdIn("print(2**20)")
MsgBox pythonProcess.stopAndGetStr()
; if you want to read stdout in real time then I'd have to think of something else