
StdoutToVar

For 64 bit, I tried to change variable types and recalculated the struct positions, but the ReadFile function (step 11) still fails. Anybody has an idea what's wrong?
Script I use for testing:
sOutput:=StdoutToVar_CreateProcess(A_WinDir "\system32\ipconfig.exe") Exitapp StdoutToVar_CreateProcess(sCmd) { Success:=DllCall("CreatePipe", "Ptr*", hStdInRd , "Ptr*", hStdInWr , "Uint", 0 , "Uint", 0) if Success=0 { msgbox,Step 1 (CreatePipe)`nSuccess=%Success% Return } Success:=DllCall("CreatePipe", "Ptr*", hStdOutRd , "Ptr*", hStdOutWr , "Uint", 0 , "Uint", 0) if Success=0 { msgbox,Step 2 (CreatePipe)`nSuccess=%Success% Return } Success:=DllCall("SetHandleInformation", "Ptr", hStdInRd , "Uint", 1 , "Uint", 1) if Success=0 { msgbox,Step 3 (SetHandleInformation)`nSuccess=%Success% Return } Success:=DllCall("SetHandleInformation", "Ptr", hStdOutWr , "Uint", 1 , "Uint", 1) if Success=0 { msgbox,Step 4 (SetHandleInformation)`nSuccess=%Success% Return } VarSetCapacity(pi, 24, 0) NumPut(VarSetCapacity(si,96,0), si, 0, "UInt") NumPut(0x100 , si, 56, "UInt") ; DWORD Flags NumPut(hStdInRd , si, 72, "UPtr") ; 64bit HANDLE StdInput NumPut(hStdOutWr, si, 80, "UPtr") ; 64bit HANDLE StdOutput NumPut(hStdOutWr, si, 88, "UPtr") ; 64bit HANDLE StdError Success:=DllCall("CreateProcess", "Uint", 0 , "Ptr", &sCmd , "Uint", 0 , "Uint", 0 , "int", True , "Uint", 0x08000000 , "Uint", 0 , "Uint", 0 , "UPtr", &si , "UPtr", &pi) if Success=0 { msgbox,Step 5 (CreateProcess)`nSuccess=%Success% Return } Success:=DllCall("CloseHandle","UPtr",NumGet(pi,0,"UPtr")) if Success=0 { msgbox,Step 6 (CloseHandle)`nSuccess=%Success% Return } Success:=DllCall("CloseHandle","UPtr",NumGet(pi,8,"UPtr")) if Success=0 { msgbox,Step 7 (CloseHandle)`nSuccess=%Success% Return } Success:=DllCall("CloseHandle","UPtr",hStdOutWr) if Success=0 { msgbox,Step 8 (CloseHandle)`nSuccess=%Success% Return } Success:=DllCall("CloseHandle","UPtr",hStdInRd) if Success=0 { msgbox,Step 9 (CloseHandle)`nSuccess=%Success% Return } Success:=DllCall("CloseHandle","UPtr",hStdInWr) if Success=0 { msgbox,Step 10 (CloseHandle)`nSuccess=%Success% Return } VarSetCapacity(sTemp,4095) nSize:=0 Success:=DllCall("ReadFile", "Ptr*", hStdOutRd , "Uint", &sTemp , "Uint", 4095 , "UintP", nSize , "Uint", 0) If Success { msgbox,Step 11`nSuccess=%Success% NumPut(0,sTemp,nSize,"Uchar") VarSetCapacity(sTemp,-1) sOutput:=StrGet(&sTemp,nSize,"") msgbox,%sOutput% } else msgbox,Step 11 (ReadFile) failed Success:=DllCall("CloseHandle", "Ptr", hStdOutRd) if Success=0 { msgbox,Step 12 (CloseHandle)`nSuccess=%Success% Return } Return sOutput }I assumed 64 bit STARTUPINFO struct would look like this:
0 DWORD cb;------------------- 4 LPTSTR lpReserved; 8 Bytes 12 LPTSTR lpDesktop; 8 Bytes 20 LPTSTR lpTitle; 8 Bytes 28 DWORD dwX; 32 DWORD dwY; 36 DWORD dwXSize; 40 DWORD dwYSize; 44 DWORD dwXCountChars; 48 DWORD dwYCountChars; 52 DWORD dwFillAttribute; 56 DWORD dwFlags;-------------- 60 WORD wShowWindow; 62 WORD cbReserved2; 64 LPBYTE lpReserved2; Lenght 8 bytes? 72 HANDLE hStdInput;------------8 Bytes 80 HANDLE hStdOutput;-----------8 Bytes 88 HANDLE hStdError;------------8 Bytes 96and PROCESS_INFORMATION struct like this:
0 HANDLE hProcess; 8 Bytes 8 HANDLE hThread; 8 Bytes 16 DWORD dwProcessId; 20 DWORD dwThreadId; 24

