[2.0-beta.1] RunTerminal

Post your working scripts, libraries and tools.
tranht17
Posts: 52
Joined: 03 May 2017, 02:55

[2.0-beta.1] RunTerminal

Post by tranht17 » 19 Aug 2021, 20:22

I converted to v2 from https://www.autohotkey.com/boards/viewtopic.php?t=74647

Code: Select all

RunTerminal(CmdLine, WorkingDir:="", Codepage:="CP0", Fn:="RunTerminal_Output") {  
  Global A_Args
  DllCall("CreatePipe", "PtrP",&hPipeR:=0, "PtrP",&hPipeW:=0, "Ptr",0, "Int",0)
, DllCall("SetHandleInformation", "Ptr",hPipeW, "Int",1, "Int",1)
, DllCall("SetNamedPipeHandleState","Ptr",hPipeR, "UIntP",&PIPE_NOWAIT:=1, "Ptr",0, "Ptr",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", hPipeW, SI, P8 ? 88 : 60)                              ; hStdOutput
, NumPut("Ptr", hPipeW, SI, P8 ? 96 : 64)                              ; hStdError
, PI:=Buffer(P8 ? 24 : 16)                              ; PROCESS_INFORMATION structure

  If not DllCall("CreateProcess", "Ptr",0, "Str",CmdLine, "Ptr",0, "Int",0, "Int",True
                ,"Int",0x08000000 | DllCall("GetPriorityClass", "Ptr",-1, "UInt"), "Int",0
                ,"Ptr",WorkingDir ? StrPtr(WorkingDir) : 0, "Ptr",SI.ptr, "Ptr",PI.ptr)  
     Return Format("{1:}", "", -1
                   ,DllCall("CloseHandle", "Ptr",hPipeW), DllCall("CloseHandle", "Ptr",hPipeR))

  DllCall("CloseHandle", "Ptr",hPipeW)
, A_Args.RunTerminal := { PID: NumGet(PI, P8 ? 16 : 8, "UInt") }
, File := FileOpen(hPipeR, "h", Codepage)

, LineNum := 1,  sOutput := ""
  While (A_Args.RunTerminal.PID + DllCall("Sleep", "Int",0))
    and DllCall("PeekNamedPipe", "Ptr",hPipeR, "Ptr",0, "Int",0, "Ptr",0, "Ptr",0, "Ptr",0)
        While A_Args.RunTerminal.PID and !File.AtEOF
			Line := File.ReadLine(), sOutput .= Type(Fn)="Func" ? Fn.Call(Line, LineNum++) : Line
  A_Args.RunTerminal.PID := 0
, hProcess := NumGet(PI, 0, "Ptr")
, hThread  := NumGet(PI, A_PtrSize, "Ptr")

, DllCall("CloseHandle", "Ptr",hProcess)
, DllCall("CloseHandle", "Ptr",hThread)
, DllCall("CloseHandle", "Ptr",hPipeR)

Return sOutput  
}
Example:

Code: Select all

MsgBox RunTerminal("ping autohotkey.com -n 1",,,pingHelper)

pingHelper(Line, LineNum) {
  If (LineNum=2)
    {
      A_Args.RunCMD.PID := 0 ; Cancel RunTerminal()
      Return StrSplit(Line, ["[","]"])[2]
    }  
}
This is a non-Global version for anyone who wants it.

Code: Select all

RunTerminal(CmdLine, WorkingDir:="", Codepage:="CP0", Fn:="RunTerminal_Output") {  
  DllCall("CreatePipe", "PtrP",&hPipeR:=0, "PtrP",&hPipeW:=0, "Ptr",0, "Int",0)
, DllCall("SetHandleInformation", "Ptr",hPipeW, "Int",1, "Int",1)
, DllCall("SetNamedPipeHandleState","Ptr",hPipeR, "UIntP",&PIPE_NOWAIT:=1, "Ptr",0, "Ptr",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", hPipeW, SI, P8 ? 88 : 60)                              ; hStdOutput
, NumPut("Ptr", hPipeW, SI, P8 ? 96 : 64)                              ; hStdError
, PI:=Buffer(P8 ? 24 : 16)                              ; PROCESS_INFORMATION structure

  If not DllCall("CreateProcess", "Ptr",0, "Str",CmdLine, "Ptr",0, "Int",0, "Int",True
                ,"Int",0x08000000 | DllCall("GetPriorityClass", "Ptr",-1, "UInt"), "Int",0
                ,"Ptr",WorkingDir ? StrPtr(WorkingDir) : 0, "Ptr",SI.ptr, "Ptr",PI.ptr)  
     Return Format("{1:}", "", -1
                   ,DllCall("CloseHandle", "Ptr",hPipeW), DllCall("CloseHandle", "Ptr",hPipeR))

  DllCall("CloseHandle", "Ptr",hPipeW)
,PID := NumGet(PI, P8 ? 16 : 8, "UInt")
, File := FileOpen(hPipeR, "h", Codepage)

, LineNum := 1,  sOutput := ""
  While (PID + DllCall("Sleep", "Int",0))
    and DllCall("PeekNamedPipe", "Ptr",hPipeR, "Ptr",0, "Int",0, "Ptr",0, "Ptr",0, "Ptr",0)
        While PID and !File.AtEOF
			Line := File.ReadLine(), sOutput .= Type(Fn)="Func" ? Fn.Call(Line, LineNum++,&PID) : Line	
  PID := 0
, hProcess := NumGet(PI, 0, "Ptr")
, hThread  := NumGet(PI, A_PtrSize, "Ptr")

, DllCall("CloseHandle", "Ptr",hProcess)
, DllCall("CloseHandle", "Ptr",hThread)
, DllCall("CloseHandle", "Ptr",hPipeR)

Return sOutput  
}
Example:

Code: Select all

MsgBox RunTerminal("ping autohotkey.com -n 1",,,pingHelper)

pingHelper(Line, LineNum, &PID) {
  If (LineNum=2)
    {
      PID := 0 ; Cancel RunTerminal()
      Return StrSplit(Line, ["[","]"])[2]
    }  
}
Last edited by tranht17 on 20 Aug 2021, 20:14, edited 5 times in total.

User avatar
kczx3
Posts: 1649
Joined: 06 Oct 2015, 21:39

Re: [2.0-beta.1] RunCMD

Post by kczx3 » 19 Aug 2021, 20:59

What is the purpose of A_Args here?

tranht17
Posts: 52
Joined: 03 May 2017, 02:55

Re: [2.0-beta.1] RunCMD

Post by tranht17 » 19 Aug 2021, 21:18

Code: Select all

MsgBox RunCmd("ping autohotkey.com -n 1",,,pingHelper)

pingHelper(Line, LineNum) {
  If (LineNum=2)
    {
      A_Args.RunCMD.PID := 0 ; Cancel RunCMD()
      Return StrSplit(Line, ["[","]"])[2]
    }  
}
You can see the example of v1 for more details
https://www.autohotkey.com/boards/viewtopic.php?t=74647

User avatar
kczx3
Posts: 1649
Joined: 06 Oct 2015, 21:39

Re: [2.0-beta.1] RunCMD

Post by kczx3 » 19 Aug 2021, 21:20

I’d strongly urge against bastardizing a built-in variable that way. Just pass the object to the callback function…

guest3456
Posts: 3463
Joined: 09 Oct 2013, 10:31

Re: [2.0-beta.1] RunCMD

Post by guest3456 » 19 Aug 2021, 21:30

kczx3 wrote:
19 Aug 2021, 21:20
I’d strongly urge against bastardizing a built-in variable that way. Just pass the object to the callback function…
+100000


tranht17
Posts: 52
Joined: 03 May 2017, 02:55

Re: [2.0-beta.1] RunCMD

Post by tranht17 » 19 Aug 2021, 21:32

kczx3 wrote:
19 Aug 2021, 21:20
I’d strongly urge against bastardizing a built-in variable that way. Just pass the object to the callback function…
I recommend you to re-read all the posts in v1 to know how it works
https://www.autohotkey.com/boards/viewtopic.php?f=6&t=74647

User avatar
SKAN
Posts: 1551
Joined: 29 Sep 2013, 16:58

Re: [2.0-beta.1] RunCMD

Post by SKAN » 20 Aug 2021, 00:35

@tranht17

I will rewrite and post my v1 functions (as time permits).
Please rename function to avoid future confusion.

arcticir
Posts: 694
Joined: 17 Nov 2013, 11:32

Re: [2.0-beta.1] RunCMD

Post by arcticir » 20 Aug 2021, 00:41

@tranht17

I think kczx3 understands RunTerminal(), and you don't understand what kczx3 is saying.

You can imagine the confusion if common LIBs like ACC.AHK and GDIP.AHK all use A_Args internally.

You could even just remove all the "A_Args" code and it would still work fine.
e.g.

Code: Select all

DllCall("CloseHandle", "Ptr",hPipeW)
, RunCMD.pid := NumGet(PI, P8 ? 16 : 8, "UInt")
, File := FileOpen(hPipeR, "h", Codepage)
Yes, the function name is a global object that can be read and written, although this is still a very poor usage.

User avatar
hyaray
Posts: 85
Joined: 20 Jun 2015, 01:37
Contact:

Re: [2.0-beta.1] RunTerminal

Post by hyaray » 05 Jan 2022, 00:42

@tranht17
I'm puzzled in AutoHotkey64,
SI.size=104, why not 40 + A_PtrSize * 7
and dwFlags I think is 32+A_PtrSize*3 but it's 60
I'm looking at https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/ns-processthreadsapi-startupinfow

Thanks a lot for you function!!

Code: Select all

, 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", hPipeW, SI, P8 ? 88 : 60)                              ; hStdOutput
, NumPut("Ptr", hPipeW, SI, P8 ? 96 : 64)

neogna2
Posts: 598
Joined: 15 Sep 2016, 15:44

Re: [2.0-beta.1] RunTerminal

Post by neogna2 » 01 Mar 2022, 10:02

Worth mentioning here that this v2 version has an issue that can affect some use cases. It is due to v2 File.ReadLine() omitting LF/CRLF as described by SKAN here.

For example running this (which adds linebreak in the outside function)

Code: Select all

RunTerminal("ping 8.8.8.8 -n 20", , "UTF-8", PingToolTip)

PingToolTip(Line, LineNum, &PID) {
  static Lines := ""
  ToolTip(Lines .= Line "`n")
}
with the non-global RunTerminal version we get tooltip output that looks like this.
tooltip.png
tooltip.png (10.54 KiB) Viewed 1994 times
Notice the irregularity in the linebreaks.

Use cases that don't care about linebreaks would still work ok I think. For example parsing the stream output until some string pattern appears.

Post Reply

Return to “Scripts and Functions (v2)”