VBscript code to capture stdout, without showing console window

79,427

Solution 1

This proof of concept script:

' pocBTicks.vbs - poor man's version of backticks (POC)

Option Explicit

' Globals

Const SW_SHOWMINNOACTIVE =  7
Const ForReading         =  1

Dim goFS  : Set goFS  = CreateObject( "Scripting.FileSystemObject" )
Dim goWSH : Set goWSH = CreateObject( "WScript.Shell" )

' Dispatch
WScript.Quit demoBTicks()

' demoBTicks -
Function demoBTicks()
  demoBTicks = 1
  Dim aCmds : aCmds = Array( _
      "dir pocBTicks.vbs" _
    , "dur pocBTicks.vbs" _
    , "xcopy /?" _
  )
  Dim sCmd
  For Each sCmd In aCmds
      WScript.Echo "########", sCmd
      Dim aRet : aRet = BTicks( sCmd )
      Dim nIdx
      For nIdx = 0 To UBound( aRet )
          WScript.Echo "--------", nIdx
          WScript.Echo aRet( nIdx )
      Next
  Next
  demoBTicks = 0
End Function ' demoBTicks

' BTicks - execute sCmd via WSH.Run
'  aRet( 0 ) : goWSH.Run() result
'  aRet( 1 ) : StdErr / error message
'  aRet( 2 ) : StdOut
'  aRet( 3 ) : command to run
Function BTicks( sCmd )
  Dim aRet    : aRet     = Array( -1, "", "", "" )
  Dim sFSpec2 : sFSpec2  = goFS.GetAbsolutePathName( "." )
  Dim sFSpec1 : sFSpec1  = goFS.BuildPath( sFSpec2, goFS.GetTempName() )
                sFSpec2  = goFS.BuildPath( sFSpec2, goFS.GetTempName() )

  aRet( 3 ) = """%COMSPEC%"" /c """ + sCmd + " 1>""" + sFSpec1 + """ 2>""" +  sFSpec2 + """"""
  Dim aErr
 On Error Resume Next
  aRet( 0 ) = goWSH.Run( aRet( 3 ), SW_SHOWMINNOACTIVE, True )
  aErr      = Array( Err.Number, Err.Description, Err.Source )
 On Error GoTo 0
  If 0 <> aErr( 0 ) Then
     aRet( 0 ) = aErr( 0 )
     aRet( 1 ) = Join( Array( aErr( 1 ), aErr( 2 ), "(BTicks)" ), vbCrLf )
     BTicks    = aRet
     Exit Function
  End If

  Dim nIdx : nIdx = 1
  Dim sFSpec
  For Each sFSpec In Array( sFSpec2, sFSpec1 )
      If goFS.FileExists( sFSpec ) Then
         Dim oFile : Set oFile = goFS.GetFile( sFSpec )
         If 0 < oFile.Size Then
            aRet( nIdx ) = oFile.OpenAsTextStream( ForReading ).ReadAll()
            goFS.DeleteFile sFSpec
         End If
      End If
      nIdx = nIdx + 1
  Next
  BTicks = aRet
End Function

shows how to use .Run and temporary files to get something like backticks with a hidden console. Decent file handling, quoting in sCmd, cleaning of the returned strings, and dealing with encodings will require more work. But perhaps you can use the strategy to implement something that fits your needs.

Solution 2

I usually use this:

Wscript.echo execStdOut("ping google.com")

Function execStdOut(cmd)
   Dim goWSH : Set goWSH = CreateObject( "WScript.Shell" ) 
   Dim aRet: Set aRet = goWSH.exec(cmd)
   execStdOut = aRet.StdOut.ReadAll()
End Function 

For more advanced commands youc an wrap to comspec (cmd)

