Ever found that the Visual Basic Application Setup Wizard does not meet your needs? Ever found it too expensive to purchase InstallShield? Don’t despair. Make your own setup program!
This article takes you through the basic steps of making a simple and effective setup program.
Where do you start?
The best place is at the beginning. Open up VB and make a new project. Add a module and a progress bar to the form. Name the progress bar pbProgress.
The first thing we need to do is to code in some constants and useful API calls, as we shall be using the registry to store some information. Add this code to the module:
Option Explicit 'Set Public Constants Public Const INVALID_HANDLE_VALUE = -1 Public Const MAX_PATH = 260 Public Const ThisApp = "VBS" Public Const ThisKey = "Setup Master" Public Const DestPath = "c:vbsquare" Type FILETIME dwLowDateTime As Long dwHighDateTime As Long End Type Type WIN32_FIND_DATA dwFileAttributes As Long ftCreationTime As FILETIME ftLastAccessTime As FILETIME ftLastWriteTime As FILETIME nFileSizeHigh As Long nFileSizeLow As Long dwReserved0 As Long dwReserved1 As Long cFileName As String * MAX_PATH cAlternate As String * 14 End Type Type SECURITY_ATTRIBUTES nLength As Long lpSecurityDescriptor As Long bInheritHandle As Long End Type Declare Function CreateDirectory Lib "kernel32" Alias "CreateDirectoryA" _ (ByVal lpPathName As String, _ lpSecurityAttributes As SECURITY_ATTRIBUTES) As Long Declare Function CopyFile Lib "kernel32" Alias "CopyFileA" _ (ByVal lpExistingFileName As String, _ ByVal lpNewFileName As String, _ ByVal bFailIfExists As Long) As Long Declare Function FindFirstFile Lib "kernel32" Alias "FindFirstFileA" _ (ByVal lpFileName As String, _ lpFindFileData As WIN32_FIND_DATA) As Long Declare Function FindNextFile Lib "kernel32" Alias "FindNextFileA" _ (ByVal hFindFile As Long, lpFindFileData As WIN32_FIND_DATA) As Long Declare Function FindClose Lib "kernel32" _ (ByVal hFindFile As Long) As Long 'API Calls for Registry Declare Function RegOpenKeyEx Lib "advapi32.dll" Alias "RegOpenKeyExA" _ (ByVal hKey As Long, ByVal lpSubKey As String, ByVal ulOptions As Long, _ ByVal samDesired As Long, phkResult As Long) As Long Declare Function RegQueryValueEx Lib "advapi32.dll" Alias "RegQueryValueExA" _ (ByVal hKey As Long, ByVal lpValueName As String, ByVal _ lpReserved As Long, ByVal lpType As Long, ByVal lpData As Any, _ lpcbData As Long) As Long Declare Function RegCloseKey Lib "advapi32.dll" (ByVal hKey As Long) As Long 'API Call for Control Registration Declare Function RegComCtl32 Lib "c:windowssystemComCtl32.dll" Alias _ "DllRegisterServer" () As Long 'Set other constants for Registry Const HKEY_LOCAL_MACHINE = &H80000002 Const HKEY_CURRENT_USER = &H80000001 Const READ_CONTROL = &H20000 Const KEY_QUERY_VALUE = &H1 Const KEY_ENUMERATE_SUB_KEYS = &H8 Const KEY_NOTIFY = &H10 Const SYNCHRONIZE = &H100000 Const ERROR_SUCCESS = 0& Const ERROR_MORE_DATA = 234 Const REG_SZ = 1 Const KEY_READ = ((READ_CONTROL Or _ KEY_QUERY_VALUE Or KEY_ENUMERATE_SUB_KEYS _ Or KEY_NOTIFY) And (Not SYNCHRONIZE)) Const KEY_EXECUTE = ((KEY_READ) And (Not SYNCHRONIZE))
Copying multiple files
A little routine which will save you some time if you have a lot of files with the same
extension in the same directory which you want to copy to your new directory. Add this
function to the module:
Public Function CopyFiles(sSourcePath As String, sDestination _ As String, sFiles As String) As Long 'Declare Variables Dim WFD As WIN32_FIND_DATA Dim SA As SECURITY_ATTRIBUTES Dim r As Long Dim hFile As Long Dim bNext As Long Dim copied As Long Dim currFile As String 'Make the dir r = CreateDirectory(sDestination, SA) hFile = FindFirstFile(sSourcePath & sFiles, WFD) 'Error Handling If (hFile = INVALID_HANDLE_VALUE) Then MsgBox "No " & sFiles & " files found." Exit Function End If 'Copy the files If hFile Then Do currFile = Left$(WFD.cFileName, InStr(WFD.cFileName, Chr$(0))) r = CopyFile(sSourcePath & currFile, sDestination & currFile, False) copied = copied + 1 bNext = FindNextFile(hFile, WFD) Loop Until bNext = 0 End If 'End Copying r = FindClose(hFile) CopyFiles = copied End Function
The function below is designed to copy all .htm files from one directory to another. Add it to the module and make necessary changes:
Public Function HTMLFiles() 'Declare Variables Dim sSourcePath As String Dim sDestination As String Dim sFiles As String Dim numCopied As Long 'Set the variables sSourcePath = App.Path + "" sDestination = DestPath sFiles = "*.htm" 'Copy the files numCopied = CopyFiles(sSourcePath, sDestination, sFiles) End Function
Copying Individual Files
If you have different file extensions then you can easily copy them using the FileCopy command. Just use this code:
FileCopy "c:windowscontrol.exe", "c:windowssystemcontrol.exe"
Events
Now that the base code is in place we need to actually do something with it. The following code shows a basic order of events. Add it to the form’s Load procedure:
Private Sub Form_Load() 'Load the form Show DoEvents pbProgress.Value = 0 DoEvents 'Declare Variables Dim Run As String 'Retrieve the Registry settings Run = GetSetting(ThisApp, ThisKey, "Run", "") DoEvents 'Check if Run is True If Run = "True" Then Unload me MsgBox "This program has been run before and therefore cannot be run again." Exit Sub End If SaveSetting ThisApp, ThisKey, "Run", "True" DoEvents 'Make the dir MkDir DestPath DoEvents pbProgress.Value = 33 DoEvents 'Copy the files HTMLFiles FileCopy App.Path & "index.html", DestPath & "index.html" DoEvents pbProgress.Value = 66 DoEvents pbProgress.Value = 100 DoEvents 'Quit the setup program Unload Me End End Sub
Distribution
For the program to work correctly you will need to supply to installable files with the
setup program. You can easily do this using a .zip file, using Winzip [ http://www.winzip.com ] or use a program such as the
Self Extracting Setup Wizard [ http://home.epix.net/~sirmango/wizard.html
]
Example
You can download a sample of this setup program at http://www.vbsquare.com/demos/setup-demo/index.html