A Self Installing VBScript
28 Feb 2014Over the years I’ve been doing technical support I’ve had quite a few useful VBS scripts that I’d copy over to a user’s machine. Then if it necessary, they could be run again at a later date (by remote control or referring the user to it’s location and get them to run it). However I quickly tired of manually doing this and dealing with the instances where I’d accidentally copied the script to the user’s desktop instead of the location I’d really wanted. As a result I created some extra lines to add into these scripts that would install the script to the desired location and then run it from there. This meant I could run it from a flash or network drive and it would automatically go to the right place - even updating older scripts.
The script below is purely an example of what I used. The main processing for the script would simply be added into the “Main” sub routine. The script as it is presented here will just display a message box containing it’s location when run from the desired location.
Settings
The script has a setting at the top, called INSTALL_LOCATION. This is the path to the directory in which the script should be installed (copied). This can be an explicit path or a path containing environment variables - delimited by percentages. The example path shows how the script will cope with multiple (even successive) environment variables.
A second setting RUN_AFTER_INSTALL determines if the script will be run immediately following an install.
What it does
When the script is run it checks where it is located. If this doesn’t match the desired installation location, the script will take steps to install itself to that location (and optionally run it).
The installation process first checks to see if the installation folder exists. If it doesn’t then the script uses the MKDIR DOS command to create the folder path. It uses this as (with command extensions enabled) it will create multiple levels of missing directories. It then copies the script file to the location overwriting any existing version automatically.
Finally (if the RUN_AFTER_INSTALL is set to true), the script will be executed from the installation location.
The Script
Option Explicit
'Directory in which to install the script (it must end with a "\")
Const INSTALL_LOCATION = "%HOMEDRIVE%%HOMEPATH%\Scripts\vbs\Self Installer\%USERDOMAIN%\"
'When run after install is true, the installed script will be automatically run.
'When set to false, the script file will only be copied to the installation folder.
Const RUN_AFTER_INSTALL = true
'If this script isn't being run from the install location, install it to that location
'If this script is being run from the install location then execute the main code
If ScriptLocation <> GetInstallLocation Then
InstallScript RUN_AFTER_INSTALL
Else
Main
End If
'############################################
' SUB ROUTINES
'############################################
'Main routine
Sub Main
MsgBox "My file path is " & WScript.ScriptFullName, vbInformation, "Installed Script"
End Sub
'Install the script
Sub InstallScript(p_bRunAfterInstall)
'Initialise
Dim objFSO, objShell
Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objShell = Wscript.CreateObject("WScript.Shell")
'If the install directory does not exist, create it (using a call out to the DOS command MKDIR
If Not objFSO.FolderExists(GetInstallLocation) Then ExecCmd "mkdir """ & GetInstallLocation & """"
'Copy the new script file into the install directory
objFSO.CopyFile WScript.ScriptFullName, GetInstallLocation, true
'Give the OS some time to copy if it is a bit laggy - seen occasional issues here
Dim intCounter
Const WAIT_MS = 100
Const MAXTIME_MS = 4000
intCounter = 0
Do While Not(objFSO.FileExists(InstalledScriptPath) OR intCounter > (MAXTIME_MS/WAIT_MS))
WScript.Sleep WAIT_MS
intCounter = intCounter + 1
Loop
'If the script wasn't copied output an error message, otherwise run if required.
If objFSO.FileExists(InstalledScriptPath) Then
If p_bRunAfterInstall then objShell.Run """" & InstalledScriptPath & """"
Else
MsgBox "Installation timed out.", vbCritical, "Installation"
End If
End Sub
'Execute a DOS command
Sub ExecCmd(p_strDOSCmd)
Dim objCommand
Set objCommand = New clsDOSCommandExecutor
objCommand.ExecuteCommand(p_strDOSCmd)
End Sub
'############################################
' FUNCTIONS
'############################################
'Return the directory the script is in
Function ScriptLocation()
ScriptLocation = Replace(WScript.ScriptFullName, WScript.ScriptName, "")
End Function
'Return the full path to the installed script
Function InstalledScriptPath()
InstalledScriptPath = GetInstallLocation & WScript.ScriptName
End Function
'Return the installation directory with environment variables expanded
Function GetInstallLocation()
GetInstallLocation = SubstituteEnvironmentVariables(INSTALL_LOCATION)
End Function
'Replace environment variables in a string (delimited by %'s) with the expanded values
Function SubstituteEnvironmentVariables(p_strInput)
Dim objShell
Dim astrInput, intItem
Set objShell = Wscript.CreateObject("WScript.Shell")
'Tokenise the input on percentages and initialise the return string
astrInput = Split(p_strInput, "%")
SubstituteEnvironmentVariables = ""
'Work through the elements and carry out any substitutions
For intItem = 0 to (UBound(astrInput))
'If we're on an odd item it must be an environment variable
If IsOdd(intItem) Then
'Expand the environment variable
SubstituteEnvironmentVariables = SubstituteEnvironmentVariables & objShell.ExpandEnvironmentStrings("%" & astrInput(intItem) & "%")
Else
SubstituteEnvironmentVariables = SubstituteEnvironmentVariables & astrInput(intItem)
End If
next
End Function
'Determine if a number is odd
Function IsOdd(p_intValue)
'Set default
IsOdd = false
'Now check if there's a remainder from modulo 2
If p_intValue mod 2 = 1 Then IsOdd = true
End Function
'############################################
' CLASSES
'############################################
'DOS command execution class
'Based on http://www.thoughtasylum.com/blog/2009/8/8/dos-command-class-for-vbscript.html
'Slight modification to enforce command extensions to be on as MKDIR will require this.
Class clsDOSCommandExecutor
Dim objShell, objExec
Dim strCommand
Dim strError
Dim objError
Dim objOutput
Dim strOutput
Sub ExecuteCommand(p_strCommand)
strCommand = "cmd /E:ON /c " & p_strCommand
Set objShell = CreateObject("Wscript.Shell" )
objShell.Exec(strCommand)
Set objExec = objShell.Exec(strCommand)
Do Until objExec.Status
Wscript.Sleep 200
Loop
Set objError = objExec.StdErr
strError = objError.ReadAll
Set objOutput = objExec.stdOut
strOutput = objOutput.ReadAll
End Sub
Function GetOutput()
GetOutput = strOutput
End Function
Function GetError()
GetError = strError
End Function
Function Failed()
If strError = "" Then
Failed = false
Else
Failed = true
End If
End Function
End Class