来自WScript.Shell Exec的实时控制台输出

时间:2015-02-05 20:35:10

标签: excel-vba vbscript console wsh vba

我大部分时间都在寻找解决方案,我开始认为它可能不符合我的要求

我的基本设置是运行从excel vba代码调用的vbscript(.vbs)。 vba代码必须继续并保持vbscript运行,但会使用Exec.Status

不时监视它

在vbscript中,我正在使用WScript.StdOut.WriteLine "whatever"来跟踪/调试它的进度,但是就目前而言,我只能在excel vba代码完成后才能读取它的输出。

我想要的是从vbscript

看到实时输出到控制台

这是vba代码......

Dim WSH As IWshRuntimeLibrary.WshShell   'Windows Script Host Object Model
Dim Exec As WshExec 

Set WSH = CreateObject("WScript.Shell")
Set Exec = WSH.Exec("%COMSPEC% /C CSCRIPT.EXE //nologo " _
    & VbsFileDir _
    & " " & Arg1 _
    & " " & Arg2 _
    & " " & Arg3 _
    & " " & Arg4)

我已经能够通过从WSH.Exec转换为WSH.Run获得实时输出,但我确实需要Exec.Status的访问权限,WSH.Run下无法访问WSH.Run("cscript C:\28353522-B.vbs") }


更新 - 2015-02-06

进一步澄清......使用@ Ekkehard.Horner答案提供的示例'... B.vbs'代码......以下excel-vba代码将向控制台显示实时输出...

WSH.Exec("cscript C:\28353522-B.vbs")

...但以下内容不会向控制台显示任何内容

.Run()

我无法使用.Status,因为我使用.Exec()中的{{1}}标记 此外,我不能将vbscript移动到VBA代码中,因为VBA继续与vbscript并行执行其他任务。

P.S。如果有人可以提交答案解释为什么不能这样做,那么我会将其标记为已接受。

3 个答案:

答案 0 :(得分:3)

使用.Stdout.ReadLine()直到进程完成并且.Stdout.ReadAll()来覆盖输出的其余部分 - 如

28353522-A.vbs

Option Explicit

Const WshFinished = 1

Dim oExc : Set oExc = CreateObject("WScript.Shell").Exec("cscript 28353522-B.vbs")
WScript.Echo "A", "start"
Do While True
   If oExc.Status = WshFinished Then
      WScript.Echo "A", "WshFinished"
      Exit Do
   End If
   WScript.Sleep 500
   If Not oExc.Stdout.AtEndOfStream Then WScript.Echo "A", oExc.Stdout.ReadLine()
Loop
If Not oExc.Stdout.AtEndOfStream Then WScript.Echo "A", oExc.Stdout.ReadAll()

28353522-B.vbs

Option Explicit

Dim i
For i = 1 To 10
    WScript.Echo "B", i, "whatever"
    WScript.Sleep 100
Next

输出:

cscript 28353522-A.vbs
A start
A B 1 whatever
A B 2 whatever
A B 3 whatever
A WshFinished
A B 4 whatever
B 5 whatever
B 6 whatever
B 7 whatever
B 8 whatever
B 9 whatever
B 10 whatever

BTW - 你是如何通过.Run()获得实时输出的?

答案 1 :(得分:0)

为什么要运行两个文件?没有必要。

VBA是完全基本的,可以写入自己的控制台。

所以把你的vbs放到VBA中(你可以将VBS剪切并粘贴到VBA中,如果你把sub / end sub放在它周围它会起作用)。让VBS在VBA中运行时会放一个触发它的计时器。

这是VBA创建/读/写控制台的类模块。

'User global var gconsole
Public Function WriteToConsoles(sOut As String)
    If IsConsoleAvailable() = True Then
        Dim Result As Long, cWritten As Long
        Result = WriteConsole(hConsole, ByVal sOut, Len(sOut), cWritten, ByVal 0&)
    End If
End Function

Public Sub ExecuteCommand(Cmd As String, ReturnToPrompt As Boolean)
    If IsConsoleAvailable() = True Then
        If Len(Cmd) <> 0 Then
            If ReturnToPrompt = True Then
                Shell Environ$("comspec") & " /k " & Cmd
            Else
                Shell Environ$("comspec") & " /c " & Cmd
            End If
        End If
    End If
End Sub

Public Sub CreateConsole()
    If IsConsoleAvailable() = False Then
        If AllocConsole() Then
            hConsole = GetStdHandle(STD_OUTPUT_HANDLE)
            If hConsole = 0 Then MsgBox "Couldn't allocate STDOUT"
        Else
            MsgBox "Couldn't allocate console"
        End If
    End If
End Sub

Public Sub CloseConsole()
    If IsConsoleAvailable() = True Then
        CloseHandle hConsole
        hConsole = 0
        FreeConsole
    End If
End Sub

Public Function IsConsoleAvailable() As Boolean
        If hConsole <> 0 Then
            IsConsoleAvailable = True
        Else
            IsConsoleAvailable = False
        End If
End Function

答案 2 :(得分:0)

我已经找到了自己问题的答案。虽然这不是一个首选的解决方案(我将在下面解释),所以我不会标记为正确,但也许有人可以通过解决方案解决问题? (如果是的话,发一个答案,我会标记为正确)

首先,@ Ekkehard.Horner给出了启发此解决方案的答案。

创建文件&#39; B.vbs&#39;代表我要运行的主要vbscript。

Option Explicit
Dim i
For i = 1 to 10
    Wscript.Echo "B", i, "whatever"
    Wscript.Sleep 100
Next

创建文件&#39; A.vbs&#39;充当主vbscript和我的Excel VBA代码之间的中间人

Option Explicit
Dim WSH
Set WSH = CreateObject("WScript.Shell")
WSH.Run "cscript C:\B.vbs", , True
Set WSH = Nothing

现在是excel VBA代码......

Option Explicit
Sub Test()
    Dim WSH As IWshRuntimeLibrary.WshShell
    Dim Exec As WshExec
    Set WSH = CreateObject("WScript.Shell")

    Set Exec = WSH.Exec("cscript C:\A.vbs")

    'Showing that I can still access the Exec.Status
    While Exec.Status = WshRunning
        Debug.Print "Running"
    Wend
    'But downside is nothing is avaiable from Stdout
    Debug.Print Exec.StdOut.ReadAll

    Set Exec = Nothing
    Set WSH = Nothing

End Sub

所以Excel VBA称之为'A.vbs&#39;仍在使用WSH.Exec(),然后会调用&#39; B.vbs&#39;使用WSH.Run(),打开第二个控制台窗口,显示实时输出

<强>优点

  • Excel VBA仍然可以准确监控Exec.Status
  • B.vbs&#39;的进展可以从实时控制台输出中查看

缺点(原因我没有标记为正确)

  • Exec.Terminate()只会终止&#39; A.vbs&#39; (第一个控制台窗口),&#39; B.vbs&#39;将继续运行
  • Exec.StdOut.无法读取&#39; B.vbs&#39;
  • 的输出