Skytunnel
Skytunnel

Reputation: 1083

Real time console output from WScript.Shell Exec

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

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.

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

Here's the vba code...

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


UPDATE - 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...

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.

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

Upvotes: 5

Views: 16808

Answers (3)

Skytunnel
Skytunnel

Reputation: 1083

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.

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

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

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

Now the excel VBA code...

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

Advantages

  • Excel VBA can still monitor the Exec.Status accurately
  • Progress of 'B.vbs' can be viewed from real-time console output

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
  • The Exec.StdOut. cannot read the output from 'B.vbs'

Upvotes: 0

user4532213
user4532213

Reputation: 91

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

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

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.

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

'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

Upvotes: 0

Ekkehard.Horner
Ekkehard.Horner

Reputation: 38745

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

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

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()?

Upvotes: 4

Related Questions