STARTUPINFO: [color=brown] 0 DWORD cb; 4 (padding)[/color] 8 LPTSTR lpReserved; [color=brown]16 LPTSTR lpDesktop;[/color] 24 LPTSTR lpTitle; [color=brown]32 DWORD dwX; 36 DWORD dwY;[/color] 40 DWORD dwXSize; 44 DWORD dwYSize; [color=brown]48 DWORD dwXCountChars; 52 DWORD dwYCountChars; [/color] 56 DWORD dwFillAttribute; 60 DWORD dwFlags; [color=brown]64 WORD wShowWindow; 66 WORD cbReserved2; 68 (padding)[/color] 72 LPBYTE lpReserved2; [color=brown]80 HANDLE hStdInput;[/color] 88 HANDLE hStdOutput; [color=brown]96 HANDLE hStdError;[/color] 104

I now have a partial version of StdoutToVar_CreateProcess that seems to work well both in x32 and x64 unicode AhkL. The bStream, sDir and sInput parameters are disabled though, since I didn't include the parts of Sean's script that I didn't understand too well or need right away.
I'm not at all sure if the Pointers in CreateProcess and ReadFile are ok the way they are of should remain UInt, but it seems to work alright.
StdoutToVar_CreateProcess(sCmd, bStream="", sDir="", sInput="") { bStream= ; not implemented sDir= ; not implemented sInput= ; not implemented DllCall("CreatePipe","Ptr*",hStdInRd ,"Ptr*",hStdInWr ,"Uint",0 ,"Uint",0) DllCall("CreatePipe","Ptr*",hStdOutRd ,"Ptr*",hStdOutWr ,"Uint",0 ,"Uint",0) DllCall("SetHandleInformation","Ptr",hStdInRd ,"Uint",1 ,"Uint",1) DllCall("SetHandleInformation","Ptr",hStdOutWr ,"Uint",1 ,"Uint",1) if A_PtrSize=4 { VarSetCapacity(pi, 16, 0) sisize:=VarSetCapacity(si,68,0) NumPut(sisize, si, 0, "UInt") NumPut(0x100, si, 44, "UInt") NumPut(hStdInRd , si, 56, "Ptr") NumPut(hStdOutWr, si, 60, "Ptr") NumPut(hStdOutWr, si, 64, "Ptr") } else if A_PtrSize=8 { VarSetCapacity(pi, 24, 0) sisize:=VarSetCapacity(si,96,0) NumPut(sisize, si, 0, "UInt") NumPut(0x100, si, 60, "UInt") NumPut(hStdInRd , si, 80, "Ptr") NumPut(hStdOutWr, si, 88, "Ptr") NumPut(hStdOutWr, si, 96, "Ptr") } DllCall("CreateProcess", "Uint", 0 , "Ptr", &sCmd , "Uint", 0 , "Uint", 0 , "Int", True , "Uint", 0x08000000 , "Uint", 0 , "Uint", 0 , "Ptr", &si , "Ptr", &pi) DllCall("CloseHandle","Ptr",NumGet(pi,0)) DllCall("CloseHandle","Ptr",NumGet(pi,A_PtrSize)) DllCall("CloseHandle","Ptr",hStdOutWr) DllCall("CloseHandle","Ptr",hStdInRd) DllCall("CloseHandle","Ptr",hStdInWr) VarSetCapacity(sTemp,4095) nSize:=0 loop { result:=DllCall("Kernel32.dll\ReadFile", "Uint", hStdOutRd , "Ptr", &sTemp , "Uint", 4095 ,"UintP", nSize ,"Uint", 0) if (result="0") break else sOutput:= sOutput . StrGet(&sTemp,nSize,"CP850") } DllCall("CloseHandle","Ptr",hStdOutRd) Return,sOutput }

Hey Sean if you are still watching.
I modified function to support ExitCode (via ErrorLevel as usual) and made some trivial changes (changed func name and slightly param names and order so its more similar to Run command)
I also made formal documentation.
I will prolly add some more features when time comes to it, like injection of environment variables, and option not to block script (like Run command does).
Cheers and thx again for this awesomeness.
Function
Documentation
This appears to be the best version of calling an external program and trapping the output into a variable.
This function should be included in AHKL.

