However I tried the asynchronous approach:
Code: Select all
MsgBox, % ReadStdOut("notepad",,, 3000)
ExitApp
ReadStdOut(cmd, encoding := "", callBack := "", timeout := 0xFFFFFFFF) {
(encoding = "" && encoding := "cp" . DllCall("GetOEMCP", "UInt"))
Output := new CmdStdOutAsync(cmd, encoding, callBack)
start := A_TickCount
while !Output.complete && A_TickCount - start < timeout
Sleep, 100
Sleep, 200
Return Output.complete ? Output.outData : "time is out"
}
class CmdStdOutAsync
{
__New(cmd, encoding, callBackFunc := "") {
UserFunc := IsObject(callBackFunc) ? callBackFunc : Func(callBackFunc)
this._OutData := []
this._complete := [false]
this.Event := new this._Event()
this.SetCapacity("buffer", 4096)
pBuffer := this.GetAddress("buffer")
this.SetCapacity("overlapped", A_PtrSize*3 + 8)
this.pOverlapped := this.GetAddress("overlapped")
params := [ pBuffer, this.pOverlapped, this.Event.handle
, this._OutData, encoding, this._complete, UserFunc ]
if !this.Process := new this._Process(cmd, params*) {
MsgBox, Failed to create process
Return false
}
this.EventSignal := new this._EventSignal(this.Process, params*)
this.Process.Read()
}
complete[] {
get {
Return this._complete[1]
}
}
outData[] {
get {
str := ""
for k, v in this._OutData
str .= v
Return str
}
}
__Delete() {
DllCall("CancelIoEx", "Ptr", this.Process.hPipeRead, "Ptr", this.pOverlapped)
this.Event.Set()
this.EventSignal.Clear()
this.Process.Clear()
this.SetCapacity("buffer", 0)
this.buffer := this._OutData := ""
}
class _Event {
__New() {
this.handle := DllCall("CreateEvent", "Int", 0, "Int", 0, "Int", 0, "Int", 0, "Ptr")
}
Set() {
DllCall("SetEvent", "Ptr", this.handle)
}
__Delete() {
DllCall("CloseHandle", "Ptr", this.handle)
}
}
class _Process {
__New(cmd, params*) {
for k, v in ["pBuffer", "pOverlapped", "hEvent", "Data", "encoding", "complete", "UserFunc"]
this[v] := params[k]
this.CreatePipes()
Return this.CreateProcess(cmd) ? this : false
}
Clear() {
DllCall("CloseHandle", "Ptr", this.hPipeRead)
( this.hPipeWrite && DllCall("CloseHandle", "Ptr", this.hPipeWrite) )
}
CreatePipes() {
static FILE_FLAG_OVERLAPPED := 0x40000000, PIPE_ACCESS_INBOUND := 0x1
, PIPE_TYPE_BYTE := 0, PIPE_WAIT := 0
, GENERIC_WRITE := 0x40000000, OPEN_EXISTING := 0x3
, FILE_ATTRIBUTE_NORMAL := 0x80, HANDLE_FLAG_INHERIT := 0x1
this.hPipeRead := DllCall("CreateNamedPipe", "Str", pipeName := "\\.\pipe\StdOut_" . A_TickCount
, "UInt", PIPE_ACCESS_INBOUND|FILE_FLAG_OVERLAPPED
, "UInt", PIPE_TYPE_BYTE|PIPE_WAIT, "UInt", 1
, "UInt", 4096, "UInt", 4096, "UInt", 120000, "Ptr", 0, "Ptr")
this.hPipeWrite := DllCall("CreateFile", "Str", pipeName, "UInt", GENERIC_WRITE, "UInt", 0, "Ptr", 0
, "UInt", OPEN_EXISTING, "UInt", FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED, "Ptr", 0, "Ptr")
DllCall("SetHandleInformation", "Ptr", this.hPipeWrite, "UInt", HANDLE_FLAG_INHERIT, "UInt", HANDLE_FLAG_INHERIT)
}
CreateProcess(cmd) {
static STARTF_USESTDHANDLES := 0x100, CREATE_NO_WINDOW := 0x8000000
VarSetCapacity(STARTUPINFO , siSize := A_PtrSize*9 + 4*8, 0)
NumPut(siSize , STARTUPINFO)
NumPut(STARTF_USESTDHANDLES, STARTUPINFO, A_PtrSize*4 + 4*7)
NumPut(this.hPipeWrite , STARTUPINFO, siSize - A_PtrSize*2)
NumPut(this.hPipeWrite , STARTUPINFO, siSize - A_PtrSize)
VarSetCapacity(PROCESS_INFORMATION, A_PtrSize*2 + 4*2, 0)
if !DllCall("CreateProcess", "Ptr", 0, "Str", cmd, "Ptr", 0, "Ptr", 0, "UInt", true, "UInt", CREATE_NO_WINDOW
, "Ptr", 0, "Ptr", 0, "Ptr", &STARTUPINFO, "Ptr", &PROCESS_INFORMATION)
Return this.Clear()
DllCall("CloseHandle", "Ptr", this.hPipeWrite), this.hPipeWrite := 0
Return true
}
Read() {
DllCall("RtlZeroMemory", "Ptr", this.pOverlapped, "Ptr", A_PtrSize*3 + 8)
NumPut(this.hEvent, this.pOverlapped + A_PtrSize*2 + 8)
bool := DllCall("ReadFile", "Ptr", this.hPipeRead, "Ptr", this.pBuffer, "UInt", 4096, "UIntP", size := 0, "Ptr", this.pOverlapped)
if bool {
this.Data.Push( str := StrGet(this.pBuffer, size, this.encoding) )
( this.UserFunc && this.UserFunc.Call(str) )
this.Read()
}
else if !bool && A_LastError != ERROR_IO_PENDING := 997
this.complete[1] := true
}
}
class _EventSignal {
__New(params*) {
for k, v in ["StdOut", "pBuffer", "pOverlapped", "hEvent", "Data", "encoding", "complete", "UserFunc"]
this[v] := params[k]
this.msg := DllCall("RegisterWindowMessage", "Str", "WM_EVENTSIGNAL")
OnMessage(this.msg, this.OnEvent := ObjBindMethod(this, "WM_EVENTSIGNAL"))
this.WaitFn := new this.WaitFunc(this.hEvent, A_ScriptHwnd, this.msg)
this.Thread := new this._Thread( this.WaitFn.startAddress )
}
Clear() {
this.Thread.Wait()
OnMessage(this.msg, this.OnEvent, 0)
this.WaitFn := this.OnEvent := ""
}
WM_EVENTSIGNAL(hEvent) {
if (this.hEvent != hEvent)
Return
if !DllCall("GetOverlappedResult", "Ptr", hEvent, "Ptr", this.pOverlapped, "UIntP", size := 0, "UInt", false)
Return this.complete[1] := true
this.Data.Push( str := StrGet(this.pBuffer, size, this.encoding) )
( this.UserFunc && this.UserFunc.Call(str) )
this.StdOut.Read()
this.Thread.Wait()
this.Thread := new this._Thread( this.WaitFn.startAddress )
}
class WaitFunc {
MEM_COMMIT := 0x1000, MEM_DECOMMIT := 0x4000, PAGE_EXECUTE_READWRITE := 0x40, size := A_PtrSize = 4 ? 49 : 85
__New(hEvent, hWnd, msg, timeout := -1) {
this.ptr := ptr := DllCall("VirtualAlloc", "Ptr", 0, "Ptr", this.size, "UInt", this.MEM_COMMIT
, "UInt", this.PAGE_EXECUTE_READWRITE, "Ptr")
for dll, api in {kernel32: "WaitForSingleObject", user32: "PostMessageW"} {
hModule := DllCall("GetModuleHandle", "Str", dll, "Ptr")
pFunc := DllCall("GetProcAddress" , "Ptr", hModule, "AStr", api, "Ptr")
NumPut(pFunc, ptr + A_PtrSize*(A_Index - 1))
}
if (A_PtrSize = 4) {
NumPut(0x68 , ptr + 8)
NumPut(timeout, ptr + 9) , NumPut(0x68 , ptr + 13)
NumPut(hEvent , ptr + 14) , NumPut(0x15FF, ptr + 18)
NumPut(ptr , ptr + 20) , NumPut(0x6850, ptr + 24)
NumPut(hEvent , ptr + 26) , NumPut(0x68 , ptr + 30)
NumPut(msg , ptr + 31) , NumPut(0x68 , ptr + 35)
NumPut(hWnd , ptr + 36) , NumPut(0x15FF, ptr + 40)
NumPut(ptr + 4, ptr + 42) , NumPut(0xC2 , ptr + 46, "UChar")
NumPut(4 , ptr + 47, "UShort")
}
else {
NumPut(0x53 , ptr + 16)
NumPut(0x20EC8348, ptr + 17) , NumPut(0xBACB8948, ptr + 21)
NumPut(timeout , ptr + 25) , NumPut(0xB948 , ptr + 29)
NumPut(hEvent , ptr + 31) , NumPut(0x15FF , ptr + 39)
NumPut(-45 , ptr + 41) , NumPut(0xB849 , ptr + 45)
NumPut(hEvent , ptr + 47) , NumPut(0xBA , ptr + 55)
NumPut(msg , ptr + 56) , NumPut(0xB948 , ptr + 60)
NumPut(hWnd , ptr + 62) , NumPut(0xC18941 , ptr + 70)
NumPut(0x15FF , ptr + 73) , NumPut(-71 , ptr + 75)
NumPut(0x20C48348, ptr + 79, "UInt"), NumPut(0xC35B , ptr + 83, "UShort")
}
this.startAddress := ptr + A_PtrSize*2
}
__Delete() {
DllCall("VirtualFree", "Ptr", this.ptr, "Ptr", this.size, "UInt", this.MEM_DECOMMIT)
}
}
class _Thread {
__New(startAddress) {
if !this.handle := DllCall("CreateThread", "Int", 0, "Int", 0, "Ptr", startAddress, "Int", 0, "UInt", 0, "Int", 0, "Ptr")
throw Exception("Failed to create thread.`nError code: " . A_LastError)
}
Wait() {
DllCall("WaitForSingleObject", "Ptr", this.handle, "Int", -1)
}
__Delete() {
DllCall("CloseHandle", "Ptr", this.handle)
}
}
}
}