A Self Installing VBScript

Over 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
Author: Stephen Millard
Tags: | vbs |

Buy me a coffeeBuy me a coffee



Related posts that you may also like to read