来自WScript的实时控制台输出。壳牌高管

时间:2022-03-15 21:28:51

I spent most of the day searching for a solution to this, I'm starting to think its maybe not possible for my requirements

我花了一整天的时间来寻找解决方案,我开始觉得我的要求可能是不可能的。

My basic setup is to run a vbscript (.vbs) called from an excel vba code. The vba code has to continue on and leave the vbscript running, but will monitor it from time to time using Exec.Status

我的基本设置是运行一个从excel vba代码中调用的vbscript (.vbs)。vba代码必须继续运行,并让vbscript运行,但是会不时地使用Exec.Status监视它。

In the vbscript I'm using WScript.StdOut.WriteLine "whatever" to track/debug it's progress, but as it stands I can only read it's output after the excel vba code is finished what it needs to do.

在vbscript中,我使用WScript.StdOut。WriteLine“无论”如何跟踪/调试它的进程,但是当它站起来的时候,我只能读取它的输出,因为excel vba代码完成了它需要做的事情。

What I want is to see a real time output to the console from the vbscript

我想要的是看到一个实时输出到控制台的vbscript !

Here's the vba code...

这是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)

I have been able to get a real time output by converting from WSH.Exec to WSH.Run, but I do need the access to Exec.Status, which is not available under WSH.Run

我已经能够通过从WSH转换得到一个实时输出。Exec WSH。运行,但我确实需要访问Exec。状态,在WSH.Run下不可用。


UPDATE - 2015-02-06

更新- 2015-02-06

To clarify further... Using the example '...B.vbs' code provided by @Ekkehard.Horner's answer... The following excel-vba code WILL display a real-time output to the console...

进一步澄清……使用的例子…B。vbs的代码由@Ekkehard提供。霍纳的回答…下面的excel-vba代码将向控制台显示实时输出…

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

...but the following WILL NOT display anything to the console

…但是下面的内容将不会显示到控制台。

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

I can't use the .Run() because I use the .Status flag from .Exec() Also I can't just move the vbscript into the VBA code because the VBA goes on to do other tasks in parallel with the vbscript.

我不能使用. run(),因为我使用. exec()的. status标志(),也不能仅仅将vbscript移到VBA代码中,因为VBA继续执行与vbscript并行的其他任务。

P.s. If anyone can submit an answer explaining why it can't be done, then I will mark that as accepted.

如果任何人都可以提交一份解释为什么不能这样做的答案,那么我将把它标记为已接受。

3 个解决方案

#1


3  

Use .Stdout.ReadLine() until the process has finished and .Stdout.ReadAll() to slurp the rest of the output - as in

使用.Stdout.ReadLine(),直到进程结束和.Stdout.ReadAll()将输出的其余部分发出声音,如in。

28353522-A.vbs

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

28353522——b.vbs

Option Explicit

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

output:

输出:

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 - How did you get real-time output with .Run()?

顺便说一下,你是如何获得实时输出的。run ()?

#2


0  

Why are you running two files? There is no need.

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

VBA, being full basic, can write to it's own console.

VBA是一个完整的基础,可以编写自己的控制台。

So put your vbs into VBA (you can cut and paste VBS into VBA and it will work if you put sub/end sub around it). To have the VBS run in VBA put a timer that fires it.

因此,将vbs放入VBA中(您可以将vbs剪切并粘贴到VBA中,如果您将子/end子放到VBA中,它将会起作用)。要让VBS在VBA中运行,请放置一个触发它的计时器。

Here's a class module for VBA to create/read/write consoles.

这是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

#3


0  

I've come up with an answer to my own question. Though this isn't a preferred solution (as I will explain below), so I'll not be marking as correct, but maybe someone can fix the issues with solution? (if so, post an answer and I'll mark as correct)

我提出了一个自己的问题的答案。虽然这不是一个首选的解决方案(我将在下面解释),所以我不会标记为正确,但是也许有人可以解决问题的解决方案?(如果是这样的话,那就给我一个答复,我就把它标记为正确)

First off, +1 to @Ekkehard.Horner's answer for inspiring this solution.

首先,+1到@Ekkehard。霍纳的答案启发了这个解决方案。

Create the file 'B.vbs' representing my main vbscript to be run.

创建文件的B。vbs表示要运行的主vbscript。

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