StdoutToVar_CreateProcess(sCmd, bStream="", sDir="", sInput="") { bStream= ; not implemented DllCall("CreatePipe","Ptr*",hStdInRd,"Ptr*",hStdInWr,"Uint",0,"Uint",0) DllCall("CreatePipe","Ptr*",hStdOutRd,"Ptr*",hStdOutWr,"Uint",0,"Uint",0) DllCall("SetHandleInformation","Ptr",hStdInRd,"Uint",1,"Uint",1) DllCall("SetHandleInformation","Ptr",hStdOutWr,"Uint",1,"Uint",1) if A_PtrSize=4 { VarSetCapacity(pi, 16, 0) sisize:=VarSetCapacity(si,68,0) NumPut(sisize, si, 0, "UInt") NumPut(0x100, si, 44, "UInt") NumPut(hStdInRd , si, 56, "Ptr") NumPut(hStdOutWr, si, 60, "Ptr") NumPut(hStdOutWr, si, 64, "Ptr") } else if A_PtrSize=8 { VarSetCapacity(pi, 24, 0) sisize:=VarSetCapacity(si,96,0) NumPut(sisize, si, 0, "UInt") NumPut(0x100, si, 60, "UInt") NumPut(hStdInRd , si, 80, "Ptr") NumPut(hStdOutWr, si, 88, "Ptr") NumPut(hStdOutWr, si, 96, "Ptr") } DllCall("CreateProcess", "Uint", 0, "Ptr", &sCmd, "Uint", 0, "Uint", 0, "Int", True, "Uint", 0x08000000, "Uint", 0, "Ptr", sDir ? &sDir : 0, "Ptr", &si, "Ptr", &pi) DllCall("CloseHandle","Ptr",NumGet(pi,0)) DllCall("CloseHandle","Ptr",NumGet(pi,A_PtrSize)) DllCall("CloseHandle","Ptr",hStdOutWr) DllCall("CloseHandle","Ptr",hStdInRd) If sInput <> FileOpen(hStdInWr, "h", "UTF-8").Write(sInput) DllCall("CloseHandle","Ptr",hStdInWr) VarSetCapacity(sTemp,4095) nSize:=0 loop { result:=DllCall("Kernel32.dll\ReadFile", "Uint", hStdOutRd, "Ptr", &sTemp, "Uint", 4095,"UintP", nSize,"Uint", 0) if (result="0") break else sOutput:= sOutput . StrGet(&sTemp,nSize,"UTF-8") } DllCall("CloseHandle","Ptr",hStdOutRd) Return,sOutput }

Lexikos, thank you very much for your help.
I now have a partial version of StdoutToVar_CreateProcess that seems to work well both in x32 and x64 unicode AhkL. The bStream, sDir and sInput parameters are disabled though, since I didn't include the parts of Sean's script that I didn't understand too well or need right away.
I'm not at all sure if the Pointers in CreateProcess and ReadFile are ok the way they are of should remain UInt, but it seems to work alright.StdoutToVar_CreateProcess(sCmd, bStream="", sDir="", sInput="") { bStream= ; not implemented sDir= ; not implemented sInput= ; not implemented DllCall("CreatePipe","Ptr*",hStdInRd ,"Ptr*",hStdInWr ,"Uint",0 ,"Uint",0) DllCall("CreatePipe","Ptr*",hStdOutRd ,"Ptr*",hStdOutWr ,"Uint",0 ,"Uint",0) DllCall("SetHandleInformation","Ptr",hStdInRd ,"Uint",1 ,"Uint",1) DllCall("SetHandleInformation","Ptr",hStdOutWr ,"Uint",1 ,"Uint",1) if A_PtrSize=4 { VarSetCapacity(pi, 16, 0) sisize:=VarSetCapacity(si,68,0) NumPut(sisize, si, 0, "UInt") NumPut(0x100, si, 44, "UInt") NumPut(hStdInRd , si, 56, "Ptr") NumPut(hStdOutWr, si, 60, "Ptr") NumPut(hStdOutWr, si, 64, "Ptr") } else if A_PtrSize=8 { VarSetCapacity(pi, 24, 0) sisize:=VarSetCapacity(si,96,0) NumPut(sisize, si, 0, "UInt") NumPut(0x100, si, 60, "UInt") NumPut(hStdInRd , si, 80, "Ptr") NumPut(hStdOutWr, si, 88, "Ptr") NumPut(hStdOutWr, si, 96, "Ptr") } DllCall("CreateProcess", "Uint", 0 , "Ptr", &sCmd , "Uint", 0 , "Uint", 0 , "Int", True , "Uint", 0x08000000 , "Uint", 0 , "Uint", 0 , "Ptr", &si , "Ptr", &pi) DllCall("CloseHandle","Ptr",NumGet(pi,0)) DllCall("CloseHandle","Ptr",NumGet(pi,A_PtrSize)) DllCall("CloseHandle","Ptr",hStdOutWr) DllCall("CloseHandle","Ptr",hStdInRd) DllCall("CloseHandle","Ptr",hStdInWr) VarSetCapacity(sTemp,4095) nSize:=0 loop { result:=DllCall("Kernel32.dll\ReadFile", "Uint", hStdOutRd , "Ptr", &sTemp , "Uint", 4095 ,"UintP", nSize ,"Uint", 0) if (result="0") break else sOutput:= sOutput . StrGet(&sTemp,nSize,"CP850") } DllCall("CloseHandle","Ptr",hStdOutRd) Return,sOutput }
Thanks so much!
You just saved my time in doing the same thing, and it seems to work just fine so far.
I wish the original author could merge this into the first post.

