使用 Autohotkey 读取 StdErr 和 StdOut

Posted

技术标签:

【中文标题】使用 Autohotkey 读取 StdErr 和 StdOut【英文标题】:Reading StdErr and StdOut with Autohotkey 【发布时间】:2020-03-13 03:22:29 【问题描述】:

我正在尝试编写一个在后台执行控制台命令并侦听 StdOut 和 StdErr 的函数。如果发生错误,它会抛出 StdErr 和 StdOut。我正在尝试在 Autohotkey 中执行此操作。首先我尝试使用 WScript.Shell COM 对象,但 StdErr 和 StdOut 始终为空,尽管 StdOut 绝对应该是非空的。关于 WScript.Shell COM 对象的信息不多,所有 Microsoft 文档都在技术档案中。

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

然后我尝试使用 GetStdHandle。在 SciTe4Autohotkeys(或任何控制台)中成功创建了句柄,但是当尝试写入 StdInn 或读取 StdOut/StdErr 时,我得到 ERROR_ACCESS_DENIED (5)。但是,写入 StdOut/StdErr 或读取 StdInn 工作正常,但对我来说没用。然后我尝试在没有控制台的情况下启动它,但我无法创建带有错误 ERROR_SEM_NOT_FOUND (187) 的句柄。在脚本的开头,它以隐藏模式启动 cmd ant 附加到它。如何获得具有正确读/写访问权限的附加控制台的句柄?

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】:

可以做到,但我不确定您要完成什么。

您使用WScript.Shell 然后在.Exec(ComSpec " /C cscript " command) 方法中传递命令并返回exec.StdOut.ReadAll()

我有一个工作示例如下,也许它可以帮助你:

;// 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 /nologo " command)
    return exec.StdOut.ReadAll()


/* 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
*/

第,

【讨论】:

谢谢。我让它与WScript.Shell 一起工作。但现在我遇到了编码问题。我需要阅读 utf-8。我尝试了/u 选项并在命令之前执行chcp 65001。实际的脚本是用 UTF-8 编写的。我使用 Unicode 32 位 ahk 来执行它。

以上是关于使用 Autohotkey 读取 StdErr 和 StdOut的主要内容,如果未能解决你的问题,请参考以下文章

Autohotkey 和 Windows 10:如何获取当前资源管理器路径

如何在 Perl 中读取和写入大缓冲区到进程 stdin/stdout/stderr?

Python分别从子进程stdout和stderr读取,同时保留顺序

Python asyncio子进程连续写入stdin和读取stdout/stderr

Python子进程 - stderr在读取后擦除

autohotkey怎么快速改建?