Visual Basic Interface

Working with COM Objects in Powershell is a great way to interface with programs from the command line such as;

*) Microsoft Excel
*) Microsoft Word
*) Microsoft Visual FoxPro
*) Microsoft Outlook

...and many other COM Objects.

The TCC PSHELL Command and @PSHELL Function have many quirks which make them an un-reliable method of working with COM objects via PowerShell.

VBScript is also a great way to interface with COM Objects, but not from the command line.

A while back, I found code for a Simple VB Shell, that allows me to have a command line interface for VBScript.

I have ammended the code, so that I can work with COM Objects, and with TCC, from the VBI command line.

I am using the Microsoft (R) Windows Script Host Version 5.8 on my Windows 7 64-bit system.

Examples:

First off, to exit from VBI:
Code:
.quit
This is how I start VBI from the TCC command line:
Code:
cscript //nologo c:\utils\vbi.vbs
To launch the Microsoft Windows calculator:
Code:
.echo tcc("detach calc.exe")
To use TCC to display the ISO Date:
Code:
.echo tcc("echo %_isodate")
To use TCC to store the ISO Date into a VBScript variable called result:
Code:
result = tcc("echo %_isodate")
Normally, one cannot set an environment variable in VBScript. This can now be done with help from TCC:
Code:
.echo tcc("setp " &  VBIPID & " bdate=1995")
.echo GetVar("%bdate%")
1995
What is VBIPID? This is a function I borrowed to obtain the VBI Process ID:
Code:
.echo VBIPID
25380
To retrieve an environment variable from VBI:
Code:
.echo GetVar("%comspec%")
...or, to store the result into a VBScript variable called result:
Code:
result = GetVar("%comspec%")
.echo result
While you may not have Microsoft Visual FoxPro on your system, here is how I can now interface with it:
Code:
vfp.docmd("use c:\utils\orders.dbf shared")
.echo vfp.Eval("reccount()")
16
vfp.docmd("use")
It's quite easy to add commands and functions to VBI. For example, VBScript does not have a command to clear the screen, but TCC does have a command to clear the screen:
Code:
Sub Cls
  TCC("cls")
End Sub
...so now, from the VBI command line, I can just type cls to clear the screen.

If you want to use cmd.exe instead of tcc.exe, there is the ExecCmd() function. Example:
Code:
.echo ExecCmd("cmd.exe /c date /t")
Tue 04/23/2019
The original author has made comments throughout the source code, and I have made numerous comments throughout the source code for my ammendments, so please review in detail for more info.

There are limitations to VBI, but if others are interested in enhancing it, please do so, and please share your enhancements.

Joe
Code:
' VBI.vbs -- simple VB shell  
' Miles Willmek 2007-02-25  
' https://gallery.technet.microsoft.com/scriptcenter/5808b0d2-f16f-4aa9-90df-c849371f1e61
'
' Amended to work with TCC and COM Objects by Joe Caverly
' 
On Error Resume Next                                                       'Turn on Error Handline  
 
Const BQ = "`"                                                             'Default Dos Command Delimiter  
 
Dim sh, ox, st  
Set sh = CreateObject("WScript.Shell")                                     'Init WshShell Object  
'
' I can use Visual FoxPro from the VBI Command line
'
' Remove the comment from the following line if you
'   have Visual FoxPro on your system.
' Set vfp = CreateObject("visualfoxpro.application")
'
' .echo vfp.Version
' 9.0
'
' .echo vfp.DoCmd("use c:\utils\orders.dbf shared")
' .echo vfp.Eval("reccount()")
' 16
'
' .echo vfp.DoCmd("use")
'
' If you have them installed, you can use;
' Microsoft Excel
' Microsoft Word
' Microsoft Outlook
' ...or any other COM object
 
' Main Shell Loop  
' Exit with ^C or Wscript.Quit  
'  
Do While vbTrue                                                            'Forever  
 
    Wscript.StdOut.Write "VB> "                                            'Prompt  
    st = Wscript.StdIn.ReadLine  
 
    With Wscript                                                           'Handy Access to .Echo and .Quit  
        If Left(st, 1) = BQ Then                                           'If first char is BQ then echo results of DOS cmd 
 
            Execute "Wscript.Stdout.Write " & ReplaceBQ(st)  
        Else  
            Execute ReplaceBQ(st)                                          'Otherwise just execute  
        End If  
    End With  
 
    If Err.number <> 0 Then                                                'Check for Error from Execute  
        Wscript.Stdout.WriteLine _  
            "*** " & Err.Source & vbCrLf & _  
            "*** " & Chr(34) & st & Chr(34) & vbCrLf & _  
            "*** Error " & Err.Number & ": " & Err.Description & vbCrLf    'Display Error Information  
        Err.Clear                                                          'Reset Error Object  
    End If  
Loop  
 
' Send command to WshShell.Exec and return output  
'  
Function ExecCmd(cmd)  
    Set ox = sh.Exec(cmd)                                                  'Exec object is returned from Exec call  
    ExecCmd = ox.StdOut.ReadAll & ox.StdErr.ReadAll                        'Read StdOut/StdErr streams from Exec object  
    Set ox = Nothing  
End Function  

Function TCC(cmd)
  'You can hard-code the location of TCC.EXE
  '
  'comspec = "C:\Program Files\JPSoft\TCMD24\TCC.EXE"
  comspec = "C:\Program Files\JPSoft\TCC_RT_24\tcc.exe"
  '
  '...then use that in the function
  TCC = ExecCmd(comspec & " /c " & cmd)
  '
  '...or you can use the %comspec% environment variable
  'TCC = ExecCmd(getvar("%comspec%") & " /c " & cmd)
