You are on page 1of 5

' RunProgram.vbs - Written by Bill Stewart (bill.stewart@frenchmortuary.

com)
'
' Uses WMI to run a program on the local or a remote computer. It can also wait
' for the program to finish and optionally display an elapsed time. For
' security reasons, running a program on a remote computer always executes in a
' hidden window on Windows 2000 SP3 and later.
'
' Limitations:
'
' * A program started on a remote computer will not have network access.
' * The /wait option imposes some amount of processor overhead because it
' repeatedly executes a WMI query to determine if the process exists.
' * Embedding double quotes in the command line is awkward because the WSH
' runtime doesn't provide an escape mechanism (like \" to represent ", etc.)
' As a workaround, the script uses VBScript's Unescape() function, so you
' can type %22 (hex 22 = dec 34 = " character). Naturally, this won't work
' if the command line actually contains %xx sequences, but this should be
' pretty rare.
'
' Version history:
' * 1.0: Initial version.
Option Explicit
Const SCRIPT_NAME = "RunProgram.vbs"
' This constant defines the number of mulliseconds
' to wait between each WMI query when using /wait
Const PROCESS_CHECK_INTERVAL = 500
Main
' Displays the script's usage message
Sub ShowUsage()
WScript.Echo "Runs a program on the local or a remote computer." & vbNewLine _
& vbNewLine _
& "Usage: " & SCRIPT_NAME & " ""<command>"" [/startin:<path>] [/window:<n>]"
& vbNewLine _
& " [/computer:<computer> [/username:<username>] [/password:<password>]]
" & vbNewLine _
& " [/wait [/elapsed]]" & vbNewLine _
& vbNewLine _
& "<command>: The command line to run (must start with an executable)." & vb
NewLine _
& "/startin: Sets the starting directory for the program." & vbNewLine _
& "/window: Number that sets the program's initial window state. (Note: for"
& vbNewLine _
& " security reasons, a program run on a remote computer always starts i
n a" & vbNewLine _
& " hidden window on Windows 2000 SP3 and later.)" & vbNewLine _
& "/computer: The computer on which to run the program." & vbNewLine _
& "/username: Connects with the specified username (requires /computer)." &
vbNewLine _
& "/password: Use the specified password (requires /computer)." & vbNewLine
_
& "/wait: Waits for the program to end." & vbNewLine _
& "/elapsed: Displays the program's elapsed time (requires /wait)." & vbNewL
ine _
& vbNewLine _
& "Enclose the command line in double-quotes (""). To embed double quotes in
the" & vbNewLine _
& "command line, use the string ""%22""." & vbNewLine _
& vbNewLine _
& "Example: " & SCRIPT_NAME & " ""cmd /c dir %22C:\Program Files%22 > C:\Lis
t.txt"""
WScript.Quit 0
End Sub
' Returns the script host (cscript.exe or wscript.exe) in lowercase
Function ScriptHost()
ScriptHost = LCase(Mid(WScript.FullName, Len(WScript.Path) + 2))
End Function
' Displays the elapsed time in hour(s), minute(s), and second(s) based
' on the Secs parameter
Function GetElapsed(ByVal Secs)
Dim Result, Hrs, Mins
Result = ""
Hrs = Secs \ 3600
If Hrs > 0 Then
Result = CStr(Hrs) & " hour(s), "
Secs = Secs - (3600 * Hrs)
End If
Mins = Secs \ 60
If Mins > 0 Then
Result = Result & CStr(Mins) & " minute(s), "
Secs = Secs - (60 * Mins)
End If
GetElapsed = Result & CStr(Secs) & " second(s)"
End Function
' Main subroutine
Sub Main()
Dim Args
Dim Command, StartIn, Computer, UserName, Password
Dim Wait, Elapsed
Dim ShowWindow, Result
Dim StartTime, EndTime
Dim Output, TimeInfo
Dim WMIRun
Set Args = WScript.Arguments
' If there are no unnamed arguments, or if the /? argument
' is present, show the usage message
If (Args.Unnamed.Count = 0) Or Args.Named.Exists("?") Then _
ShowUsage
' CALLOUT A
Command = Unescape(Args.Unnamed(0))
' END CALLOUT A
StartIn = Args.Named("startin")
ShowWindow = Args.Named("window")
If Not IsNumeric(ShowWindow) Then ShowUsage
If (ShowWindow < 0) Or (ShowWindow > 65535) Then ShowUsage
Computer = Args.Named("computer")
UserName = Args.Named("username")
Password = Args.Named("password")
Wait = Args.Named.Exists("wait")
Elapsed = Args.Named.Exists("elapsed")
' Create instance of class
' CALLOUT B
Set WMIRun = New WMIExec
' END CALLOUT B
Result = WMIRun.ConnectServer(Computer, UserName, Password)
If Result <> 0 Then
WScript.Echo "Error 0x" & Hex(Result) & " connecting to '" _
& Computer & "'"
WScript.Quit Result
End If
If Elapsed Then StartTime = Now()
Result = WMIRun.RunProgram(Command, StartIn, ShowWindow)
If Result <> 0 Then
WScript.Echo "Error 0x" & Hex(Result) & " starting '" _
& Command & "'"
WScript.Quit Result
End If
' Output initial status message
WScript.Echo WMIRun.Status
If Wait Then
' Suspend script while process is running
Do While WMIRun.ProcessExists()
WScript.Sleep PROCESS_CHECK_INTERVAL
Loop
If Elapsed Then
EndTime = Now()
TimeInfo = "Started: " & CStr(StartTime) & vbNewLine _
& "Ended: " & CStr(EndTime) & vbNewLine _
& "Elapsed: " & GetElapsed(DateDiff("s", StartTime, EndTime))
' Output full status if running from wscript.exe
If ScriptHost() = "wscript.exe" Then
Output = WMIRun.Status & vbNewLine & TimeInfo
Else
Output = TimeInfo
End If
WScript.Echo Output
End If
End If
End Sub
' WMIExec class definition
Class WMIExec
' The c_ prefix denotes variables global to the class
Private c_SWbemServices
Private c_Server, c_Command, c_StartIn
Private c_ProcID
' Connects to the specified computer; returns 0 for success
Public Function ConnectServer(ByVal Server, ByVal User, _
ByVal Password)
Dim SWbemLocator
' Update the class variable
c_Server = Server
' Assume the local computer if not specified
If c_Server = "" Then c_Server = "."
' Ignore user and password if connecting
' to the local computer
If c_Server = "." Then
User = ""
Password = ""
End If
Set SWbemLocator = CreateObject("WbemScripting.SWbemLocator")
SWbemLocator.Security_.ImpersonationLevel = 3 ' impersonate
On Error Resume Next
Set c_SWbemServices = SWbemLocator.ConnectServer(c_Server, _
"root/cimv2", User, Password)
ConnectServer = Err.Number
End Function
' Run the specified command line, starting in the specified
' directory, with the specified initial window state; returns
' 0 for success
Public Function RunProgram(ByVal Command, ByVal StartIn, _
ByVal ShowWindow)
Dim Startup, Process
' Update the class variables
c_Command = Command
c_StartIn = StartIn
' Create a Win32_ProcessStartup object and configure
' its ShowWindow property
Set Startup = _
c_SWbemServices.Get("Win32_ProcessStartup").SpawnInstance_()
Startup.ShowWindow = ShowWindow
Set Process = c_SWbemServices.Get("Win32_Process")
RunProgram = Process.Create(c_Command, c_StartIn, Startup, c_ProcID)
End Function
' Returns True if the process exists
Public Function ProcessExists()
ProcessExists = c_SWbemServices.ExecQuery("SELECT ProcessID FROM " _
& "Win32_Process WHERE ProcessID=" & c_ProcID).Count > 0
End Function
' Returns a status message using the class variables
Public Function Status()
Dim Server, StartIn
If c_Server = "." Then Server = "(LocalSystem)"
If c_StartIn = "" Then StartIn = "(Default)" Else StartIn = c_StartIn
Status = "Computer: " & Server & vbNewLine _
& "Command line: " & c_Command & vbNewLine _
& "Starting directory: " & StartIn & vbNewLine _
& "Process ID: " & CStr(c_ProcID)
End Function
End Class

You might also like