0
votes

I am trying to write a function that executes console commands in the background and listens StdOut and StdErr. In case of an error, it throws StdErr and StdOut. I am trying to do it in Autohotkey. First I tried using WScript.Shell COM Object, but StdErr and StdOut always empty, although StdOut definitely should be non-empty. There isn't much information on WScript.Shell COM Object and all Microsoft docs are in tech archive.

Util_Command(command) {
    dhw := A_DetectHiddenWindows
    DetectHiddenWindows On
    Run "%ComSpec%" /k,, Hide, pid
    while !(hConsole := WinExist("ahk_pid" pid))
        Sleep 10
    DllCall("AttachConsole", "UInt", pid)
    DetectHiddenWindows %dhw%
    objShell := ComObjCreate("WScript.Shell")
    objExec := objShell.Exec(command)
    While (!objExec.Status) {
        Sleep 100
    }
    err := objExec.ExitCode
    StdErr := objExec.StdErr.ReadAll()
    StdOut := objExec.StdOut.ReadAll()
    DllCall("FreeConsole")
    Process Exist, %pid%
    if (ErrorLevel == pid) {
        Process Close, %pid%
    }
    if (err) {
        if (!StdErr) {
            throw StdOut
        }
        throw StdErr
    }
}

try {
    Util_Command("""C:\Program Files (x86)\Resource Hacker\ResourceHacker.exe"" -open ""non/existent/dir"" -save ""C:\Users\486B~1\AppData\Local\Temp\~temp5812406.res"" -action compile")
} catch e {
    MsgBox % e
}

Then I tried doing it with GetStdHandle. In SciTe4Autohotkeys (or in any console) handle gets created successfully, but when trying to write to StdInn or read StdOut/StdErr I get ERROR_ACCESS_DENIED (5). However, writing to StdOut/StdErr or reading StdInn works fine but useless to me. Then I tried launching it without a console and I can't create a handle with error ERROR_SEM_NOT_FOUND (187). At the beginning of the script, it launches cmd in hidden mode ant attaches to it. How can I get handles of the attached console with proper read/write access?

Util_Command(lpCommand) {
    dhw := A_DetectHiddenWindows
    DetectHiddenWindows On
    Run "%ComSpec%" /k,, Hide, pid
    while !(hConsole := WinExist("ahk_pid" pid))
        Sleep 10
    DllCall("AttachConsole", "UInt", pid)
    DetectHiddenWindows %dhw%
    hStdIn := DllCall("GetStdHandle", "Int", -10)
    hStdOut := DllCall("GetStdHandle", "Int", -11)
    hStdErr := DllCall("GetStdHandle", "Int", -12)
    StrPut(lpCommand, "UTF-8")
    VarSetCapacity(lpCommand, 1024)
    VarSetCapacity(lpStdErrBuffer, 2048)
    a := DllCall("WriteFile"
        , "Ptr", hStdIn
        , "Ptr", &lpCommand
        , "UInt", 1024
        , "UInt", 0
        , "UInt", 0)
    a := DllCall("ReadFile"
        , "Ptr", hStdOut
        , "Ptr", &lpStdOutBuffer
        , "UInt", 2048
        , "UInt", 0
        , "UInt", 0)
    a := DllCall("ReadFile"
        , "Ptr", hStdErr
        , "Ptr", &lpStdErrBuffer
        , "UInt", 2048
        , "UInt", 0
        , "UInt", 0)
    FileAppend, stdErr - %lpStdErrBuffer%`nstdOut - %lpStdOutBuffer%`n, *
    DllCall("FreeConsole")
    Process Exist, %pid%
    if (ErrorLevel == pid) {
        Process Close, %pid%
    }
    if (err) {
        if (!StdErr) {
            throw StdOut
        }
        throw StdErr
    }
}

try {
    Util_Command("""C:\Program Files (x86)\Resource Hacker\ResourceHacker.exe"" -open ""non/existent/dir"" -save ""C:\Users\486B~1\AppData\Local\Temp\~temp5812406.res"" -action compile\n")
} catch e {
    MsgBox % e
}
1

1 Answers

0
votes

It can be done, but I'm not sure what you are trying to accomplish.

You use the WScript.Shell then pass the command in the .Exec(ComSpec " /C cscript " command) method and return the exec.StdOut.ReadAll()

I have a working example as follows, maybe it helps you:

;// The following runs a command (add.vbs) 
;// and retrieves its output via StdOut:

InputBox, x,,"Enter two numbers"
MsgBox % """" RunWaitOne("add.vbs " x) """" ;// result in quotes
ExitApp

RunWaitOne(command)
{
    shell := ComObjCreate("WScript.Shell")
    exec := shell.Exec(ComSpec " /C cscript " command)
    return Substr(exec.StdOut.ReadAll(), 109)  ;//  remove 109 characters in WScript header
}

/* This is the external "add.vbs" command:
a=3
b=4
if WScript.Arguments.Count > 0 Then
    a=cint(WScript.Arguments.Item(0))
    b=cint(WScript.Arguments.Item(1))
End If
Dim StdOut : Set StdOut = CreateObject("Scripting.FileSystemObject").GetStandardStream(1)
x=a+b
StdOut.Write x
*/

Hth,