When using the stream or true-flag, the last part of the stdout will not be streamed at all.
The text in Output is complete but the last part of the loop is not displayed.
This is only when there is no carriage return behind the last text. Ping works well, but I have a tool that does not create an addtional line...
What can I do here ? In my case the OutputDebug %sString% misses the last line.
I just have a workaround, to "stream" the output itself instead of the stream value...

Please Lexikos, considere to include this on main code (maybe AHK v2)
Its really a pitty that this jewel of software (StdoutToVar) is so bad supported and forgotten...
- Original Sean has not 64bits, nor utf-8
- Modified Sean has some "expect" aproach
- maraskan_user has 64 bits aproach
- nfl has utf-8 support but no bStream
- None of then has stdin support (I only find COM stuff)...
Please, I think connection with commandline apps its a main priority, and one of worse supported by ahk.
PS. Im harly use ahk and curl and I think together are one of the best tools ever...

I my case the program waits until the user has connected a device and forces him to press CTRL+C start an action.
I can stream the output text but I have no clue to send the CTRL+C to the waiting programm because there is no console window.
With console windows this no problem of course but I want to hide it completely.

StdoutToVar_CreateProcess(sCmd, bStream="", sDir="", sInput="") { bStream= ; not implemented sDir= ; not implemented sInput= ; not implemented DllCall("CreatePipe", "Ptr*", hStdInRd , "Ptr*", hStdInWr , "Uint", 0, "Uint", 0) DllCall("CreatePipe", "Ptr*", hStdOutRd, "Ptr*", hStdOutWr, "Uint", 0, "Uint", 0) DllCall("SetHandleInformation", "Ptr", hStdInRd , "Uint", 1, "Uint", 1) DllCall("SetHandleInformation", "Ptr", hStdOutWr, "Uint", 1, "Uint", 1) ; Fill a StartupInfo structure if A_PtrSize = 4 ; We're on a 32-bit system. { VarSetCapacity(pi, 16, 0) sisize := VarSetCapacity(si, 68, 0) NumPut(sisize, si, 0, "UInt") NumPut(0x100, si, 44, "UInt") NumPut(hStdInRd , si, 56, "Ptr") ; stdin NumPut(hStdOutWr, si, 60, "Ptr") ; stdout NumPut(hStdOutWr, si, 64, "Ptr") ; stderr } else if A_PtrSize = 8 ; We're on a 64-bit system. { VarSetCapacity(pi, 24, 0) sisize := VarSetCapacity(si, 96, 0) NumPut(sisize, si, 0, "UInt") NumPut(0x100, si, 60, "UInt") NumPut(hStdInRd , si, 80, "Ptr") ; stdin NumPut(hStdOutWr, si, 88, "Ptr") ; stdout NumPut(hStdOutWr, si, 96, "Ptr") ; stderr } DllCall("CreateProcess", "Uint", 0 ; Application Name , "Ptr", &sCmd ; Command Line , "Uint", 0 ; Process Attributes , "Uint", 0 ; Thread Attributes , "Int", True ; Inherit Handles , "Uint", 0x08000000 ; Creation Flags (0x08000000 = Suppress console window) , "Uint", 0 ; Environment , "Uint", 0 ; Current Directory , "Ptr", &si ; Startup Info , "Ptr", &pi) ; Process Information DllCall("CloseHandle", "Ptr", NumGet(pi, 0)) DllCall("CloseHandle", "Ptr", NumGet(pi, A_PtrSize)) DllCall("CloseHandle", "Ptr", hStdOutWr) DllCall("CloseHandle", "Ptr", hStdInRd) DllCall("CloseHandle", "Ptr", hStdInWr) VarSetCapacity(sTemp, 4095) nSize := 0 loop { result := DllCall("Kernel32.dll\ReadFile", "Uint", hStdOutRd, "Ptr", &sTemp, "Uint", 4095, "UintP", nSize, "Uint", 0) if (result = "0") break else sOutput := sOutput . StrGet(&sTemp, nSize, "CP850") } DllCall("CloseHandle", "Ptr", hStdOutRd) return, sOutput }Now I am trying to figure out how to reactivate stream support.
P.S.: Sean, your code looks like you were deliberately obfuscating it. There is no price for the most statements per line.


