Customising your Setup - Part 2
There's the astonishing thing about human nature or should I say programmer nature that once you give someone a bit of excitement, they suddenly want more!
Hopefully that's what happened when you read the first part of my setup tutorial and due to the wonderful response, I decided to put together all your wildest fantasies come true Part Two!
Last time we created a variation of the default setup program with VB 6. This time around, we're going to add even more features, with the visual interface hitting an all-time high and functionality blasting through the roof, bringing shame on the likes of Visual Installer that ships with VB Service Pack 4.
<Dax suddenly notices the Microsoft lawsuit proceedings in his Inbox and decides to withdraw all previously made comments>
Intrigued? Well, let's look at what we're going to accomplish in this article...
Firstly, we'll enhance some existing setup dialogues, making them even brighter than before. Next, we'll perform a little editing to launch sub-setup programs, such as installing MS Agent and such. And to top it all off, we'll implement the "Function Bank", a truckload of neat, commented functions you'll find indispensable plus we'll even demonstrate how to use a couple of 'em!
Well, now I'm sure you're intrigued... enchanted even! ;)
<Ed: That's right, Dax. Enchanted. Uhuh.>
After all, if Microsoft made it this good... well, we can make it better! So let's get on with it...
<The Inbox of Dax buzzes and yet another lawsuit falls into his hands>
NOTE: Attention heroic/suicidal programmers. Setup1.exe was tested on Windows 2000 and nothing works!!! Sometime you get an "entry missing" error, sometimes an endless reboot, sometimes horrible crashes. Once it was so bad I had to re-install Win2k all over again! So beware
NOTE: We're using the same project we worked on in Part 1 of 'Customizing Your Setup'! We'll enhance that only! Don't create a new one, otherwise you may not be able to fully complete the work.
First off, we need to make sure it looks good! Your end-users might not appreciate the functionality, but they'll surely like the looks of your setup. And to do that, we need add a few more dialogs and perhaps enhance those already there.
According to the recent Dax Pandhi Setup Research Foundation survey, all of us are bored of the dialog shown while files are copied! ;) Well, my friends, not anymore
- Open VB and load Setup1.vbp from where you left it in Part 1
- Open FrmCopy in Design View. It looks something like this:
Well after we finish with it, it'll look like this:
- Set the form's Height property to 5000
- Drag all the existing controls to the lower part of the form as in the picture above
- Now, add an Image control as in the picture above
- Changes it's BorderStyle to "1 Fixed Single" and the Stretch property to True
- Load your splash screen or billboard or whatever you want into the Picture property. It is great to put advertising in this dialog box! ;)
Why not spruce up a few other setup forms, too? Or even add a host of new forms, as we did in the last tutorial?
Next up, we're going to add a little code to ensure your setup has that 'customized feel'.
Let's suppose you want to launch a sub-installer, which will install certain DLLs or components - Microsoft Agent, for example. You want that sub-installer to be launched before the setup copies its files. Here's what to do...
- In the code of frmBegin's cmdInstall, change this:
Private Sub cmdInstall_Click() If IsValidDestDir(gstrDestDir) = True Then Unload Me DoEvents End IfEnd Sub
Private Sub cmdInstall_Click() If IsValidDestDir(gstrDestDir) = True Then Shell GetWindowsDir & "myexe.exe" DoEvents ' Release the pressure off the processor. Unload Me DoEvents End IfEnd Sub
Now, this is what to do after the P&D Wizard creates your package:
- Open setup.lst in Notepad
Setup.lst has a [BootStrap] Section. It will look something like this:
File1=@VB6STKIT.DLL,$(WinSysPathSysFile),,,3/26/99 12:00:00 AM,101888,188.8.131.52
File2=@COMCAT.DLL,$(WinSysPathSysFile),$(DLLSelfRegister),,5/31/98 12:00:00 AM,22288,4.71.1460.1
File3=@STDOLE2.TLB,$(WinSysPathSysFile),$(TLBRegister),,6/8/99 7:44:36 AM,17920,2.40.4276.1
File4=@ASYCFILT.DLL,$(WinSysPathSysFile),,,6/8/99 7:44:36 AM,143632,2.40.4276.1
File5email@example.com,$(WinSysPathSysFile),$(DLLSelfRegister),,6/7/99 1:37:36 AM,164112,5.0.4275.1
File6=@OLEAUT32.DLL,$(WinSysPathSysFile),$(DLLSelfRegister),,6/8/99 7:44:36 AM,598288,2.40.4276.1
File7=@MSVBVM60.DLL,$(WinSysPathSysFile),$(DLLSelfRegister),,5/10/99 12:00:00 AM,1384448,184.108.40.206
- Add another line below the above:
Note: There is no comma, just a space between [EXE_Date] & [EXE_Time]! Be careful with that...
When packaging the setup in the P&D Wizard, be sure to add myexe.exe to the package! Fill in the info as stated in the line, which is: the EXE's Date, time, size and version. All must be exact.
Top Tip: Suicidal Win2k users, change the date from 1/1/11 to 1.1.11 At least you'll survive one error.
Shell command to execute scripts or such. Refer to the VB-World tips section or the VB help file for further information. Just add them to the setup.lst (Bootstrap File) file in the BootStrap Section.
Top Tip: If you don't want the user to see the sub-installer, use Shell [command_line], vbHide
Just make sure they're put in the frmBegin's cmdInstall_Click() code and followed by a DoEvents otherwise if the sub-installer is very memory consuming or is hogging the system's resources, the system may go very slow or cause crashes.
If you remember, we added NetCheck to setup1.vbp in Part 1 of this article. You can use the ITC (Internet Transfer Control) to download a file from the Net if your user wants to. For example, you may give him/her a Command Button, which if clicked downloads some EXEs or other installers from the net.
If you're not distributing over the Net, then it might be a problem to those without such a connection! So beware
We all know that you'll go around customizing these forms and adding more of them here and there.
But if you're going to get serious about customizing your setup, you'll need more functionality. And that means more coding, more work, and more bloomin' hassle... well no more!
Here, is a commented code dump of the best and most popular functions you're likely to need in your setup program... put together specially by yours truly.
Just add the following code to a new module in your setup project:
Public Function SetCurrentDirectory& Lib "kernel32" Alias _"SetCurrentDirectoryA" (ByVal lpPathName As String)Public Declare Function ShellExecute& _ Lib "shell32.dll" Alias _"ShellExecuteA" (ByVal hwnd As Long, _ ByVal lpOperation As String, _ ByVal lpFile As String, _ ByVal lpParameters As String, _ ByVal lpDirectory As String, _ ByVal nShowCmd As Long)Public Const SW_NORMAL = 1Public Declare Function RegCreateKeyEx& _ Lib "advapi32.dll" Alias _ "RegCreateKeyExA" (ByVal hKey As Long, _ ByVal lpSubKey As String, _ ByVal Reserved As Long, _ ByVal lpClass As String, _ ByVal dwOptions As Long, _ ByVal samDesired As Long, _ lpSecurityAttributes As Any, _ phkResult As Long, _ lpdwDisposition As Long)Public Declare Function RegSetValueEx& _ Lib "advapi32.dll" Alias "RegSetValueExA" _ (ByVal hKey As Long, ByVal lpValueName As String, _ ByVal Reserved As Long, _ ByVal dwType As Long, lpData As Any, _ ByVal cbData As Long)Public Declare Function RegCloseKey& _ Lib "advapi32.dll" _ (ByVal hKey As Long)Public Const HKEY_CURRENT_USER = &H80000001Public Const HKEY_LOCAL_MACHINE = &H80000002Public Const REG_OPTION_NON_VOLATILE = 0Public Const SYNCHRONIZE = &H100000Public Const STANDARD_RIGHTS_ALL = &H1F0000Public Const KEY_QUERY_VALUE = &H1Public Const KEY_SET_VALUE = &H2Public Const KEY_CREATE_SUB_KEY = &H4Public Const KEY_ENUMERATE_SUB_KEYS = &H8Public Const KEY_NOTIFY = &H10Public Const KEY_CREATE_LINK = &H20Public Const KEY_ALL_ACCESS = ((STANDARD_RIGHTS_ALL Or _KEY_QUERY_VALUE Or KEY_SET_VALUE Or KEY_CREATE_SUB_KEY Or _KEY_ENUMERATE_SUB_KEYS Or KEY_NOTIFY Or KEY_CREATE_LINK) _And (Not SYNCHRONIZE))Public Const ERROR_SUCCESS = 0&Public Const REG_SZ = 1Public Declare Function ExitWindowsEx& Lib "user32" _(ByVal uFlags As Long, ByVal dwReserved As Long)Public Declare Sub GlobalMemoryStatus Lib "kernel32" _(lpBuffer As MEMORYSTATUS)Public Type MEMORYSTATUS DwLength As Long DwMemoryLoad As Long DwTotalPhys As Long DwAvailPhys As Long DwTotalPageFile As Long DwAvailPageFile As Long dwTotalVirtual As Long dwAvailVirtual As LongEnd TypePublic UsedPhysicalMemory As LongPublic TotalPhysicalMemory As LongPublic AvailablePhysicalMemory As LongPublic TotalPageFile As LongPublic AvailablePageFile As LongPublic TotalVirtualMemory As LongPublic AvailableVirtualMemory As LongPublic Const EWX_LOGOFF = 0Public Const EWX_SHUTDOWN = 1Public Const EWX_REBOOT = 2Public Const EWX_FORCE = 4Public Declare Function FindWindow& Lib "user32" Alias _"FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String)Public Declare Function SendMessage& Lib "user32" Alias _"SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal _wParam As Long, lParam As Any)Public Const WM_CLOSE = &H10Public Declare Function OpenProcess& Lib "kernel32" _(ByVal dwDesiredAccess As Long, ByVal bInheritHandle As Long, _ByVal dwProcessId As Long)Public Declare Function GetExitCodeProcess& Lib "kernel32" _(ByVal hProcess As Long, lpExitCode As Long)Public Declare Function CloseHandle& Lib "kernel32" _(ByVal hObject As Long)Public Const STILL_ACTIVE = &H103Public Const PROCESS_QUERY_INFORMATION = &H400Public Declare Function SetWindowPos& Lib "user32" (ByVal _hwnd As Long, ByVal hWndInsertAfter As Long, ByVal X As Long, _ByVal Y As Long, ByVal cx As Long, ByVal cy As Long, _ByVal wFlags As Long)Const HWND_TOPMOST = -1Const SWP_NOSIZE = &H1Const SWP_NOMOVE = &H2' Set frm to the Form you want to put on top.Public Sub StayOnTop(frm As Form) SetWindowPos frm.hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE Or SWP_NOSIZEEnd Sub' Set strPath to the path of the appPublic Sub ShellWaitUntilClosed(strPath As String)Dim lngPID As LongDim lngHP As LongDim lngExitCode As LonglngPID = Shell(strPath, vbNormalFocus)lngHP = OpenProcess(PROCESS_QUERY_INFORMATION, False, lngPID)DoGetExitCodeProcess lngHP, lngExitCodeDoEventsLoop While (lngExitCode = STILL_ACTIVE)CloseHandle lngHPEnd Sub' Closes app by title. Set strTitle to the title of the app.Public Sub CloseAppByTitle(strTitle As String)Dim lngHwnd As LonglngHwnd = FindWindow(vbNullString, strTitle)SendMessage lngHwnd, WM_CLOSE, 0, 0End Sub' Exit windows use either, EWX_LOGOFF, EWX_SHUTDOWN, ' EWX_REBOOT or EWX_FORCE in bytCloseType.Public Sub ExitWindows(bytCloseType As Byte) If ExitWindowsEx(bytCloseType, 0) = 0 Then Else End End IfEnd Sub' Opens WebsitesPublic Sub OpenWebsite(strWebsite As String) If ShellExecute(&O0, "Open", strWebsite, vbNullString, vbNullString, _ SW_NORMAL) < 33 Then End IfEnd Sub' Sets current directoryPublic Sub SetCurrentDir(strDirectory As String) SetCurrentDir = SetCurrentDirectory(strDirectory) If SetCurrentDir = 0 Then End IfEnd Sub'Sets a key in the registry to have your app run the next ' time Windows is rebooted, or everytime Windows is rebooted.' Set AppName to the name of your application' Set CmdLine to the path of you application with any other arguments following' Set ThisUserOnly to true if the application should only be run ' when the current user reboots' Set RunEveryBoot to true if the application should run every ' reboot, instead of just the next timePublic Function RunNextBoot(ByVal AppName As String, ByVal _CmdLine As String, Optional ThisUserOnly As Boolean = False, _Optional RunEveryBoot As Boolean = False) Dim TopKey As Long Dim SubKey As String Dim nRet As Long Dim hKey As Long Dim nResult As Long If RunEveryBoot Then SubKey = "SOFTWARE\Microsoft\Windows\CurrentVersion\Run" Else SubKey = "SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce" End If If ThisUserOnly Then TopKey = HKEY_CURRENT_USER Else TopKey = HKEY_LOCAL_MACHINE End If nRet = RegCreateKeyEx(TopKey, SubKey, 0&, vbNullString, _ REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, ByVal 0&, hKey, nResult) If nRet = ERROR_SUCCESS Then nRet = RegSetValueEx(hKey, AppName, 0&, REG_SZ, _ByVal CmdLine, Len(CmdLine)) Call RegCloseKey(hKey) End If RunNextBoot = (nRet = ERROR_SUCCESS)End Function' Great for normal encryption. Set strKey to the password, strInPath ' to the file you want to encode/decode and strOutPath to ' the destination file.Public Sub XOREncDecFile(strKey As String, strInPath As String, strOutPath As String) Dim strInput As String Dim strOutput As String Dim dblX As Double Open strInPath For Binary As #1 strInput = Input(LOF(1), 1) Close #1 For dblX = 1 To Len(strInput) strKey = strKey & strKey If Len(strKey) >= Len(strInput) Then dblX = Len(strInput) End If Next dblX For dblX = 1 To Len(strInput) strOutput = strOutput & Chr(Asc(Mid(strInput, dblX, 1)) Xor Asc(Mid(strKey, dblX, 1))) Next dblX Open strOutPath For Binary As #1 Put #1, , strOutput Close #1End Sub' Creates a Net shortcut like in the Favorites folder.Public Sub CreateINetShortcut(strPath As String, strName As String, strURL As String) strPath = strName & ".url" Open strPath For Output As #1 Print #1, "[InternetShortcut]" Print #1, "URL=" & strURL Close #1End Sub'Gets information about your system's memory'Call the function by itself. It then loads some public variables with'the appropriate values.' UsedPhysicalMemory - what percentage of memory is currently in use' TotalPhysicalMemory - total physical memory in bytes' AvailablePhysicalMemory - available physical memory in bytes' TotalPageFile - number of bytes that the paging file can hold' AvailablePageFile - available space in the paging file in bytes' TotalVirtualMemory - the number of bytes in the virtual address space' accessible to a process while in user mode' AvailableVirtualMemory - the number of bytes in the virtual address' space accessible to a process while in user' mode that is unreserved and uncommittedPublic Sub GetMemoryInfo() Dim MemStatus As MEMORYSTATUS MemStatus.dwLength = Len(MemStatus) GlobalMemoryStatus MemStatus UsedPhysicalMemory = MemStatus.dwMemoryLoad TotalPhysicalMemory = MemStatus.dwTotalPhys AvailablePhysicalMemory = MemStatus.dwAvailPhys TotalPageFile = MemStatus.dwTotalPageFile AvailablePageFile = MemStatus.dwAvailPageFile TotalVirtualMemory = MemStatus.dwTotalVirtual AvailableVirtualMemory = MemStatus.dwAvailVirtualEnd Sub
When you run the P&D Wizard, it creates Setup.exe, the CAB files and a setup.lst file.
This setup.lst file is the data source of the setup program. As the EXE (setup1) is already compiled, it needs external data to work from. Setup.lst (or the BootStrap as Microsoft likes to call it) is just like an INI file to the setup program! It has it's own macros and styles. In this section, we'll learn a little about it, plus figure out how to edit the file...
First off, you should know about "macros", keywords in the setup.lst file which are replaced with something else when the setup program runs. This list describes the most common:
|$(WinSysPath)||System Folder of the OS|
|$(WinSysPathSysFile)||System File installed in System Folder. This file will not be removed during uninstallation|
|$(AppPath)||Application Folder where the software will be installed|
|$(AppPath)\folder||Use this to create \folder sub-folder in $(AppPath). Any path can be created with this.|
|$(CommonFiles)||Common Files folder. Generally combined with sub folder path.|
Same as $(CommonFiles)\System.
|$(ProgramFiles)||The program files folder on the user's system.|
|$(MSDAOPath)||Yes! The path for MS DAO installation! They thought of everything.|
|$(DLLSelfRegister)||Self-registering .dll, .ocx, or any other .dll file with self-registering information (exports DllRegisterServer and DllUnregisterServer|
|$(EXESelfRegister)||ActiveX .exe or any other .exe file supporting /RegServer & /UnRegServer command line switches|
|$(TLBSelfRegister)||For Type Library File|
|$(Remote)||Remote support file (.vbr)|
|$(Shared)||Makes the file shared. If file is installed in \Windows or \Windows\System, it is automatically shared|
|$(Programs)||Special for Program Groups|
NOTE: Only $(WinPath) and $(WinSysPath) are valid in the [BootStrapFiles] section
WARNING: Never use normal path like c:\somefolder\myfolder\! This will not be good for your setup program on other systems.
Experimenting with the BootStrap file is easy. In fact, it is highly encouraged! You just might learn something! ;)
If you use additional DLL or OCX files in setup1.vbp, like a custom progress bar or other components, specify them in the [BootStrapFiles] section in setup.lst like this:
File7=@MyDll.DLL,$(WinSysPathSysFile),$(DLLSelfRegister),,5/10/99 12:00:00 AM,1384448,220.127.116.11
Adding a comma can skip any parameter you don't want to specify!
Now, let's explore a few sections in the BootStrap file, with comments in red:
This is the title that appears in the pre-setup dialog.
SetupText=Copying Files, please stand by.
This is the text that appears in the pre-setup dialog.
This is the cab file name.
This is the EXE to launch for the setup program.
This is the uninstallation program.
This is the name of the temp folder setup will create in Windows\Temp\ for the temp files.
The number of cabs
The title that appears on the blue washed background.
Folder where the software will be installed
Main application EXE
App to uninstall
The bootstrap is easy to edit. Try messing around in it! You'll learn how easy it is!
NOTE: I covered a little of what can be done in the setup.lst file. However I highly recommend visiting http://msdn.microsoft.com/library/ or checking out VB's documentation for more on the SETUP.lst file and 'Packaging and Deployment' in general.
If you're using VB as a hobby, just learning the language or simply fooling around, no problemo. But when you go commercial, you'll encounter lots of problems and barriers. From my experience the following are the most common, and their solutions are the easiest (to be used with my function module listed earlier).
I give thee the power use it well J
Sometimes you need to create an Internet Shortcut. Use the following.
CreateINetShortcut(strPath As String, strName As String, strURL As String)
StrPath is the path of the shortcut for example "c:\windows\desktop\mynetshortcut.url"
StrName is the name of the shortcut for example "MyShortcut"
StrURL is the URL of the site for example "http://www.vb-world.net"
Sometimes you have a little information you may want to keep secure. Use XOR Encryption/decryption like this:
XOREncDecFile(strKey As String, strInPath As String, strOutPath As String)
StrKey is your password for the file NEVER use anything except A-Z and 0-9! Its been known to cause problems.
StrInPath is the encoded/normal file
StrOutPath is the decoded/encoded output file.
It's good if you don't use the same path for both strInPath and strOutPath.
Win2k and Package & Deployment Wizard: File dates are stored differently and errors can occur!
Of course, Microsoft sometimes forgets to report some bugs until after it has been fixed. Endless Reboot and Windows' system file corruption are just are a few.
To prevent such problems, I highly recommend you install the latest service packs for both your OS and Visual Basic. Service Pack 4 of Visual Studio/Visual Basic solves the Endless Reboot problem and a few more bugs.
Wahoo we've finished the project already!
Ah, wasn't it cool adding new interface elements, using encryption, sub-installers and a host of new functions? So go build that new setup EXE and have some fun!
If you need help or have comments, you can count on your setup guru. I can be contacted via that link way at the bottom of this page with an answer guaranteed within 24 hours or I'll have my money back! ;)
Due to heavy demand of new features such as AVI, MP3, MOD and WAV support in setup programs, we'll have yet another part of this great series. As you know, we give you what you want, so click the link below and tell us what you want and we'll add it to the next part!
Till Microsoft goes down the drain, you'll keep hearing from me.
Good luck! - Dax Pandhi, Setup Guru, VB-World
<After Dax has finished sorting out his lawsuits, he'll be straight back with more practical setup hints and tips, exclusively here at Developer.com. So join him once again in a few weeks time, same VB.Place, same VB.Time!>
E-mail Dax direct at firstname.lastname@example.org
Dax Pandhi is co-founder of Extreme Technovision. He lives in Bhuj, India and has over 10 years experience in programming. He holds the 1999-2000 international award for Best Amateur Animation, and has interests ranging from computers to dinosaur bone hunting. The Dax Pandhi personal Web page can be found at www.extremetechnovision.com/dax/