End Function
 
Function GetVar(theVar)
  Set wshShell = CreateObject( "WScript.Shell" )

  GetVar  = wshShell.ExpandEnvironmentStrings( theVar )

End Function

' Scan VB text line and replace BQ delimited commands  
' with proper VB call sequences for shell commands  
'  

Function ReplaceBQ(vb)  
    out = ""  
    i = 0  
    Do While i <= Len(vb)                                                  'Scan each char  
        i = i + 1  
        ch = Mid(vb, i, 1)  
        If ch = BQ Then                                                    'Found opening BQ  
            p = InStr(i + 1, vb, BQ)                                       'Position of closing BQ  
            If p = 0 Then  
               vb = vb & BQ                                                'Add BQ if no closing BQ  
               p = Len(vb)                                                 'and mark its position  
            End If  
            cmd = Mid(vb, i + 1, p - i - 1)                                'command is substring between i and p  
 
            If Left(cmd, 1) = "@" Then cmd = "%comspec% /c " & Mid(cmd, 2) 'cmd.exe used for DOS builtins  
 
            out = out & "ExecCmd(" & Chr(34) & cmd & Chr(34) & ")"         'substitute WshShell.Exec wrapper call  
            i = p                                                          'and skip past closing BQ  
        Else  
            out = out & ch                                                 'if not BQ command just pass through to out  
        End If  
    Loop  
 
    ReplaceBQ = out                                                        'set the function return value  
End Function

Function VBIPID
' Ref: https://stackoverflow.com/questions/8296037/find-my-own-process-id-in-vbscript/13212628
  Set com = CreateObject("Wscript.Shell")

  Set objSWbemServices = GetObject ("WinMgmts:Root\Cimv2")
  Set colProcess = objSWbemServices.ExecQuery ("Select * From Win32_Process")
  Dim toto, thisPid

  thisPid=""
  toto=200 ' just a high value like 200sec 
  For Each objProcess In colProcess

    If InStr (objProcess.CommandLine, WScript.ScriptName) <> 0  Then
       Ptime=((Cdbl(objProcess.UserModeTime)+Cdbl(objProcess.KernelModeTime))/10000000)
       if toto > Ptime then
           toto = Ptime
           thisPid = objProcess.ProcessId
       End If
    End If
  Next

  If thisPid="" then
    'WScript.Echo "unable to get the PID"
    VBIPID = 0
  Else
    'WScript.Echo "PID of this script : "&thisPid
    VBIPID = thisPid
  End If
End Function

Sub Help
  WScript.echo "GetVar - Obtain an environment variable"
  WScript.echo "USAGE: .echo GetVar(" & Chr(34) & "%comspec%" & Chr(34) & ")"
  WScript.Echo "       result = GetVar(" & Chr(34) & "%comspec%" & Chr(34) & ")"
  WScript.Echo "       .echo result"
  WScript.Echo ""
  WScript.Echo "VBIPID - Obtain the Process ID for this VBI Session"
  WScript.Echo "USAGE: .echo VBIPID"
  WScript.Echo "       thePID = VBIPID"
  WScript.Echo ""
  WScript.Echo "TCC - Run a TCC command or .BTM"
  WScript.Echo "USAGE: .echo tcc(" & Chr(34) & "detach calc.exe" & Chr(34) & ")"
  WScript.Echo "       .echo tcc(" & Chr(34) & "echo %_isodate" & Chr(34) & ")"
  WScript.Echo "       result = tcc(" & Chr(34) & "echo %_isodate" & Chr(34) & ")"
  WScript.Echo ""
  WScript.Echo "Set Environment Variable in VBI"
  WScript.Echo "Example: You can set an environment variable in VBScript via TCC"
  WScript.Echo ""
  WScript.Echo "         .echo tcc(" & Chr(34) & "setp " & Chr(34) & " & " & " VBIPID" & " & " & Chr(34) & " bdate=1995" & Chr(34) & ")"
  WScript.Echo "         .echo GetVar(" & Chr(34) & "%bdate%" & Chr(34) & ")"
  WScript.Echo ""
  WScript.Echo ".quit - Exit this VBI session"
End Sub

Sub Cls
  TCC("cls")
End Sub

Sub Dir
  WScript.echo TCC("dir")
End Sub
 
May 20, 2008
9,907
71
Syracuse, NY, USA
Interesting!

I first noticed a peculiar interaction. As soon as I used the "TCC" function, I would lose the cursor at the VB> command line. My TCSTART.BTM begins like this:
Code:
IF %_PIPE == 1 ( CURSOR OFF & QUIT )
IF %_TRANSIENT == 1 QUIT
It took me a bit to realize it, but since CSCRIPT.EXE is capturing TCC's output, TCC is, in fact, running in a pipe! And Wscript.StdIn.ReadLine does not turn the cursor back on (as TCC issuing a prompt does).
 
May 20, 2008
9,907
71
Syracuse, NY, USA
Here's the first two lines of my TCSTART.btm;
Code:
@if %_pipe != 0 .or. %_transient != 0 .or. %_ide != 0 quit
@iff %_pipe ne 1 .and. %_transient ne 1 then
Joe
Hmmm! The IFF looks superfluous. If you don't QUIT at the first line, then _PIPE and _TRANSIENT (and _IDE) must all be 0. The IFF condition will necessarily be true.