Create the file 'A.vbs' to act as a middle man between the main vbscript and my Excel VBA code

创建文件”。vbs在主vbscript和我的Excel VBA代码之间充当中间人。

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

Now the excel VBA code...

现在,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

So the Excel VBA calls the 'A.vbs' still using WSH.Exec(), then that will call the 'B.vbs' using WSH.Run(), which opens a second console window which will display the real-time output

Excel VBA调用了A。vbs仍然使用WSH.Exec(),然后调用“B”。使用WSH.Run()打开第二个控制台窗口,该窗口将显示实时输出。

Advantages

优势

  • Excel VBA can still monitor the Exec.Status accurately
  • Excel VBA仍然可以监控Exec。状态准确
  • Progress of 'B.vbs' can be viewed from real-time console output
  • " B的进展。可以从实时控制台输出查看vbs。

Disadvantages (reasons I'm not marking as correct)

缺点(我不认为正确的原因)

  • The Exec.Terminate() will only terminate the 'A.vbs' (first console window), the 'B.vbs' will remain running
  • 终止()只会终止“A”。vbs(第一个控制台窗口),B。根据“将继续运行
  • The Exec.StdOut. cannot read the output from 'B.vbs'
  • Exec.StdOut。无法读取“B.vbs”的输出

#1


3  

Use .Stdout.ReadLine() until the process has finished and .Stdout.ReadAll() to slurp the rest of the output - as in

使用.Stdout.ReadLine(),直到进程结束和.Stdout.ReadAll()将输出的其余部分发出声音,如in。

28353522-A.vbs

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

28353522——b.vbs

Option Explicit

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

output:

输出:

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 - How did you get real-time output with .Run()?

顺便说一下,你是如何获得实时输出的。run ()?

#2


0  

Why are you running two files? There is no need.

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

VBA, being full basic, can write to it's own console.

VBA是一个完整的基础,可以编写自己的控制台。

So put your vbs into VBA (you can cut and paste VBS into VBA and it will work if you put sub/end sub around it). To have the VBS run in VBA put a timer that fires it.

因此,将vbs放入VBA中(您可以将vbs剪切并粘贴到VBA中,如果您将子/end子放到VBA中,它将会起作用)。要让VBS在VBA中运行,请放置一个触发它的计时器。

Here's a class module for VBA to create/read/write consoles.

这是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

#3


0  

I've come up with an answer to my own question. Though this isn't a preferred solution (as I will explain below), so I'll not be marking as correct, but maybe someone can fix the issues with solution? (if so, post an answer and I'll mark as correct)

我提出了一个自己的问题的答案。虽然这不是一个首选的解决方案(我将在下面解释),所以我不会标记为正确,但是也许有人可以解决问题的解决方案?(如果是这样的话,那就给我一个答复,我就把它标记为正确)

First off, +1 to @Ekkehard.Horner's answer for inspiring this solution.

首先,+1到@Ekkehard。霍纳的答案启发了这个解决方案。

Create the file 'B.vbs' representing my main vbscript to be run.

创建文件的B。vbs表示要运行的主vbscript。

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

Create the file 'A.vbs' to act as a middle man between the main vbscript and my Excel VBA code

创建文件”。vbs在主vbscript和我的Excel VBA代码之间充当中间人。

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

Now the excel VBA code...

现在,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

So the Excel VBA calls the 'A.vbs' still using WSH.Exec(), then that will call the 'B.vbs' using WSH.Run(), which opens a second console window which will display the real-time output

Excel VBA调用了A。vbs仍然使用WSH.Exec(),然后调用“B”。使用WSH.Run()打开第二个控制台窗口,该窗口将显示实时输出。

Advantages

优势

  • Excel VBA can still monitor the Exec.Status accurately
  • Excel VBA仍然可以监控Exec。状态准确
  • Progress of 'B.vbs' can be viewed from real-time console output
  • " B的进展。可以从实时控制台输出查看vbs。

Disadvantages (reasons I'm not marking as correct)

缺点(我不认为正确的原因)

  • The Exec.Terminate() will only terminate the 'A.vbs' (first console window), the 'B.vbs' will remain running
  • 终止()只会终止“A”。vbs(第一个控制台窗口),B。根据“将继续运行
  • The Exec.StdOut. cannot read the output from 'B.vbs'
  • Exec.StdOut。无法读取“B.vbs”的输出