(anyone that can possibly help me plz see here: <!-- m -->http://www.autohotke... ... 753#513753<!-- m -->)

Your modified solution worked just fine on Unicode AHK_L x64

This is the example...
cli_stdout(chars="",codepage="") { global hStdOutRd if (codepage="") codepage:=A_FileEncoding fout:=FileOpen(hStdOutRd, "h", codepage) if (IsObject(fout) and fout.AtEOF=0) return fout.Read() return "" } cli_stdin(sInput="",codepage="") { global hStdInWr if (codepage="") codepage:=A_FileEncoding If sInput <> FileOpen(hStdInWr, "h", codepage).Write(sInput) } cli_close() { global hStdInWr, hStdOutRd DllCall("CloseHandle","Ptr",hStdInWr) DllCall("CloseHandle","Ptr",hStdOutRd) } cli_createprocess(sCmd, sDir="") { global hStdInWr, hStdOutRd DllCall("CreatePipe","Ptr*",hStdInRd,"Ptr*",hStdInWr,"Uint",0,"Uint",0) DllCall("CreatePipe","Ptr*",hStdOutRd,"Ptr*",hStdOutWr,"Uint",0,"Uint",0) DllCall("SetHandleInformation","Ptr",hStdInRd,"Uint",1,"Uint",1) DllCall("SetHandleInformation","Ptr",hStdOutWr,"Uint",1,"Uint",1) if A_PtrSize=4 { VarSetCapacity(pi, 16, 0) sisize:=VarSetCapacity(si,68,0) NumPut(sisize, si, 0, "UInt") NumPut(0x100, si, 44, "UInt") NumPut(hStdInRd , si, 56, "Ptr") NumPut(hStdOutWr, si, 60, "Ptr") NumPut(hStdOutWr, si, 64, "Ptr") } else if A_PtrSize=8 { VarSetCapacity(pi, 24, 0) sisize:=VarSetCapacity(si,96,0) NumPut(sisize, si, 0, "UInt") NumPut(0x100, si, 60, "UInt") NumPut(hStdInRd , si, 80, "Ptr") NumPut(hStdOutWr, si, 88, "Ptr") NumPut(hStdOutWr, si, 96, "Ptr") } DllCall("CreateProcess", "Uint", 0, "Ptr", &sCmd, "Uint", 0, "Uint", 0, "Int", True, "Uint", 0x08000000, "Uint", 0, "Ptr", sDir ? &sDir : 0, "Ptr", &si, "Ptr", &pi) DllCall("CloseHandle","Ptr",NumGet(pi,0)) DllCall("CloseHandle","Ptr",NumGet(pi,A_PtrSize)) DllCall("CloseHandle","Ptr",hStdOutWr) DllCall("CloseHandle","Ptr",hStdInRd) } FileEncoding, CP850 cli_createprocess("netsh.exe") sleep 300 cli_stdin("firewall`r`n") sleep 100 cli_stdin("show config`r`n") sleep 1000 out:=cli_stdout() msgbox,, FIREWALL CONFIGURATION:, %out% cli_stdin("bye`r`n") cli_close()
Now, we need:
- stderror support
- support for mĂșltlple commands... object oriented versions...
Please support this to get full cli support...
Thanks