my res = execStdOut("%comspec%" & " /c " & """" & "dir /b c:\windows\*.exe" & """" & " && Echo. && Echo finished") 

Solution 3

In order to redirect the output to the console, run the script using cscript, ex.: c:\cscript myscript.vbs.

cscript has a few command line options. The most important (to me) is the switch //NOLOGO. If yoy use it (cscript //nologo myscript.vbs) it will omit Microsoft merchandise...

Solution 4

To return in VBA all subfolders in G:\OF

sub M_snb()
  c00= createobejct("wscript.Shell").exec("cmd /c Dir G:\OF\*. /s/b").stdout.readall
end sub

to split the returned string into an array

sub M_snb()
  sn=split(createobejct("wscript.Shell").exec("cmd /c Dir G:\OF\*. /s/b").stdout.readall,vbCrLf)

  for j=0 to ubound(sn)
     msgbox sn(j)
  next
End Sub

Solution 5

If you don't mind having the taskbar button appear, you can just move the console window offscreen before launching it.

If the HKCU\Console\WindowPosition key exists, Windows will use its value to position the console window. If the key doesn't exist, you'll get a system-positioned window.

So, save the original value of this key, set your own value to position it offscreen, call Exec() and capture its output, then restore the key's original value.

The WindowPosition key expects a 32-bit value. The high word is the X coordinate and the low word is the Y coordinate (XXXXYYYY).

With CreateObject("WScript.Shell")

    ' Save the original window position. If system-positioned, this key will not exist.
    On Error Resume Next
    intWindowPos = .RegRead("HKCU\Console\WindowPosition")
    On Error GoTo 0

    ' Set Y coordinate to something crazy...
    .RegWrite "HKCU\Console\WindowPosition", &H1000, "REG_DWORD"

    ' Run Exec() and capture output (already demonstrated by others)...
    .Exec(...)

    ' Restore window position, if previously set. Otherwise, remove key...
    If Len(intWindowPos) > 0 Then
        .RegWrite "HKCU\Console\WindowPosition", intWindowPos, "REG_DWORD"
    Else
        .RegDelete "HKCU\Console\WindowPosition"
    End If

End With

If you really want to make sure the coordinates are offscreen, you can get the screen dimensions via VBScript by using IE or other tools.

Share:
79,427

Related videos on Youtube

mgr326639
Author by

mgr326639

Updated on October 02, 2020

Comments

  • mgr326639
    mgr326639 over 3 years

    This is a VBScript code example that shows how to catch whatever a command line program sends to standard output. It executes the command xcopy /? and shows the output in a message box. Before the message box appears, for a split second you see the console window popping up.

    Set objShell = WScript.CreateObject("WScript.Shell")
    Set objExec = objShell.Exec("xcopy /?")
    Do
        line = objExec.StdOut.ReadLine()
        s = s & line & vbcrlf
    Loop While Not objExec.Stdout.atEndOfStream
    WScript.Echo s
    

    Here is an other VBScript code example that shows how to execute a script without showing the console window.

    objShell.Run "c:\temp\mybatch.bat C:\WINDOWS\system32\cmd.exe", 0
    

    or

    objShell.Run "c:\temp\myscript.vbs C:\WINDOWS\system32\cscript.exe", 0
    

    As you can see it has the form <script><space><executor>. The last example uses objShell.Run instead of objShell.Exec

    What I don't know is how to execute a command line program (if necessary from a batch file), catch the standard output, without showing the console window. Any ideas?

  • Camilo Martin
    Camilo Martin over 11 years
    Note that this will hang if your program outputs too much (about 4KB IIRC), and it's not hidden.
  • mgr326639
    mgr326639 about 10 years
    Interesting piece of code. But I don't really see the advantage in this case, because you are still relying on a temporary file.
  • tresf
    tresf almost 9 years
    @CamiloMartin Where are you getting the 4KB limitation from? Is that a limitation of StdOut.ReadAll(). I just used StdOut.ReadLine() on a 109KB file without issues per ss64.com/vb/stdoutread.html. Although I can confirm the "not hidden" observation, making this solution non-ideal for those seeking a "hidden window" as a strict requirement, per the OP.
  • Camilo Martin
    Camilo Martin almost 9 years
    @QZSupport Honestly I don't have the faintest clue anymore, but probably I tried it and something hung after 4KB, then other two people came and upvoted it because something hung for them too. If it actually works that's great, just make sure to test on all Windows versions you're supporting. (Also, it should be noticed: it is very likely ReadAll will read everything into RAM, and would be very ugly on its own - I'd say you should stream the StdOut instead, but again I don't know how; ReadLine wouldn't be good for binary output).
  • tresf
    tresf almost 9 years
    @CamiloMartin, good point about the binary and ReadLine(), that could get messy when the CRLFs never make it into the stream. However, I'd like to reiterate the OP's requirement of StdOut, which tends to favor non-binary representations of data. In regards to multiple platforms, I'll perform some testing as far back as XP and post an update.
  • tresf
    tresf almost 9 years
    @CamiloMartin Edit: I just read 1145 lines on text weighing in at about 160KB and worked just fine on Windows XP SP3 (as long as I remember to use cmd /c and not cmd /k). For reading multi-line output, I see this solution as being perfectly adequate for most people, but I would recommend to use ReadLine() over ReadAll(). In terms of the 4KB limitation, it is explained further here: support.microsoft.com/en-us/kb/960246
  • Pankaj Jaju
    Pankaj Jaju about 7 years
    @QZSupport - support.microsoft.com/en-us/help/960246/… talks about the 4kb limit
  • tresf
    tresf about 7 years
    @PankajJaju thanks. Per that same article Generally, this occurs if the spawned application is writing to both the StdOut and StdErr streams. So applications that do not write to both stderr AND stdout shouldn't suffer this bug.
  • gao.xiangyang
    gao.xiangyang about 4 years
    But it still has an icon displayed in the taskbar,How to do?

Related