home *** CD-ROM | disk | FTP | other *** search
/ QBasic & Borland Pascal & C / Delphi5.iso / Basic / Visual Basic.60 / VB98 / WIZARDS / PDWIZARD / SETUP1 / SETUP1.BAS < prev    next >
Encoding:
BASIC Source File  |  1998-06-20  |  208.5 KB  |  5,670 lines

  1. Attribute VB_Name = "basSetup1"
  2. Option Explicit
  3. Option Compare Text
  4.  
  5. '
  6. ' Global Constants
  7. '
  8.  
  9. Public Enum OverwriteReturnVal
  10.     owYes
  11.     owNo
  12.     owNoToAll
  13. End Enum
  14.  
  15. 'Return values for setup toolkit functions
  16. Global Const gintRET_CONT% = 1
  17. Global Const gintRET_CANCEL% = 2
  18. Global Const gintRET_EXIT% = 3
  19. Global Const gintRET_ABORT% = 4
  20. Global Const gintRET_FATAL% = 5
  21. Global Const gintRET_FINISHEDSUCCESS% = 6 'Used only as parameter to ExitSetup at end of successful install
  22.  
  23. 'Error levels for GetAppRemovalCmdLine()
  24. Global Const APPREMERR_NONE = 0 'no error
  25. Global Const APPREMERR_FATAL = 1 'fatal error
  26. Global Const APPREMERR_NONFATAL = 2 'non-fatal error, user chose to abort
  27. Global Const APPREMERR_USERCANCEL = 3 'user chose to cancel (no error)
  28.  
  29. 'Flag for Path Dialog specifying Source or Dest directory needed
  30. Global Const gstrDIR_SRC$ = "S"
  31. Global Const gstrDIR_DEST$ = "D"
  32.  
  33. 'Beginning of lines in [Files], [Bootstrap], and [Licenses] sections of SETUP.LST
  34. Global Const gstrINI_FILE$ = "File"
  35. Global Const gstrINI_REMOTE$ = "Remote"
  36. Global Const gstrINI_LICENSE$ = "License"
  37. '
  38. ' Command line constants
  39. '
  40. Global Const gstrSILENTSWITCH = "s"
  41. Global Const gstrSMSSWITCH = "q"
  42. '
  43. ' Icon Information
  44. '
  45. Global Const gsGROUP As String = "Group"
  46. Global Const gsICON As String = "Icon"
  47. Global Const gsTITLE As String = "Title"
  48. Global Const gsICONGROUP As String = "IconGroups"
  49.  
  50. Global Const gstrINI_BOOTFILES$ = "Bootstrap Files"
  51.  
  52. 'Font info
  53. Global Const gsEXT_FONTTTF As String = "TTF"
  54. Global Const gsEXT_FONTFON As String = "FON"
  55. Declare Function AddFontResource Lib "gdi32" Alias "AddFontResourceA" (ByVal lpFilename As String) As Long
  56.  
  57. 'Registry files (execute them based on .reg extension)
  58. Global Const gsREGEDIT As String = "regedit /s "
  59. Global Const gsEXT_REG As String = "reg"
  60. '
  61. 'Type Definitions
  62. '
  63. Type FILEINFO                                               ' Setup information file line format
  64.     intDiskNum As Integer                                   ' disk number
  65.     fSplit As Integer                                       ' split flag
  66.     strSrcName As String                                    ' name of source file
  67.     strDestName As String                                   ' name of destination file
  68.     strDestDir As String                                    ' destination directory
  69.     strRegister As String                                   ' registration info
  70.     fShared As Boolean                                      ' whether the file is shared or private
  71.     fSystem As Boolean                                      ' whether the file is a system file (i.e. should be installed but never removed)
  72.     varDate As Date                                         ' file date
  73.     lFileSize As Long                                       ' file size
  74.     sVerInfo As VERINFO                                     ' file version number
  75.     strReserved As String                                   ' Reserved. Leave empty, or error.
  76.     strProgramIconTitle As String                                ' Caption for icon in program group
  77.     strProgramIconCmdLine As String                         ' Command Line for icon in program group
  78. End Type
  79.  
  80. Type DISKINFO                                               ' Disk drive information
  81.     lAvail As Long                                          ' Bytes available on drive
  82.     lReq As Long                                            ' Bytes required for setup
  83.     lMinAlloc As Long                                       ' minimum allocation unit
  84. End Type
  85.  
  86. Type DESTINFO                                               ' save dest dir for certain files
  87.     strAppDir As String
  88.     strAUTMGR32 As String
  89.     strRACMGR32 As String
  90. End Type
  91.  
  92. Type REGINFO                                                ' save registration info for files
  93.     strFilename As String
  94.     strRegister As String
  95.     
  96.     'The following are used only for remote server registration
  97.     strNetworkAddress As String
  98.     strNetworkProtocol As String
  99.     intAuthentication As Integer
  100.     fDCOM As Boolean      ' True if DCOM, otherwise False
  101. End Type
  102.  
  103. '
  104. 'Global Variables
  105. '
  106. Global gstrSETMSG As String
  107. Global gfRetVal As Integer                                  'return value for form based functions
  108. Global gstrAppName As String                                'name of app being installed
  109. Global gintCabs As Long
  110. Global gstrTitle As String                                  '"setup" name of app being installed
  111. Public gstrDefGroup As String                               'Default name for group -- from setup.lst
  112. Global gstrDestDir As String                                'dest dir for application files
  113. Global gstrAppExe As String                                 'name of app .EXE being installed
  114. Public gstrAppToUninstall As String                         ' Name of app exe/ocx/dll to be uninstalled.  Should be the same as gstrAppExe in most cases.
  115. Global gstrSrcPath As String                                'path of source files
  116. Global gstrSetupInfoFile As String                          'pathname of SETUP.LST file
  117. Global gstrWinDir As String                                 'windows directory
  118. Global gstrFontDir As String                                'windows\font directory
  119. Global gstrWinSysDir As String                              'windows\system directory
  120. Global gsDiskSpace() As DISKINFO                            'disk space for target drives
  121. Global gstrDrivesUsed As String                             'dest drives used by setup
  122. Global glTotalCopied As Long                                'total bytes copied so far
  123. Global gintCurrentDisk As Integer                           'current disk number being installed
  124. Global gsDest As DESTINFO                                   'dest dirs for certain files
  125. Global gstrAppRemovalLog As String                           'name of the app removal logfile
  126. Global gstrAppRemovalEXE As String                           'name of the app removal executable
  127. Global gfAppRemovalFilesMoved As Boolean                     'whether or not the app removal files have been moved to the application directory
  128. Global gfForceUseDefDest As Boolean                         'If set to true, then the user will not be prompted for the destination directory
  129. Global fMainGroupWasCreated As Boolean                     'Whether or not a main folder/group has been created
  130. Public gfRegDAO As Boolean                                 ' If this gets set to true in the code, then
  131.                                                            ' we need to add some registration info for DAO
  132.                                                            ' to the registry.
  133.  
  134. Global gsCABNAME As String
  135. Global gsTEMPDIR As String
  136.  
  137. Global Const gsINI_CABNAME As String = "Cab"
  138. Global Const gsINI_TEMPDIR As String = "TmpDir"
  139. '
  140. 'Form/Module Constants
  141. '
  142.  
  143. 'SetFileTime junk
  144. Public Type FileTime
  145.         dwLowDateTime As Long
  146.         dwHighDateTime As Long
  147. End Type
  148. Public Type SYSTEMTIME
  149.         wYear As Integer
  150.         wMonth As Integer
  151.         wDayOfWeek As Integer
  152.         wDay As Integer
  153.         wHour As Integer
  154.         wMinute As Integer
  155.         wSecond As Integer
  156.         wMilliseconds As Integer
  157. End Type
  158.  
  159. Public Const GENERIC_WRITE = &H40000000
  160. Public Const GENERIC_READ = &H80000000
  161. Public Const FILE_ATTRIBUTE_NORMAL = &H80
  162. Public Const INVALID_HANDLE_VALUE = -1
  163. Public Const FILE_SHARE_READ = &H1
  164. Public Const FILE_SHARE_WRITE = &H2
  165. Public Const CREATE_NEW = 1
  166. Public Const CREATE_ALWAYS = 2
  167. Public Const OPEN_EXISTING = 3
  168. Public Const OPEN_ALWAYS = 4
  169.  
  170. Public Declare Function LocalFileTimeToFileTime Lib "Kernel32" (lpFileTime As FileTime, lpLocalFileTime As FileTime) As Long
  171. Public Declare Function CreateFile Lib "Kernel32" Alias "CreateFileA" (ByVal lpFilename As String, ByVal dwDesiredAccess As Long, ByVal dwShareMode As Long, ByVal lpSecurityAttributes As Long, ByVal dwCreationDisposition As Long, ByVal dwFlagsAndAttributes As Long, ByVal hTemplateFile As Long) As Long
  172. Public Declare Function SetFileTime Lib "Kernel32" (ByVal hFile As Long, lpCreationTime As FileTime, lpLastAccessTime As FileTime, lpLastWriteTime As FileTime) As Long
  173. Public Declare Function CloseHandle Lib "Kernel32" (ByVal hObject As Long) As Long
  174. Public Declare Function SystemTimeToFileTime Lib "Kernel32" (lpSystemTime As SYSTEMTIME, lpFileTime As FileTime) As Long
  175. Public Declare Function VariantChangeTypeEx Lib "oleaut32.dll" (ByVal pvArgDest As Long, ByVal pvArgSrc As Long, ByVal LCID As Long, ByVal wFlags As Integer, ByVal VarType As Integer) As Long
  176. Public Declare Function VariantTimeToSystemTime Lib "oleaut32.dll" (ByVal vtime As Date, lpSystemTime As SYSTEMTIME) As Long
  177.  
  178. 'Possible ProgMan actions
  179. Const mintDDE_ITEMADD% = 1                                  'AddProgManItem flag
  180. Const mintDDE_GRPADD% = 2                                   'AddProgManGroup flag
  181.  
  182. 'Special file names
  183. Const mstrFILE_APPREMOVALLOGBASE$ = "ST6UNST"               'Base name of the app removal logfile
  184. Const mstrFILE_APPREMOVALLOGEXT$ = ".LOG"                   'Default extension for the app removal logfile
  185. Const mstrFILE_AUTMGR32 = "AUTMGR32.EXE"
  186. Const mstrFILE_RACMGR32 = "RACMGR32.EXE"
  187. Const mstrFILE_RICHED32$ = "RICHED32.DLL"
  188.  
  189. 'Name of temporary file used for concatenation of split files
  190. Const mstrCONCATFILE$ = "VB5STTMP.CCT"
  191.  
  192. 'setup information file registration macros
  193. Const mstrDLLSELFREGISTER$ = "$(DLLSELFREGISTER)"
  194. Const mstrEXESELFREGISTER$ = "$(EXESELFREGISTER)"
  195. Const mstrTLBREGISTER$ = "$(TLBREGISTER)"
  196. Const mstrREMOTEREGISTER$ = "$(REMOTE)"
  197. Const mstrVBLREGISTER$ = "$(VBLREGISTER)"  ' Bug 5-8039
  198.  
  199. '
  200. 'Form/Module Variables
  201. '
  202. Private msRegInfo() As REGINFO                                  'files to be registered
  203. Private mlTotalToCopy As Long                                   'total bytes to copy
  204. Private mintConcatFile As Integer                               'handle of dest file for concatenation
  205. Private mlSpaceForConcat As Long                                'extra space required for concatenation
  206. Private mstrConcatDrive As String                               'drive to use for concatenation
  207. Private mstrVerTmpName As String                                'temp file name for VerInstallFile API
  208.  
  209. ' Hkey cache (used for logging purposes)
  210. Private Type HKEY_CACHE
  211.     hKey As Long
  212.     strHkey As String
  213. End Type
  214.  
  215. Private hkeyCache() As HKEY_CACHE
  216.  
  217. ' Registry manipulation API's (32-bit)
  218. Global Const HKEY_CLASSES_ROOT = &H80000000
  219. Global Const HKEY_CURRENT_USER = &H80000001
  220. Global Const HKEY_LOCAL_MACHINE = &H80000002
  221. Global Const HKEY_USERS = &H80000003
  222. Const ERROR_SUCCESS = 0&
  223. Const ERROR_NO_MORE_ITEMS = 259&
  224.  
  225. Const REG_SZ = 1
  226. Const REG_BINARY = 3
  227. Const REG_DWORD = 4
  228.  
  229.  
  230. Declare Function OSRegCloseKey Lib "advapi32" Alias "RegCloseKey" (ByVal hKey As Long) As Long
  231. Declare Function OSRegCreateKey Lib "advapi32" Alias "RegCreateKeyA" (ByVal hKey As Long, ByVal lpszSubKey As String, phkResult As Long) As Long
  232. Declare Function OSRegDeleteKey Lib "advapi32" Alias "RegDeleteKeyA" (ByVal hKey As Long, ByVal lpszSubKey As String) As Long
  233. Declare Function OSRegEnumKey Lib "advapi32" Alias "RegEnumKeyA" (ByVal hKey As Long, ByVal iSubKey As Long, ByVal lpszName As String, ByVal cchName As Long) As Long
  234. Declare Function OSRegOpenKey Lib "advapi32" Alias "RegOpenKeyA" (ByVal hKey As Long, ByVal lpszSubKey As String, phkResult As Long) As Long
  235. Declare Function OSRegQueryValueEx Lib "advapi32" Alias "RegQueryValueExA" (ByVal hKey As Long, ByVal lpszValueName As String, ByVal dwReserved As Long, lpdwType As Long, lpbData As Any, cbData As Long) As Long
  236. Declare Function OSRegSetValueEx Lib "advapi32" Alias "RegSetValueExA" (ByVal hKey As Long, ByVal lpValueName As String, ByVal Reserved As Long, ByVal dwType As Long, ByVal lpData As String, ByVal cbData As Long) As Long
  237. Declare Function OSRegSetValueNumEx Lib "advapi32" 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) As Long
  238.  
  239. Declare Sub lstrcpyn Lib "Kernel32" (ByVal strDest As String, ByVal strSrc As Any, ByVal lBytes As Long)
  240. Declare Function GetCurrentProcessId Lib "Kernel32" () As Long
  241. Declare Function ExtractFileFromCab Lib "vb6stkit.dll" (ByVal Cab As String, ByVal File As String, ByVal Dest As String, ByVal iCab As Long, ByVal sSrc As String) As Long
  242. 'Reboot info
  243. Public Const ANYSIZE_ARRAY = 1
  244.  
  245. Type LARGE_INTEGER
  246.     lowpart As Long
  247.     highpart As Long
  248. End Type
  249.  
  250. Type LUID_AND_ATTRIBUTES
  251.         pLuid As LARGE_INTEGER
  252.         Attributes As Long
  253. End Type
  254.  
  255. Type TOKEN_PRIVILEGES
  256.     PrivilegeCount As Long
  257.     Privileges(ANYSIZE_ARRAY) As LUID_AND_ATTRIBUTES
  258. End Type
  259.  
  260. Public Const TOKEN_ADJUST_PRIVILEGES = 32
  261. Public Const TOKEN_QUERY = 8
  262. Public Const SE_PRIVILEGE_ENABLED As Long = 2
  263.  
  264. Declare Function LookupPrivilegeValue Lib "advapi32.dll" Alias "LookupPrivilegeValueA" (ByVal lpSystemName As String, ByVal lpName As String, lpLuid As LARGE_INTEGER) As Long
  265. Declare Function GetCurrentProcess Lib "Kernel32" () As Long
  266. Declare Function AdjustTokenPrivileges Lib "advapi32.dll" (ByVal TokenHandle As Long, ByVal DisableAllPrivileges As Long, NewState As TOKEN_PRIVILEGES, ByVal BufferLength As Long, PreviousState As TOKEN_PRIVILEGES, ReturnLength As Long) As Long
  267. Declare Function OpenProcessToken Lib "advapi32.dll" (ByVal ProcessHandle As Long, ByVal DesiredAccess As Long, TokenHandle As Long) As Long
  268. 'Exit the program and return an error code
  269. Private Declare Sub ExitProcess Lib "Kernel32" (ByVal uExitCode As Long)
  270. '-----------------------------------------------------------
  271. ' SUB: AddPerAppPath
  272. '
  273. ' Adds an application's full pathname and per-app path to the
  274. '   system registry (this is currently only meaningful to
  275. '   Windows 95).
  276. '
  277. ' IN: [strAppExe] - app EXE name, not including path
  278. '     [strAppDir] - full path of EXE, not including filename
  279. '     [strAppPath] - per-app path for this application
  280. '       (semicolon-separated list of directory path names)
  281. '       If this is the empty string (""), no per-app path
  282. '       is registered, but the full pathname of the
  283. '       exe IS still registered.
  284. '
  285. ' OUT:
  286. '   Example registry entries:
  287. '     HKEY_LOCAL_MACHINE\[strPathsBaseKeyName]\MyApp.Exe
  288. '       [Default]=C:\Program Files\MyApp\MyApp.Exe
  289. '       [Path]=C:\Program Files\MyApp;C:\Program Files\MyApp\System
  290. '
  291. '-----------------------------------------------------------
  292. '
  293. Sub AddPerAppPath(ByVal strAppExe As String, ByVal strAppDir As String, ByVal strPerAppPath As String)
  294.     If Not TreatAsWin95() Then
  295.         Exit Sub
  296.     End If
  297.     
  298.     Dim strPathsBaseKeyName As String
  299.     Const strAppPaths$ = "App Paths"
  300.     Const strAppPathKeyName = "Path"
  301.     Dim fOk As Boolean
  302.     Dim hKey As Long
  303.     
  304.     AddDirSep strAppDir
  305.     
  306.     ' Create the new key, whose name is based on the app's name
  307.    If Not RegCreateKey(HKEY_LOCAL_MACHINE, RegPathWinCurrentVersion(), strAppPaths & gstrSEP_DIR & strAppExe, hKey) Then
  308.         GoTo Err
  309.     End If
  310.     
  311.     fOk = True
  312.     
  313.     ' Default value indicates full EXE pathname
  314.     fOk = fOk And RegSetStringValue(hKey, "", strAppDir & strAppExe)
  315.     
  316.     ' [Path] value indicates the per-app path
  317.     If strPerAppPath <> "" Then
  318.         fOk = fOk And RegSetStringValue(hKey, strAppPathKeyName, strPerAppPath)
  319.     End If
  320.     
  321.     If Not fOk Then
  322.         GoTo Err
  323.     End If
  324.     
  325.     RegCloseKey hKey
  326.     
  327.     Exit Sub
  328.     
  329. Err:
  330.     MsgError ResolveResString(resERR_REG), vbExclamation Or vbOKOnly, gstrTitle
  331.     '
  332.     ' If we are running an SMS install, we can't continue.
  333.     '
  334.     If gfSMS Then
  335.         ExitSetup frmSetup1, gintRET_FATAL
  336.     End If
  337. End Sub
  338.  
  339. '-----------------------------------------------------------
  340. ' FUNCTION: AddQuotesToFN
  341. '
  342. ' Given a pathname (directory and/or filename), returns
  343. '   that pathname surrounded by double quotes if the
  344. '   path contains spaces or commas.  This is required for
  345. '   setting up an icon correctly, since otherwise such paths
  346. '   would be interpreted as a pathname plus arguments.
  347. '-----------------------------------------------------------
  348. '
  349. Function AddQuotesToFN(ByVal strFilename) As String
  350.     If InStr(strFilename, " ") Or InStr(strFilename, ",") Then
  351.         AddQuotesToFN = """" & strFilename & """"
  352.     Else
  353.         AddQuotesToFN = strFilename
  354.     End If
  355. End Function
  356.  
  357. '-----------------------------------------------------------
  358. ' SUB: CalcDiskSpace
  359. '
  360. ' Calculates disk space required for installing the files
  361. ' listed in the specified section of the setup information
  362. ' file (SETUP.LST)
  363. '-----------------------------------------------------------
  364. '
  365. Sub CalcDiskSpace(ByVal strsection As String)
  366.     Static fSplitFile As Integer
  367.     Static lDestFileSpace As Long
  368.  
  369.     Dim intIdx As Integer
  370.     Dim intDrvIdx As Integer
  371.     Dim sFile As FILEINFO
  372.     Dim strDrive As String
  373.     Dim lThisFileSpace As Long
  374.  
  375.     intIdx = 1
  376.  
  377.     On Error GoTo CalcDSError
  378.  
  379.     '
  380.     'For each file in the specified section, read info from the setup info file
  381.     '
  382.     Do While ReadSetupFileLine(strsection, intIdx, sFile) = True
  383.         '
  384.         'if the file isn't split or if this is the first section of a split file
  385.         '
  386.         If sFile.strDestDir <> vbNullString Then
  387.             fSplitFile = sFile.fSplit
  388.  
  389.             '
  390.             'Get the dest drive used for this file.  If this is the first file using
  391.             'the drive for a destination, add the drive to the drives used 'table',
  392.             'allocate an array element for the holding the drive info, and get
  393.             'available disk space and minimum allocation unit
  394.             '
  395.             strDrive = Left$(sFile.strDestDir, 1)
  396.         
  397.             intDrvIdx = InStr(gstrDrivesUsed, strDrive)
  398.             If intDrvIdx = 0 Then
  399.                 gstrDrivesUsed = gstrDrivesUsed & strDrive
  400.                 intDrvIdx = Len(gstrDrivesUsed)
  401.  
  402.                 ReDim Preserve gsDiskSpace(intDrvIdx)
  403.                 gsDiskSpace(intDrvIdx).lAvail = GetDiskSpaceFree(strDrive)
  404.  
  405.                 gsDiskSpace(intDrvIdx).lMinAlloc = GetDrivesAllocUnit(strDrive)
  406.             End If
  407.  
  408.             '
  409.             'Calculate size of the dest final (file size + minimum allocation for drive)
  410.             '
  411.             lThisFileSpace = CalcFinalSize(sFile.lFileSize, strDrive)
  412.             mlTotalToCopy = mlTotalToCopy + lThisFileSpace
  413.  
  414.             '
  415.             'If the file already exists, then if we copy it at all, we'll be
  416.             'replacing it.  So, we get the size of the existing dest file so
  417.             'that we can subtract it from the amount needed later.
  418.             '
  419.             If FileExists(sFile.strDestDir & sFile.strDestName) Then
  420.                 lDestFileSpace = FileLen(sFile.strDestDir & sFile.strDestName)
  421.             Else
  422.                 lDestFileSpace = 0
  423.             End If
  424.         End If
  425.  
  426.         '
  427.         'If file not split, or if the last section of a split file
  428.         '
  429.         If sFile.fSplit = False Then
  430.             '
  431.             'If this is the last section of a split file, then if it's the *largest*
  432.             'split file, set the extra space needed for concatenation to this size
  433.             '
  434.             If fSplitFile = True And lThisFileSpace > mlSpaceForConcat Then
  435.                 mlSpaceForConcat = lThisFileSpace
  436.             End If
  437.  
  438.             '
  439.             'Subtract size of existing dest file, if applicable and then accumulate
  440.             'space required
  441.             '
  442.             lThisFileSpace = lThisFileSpace - lDestFileSpace
  443.             If lThisFileSpace < 0 Then
  444.                 lThisFileSpace = 0
  445.             End If
  446.  
  447.             gsDiskSpace(intDrvIdx).lReq = gsDiskSpace(intDrvIdx).lReq + lThisFileSpace
  448.         End If
  449.  
  450.         intIdx = intIdx + 1
  451.     Loop
  452.  
  453.     Exit Sub
  454.  
  455. CalcDSError:
  456.     MsgError Error$ & vbLf & vbLf & ResolveResString(resCALCSPACE), vbCritical, gstrSETMSG
  457.     ExitSetup frmMessage, gintRET_FATAL
  458. End Sub
  459.  
  460. '-----------------------------------------------------------
  461. ' SUB: CalcFinalSize
  462. '
  463. ' Computes the space required for a file of the size
  464. ' specified on the given dest path.  This includes the
  465. ' file size plus a padding to ensure that the final size
  466. ' is a multiple of the minimum allocation unit for the
  467. ' dest drive
  468. '-----------------------------------------------------------
  469. '
  470. Function CalcFinalSize(lBaseFileSize As Long, strDestPath As String) As Long
  471.     Dim lMinAlloc As Long
  472.     Dim intPadSize As Long
  473.  
  474.     lMinAlloc = gsDiskSpace(InStr(gstrDrivesUsed, Left$(strDestPath, 1))).lMinAlloc
  475.     intPadSize = lMinAlloc - (lBaseFileSize Mod lMinAlloc)
  476.     If intPadSize = lMinAlloc Then
  477.         intPadSize = 0
  478.     End If
  479.  
  480.     CalcFinalSize = lBaseFileSize + intPadSize
  481. End Function
  482.  
  483. '-----------------------------------------------------------
  484. ' SUB: CenterForm
  485. '
  486. ' Centers the passed form just above center on the screen
  487. '-----------------------------------------------------------
  488. '
  489. Sub CenterForm(frm As Form)
  490.     SetMousePtr vbHourglass
  491.  
  492.     frm.Top = (Screen.Height * 0.85) \ 2 - frm.Height \ 2
  493.     frm.Left = Screen.Width \ 2 - frm.Width \ 2
  494.  
  495.     SetMousePtr gintMOUSE_DEFAULT
  496. End Sub
  497. '-----------------------------------------------------------
  498. ' SUB: UpdateDateTime
  499. '
  500. ' Updates the date/time for bootstrap files
  501. '-----------------------------------------------------------
  502. '
  503. Sub UpdateDateTime()
  504.     Dim intIdx As Integer
  505.     Dim sFile As FILEINFO
  506.     Dim lTime As FileTime
  507.     Dim hFile As Long
  508.     '
  509.     'For each file in the specified section, read info from the setup info file
  510.     '
  511.     intIdx = 1
  512.     Do While ReadSetupFileLine(gstrINI_BOOTFILES, intIdx, sFile) = True
  513.         Dim sCurDate As String, sFileDate As String
  514.         
  515.         sFileDate = Format(FileDateTime(sFile.strDestDir & sFile.strDestName), "m/d/yyyy h:m")
  516.         sCurDate = Format(Now, "m/d/yyyy h:m")
  517.         
  518.         If sFileDate = sCurDate Then
  519.             lTime = GetFileTime(sFile.varDate)
  520.             hFile = CreateFile(sFile.strDestDir & sFile.strDestName, GENERIC_WRITE Or GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
  521.             Call SetFileTime(hFile, lTime, lTime, lTime)
  522.             DoEvents
  523.             CloseHandle hFile
  524.         End If
  525.         intIdx = intIdx + 1
  526.     Loop
  527.     
  528. End Sub
  529.  
  530. '-----------------------------------------------------------
  531. ' FUNCTION: CheckDiskSpace
  532. '
  533. ' Reads from the space required array generated by calling
  534. ' the 'CalcDiskSpace' function and determines whether there
  535. ' is sufficient free space on all of the drives used for
  536. ' installation
  537. '
  538. ' Returns: True if there is enough space, False otherwise
  539. '-----------------------------------------------------------
  540. '
  541. Function CheckDiskSpace() As Integer
  542.     Static fDontAskOnSpaceErr As Integer
  543.  
  544.     Dim intIdx As Integer
  545.     Dim intTmpDrvIdx As Integer
  546.     Dim lDiskSpaceLeft As Long
  547.     Dim lMostSpaceLeft As Long
  548.                                              
  549.     '
  550.     'Default to True (enough space on all drives)
  551.     '
  552.     CheckDiskSpace = True
  553.  
  554.     '
  555.     'For each drive that is the destination for one or more files, compare
  556.     'the space available to the space required.
  557.     '
  558.     For intIdx = 1 To Len(gstrDrivesUsed)
  559.         lDiskSpaceLeft = gsDiskSpace(intIdx).lAvail - gsDiskSpace(intIdx).lReq
  560.         If lDiskSpaceLeft < 0 Then
  561.             GoSub CheckDSAskSpace
  562.         Else
  563.             '
  564.             'If no "TMP" drive was found, or if the "TMP" drive wasn't ready,
  565.             'save the index of the drive and the amount of space on the drive
  566.             'which will have the most free space.  If no "TMP" drive was
  567.             'found in InitDiskInfo(), then this drive will be used as a
  568.             'temporary drive for concatenating split files
  569.             '
  570.             If mstrConcatDrive = vbNullString Then
  571.                 If lDiskSpaceLeft > lMostSpaceLeft Then
  572.                     lMostSpaceLeft = lDiskSpaceLeft
  573.                     intTmpDrvIdx = intIdx
  574.                 End If
  575.             Else
  576.                 '
  577.                 '"TMP" drive was specified, so we'll use that
  578.                 '
  579.                 If Left$(mstrConcatDrive, 1) = Mid$(gstrDrivesUsed, intIdx, 1) Then
  580.                     intTmpDrvIdx = intIdx
  581.                 End If
  582.             End If
  583.         End If
  584.     Next
  585.  
  586.     '
  587.     'If at least one drive was specified as a destination (if there was at least
  588.     'one CalcDiskSpace call in Form_Load of SETUP1.FRM), then subtract the extra
  589.     'space needed for concatenation from either:
  590.     '   The "TMP" drive if available  - OR -
  591.     '   The drive with the most space remaining
  592.     '
  593.     If intTmpDrvIdx > 0 Then
  594.         gsDiskSpace(intTmpDrvIdx).lReq = gsDiskSpace(intTmpDrvIdx).lReq + mlSpaceForConcat
  595.         If gsDiskSpace(intTmpDrvIdx).lAvail < gsDiskSpace(intTmpDrvIdx).lReq Then
  596.             GoSub CheckDSAskSpace
  597.         End If
  598.  
  599.         '
  600.         'If a "TMP" drive was found, we use it regardless, otherwise we use the drive
  601.         'with the most free space
  602.         '
  603.         If mstrConcatDrive = vbNullString Then
  604.             mstrConcatDrive = Mid$(gstrDrivesUsed, intTmpDrvIdx, 1) & gstrCOLON & gstrSEP_DIR
  605.             AddDirSep mstrConcatDrive
  606.         End If
  607.     End If
  608.  
  609.     Exit Function
  610.  
  611. CheckDSAskSpace:
  612.     '
  613.     'if the user hasn't been prompted before in the event of not enough free space,
  614.     'then display table of drive space and allow them to (basically) abort, retry,
  615.     'or ignore.
  616.     '
  617.     If fDontAskOnSpaceErr = False Then
  618.         If gfNoUserInput Then
  619.             If gfSilent = True Then
  620.                 LogSilentMsg ResolveResString(resLBLNOSPACE)
  621.             End If
  622.             If gfSMS = True Then
  623.                 LogSMSMsg ResolveResString(resLBLNOSPACE)
  624.             End If
  625.             ExitSetup frmSetup1, gintRET_FATAL
  626.         Else
  627.             frmDskSpace.Show vbModal
  628.         End If
  629.         
  630.         If gfRetVal <> gintRET_CONT Then
  631.             CheckDiskSpace = False
  632.             Exit Function
  633.         Else
  634.             fDontAskOnSpaceErr = True
  635.         End If
  636.     End If
  637.  
  638.     Return
  639. End Function
  640.  
  641. '-----------------------------------------------------------
  642. ' FUNCTION: CheckDrive
  643. '
  644. ' Check to see if the specified drive is ready to be read
  645. ' from.  In the case of a drive that holds removable media,
  646. ' this would mean that formatted media was in the drive and
  647. ' that the drive door was closed.
  648. '
  649. ' IN: [strDrive] - drive to check
  650. '     [strCaption] - caption if the drive isn't ready
  651. '
  652. ' Returns: True if the drive is ready, False otherwise
  653. '-----------------------------------------------------------
  654. '
  655. Function CheckDrive(ByVal strDrive As String, ByVal strCaption As String) As Integer
  656.     Dim strDir As String
  657.     Dim strMsg As String
  658.     Dim fIsUNC As Boolean
  659.  
  660.     On Error Resume Next
  661.  
  662.     SetMousePtr vbHourglass
  663.  
  664.     Do
  665.         Err = 0
  666.         fIsUNC = False
  667.         '
  668.         'Attempt to read the current directory of the specified drive.  If
  669.         'an error occurs, we assume that the drive is not ready
  670.         '
  671.         If IsUNCName(strDrive) Then
  672.             fIsUNC = True
  673.             strDir = Dir$(GetUNCShareName(strDrive))
  674.         Else
  675.             strDir = Dir$(Left$(strDrive, 2))
  676.         End If
  677.  
  678.         If Err > 0 Then
  679.             If fIsUNC Then
  680.                 strMsg = Error$ & vbLf & vbLf & ResolveResString(resCANTREADUNC, "|1", strDrive) & vbLf & vbLf & ResolveResString(resCHECKUNC)
  681.             Else
  682.                 strMsg = Error$ & vbLf & vbLf & ResolveResString(resDRVREAD) & strDrive & vbLf & vbLf & ResolveResString(resDRVCHK)
  683.             End If
  684.             If MsgError(strMsg, vbExclamation Or vbRetryCancel, strCaption) = vbCancel Then
  685.                 CheckDrive = False
  686.                 Err = 0
  687.             End If
  688.         Else
  689.             CheckDrive = True
  690.         End If
  691.         
  692.         If Err And gfNoUserInput = True Then
  693.             ExitSetup frmSetup1, gintRET_FATAL
  694.         End If
  695.     Loop While Err
  696.  
  697.     SetMousePtr gintMOUSE_DEFAULT
  698. End Function
  699.  
  700. '-----------------------------------------------------------
  701. ' FUNCTION: CheckOverwritePrivateFile
  702. '
  703. ' Checks if a private file that we are about to install
  704. ' already exists in the destination directory.  If it
  705. ' does, it asks if they want to overwrite the file
  706. '
  707. ' IN: [strFN] - Full path of the private file that is
  708. '               about to be installed.
  709. '
  710. '-----------------------------------------------------------
  711. '
  712. Public Function CheckOverwritePrivateFile(ByVal strFN As String) As Boolean
  713.     Static fNoToAll As Boolean
  714.     
  715.     If fNoToAll Then 'They've already said no to all, don't ask again
  716.         CheckOverwritePrivateFile = False
  717.         Exit Function
  718.     End If
  719.     If FileExists(strFN) Then
  720.         Do
  721.             Select Case MsgFunc(ResolveResString(resOVERWRITEPRIVATE) & vbLf & vbLf & ResolveResString(resCANCELSETUP), vbYesNo Or vbDefaultButton1 Or vbExclamation, gstrTitle)
  722.                 Case vbYes
  723.                     'The user chose to cancel.  (This is best.)
  724.                     gfDontLogSMS = True  ' Don't log this message if SMS because we already logged the previous one and we can only use 255 characters.
  725.                     MsgError ResolveResString(resCHOOSENEWDEST), vbOKOnly, gstrTitle
  726.                     ExitSetup frmCopy, gintRET_FATAL
  727.                 Case Else
  728.                     'One more level of warning to let them know that we highly
  729.                     '  recommend cancelling setup at this point
  730.                     Select Case MsgFunc(ResolveResString(resOVERWRITEPRIVATE2) & vbLf & vbLf & ResolveResString(resVERIFYCONTINUE), vbYesNo Or vbDefaultButton2 Or vbExclamation, gstrTitle)
  731.                         Case vbNo
  732.                             'User chose "no, don't continue"
  733.                             'Repeat the first-level warning
  734.                         Case Else
  735.                             'They decided to continue anyway
  736.                             Exit Do
  737.                         'End Case
  738.                     End Select
  739.                 'End Case
  740.             End Select
  741.         Loop
  742.     Else
  743.         CheckOverwritePrivateFile = True
  744.     End If
  745. End Function
  746.  
  747. '-----------------------------------------------------------
  748. ' FUNCTION: CopyFile
  749. '
  750. ' Uses the Windows VerInstallFile API to copy a file from
  751. ' the specified source location/name to the destination
  752. ' location/name.  Split files should be combined via the
  753. ' '...Concat...' file routines before calling this
  754. ' function.
  755. ' If the file is successfully updated and the file is a
  756. ' shared file (fShared = True), then the
  757. ' files reference count is updated (32-bits only)
  758. '
  759. ' IN: [strSrcDir] - directory where source file is located
  760. '     [strDestDir] - destination directory for file
  761. '     [strSrcName] - name of source file
  762. '     [strDestName] - name of destination file
  763. '
  764. ' PRECONDITION: NewAction() must have already been called
  765. '               for this file copy (of type either
  766. '               gstrKEY_SHAREDFILE or gstrKEY_PRIVATE --
  767. '               see CopySection for an example of how
  768. '               this works).  See NewAction() and related
  769. '               functions in LOGGING.BAS for comments on
  770. '               using the logging function.
  771. '               Either CommitAction() or AbortAction() will
  772. '               allows be called by this procedure, and
  773. '               should not be done by the caller.
  774. '
  775. ' Returns: True if copy was successful, False otherwise
  776. '
  777. ' POSTCONDITION: The current action will be either committed or
  778. '                aborted.
  779. '-----------------------------------------------------------
  780. '
  781. Function CopyFile(ByVal strSrcDir As String, ByVal strDestDir As String, ByVal strSrcName As String, ByVal strDestName As String, ByVal fShared As Boolean, ByVal fSystem As Boolean, Optional ByVal fOverWrite As Boolean = False) As Boolean
  782.     Const intUNKNOWN% = 0
  783.     Const intCOPIED% = 1
  784.     Const intNOCOPY% = 2
  785.     Const intFILEUPTODATE% = 3
  786.  
  787.     '
  788.     'VerInstallFile() Flags
  789.     '
  790.     Const VIFF_FORCEINSTALL% = &H1
  791.     Const VIF_TEMPFILE& = &H1
  792.     Const VIF_SRCOLD& = &H4
  793.     Const VIF_DIFFLANG& = &H8
  794.     Const VIF_DIFFCODEPG& = &H10
  795.     Const VIF_DIFFTYPE& = &H20
  796.     Const VIF_WRITEPROT& = &H40
  797.     Const VIF_FILEINUSE& = &H80
  798.     Const VIF_OUTOFSPACE& = &H100
  799.     Const VIF_ACCESSVIOLATION& = &H200
  800.     Const VIF_SHARINGVIOLATION = &H400
  801.     Const VIF_CANNOTCREATE = &H800
  802.     Const VIF_CANNOTDELETE = &H1000
  803.     Const VIF_CANNOTRENAME = &H2000
  804.     Const VIF_OUTOFMEMORY = &H8000&
  805.     Const VIF_CANNOTREADSRC = &H10000
  806.     Const VIF_CANNOTREADDST = &H20000
  807.     Const VIF_BUFFTOOSMALL = &H40000
  808.  
  809.     Static fIgnoreWarn As Integer             'user warned about ignoring error?
  810.  
  811.     Dim strMsg As String
  812.     Dim lRC As Long
  813.     Dim lpTmpNameLen As Long
  814.     Dim intFlags As Integer
  815.     Dim intRESULT As Integer
  816.     Dim fFileAlreadyExisted
  817.  
  818.     On Error Resume Next
  819.  
  820.     CopyFile = False
  821.  
  822.     '
  823.     'Ensure that the source file is available for copying
  824.     '
  825.     If DetectFile(strSrcDir & strSrcName) = vbIgnore Then
  826.         AbortAction
  827.         Exit Function
  828.     End If
  829.     
  830.     '
  831.     ' Make sure that the Destination path (including path, filename, commandline args, etc.
  832.     ' is not longer than the max allowed.
  833.     '
  834.     If Not fCheckFNLength(strDestDir & strDestName) Then
  835.         AbortAction
  836.         strMsg = ResolveResString(resCANTCOPYPATHTOOLONG) & vbLf & vbLf & ResolveResString(resCHOOSENEWDEST) & vbLf & vbLf & strDestDir & strDestName
  837.         Call MsgError(strMsg, vbOKOnly, gstrSETMSG)
  838.         ExitSetup frmCopy, gintRET_FATAL
  839.         Exit Function
  840.     End If
  841.     '
  842.     'Make the destination directory, prompt the user to retry if there is an error
  843.     '
  844.     If Not MakePath(strDestDir) Then
  845.         AbortAction ' Abort file copy
  846.         Exit Function
  847.     End If
  848.  
  849.     '
  850.     'Make sure we have the LFN (long filename) of the destination directory
  851.     '
  852.     strDestDir = GetLongPathName(strDestDir)
  853.     
  854.     '
  855.     'Setup for VerInstallFile call
  856.     '
  857.     lpTmpNameLen = gintMAX_SIZE
  858.     mstrVerTmpName = String$(lpTmpNameLen, 0)
  859.     intFlags = 0
  860.     If fOverWrite Then intFlags = VIFF_FORCEINSTALL
  861.     fFileAlreadyExisted = FileExists(strDestDir & strDestName)
  862.  
  863.     intRESULT = intUNKNOWN
  864.  
  865.     Do While intRESULT = intUNKNOWN
  866.         'VerInstallFile under Windows 95 does not handle
  867.         '  long filenames, so we must give it the short versions
  868.         '  (32-bit only).
  869.         Dim strShortSrcName As String
  870.         Dim strShortDestName As String
  871.         Dim strShortSrcDir As String
  872.         Dim strShortDestDir As String
  873.         
  874.         strShortSrcName = strSrcName
  875.         strShortSrcDir = strSrcDir
  876.         strShortDestName = strDestName
  877.         strShortDestDir = strDestDir
  878.         If Not FileExists(strDestDir & strDestName) Then
  879.             'If the destination file does not already
  880.             '  exist, we create a dummy with the correct
  881.             '  (long) filename so that we can get its
  882.             '  short filename for VerInstallFile.
  883.             Open strDestDir & strDestName For Output Access Write As #1
  884.             Close #1
  885.         End If
  886.     
  887.         On Error GoTo UnexpectedErr
  888.         If Not IsWindowsNT() Then
  889.             Dim strTemp As String
  890.             'This conversion is not necessary under Windows NT
  891.             strShortSrcDir = GetShortPathName(strSrcDir)
  892.             If GetFileName(strSrcName) = strSrcName Then
  893.                 strShortSrcName = GetFileName(GetShortPathName(strSrcDir & strSrcName))
  894.             Else
  895.                 strTemp = GetShortPathName(strSrcDir & strSrcName)
  896.                 strShortSrcName = Mid$(strTemp, Len(strShortSrcDir) + 1)
  897.             End If
  898.             strShortDestDir = GetShortPathName(strDestDir)
  899.             strShortDestName = GetFileName(GetShortPathName(strDestDir & strDestName))
  900.         End If
  901.         On Error Resume Next
  902.             
  903.         lRC = VerInstallFile(intFlags, strShortSrcName, strShortDestName, strShortSrcDir, strShortDestDir, 0&, mstrVerTmpName, lpTmpNameLen)
  904.         If Err <> 0 Then
  905.             '
  906.             'If the version or file expansion DLLs couldn't be found, then abort setup
  907.             '
  908.             ExitSetup frmCopy, gintRET_FATAL
  909.         End If
  910.  
  911.         If lRC = 0 Then
  912.             '
  913.             'File was successfully installed, increment reference count if needed
  914.             '
  915.             
  916.             'One more kludge for long filenames: VerInstallFile may have renamed
  917.             'the file to its short version if it went through with the copy.
  918.             'Therefore we simply rename it back to what it should be.
  919.             Name strDestDir & strShortDestName As strDestDir & strDestName
  920.             intRESULT = intCOPIED
  921.         ElseIf lRC And VIF_SRCOLD Then
  922.             '
  923.             'Source file was older, so not copied, the existing version of the file
  924.             'will be used.  Increment reference count if needed
  925.             '
  926.             intRESULT = intFILEUPTODATE
  927.         ElseIf lRC And (VIF_DIFFLANG Or VIF_DIFFCODEPG Or VIF_DIFFTYPE) Then
  928.             '
  929.             'We retry and force installation for these cases.  You can modify the code
  930.             'here to prompt the user about what to do.
  931.             '
  932.             intFlags = VIFF_FORCEINSTALL
  933.         ElseIf lRC And VIF_WRITEPROT Then
  934.             strMsg = ResolveResString(resWRITEPROT)
  935.             GoSub CFMsg
  936.         ElseIf lRC And VIF_FILEINUSE Then
  937.             strMsg = ResolveResString(resINUSE)
  938.             GoSub CFMsg
  939.         ElseIf lRC And VIF_OUTOFSPACE Then
  940.             strMsg = ResolveResString(resOUTOFSPACE) & Left$(strDestDir, 2)
  941.             GoSub CFMsg
  942.         ElseIf lRC And VIF_ACCESSVIOLATION Then
  943.             strMsg = ResolveResString(resACCESSVIOLATION)
  944.             GoSub CFMsg
  945.         ElseIf lRC And VIF_SHARINGVIOLATION Then
  946.             strMsg = ResolveResString(resSHARINGVIOLATION)
  947.             GoSub CFMsg
  948.         ElseIf lRC And VIF_OUTOFMEMORY Then
  949.             strMsg = ResolveResString(resOUTOFMEMORY)
  950.             GoSub CFMsg
  951.         Else
  952.             '
  953.             ' For these cases, we generically report the error and do not install the file
  954.             ' unless this is an SMS install; in which case we abort.
  955.             '
  956.             If lRC And VIF_CANNOTCREATE Then
  957.                 strMsg = ResolveResString(resCANNOTCREATE)
  958.             ElseIf lRC And VIF_CANNOTDELETE Then
  959.                 strMsg = ResolveResString(resCANNOTDELETE)
  960.             ElseIf lRC And VIF_CANNOTRENAME Then
  961.                 strMsg = ResolveResString(resCANNOTRENAME)
  962.             ElseIf lRC And VIF_CANNOTREADSRC Then
  963.                 strMsg = ResolveResString(resCANNOTREADSRC)
  964.             ElseIf lRC And VIF_CANNOTREADDST Then
  965.                 strMsg = ResolveResString(resCANNOTREADDST)
  966.             ElseIf lRC And VIF_BUFFTOOSMALL Then
  967.                 strMsg = ResolveResString(resBUFFTOOSMALL)
  968.             End If
  969.  
  970.             strMsg = strMsg & ResolveResString(resNOINSTALL)
  971.             MsgError strMsg, vbOKOnly Or vbExclamation, gstrTitle
  972.             If gfSMS Then
  973.                 ExitSetup frmSetup1, gintRET_FATAL
  974.             End If
  975.             intRESULT = intNOCOPY
  976.         End If
  977.     Loop
  978.  
  979.     '
  980.     'If there was a temp file left over from VerInstallFile, remove it
  981.     '
  982.     If lRC And VIF_TEMPFILE Then
  983.         Kill mstrVerTmpName
  984.     End If
  985.  
  986.     'Abort or commit the current Action, and do reference counting
  987.     Select Case intRESULT
  988.         Case intNOCOPY
  989.             AbortAction
  990.         Case intCOPIED
  991.             DecideIncrementRefCount strDestDir & strDestName, fShared, fSystem, fFileAlreadyExisted
  992.             If (Extension(strDestName) = gsEXT_FONTFON) Or (Extension(strDestName) = gsEXT_FONTTTF) Then
  993.                 'do nothing
  994.             Else
  995.                 AddActionNote ResolveResString(resLOG_FILECOPIED)
  996.                 CommitAction
  997.             End If
  998.             CopyFile = True
  999.         Case intFILEUPTODATE
  1000.             DecideIncrementRefCount strDestDir & strDestName, fShared, fSystem, fFileAlreadyExisted
  1001.             If (Extension(strDestName) = gsEXT_FONTFON) Or (Extension(strDestName) = gsEXT_FONTTTF) Then
  1002.                 'do nothing
  1003.             Else
  1004.                 AddActionNote ResolveResString(resLOG_FILECOPIED)
  1005.                 CommitAction
  1006.             End If
  1007.             CopyFile = True
  1008.         Case Else
  1009.             AbortAction ' Defensive - this shouldn't be reached
  1010.         'End Case
  1011.     End Select
  1012.  
  1013.     Exit Function
  1014.  
  1015. UnexpectedErr:
  1016.     MsgError Error$ & vbLf & vbLf & ResolveResString(resUNEXPECTED), vbOKOnly Or vbExclamation, gstrTitle
  1017.     ExitSetup frmCopy, gintRET_FATAL
  1018.     
  1019. CFMsg: '(Subroutine)
  1020.     Dim intMsgRet As Integer
  1021.     strMsg = strDestDir & strDestName & vbLf & vbLf & strMsg
  1022.     intMsgRet = MsgError(strMsg, vbAbortRetryIgnore Or vbExclamation Or vbDefaultButton2, gstrTitle)
  1023.     If gfNoUserInput Then intMsgRet = vbAbort
  1024.     Select Case intMsgRet
  1025.         Case vbAbort
  1026.             ExitSetup frmCopy, gintRET_ABORT
  1027.         Case vbIgnore
  1028.             If fIgnoreWarn = True Then
  1029.                 intRESULT = intNOCOPY
  1030.             Else
  1031.                 fIgnoreWarn = True
  1032.                 strMsg = strMsg & vbLf & vbLf & ResolveResString(resWARNIGNORE)
  1033.                 If MsgError(strMsg, vbYesNo Or vbQuestion Or vbDefaultButton2, gstrTitle) = vbYes Then
  1034.                     intRESULT = intNOCOPY
  1035.                 Else
  1036.                     'Will retry
  1037.                 End If
  1038.             End If
  1039.         'End Case
  1040.     End Select
  1041.  
  1042.     Return
  1043. End Function
  1044.  
  1045. '-----------------------------------------------------------
  1046. ' SUB: CopySection
  1047. '
  1048. ' Attempts to copy the files that need to be copied from
  1049. ' the named section of the setup info file (SETUP.LST)
  1050. '
  1051. ' IN: [strSection] - name of section to copy files from
  1052. '
  1053. '-----------------------------------------------------------
  1054. '
  1055. Sub CopySection(ByVal strsection As String)
  1056.     Dim intIdx As Integer
  1057.     Dim fSplit As Integer
  1058.     Dim fSrcVer As Integer
  1059.     Dim sFile As FILEINFO
  1060.     Dim strLastFile As String
  1061.     Dim intRC As Integer
  1062.     Dim lThisFileSize As Long
  1063.     Dim strSrcDir As String
  1064.     Dim strDestDir As String
  1065.     Dim strSrcName As String
  1066.     Dim strDestName As String
  1067.     Dim strRegister As String
  1068.     Dim sSrcVerInfo As VERINFO
  1069.     Dim sDestVerInfo As VERINFO
  1070.     Dim fFileWasUpToDate As Boolean
  1071.     Dim strMultDirBaseName As String
  1072.     Dim strMsg As String
  1073.     Dim strDetectPath As String
  1074.     Dim fRemoteReg As Boolean
  1075.     Dim fOverWrite As Boolean
  1076.     Dim frm As frmOverwrite
  1077.     Static fOverwriteAll As Boolean
  1078.     
  1079.     On Error Resume Next
  1080.  
  1081.     UpdateDateTime
  1082.     strMultDirBaseName = ResolveResString(resCOMMON_MULTDIRBASENAME)
  1083.     intIdx = 1
  1084.  
  1085.     If Not FileExists(gsTEMPDIR) Then
  1086.         MkDir gsTEMPDIR
  1087.     End If
  1088.     '
  1089.     'For each file in the specified section, read info from the setup info file
  1090.     '
  1091.     Do While ReadSetupFileLine(strsection, intIdx, sFile) = True
  1092.         fFileWasUpToDate = False
  1093.         
  1094.         '
  1095.         'If last result was IGNORE, and if this is an extent of a split file,
  1096.         'then no need to process this chunk of the file either
  1097.         '
  1098.         
  1099.         If sFile.strSrcName = gstrSEP_AMPERSAND & gstrFILE_MDAG Then
  1100.             'We don't need to extract mdac_typ twice
  1101.             GoTo CSContinue
  1102.         End If
  1103.         ExtractFileFromCab GetShortPathName(gsCABNAME), sFile.strSrcName, gsTEMPDIR & sFile.strDestName, gintCabs, gstrSrcPath
  1104.         If FileExists(gsTEMPDIR & sFile.strDestName) Then
  1105.             sFile.strSrcName = gsTEMPDIR & sFile.strDestName
  1106.             sFile.intDiskNum = gintCurrentDisk
  1107.         End If
  1108.         If intRC = vbIgnore And sFile.strDestName = strDestName Then
  1109.             GoTo CSContinue
  1110.         End If
  1111.         intRC = 0
  1112.  
  1113.         '
  1114.         ' If a new disk is called for, or if for some reason we can't find the
  1115.         ' source path (user removed the install floppy, for instance) then
  1116.         ' prompt for the next disk.  The PromptForNextDisk function won't
  1117.         ' actually prompt the user unless it determines that the source drive
  1118.         ' contains removeable media or is a network connection.  Also, we don't
  1119.         ' prompt if this is a silent install.  It will fail later on a silent
  1120.         ' install when it can't find the file.
  1121.         '
  1122.         If gfNoUserInput = False And (sFile.intDiskNum <> gintCurrentDisk Or DirExists(gstrSrcPath) = False) Then
  1123.             PromptForNextDisk sFile.intDiskNum, sFile.strSrcName
  1124.         End If
  1125.  
  1126.         strSrcName = sFile.strSrcName
  1127.         '
  1128.         ' The file could exist in either the main source directory or
  1129.         ' in a subdirectory named DISK1, DISK2, etc.  Set the appropriate
  1130.         ' path.  If it's in neither place, it is an error and will be
  1131.         ' handled later.
  1132.         '
  1133.         If FileExists(strSrcName) = True Then
  1134.             strSrcDir = gsTEMPDIR
  1135.         'ElseIf FileExists(gstrSrcPath & ".." & gstrSEP_DIR & strMultDirBaseName & Format(sFile.intDiskNum) & gstrSEP_DIR & strSrcName) = True Then
  1136.             'strSrcDir = ResolveDir(gstrSrcPath & ".." & gstrSEP_DIR & strMultDirBaseName & Format(sFile.intDiskNum) & gstrSEP_DIR, False, False)
  1137.             'gstrSrcPath = strSrcDir
  1138.         Else
  1139.             '
  1140.             ' Can't find the file.
  1141.             '
  1142.             If DirExists(gstrSrcPath & strMultDirBaseName & Format(sFile.intDiskNum)) = True Then
  1143.                 strDetectPath = gstrSrcPath & strMultDirBaseName & Format(sFile.intDiskNum)
  1144.             Else
  1145.                 strDetectPath = gstrSrcPath
  1146.             End If
  1147.             strMsg = ResolveResString(resCOMMON_CANTFINDSRCFILE, "|1", strDetectPath & gstrSEP_DIR & strSrcName)
  1148.             MsgError strMsg, vbExclamation Or vbOKOnly, gstrTitle
  1149.             ExitSetup frmCopy, gintRET_FATAL
  1150.         End If
  1151.  
  1152.         '
  1153.         'if the file isn't split, or if this is the first section of a split file
  1154.         '
  1155.         If sFile.strDestDir <> vbNullString Then
  1156.             fSplit = sFile.fSplit
  1157.  
  1158.             strDestDir = sFile.strDestDir
  1159.             strDestName = sFile.strDestName
  1160.             
  1161.             'We need to go ahead and create the destination directory, or else
  1162.             'GetLongPathName() may fail
  1163.             If Not MakePath(strDestDir) Then
  1164.                 intRC = vbIgnore
  1165.             End If
  1166.             
  1167.             If intRC <> vbIgnore Then
  1168.                 Err = 0
  1169.                 strDestDir = GetLongPathName(strDestDir)
  1170.  
  1171.                 frmCopy.lblDestFile.Caption = strDestDir & sFile.strDestName
  1172.                 frmCopy.lblDestFile.Refresh
  1173.  
  1174.                 If UCase(strDestName) = gstrFILE_AXDIST Then
  1175.                     '
  1176.                     ' AXDIST.EXE is installed temporarily.  We'll be
  1177.                     ' deleting it at the end of setup.  Set gfAXDist = True
  1178.                     ' so we know we need to delete it later.
  1179.                     '
  1180.                     NewAction gstrKEY_TEMPFILE, """" & strDestDir & strDestName & """"
  1181.                     gfAXDist = True
  1182.                     gstrAXDISTInstallPath = strDestDir & strDestName
  1183.                 ElseIf UCase(strDestName) = gstrFILE_MDAG Then
  1184.                     '
  1185.                     ' mdac_typ.EXE is installed temporarily.  We'll be
  1186.                     ' deleting it at the end of setup.  Set mdag = True
  1187.                     ' so we know we need to delete it later.
  1188.                     '
  1189.                     NewAction gstrKEY_TEMPFILE, """" & strDestDir & strDestName & """"
  1190.                     gfMDag = True
  1191.                     gstrMDagInstallPath = strDestDir & strDestName
  1192.                 ElseIf UCase(strDestName) = gstrFILE_WINT351 Then
  1193.                     '
  1194.                     ' WINt351.EXE is installed temporarily.  We'll be
  1195.                     ' deleting it at the end of setup.  Set WINt351 = True
  1196.                     ' so we know we need to delete it later.  (Note, this file
  1197.                     ' is only installed if the target is nt3.51.  This is dealt
  1198.                     ' with below in this same routine.  )
  1199.                     '
  1200.                     NewAction gstrKEY_TEMPFILE, """" & strDestDir & strDestName & """"
  1201.                     gfWINt351 = True
  1202.                     gstrWINt351InstallPath = strDestDir & strDestName
  1203.                 ElseIf (Extension(sFile.strDestName) = gsEXT_FONTTTF) Then
  1204.                     'No new actions for fonts
  1205.                 ElseIf (Extension(sFile.strDestName) = gsEXT_FONTFON) Then
  1206.                     'No new actions for fonts
  1207.                 ElseIf sFile.fShared Then
  1208.                     NewAction gstrKEY_SHAREDFILE, """" & strDestDir & strDestName & """"
  1209.                 ElseIf sFile.fSystem Then
  1210.                     NewAction gstrKEY_SYSTEMFILE, """" & strDestDir & strDestName & """"
  1211.                 ElseIf (Extension(sFile.strDestName) = gsEXT_REG) Then
  1212.                     If Extension(sFile.strRegister) = gsEXT_REG Then
  1213.                         'No new actions for registration files.
  1214.                     Else
  1215.                         NewAction gstrKEY_PRIVATEFILE, """" & strDestDir & strDestName & """"
  1216.                     End If
  1217.                 Else
  1218.                     NewAction gstrKEY_PRIVATEFILE, """" & strDestDir & strDestName & """"
  1219.                 End If
  1220.             End If
  1221.             
  1222.             '
  1223.             'If the file info just read from SETUP.LST is the application .EXE
  1224.             '(i.e.; it's the value of the AppExe Key in the [Setup] section,
  1225.             'then save it's full pathname for later use
  1226.             '
  1227.             If strDestName = gstrAppExe Then
  1228.                 '
  1229.                 'Used for creating a program manager icon in Form_Load of SETUP1.FRM
  1230.                 'and for registering the per-app path
  1231.                 '
  1232.                 gsDest.strAppDir = strDestDir
  1233.             End If
  1234.  
  1235.             'Special case for RICHED32.DLL
  1236.             '-- we only install this file under Windows 95, not under Windows NT (3.51 or 4.0)
  1237.             If strDestName = mstrFILE_RICHED32 Then
  1238.                 If Not IsWindows95() Then
  1239.                     'We're not running under Win95 - do not install this file.
  1240.                     intRC = vbIgnore
  1241.                     LogNote ResolveResString(resCOMMON_RICHED32NOTCOPIED, "|1", strDestName)
  1242.                     AbortAction
  1243.                 End If
  1244.             End If
  1245.             '
  1246.             ' Special case for AXDIST.EXE
  1247.             ' If this is Win95 or NT4 and AXDIST.EXE is in the setup list, we need
  1248.             ' to execute it when setup1 is complete.  AXDIST.EXE is a self-extracting
  1249.             ' exe that installs special files needed for internet functionality.
  1250.             '
  1251.             If UCase(strDestName) = gstrFILE_AXDIST Then
  1252.                 '
  1253.                 ' Don't do anything here if this is not Win95 or NT4.
  1254.                 '
  1255.                 If Not TreatAsWin95() Then
  1256.                     'We're not running under Win95 or NT4- do not install this file.
  1257.                     intRC = vbIgnore
  1258.                     LogNote ResolveResString(resCOMMON_AXDISTNOTCOPIED, "|1", strDestName)
  1259.                     AbortAction
  1260.                     gfAXDist = False
  1261.                 End If
  1262.             End If
  1263.             '
  1264.             ' Special case for WINt351.EXE
  1265.             ' If this is NT3.51 and WINt351.EXE is in the setup list, we need
  1266.             ' to execute it when setup1 is complete.  WINt351.EXE is a self-extracting
  1267.             ' exe that installs special files needed for internet functionality.
  1268.             '
  1269.             If UCase(strDestName) = gstrFILE_WINT351 Then
  1270.                 '
  1271.                 ' Don't do anything here if this is not NT3.51.
  1272.                 '
  1273.                 If TreatAsWin95() Then
  1274.                     'We're not running under NT3.51- do not install this file.
  1275.                     intRC = vbIgnore
  1276.                     LogNote ResolveResString(resCOMMON_WINT351NOTCOPIED, "|1", strDestName)
  1277.                     AbortAction
  1278.                     gfWINt351 = False
  1279.                 End If
  1280.             End If
  1281.             
  1282.             strRegister = sFile.strRegister
  1283.  
  1284.             lThisFileSize = CalcFinalSize(sFile.lFileSize, sFile.strDestDir)
  1285.  
  1286.             '
  1287.             'The stuff below trys to save some time by pre-checking whether a file
  1288.             'should be installed before a split file is concatenated or before
  1289.             'VerInstallFile does its think which involves a full file read (for
  1290.             'a compress file) at the minimum.  Basically, if both files have
  1291.             'version numbers, they are compared.  If one file has a version number
  1292.             'and the other doesn't, the one with the version number is deemed
  1293.             '"Newer".  If neither file has a version number, we compare date.
  1294.             '
  1295.             'Always attempt to get the source file version number.  If the setup
  1296.             'info file did not contain a version number (sSrcVerInfo.nMSHi =
  1297.             'gintNOVERINFO), we attempt to read the version number from the source
  1298.             'file.  Reading the version number from a split file will always fail.
  1299.             'That's why it's a good idea to include the version number for a file
  1300.             '(especially split ones) in the setup info file (SETUP.LST)
  1301.             '
  1302.             fSrcVer = True
  1303.             sSrcVerInfo = sFile.sVerInfo
  1304.             If sSrcVerInfo.FileVerPart1 = gintNOVERINFO Then
  1305.                 fSrcVer = GetFileVerStruct(strSrcDir & strSrcName, sSrcVerInfo)
  1306.             End If
  1307.  
  1308.             '
  1309.             'If there is an existing destination file with version information, then
  1310.             'compare its version number to the source file version number.
  1311.             '
  1312.             fOverWrite = True
  1313.             If intRC <> vbIgnore Then
  1314.                 fRemoteReg = (sFile.strRegister = mstrREMOTEREGISTER)
  1315.                 If GetFileVerStruct(strDestDir & strDestName, sDestVerInfo, fRemoteReg) = True Then
  1316.                     If fSrcVer = True Then
  1317.                         If IsNewerVer(sSrcVerInfo, sDestVerInfo) = False Then
  1318.                             '
  1319.                             'Existing file is newer than the one we want to install;
  1320.                             'prompt user for what to do
  1321.                             '
  1322.                             
  1323.                             If Not fOverwriteAll Then
  1324.                                 Set frm = New frmOverwrite
  1325.                                 frm.FileName = strDestDir & strDestName
  1326.                                 With sDestVerInfo
  1327.                                     frm.Version = CStr(.FileVerPart1) & "." & CStr(.FileVerPart2) & "." & _
  1328.                                         CStr(.FileVerPart3) & "." & CStr(.FileVerPart4)
  1329.                                 End With
  1330.                                 frm.Description = GetFileDescription(strDestDir & strDestName)
  1331.                                 frm.Show vbModal, frmSetup1
  1332.                                 If frm.ReturnVal = owNo Then 'overwrite the file
  1333.                                     fOverWrite = True
  1334.                                 ElseIf frm.ReturnVal = owYes Then 'Keep this file
  1335.                                     fOverWrite = False
  1336.                                 ElseIf frm.ReturnVal = owNoToAll Then 'Overwrite all files
  1337.                                     fOverWrite = True
  1338.                                     fOverwriteAll = True
  1339.                                 End If
  1340.                             End If
  1341.                             If Not fOverWrite Then
  1342.                                 intRC = vbIgnore
  1343.                                 fFileWasUpToDate = True
  1344.                                 DecideIncrementRefCount strDestDir & strDestName, sFile.fShared, sFile.fSystem, True
  1345.                                 If (Extension(sFile.strDestName) = gsEXT_FONTFON) Or (Extension(sFile.strDestName) = gsEXT_FONTTTF) Then
  1346.                                     'do nothing
  1347.                                 Else
  1348.                                     AddActionNote ResolveResString(resLOG_FILEUPTODATE)
  1349.                                     CommitAction
  1350.                                 End If
  1351.                             End If
  1352.                         End If
  1353.                     End If
  1354.                 Else
  1355.                     '
  1356.                     'If the destination file has no version info, then we'll copy the
  1357.                     'source file if it *does* have a version.  If neither file has a
  1358.                     'version number, then we compare date.
  1359.                     '
  1360.                     If sFile.varDate <= FileDateTime(strDestDir & strDestName) Then
  1361.                         If Err = 0 Then
  1362.                             '
  1363.                             'Although neither the source nor the existing file contain version
  1364.                             'information, the existing file has a newer date so we'll use it.
  1365.                             '
  1366.                             If Not fOverwriteAll Then
  1367.                                 Set frm = New frmOverwrite
  1368.                                 frm.FileName = strDestDir & strDestName
  1369.                                 frm.Version = vbNullString
  1370.                                 frm.Description = GetFileDescription(strDestDir & strDestName)
  1371.                                 frm.Show vbModal, frmSetup1
  1372.                                 If frm.ReturnVal = owNo Then 'overwrite the file
  1373.                                     fOverWrite = True
  1374.                                 ElseIf frm.ReturnVal = owYes Then 'Keep this file
  1375.                                     fOverWrite = False
  1376.                                 ElseIf frm.ReturnVal = owNoToAll Then 'Overwrite all files
  1377.                                     fOverWrite = True
  1378.                                     fOverwriteAll = True
  1379.                                 End If
  1380.                             End If
  1381.                             If Not fOverWrite Then
  1382.                                 intRC = vbIgnore
  1383.                                 fFileWasUpToDate = True
  1384.                                 DecideIncrementRefCount strDestDir & strDestName, sFile.fShared, sFile.fSystem, True
  1385.                                 If (Extension(sFile.strDestName) = gsEXT_FONTFON) Or (Extension(sFile.strDestName) = gsEXT_FONTTTF) Then
  1386.                                     'do nothing
  1387.                                 Else
  1388.                                     AddActionNote ResolveResString(resLOG_FILEUPTODATE)
  1389.                                     CommitAction
  1390.                                 End If
  1391.                             End If
  1392.                         Else
  1393.                             Err = 0
  1394.                         End If
  1395.                     End If
  1396.                 End If
  1397.             End If
  1398.             
  1399.         End If
  1400.         If fOverwriteAll Then fOverWrite = True
  1401.         '
  1402.         'If the file wasn't split, or if this is the last extent of a split file
  1403.         '
  1404.         If fSplit = False Then
  1405.  
  1406.             '
  1407.             'After all of this, if we're still ready to copy, then give it a whirl!
  1408.             '
  1409.             If intRC <> vbIgnore Then
  1410.                 ' CopyFile will increment the reference count for us, and will either
  1411.                 ' commit or abort the current Action.
  1412.                 'Turn off READONLY flag in case we copy.
  1413.                 SetAttr strDestDir & strDestName, vbNormal
  1414.                 If Extension(sFile.strRegister) <> gsEXT_REG Then
  1415.                     intRC = IIf(CopyFile(strSrcDir, strDestDir, strDestName, strDestName, sFile.fShared, sFile.fSystem, fOverWrite), 0, vbIgnore)
  1416.                 End If
  1417.             End If
  1418.  
  1419.             '
  1420.             'Save the paths of certain files for later use, if they were
  1421.             'successfully installed or were already on the system
  1422.             '
  1423.             If (Extension(strDestDir & strDestName) = gsEXT_FONTTTF) Or (Extension(strDestDir & strDestName) = gsEXT_FONTFON) Then
  1424.                 If AddFontResource(strDestDir & strDestName) <> 0 Then
  1425.                     'Success
  1426.                 Else
  1427.                     'Failure
  1428.                 End If
  1429.             End If
  1430.             If (intRC = 0 Or fFileWasUpToDate) Then
  1431.                 Select Case strDestName
  1432.                     Case mstrFILE_AUTMGR32
  1433.                         '
  1434.                         'Used for creating an icon if installed
  1435.                         '
  1436.                         gsDest.strAUTMGR32 = strDestDir & mstrFILE_AUTMGR32
  1437.                     Case mstrFILE_RACMGR32
  1438.                         '
  1439.                         'Used for creating an icon if installed
  1440.                         '
  1441.                         gsDest.strRACMGR32 = strDestDir & mstrFILE_RACMGR32
  1442.                     'End Case
  1443.                 End Select
  1444.             
  1445.                 '
  1446.                 'If we successfully copied the file, and if registration information was
  1447.                 'specified in the setup info file, save the registration info into an
  1448.                 'array so that we can register all files requiring it in one fell swoop
  1449.                 'after all the files have been copied.
  1450.                 '
  1451.                 If strRegister <> vbNullString Then
  1452.                     Err = 0
  1453.                     ReDim Preserve msRegInfo(UBound(msRegInfo) + 1)
  1454.     
  1455.                     If Err > 0 Then
  1456.                         ReDim msRegInfo(0)
  1457.                     End If
  1458.     
  1459.                     msRegInfo(UBound(msRegInfo)).strFilename = strDestDir & strDestName
  1460.     
  1461.                     Select Case strRegister
  1462.                         Case mstrDLLSELFREGISTER, mstrEXESELFREGISTER, mstrTLBREGISTER, mstrVBLREGISTER
  1463.                             'Nothing in particular to do
  1464.                         Case mstrREMOTEREGISTER
  1465.                             'We need to look for and parse the corresponding "RemoteX=..." line
  1466.                             If Not ReadSetupRemoteLine(strsection, intIdx, msRegInfo(UBound(msRegInfo))) = True Then
  1467.                                 MsgError ResolveResString(resREMOTELINENOTFOUND, "|1", strDestName, "|2", gstrINI_REMOTE & Format$(intIdx)), vbExclamation Or vbOKOnly, gstrTitle
  1468.                                 ExitSetup frmSetup1, gintRET_FATAL
  1469.                             End If
  1470.                         Case Else
  1471.                             '
  1472.                             'If the registration info specified the name of a file with
  1473.                             'registration info (which we assume if a registration macro
  1474.                             'was not specified), then we also assume that, if no path
  1475.                             'information is available, this reginfo file is in the same
  1476.                             'directory as the file it registers
  1477.                             '
  1478.                             strRegister = ResolveDestDirs(strRegister)
  1479.                             If InStr(strRegister, gstrSEP_DIR) = 0 Then
  1480.                                 strRegister = strSrcDir & strRegister
  1481.                             End If
  1482.                         'End Case
  1483.                     End Select
  1484.     
  1485.                     If Extension(strRegister) = gsEXT_REG Then
  1486.                         SyncShell gsREGEDIT & strQuoteString(strRegister), INFINITE
  1487.                     End If
  1488.                     msRegInfo(UBound(msRegInfo)).strRegister = strRegister
  1489.                 End If
  1490.             
  1491.             End If
  1492.         End If
  1493.  
  1494.         strLastFile = sFile.strDestName
  1495.  
  1496. CSContinue:
  1497.         '
  1498.         'If the file wasn't split, or if this was the last extent of a split file, then
  1499.         'update the copy status bar.  We need to do the update regardless of whether a
  1500.         'file was actually copied or not.
  1501.         '
  1502.         If sFile.fSplit = False Then
  1503.             glTotalCopied = glTotalCopied + lThisFileSize
  1504.             UpdateStatus frmCopy.picStatus, glTotalCopied / mlTotalToCopy
  1505.         End If
  1506.  
  1507.         Dim sCurDate As String, sFileDate As String
  1508.         
  1509.         sFileDate = Format(FileDateTime(sFile.strDestDir & sFile.strDestName), "m/d/yyyy h:m")
  1510.         sCurDate = Format(Now, "m/d/yyyy h:m")
  1511.         
  1512.         If sFileDate = sCurDate Then
  1513.             Dim lTime As FileTime
  1514.             Dim hFile As Long
  1515.             
  1516.             lTime = GetFileTime(sFile.varDate)
  1517.             hFile = CreateFile(sFile.strDestDir & sFile.strDestName, GENERIC_WRITE Or GENERIC_READ, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
  1518.             Call SetFileTime(hFile, lTime, lTime, lTime)
  1519.             DoEvents
  1520.             CloseHandle hFile
  1521.         Else
  1522.             '
  1523.             'Give a chance for the 'Cancel' button command to be processed if it was pressed
  1524.             '
  1525.             DoEvents
  1526.         End If
  1527.         'Delete the files after copy...
  1528.         SetAttr gsTEMPDIR & sFile.strDestName, vbNormal
  1529.         Kill gsTEMPDIR & sFile.strDestName
  1530.         intIdx = intIdx + 1
  1531.     Loop
  1532.  
  1533.     Err = 0
  1534. End Sub
  1535. '-----------------------------------------------------------
  1536. ' SUB: CreateOSProgramGroup
  1537. '
  1538. ' Calls CreateProgManGroup under Windows NT or
  1539. ' fCreateShellGroup under Windows 95
  1540. '-----------------------------------------------------------
  1541. '
  1542. Function fCreateOSProgramGroup(frm As Form, ByVal strFolderName As String, ByVal fRetOnErr As Boolean, Optional ByVal fLog As Boolean = True, Optional ByVal fPriv As Boolean = True, Optional ByVal fStartMenu As Boolean = False) As Boolean
  1543.     If TreatAsWin95() Then
  1544.         fCreateOSProgramGroup = fCreateShellGroup(strFolderName, fRetOnErr, fLog, fPriv, fStartMenu)
  1545.     Else
  1546.         CreateProgManGroup frm, strFolderName, fRetOnErr, fLog
  1547.         fCreateOSProgramGroup = True
  1548.     End If
  1549. End Function
  1550.  
  1551. '-----------------------------------------------------------
  1552. ' SUB: CreateOSLink
  1553. '
  1554. ' Calls CreateProgManItem under Windows NT or
  1555. ' CreateFolderLink under Windows 95.
  1556. '
  1557. ' If fLog is missing, the default is True.
  1558. '-----------------------------------------------------------
  1559. '
  1560. Sub CreateOSLink(frm As Form, ByVal strGroupName As String, ByVal strLinkPath As String, ByVal strLinkArguments As String, ByVal strLinkName As String, ByVal fPrivate As Boolean, sParent As String, Optional ByVal fLog As Boolean = True)
  1561.     If TreatAsWin95() Then
  1562.         CreateShellLink strLinkPath, strGroupName, strLinkArguments, strLinkName, fPrivate, sParent, fLog
  1563.     Else
  1564.         '
  1565.         ' DDE will not work properly if you try to send NT the long filename.  If it is
  1566.         ' in quotes, then the parameters get ignored.  If there are no parameters, the
  1567.         ' long filename can be used and the following line could be skipped.
  1568.         '
  1569.         strLinkPath = GetShortPathName(strUnQuoteString(strLinkPath))
  1570.         CreateProgManItem frm, strGroupName, strLinkPath & " " & strLinkArguments, strLinkName, fLog
  1571.     End If
  1572. End Sub
  1573.  
  1574. '-----------------------------------------------------------
  1575. ' SUB: CreateProgManGroup
  1576. '
  1577. ' Creates a new group in the Windows program manager if
  1578. ' the specified groupname doesn't already exist
  1579. '
  1580. ' IN: [frm] - form containing a label named 'lblDDE'
  1581. '     [strGroupName] - text name of the group
  1582. '     [fRetOnErr]    - ignored
  1583. '     [fLog] - Whether or not to write to the logfile (default
  1584. '                is true if missing)
  1585. '-----------------------------------------------------------
  1586. '
  1587. Sub CreateProgManGroup(frm As Form, ByVal strGroupName As String, ByVal fRetOnErr As Boolean, Optional ByVal fLog As Boolean = True)
  1588.     '
  1589.     'Call generic progman DDE function with flag to add a group
  1590.     '
  1591.     
  1592.     'Perform the DDE to create the group
  1593.     PerformDDE frm, strGroupName, vbNullString, vbNullString, mintDDE_GRPADD, fLog
  1594. End Sub
  1595.  
  1596. '-----------------------------------------------------------
  1597. ' SUB: CreateProgManItem
  1598. '
  1599. ' Creates (or replaces) a program manager icon in the active
  1600. ' program manager group
  1601. '
  1602. ' IN: [frm] - form containing a label named 'lblDDE'
  1603. '     [strGroupName] - Caption of group in which icon will go.
  1604. '     [strCmdLine] - command line for the item/icon,
  1605. '                    Ex: 'c:\myapp\myapp.exe'
  1606. '                    Note:  If this path contains spaces
  1607. '                      or commas, it should be enclosed
  1608. '                      with quotes so that it is properly
  1609. '                      interpreted by Windows (see AddQuotesToFN)
  1610. '     [strIconTitle] - text caption for the icon
  1611. '     [fLog] - Whether or not to write to the logfile (default
  1612. '                is true if missing)
  1613. '
  1614. ' PRECONDITION: CreateProgManGroup has already been called.  The
  1615. '               new icon will be created in the group last created.
  1616. '-----------------------------------------------------------
  1617. '
  1618. Sub CreateProgManItem(frm As Form, ByVal strGroupName As String, ByVal strCmdLine As String, ByVal strIconTitle As String, Optional ByVal fLog As Boolean = True)
  1619.     '
  1620.     'Call generic progman DDE function with flag to add an item
  1621.     '
  1622.     PerformDDE frm, strGroupName, strCmdLine, strIconTitle, mintDDE_ITEMADD, fLog
  1623. End Sub
  1624.  
  1625. '-----------------------------------------------------------
  1626. ' SUB: fCreateShellGroup
  1627. '
  1628. ' Creates a new program group off of Start>Programs in the
  1629. ' Windows 95 shell if the specified folder doesn't already exist.
  1630. '
  1631. ' IN: [strFolderName] - text name of the folder.
  1632. '                      This parameter may not contain
  1633. '                      backslashes.
  1634. '                      ex: "My Application" - this creates
  1635. '                        the folder Start>Programs>My Application
  1636. '     [fRetOnerr] - Whether or not this routine should return if
  1637. '                   there is an error creating the group.  If false,
  1638. '                   setup aborts and does not return.  Set this to
  1639. '                   true if the user can do something to correct the
  1640. '                   error.  E.g., they entered a group name in the
  1641. '                   Choose Program Group dialog as opposed to calling
  1642. '                   this routine when creating the Remote Automation
  1643. '                   group in which the user had no control.
  1644. '     [fLog] - Whether or not to write to the logfile (default
  1645. '                is true if missing)
  1646. '-----------------------------------------------------------
  1647. '
  1648. Function fCreateShellGroup(ByVal strFolderName As String, fRetOnErr As Boolean, Optional ByVal fLog As Boolean = True, Optional ByVal fPriv As Boolean = True, Optional ByVal fStartMenu As Boolean = False) As Boolean
  1649.     Dim oMalloc As IVBMalloc
  1650.  
  1651.     ReplaceDoubleQuotes strFolderName
  1652.     
  1653.     If strFolderName = "" Then
  1654.         Exit Function
  1655.     End If
  1656.  
  1657. Retry:
  1658.     
  1659.     Dim fSuccess As Boolean
  1660.     Dim sPath As String
  1661.     Dim IDL As Long
  1662.     Dim lPrograms As SpecialFolderIDs
  1663.     
  1664.     If IsWindows95() Then
  1665.         If fStartMenu Then
  1666.             lPrograms = sfidSTARTMENU
  1667.         Else
  1668.             lPrograms = sfidPROGRAMS
  1669.         End If
  1670.     Else
  1671.         If fPriv Then
  1672.             If fStartMenu Then
  1673.                 lPrograms = sfidSTARTMENU
  1674.             Else
  1675.                 lPrograms = sfidPROGRAMS
  1676.             End If
  1677.         Else
  1678.             If fStartMenu Then
  1679.                 lPrograms = sfidCOMMON_STARTMENU
  1680.             Else
  1681.                 lPrograms = sfidCOMMON_PROGRAMS
  1682.             End If
  1683.         End If
  1684.     End If
  1685.     ' Fill the item id list with the pointer of each folder item, rtns 0 on success
  1686.     If SHGetSpecialFolderLocation(frmSetup1.hwnd, lPrograms, IDL) = NOERROR Then
  1687.         sPath = String$(gintMAX_PATH_LEN, 0)
  1688.         SHGetPathFromIDListA IDL, sPath
  1689.         SHGetMalloc oMalloc
  1690.         oMalloc.Free IDL
  1691.         sPath = StringFromBuffer(sPath)
  1692.     End If
  1693.     AddDirSep sPath
  1694.     sPath = sPath & strFolderName
  1695.     fSuccess = MakePath(sPath)
  1696.     If Not fSuccess Then
  1697.         If gfNoUserInput Or (MsgError(ResolveResString(resCANTCREATEPROGRAMGROUP, "|1", strFolderName), vbRetryCancel Or vbExclamation, gstrTitle)) = vbCancel Then
  1698.             ExitSetup frmSetup1, gintRET_EXIT
  1699.             GoTo Retry
  1700.         End If
  1701.         '
  1702.         ' Determine if we should return so the user can
  1703.         ' correct the situation.
  1704.         '
  1705.         If Not fRetOnErr Then
  1706.             '
  1707.             ' Return so we can exit setup.
  1708.             '
  1709.             GoTo Retry
  1710.         End If
  1711.     End If
  1712.  
  1713.     
  1714.     fCreateShellGroup = fSuccess
  1715. End Function
  1716.  
  1717. '-----------------------------------------------------------
  1718. ' SUB: CreateShellLink
  1719. '
  1720. ' Creates (or replaces) a link in either Start>Programs or
  1721. ' any of its immediate subfolders in the Windows 95 shell.
  1722. '
  1723. ' IN: [strLinkPath] - full path to the target of the link
  1724. '                     Ex: 'c:\Program Files\My Application\MyApp.exe"
  1725. '     [strLinkArguments] - command-line arguments for the link
  1726. '                     Ex: '-f -c "c:\Program Files\My Application\MyApp.dat" -q'
  1727. '     [strLinkName] - text caption for the link
  1728. '     [fLog] - Whether or not to write to the logfile (default
  1729. '                is true if missing)
  1730. '
  1731. ' OUT:
  1732. '   The link will be created in the folder strGroupName
  1733.  
  1734. '-----------------------------------------------------------
  1735. '
  1736. Sub CreateShellLink(ByVal strLinkPath As String, ByVal strGroupName As String, ByVal strLinkArguments As String, ByVal strLinkName As String, ByVal fPrivate As Boolean, sParent As String, Optional ByVal fLog As Boolean = True)
  1737.     
  1738.     If fLog Then
  1739.         NewAction gstrKEY_SHELLLINK, """" & strUnQuoteString(strGroupName) & """" & ", " & """" & strUnQuoteString(strLinkName) & """"
  1740.     End If
  1741.     
  1742.     'ReplaceDoubleQuotes strLinkName
  1743.     strLinkName = strUnQuoteString(strLinkName)
  1744.     strLinkPath = strUnQuoteString(strLinkPath)
  1745.     
  1746.  
  1747. Retry:
  1748.  
  1749.     Dim fSuccess As Boolean
  1750.     fSuccess = OSfCreateShellLink(strGroupName & vbNullChar, strLinkName, strLinkPath, strLinkArguments & vbNullChar, fPrivate, sParent) 'the path should never be enclosed in double quotes
  1751.     If fSuccess Then
  1752.         If fLog Then
  1753.             CommitAction
  1754.         End If
  1755.     Else
  1756.         Dim intMsgRet As Integer
  1757.         intMsgRet = MsgError(ResolveResString(resCANTCREATEPROGRAMICON, "|1", strLinkName), vbAbortRetryIgnore Or vbExclamation, gstrTitle)
  1758.         If gfNoUserInput Then
  1759.             intMsgRet = vbAbort
  1760.         End If
  1761.         Select Case intMsgRet
  1762.             Case vbAbort
  1763.                 ExitSetup frmSetup1, gintRET_ABORT
  1764.                 GoTo Retry
  1765.             Case vbRetry
  1766.                 GoTo Retry
  1767.             Case vbIgnore
  1768.                 If fLog Then
  1769.                     AbortAction
  1770.                 End If
  1771.             'End Case
  1772.         End Select
  1773.     End If
  1774. End Sub
  1775.  
  1776. '-----------------------------------------------------------
  1777. ' FUNCTION: DecideIncrementRefCount
  1778. '
  1779. ' Increments the reference count of a file under 32-bits
  1780. ' if the file is a shared file.
  1781. '
  1782. ' IN: [strFullPath] - full pathname of the file to reference
  1783. '                     count.  Example:
  1784. '                     'C:\MYAPP\MYAPP.DAT'
  1785. '     [fShared] - whether the file is shared or private
  1786. '     [fSystem] - The file is a system file
  1787. '     [fFileAlreadyExisted] - whether or not the file already
  1788. '                             existed on the hard drive
  1789. '                             before our setup program
  1790. '-----------------------------------------------------------
  1791. '
  1792. Sub DecideIncrementRefCount(ByVal strFullPath As String, ByVal fShared As Boolean, ByVal fSystem As Boolean, ByVal fFileAlreadyExisted As Boolean)
  1793.     'Reference counting takes place under both Windows 95 and Windows NT
  1794.     If fShared Or fSystem Then
  1795.         IncrementRefCount strFullPath, fFileAlreadyExisted
  1796.     End If
  1797. End Sub
  1798.             
  1799. '-----------------------------------------------------------
  1800. ' FUNCTION: DetectFile
  1801. '
  1802. ' Detects whether the specified file exists.  If it can't
  1803. ' be found, the user is given the opportunity to abort,
  1804. ' retry, or ignore finding the file.  This call is used,
  1805. ' for example, to ensure that a floppy with the specified
  1806. ' file name is in the drive before continuing.
  1807. '
  1808. ' IN: [strFileName] - name of file to detect, usually
  1809. '                     should include full path, Example:
  1810. '                     'A:\MYAPP.DAT'
  1811. '
  1812. ' Returns: TRUE if the file was detected, vbignore if
  1813. '          the user chose ignore when the file couldn't
  1814. '          be found, or calls ExitSetup upon 'Abort'
  1815. '-----------------------------------------------------------
  1816. '
  1817. Function DetectFile(ByVal strFilename As String) As Integer
  1818.     Dim strMsg As String
  1819.  
  1820.     DetectFile = True
  1821.                       
  1822.     Do While FileExists(strFilename) = False
  1823.  
  1824.  
  1825.         strMsg = ResolveResString(resCANTOPEN) & vbLf & vbLf & strFilename
  1826.         Select Case MsgError(strMsg, vbAbortRetryIgnore Or vbExclamation Or IIf(gfNoUserInput, vbDefaultButton1, vbDefaultButton2), gstrSETMSG)
  1827.             Case vbAbort
  1828.                 ExitSetup frmCopy, gintRET_ABORT
  1829.             Case vbIgnore
  1830.                 DetectFile = vbIgnore
  1831.                 Exit Do
  1832.             'End Case
  1833.         End Select
  1834.     Loop
  1835. End Function
  1836.  
  1837.  
  1838. '-----------------------------------------------------------
  1839. ' SUB: EtchedLine
  1840. '
  1841. ' Draws an 'etched' line upon the specified form starting
  1842. ' at the X,Y location passed in and of the specified length.
  1843. ' Coordinates are in the current ScaleMode of the passed
  1844. ' in form.
  1845. '
  1846. ' IN: [frmEtch] - form to draw the line upon
  1847. '     [intX1] - starting horizontal of line
  1848. '     [intY1] - starting vertical of line
  1849. '     [intLength] - length of the line
  1850. '-----------------------------------------------------------
  1851. '
  1852. Sub EtchedLine(frmEtch As Form, ByVal intX1 As Integer, ByVal intY1 As Integer, ByVal intLength As Integer)
  1853.     Const lWHITE& = vb3DHighlight
  1854.     Const lGRAY& = vb3DShadow
  1855.  
  1856.     frmEtch.Line (intX1, intY1)-(intX1 + intLength, intY1), lGRAY
  1857.     frmEtch.Line (frmEtch.CurrentX + 5, intY1 + 20)-(intX1 - 5, intY1 + 20), lWHITE
  1858. End Sub
  1859.  
  1860. '-----------------------------------------------------------
  1861. ' SUB: ExeSelfRegister
  1862. '
  1863. ' Synchronously runs the file passed in (which should be
  1864. ' an executable file that supports the /REGSERVER switch,
  1865. ' for instance, a VB5 generated ActiveX Component .EXE).
  1866. '
  1867. ' IN: [strFileName] - .EXE file to register
  1868. '-----------------------------------------------------------
  1869. '
  1870. Sub ExeSelfRegister(ByVal strFilename As String)
  1871.     Const strREGSWITCH$ = " /REGSERVER"
  1872.  
  1873.     Dim fShell As Integer
  1874.  
  1875.     '
  1876.     'Synchronously shell out and run the .EXE with the self registration switch
  1877.     '
  1878.     fShell = SyncShell(AddQuotesToFN(strFilename) & strREGSWITCH, INFINITE, , True)
  1879.     frmSetup1.Refresh
  1880. End Sub
  1881.  
  1882. '-----------------------------------------------------------
  1883. ' SUB: ExitSetup
  1884. '
  1885. ' Handles shutdown of the setup app.  Depending upon the
  1886. ' value of the intExitCode parm, may prompt the user and
  1887. ' exit the sub if the user chooses to cancel the exit
  1888. ' process.
  1889. '
  1890. ' IN: [frm] - active form to unload upon exit
  1891. '     [intExitCode] - code specifying exit action
  1892. '-----------------------------------------------------------
  1893. '
  1894. Sub ExitSetup(frm As Form, intExitCode As Integer)
  1895.     Const sKEY As String = "Software\Microsoft\Windows\CurrentVersion\RunOnce\Setup"
  1896.     Const sValue As String = "Configuring Data Access"
  1897.     
  1898.     Const iSUCCESS = 0
  1899.     Const iFAIL = 1
  1900.     Dim strMsg As String
  1901.     Dim strSilent As String
  1902.     Dim fNeedReboot As Boolean
  1903.     
  1904.     On Error Resume Next
  1905.     '
  1906.     ' If we aren't running in silent or sms mode give
  1907.     ' the user a chance to try again, if applicable.
  1908.     '
  1909.     If Not gfNoUserInput Then
  1910.         Select Case intExitCode
  1911.             Case gintRET_EXIT
  1912.                 '
  1913.                 'If user chose an Exit or Cancel button
  1914.                 '
  1915.                 If MsgWarning(ResolveResString(resASKEXIT), vbQuestion Or vbYesNo Or vbDefaultButton2, gstrTitle) = vbNo Then
  1916.                     Exit Sub
  1917.                 End If
  1918.             Case gintRET_ABORT
  1919.                 '
  1920.                 'If user chose to abort before a pending action
  1921.                 '
  1922.                 strMsg = ResolveResString(resINCOMPLETE) & vbLf & vbLf & ResolveResString(resQUITNOW) & vbLf & vbLf
  1923.                 strMsg = strMsg & ResolveResString(resQUITSETUP)
  1924.                 If MsgWarning(strMsg, vbQuestion Or vbYesNo Or IIf(gfNoUserInput, vbDefaultButton1, vbDefaultButton2), gstrSETMSG) = vbNo Then
  1925.                     Exit Sub
  1926.                 End If
  1927.             'End Case
  1928.         End Select
  1929.     End If
  1930.  
  1931.     'Abort any pending actions
  1932.     While fWithinAction()
  1933.         AbortAction
  1934.     Wend
  1935.     
  1936.     Close
  1937.  
  1938.     '
  1939.     'Clean up any temporary files from VerInstallFile or split file concatenation
  1940.     '
  1941.     Kill mstrVerTmpName
  1942.     If mintConcatFile > 0 Then
  1943.         Close mintConcatFile
  1944.         Kill mstrConcatDrive & mstrCONCATFILE
  1945.     End If
  1946.  
  1947.     If frm.hwnd <> frmSetup1.hwnd Then
  1948.         Unload frm
  1949.     End If
  1950.     
  1951.     If frmSetup1.Visible Then frmSetup1.SetFocus
  1952.  
  1953.     '
  1954.     'Give appropriate ending message depending upon exit code
  1955.     '
  1956.     Select Case intExitCode
  1957.         Case gintRET_EXIT, gintRET_ABORT
  1958.             gfSMSStatus = False
  1959.             strMsg = ResolveResString(resINTERRUPTED, "|1", gstrAppName) & vbLf & vbLf & ResolveResString(resCANRUN, "|1", gstrAppName)
  1960.             MsgWarning strMsg, vbOKOnly Or vbCritical, gstrTitle
  1961.         Case gintRET_FATAL
  1962.             gfSMSStatus = False
  1963.             MsgError ResolveResString(resERROR, "|1", gstrAppName), vbOKOnly Or vbCritical, gstrTitle
  1964.         Case gintRET_FINISHEDSUCCESS
  1965.             gfSMSStatus = True
  1966.             '
  1967.             ' Don't log this message to SMS since it is only a confirmation.
  1968.             '
  1969.             gfDontLogSMS = True
  1970.             MsgFunc ResolveResString(resSUCCESS, "|1", gstrAppName), vbOKOnly, gstrTitle
  1971.             
  1972.             If IsWindowsNT4WithoutSP2() Then
  1973.                 'Recommend that the user upgrade to NT 4.0 SP2
  1974.                 gfDontLogSMS = True
  1975.                 MsgWarning ResolveResString(resNT4WithoutSP2), vbOKOnly Or vbInformation, gstrTitle
  1976.             End If
  1977.         Case Else
  1978.             strMsg = ResolveResString(resINTERRUPTED, "|1", gstrAppName) & vbLf & vbLf & ResolveResString(resCANRUN, "|1", gstrAppName)
  1979.             MsgWarning strMsg, vbOKOnly Or vbCritical, gstrTitle
  1980.         'End Case
  1981.     End Select
  1982.  
  1983.     'Stop logging
  1984.     DisableLogging
  1985.     
  1986.     ' Clean up an aborted installation
  1987.     If (intExitCode = gintRET_FINISHEDSUCCESS) Then
  1988.         'Check to see if we need to reboot for mdac_typ
  1989.         Dim sRet As String
  1990.         If GetKeyValue(HK_LOCAL_MACHINE, sKEY, sValue, sRet) Then
  1991.             'We need to reboot
  1992.             'Warn the user before rebooting.  If they choose to reboot, do so, otherwise
  1993.             'Warn them again.
  1994.             If MsgBox(ResolveResString(resREBOOT), vbYesNo Or vbInformation, gstrTitle) = vbYes Then
  1995.                 fNeedReboot = True
  1996.             Else
  1997.                 fNeedReboot = False
  1998.                 intExitCode = gintRET_FATAL
  1999.                 MsgBox ResolveResString(resREBOOTNO), vbOKOnly Or vbExclamation, gstrTitle
  2000.             End If
  2001.         End If
  2002.     Else
  2003.         'Setup has been aborted for one reason or another
  2004.         If (gstrAppRemovalEXE <> "") Then
  2005.             Dim nErrorLevel As Integer
  2006.             Select Case intExitCode
  2007.                 Case gintRET_FATAL
  2008.                     nErrorLevel = APPREMERR_FATAL
  2009.                 Case gintRET_EXIT
  2010.                     nErrorLevel = APPREMERR_USERCANCEL
  2011.                 Case gintRET_ABORT
  2012.                     nErrorLevel = APPREMERR_NONFATAL
  2013.                 Case Else
  2014.                     nErrorLevel = APPREMERR_FATAL
  2015.                 'End Case
  2016.             End Select
  2017.         
  2018.             '
  2019.             ' We don't want to log this message to sms because it is
  2020.             ' only a confirmation message.
  2021.             '
  2022.             gfDontLogSMS = True
  2023.             MsgFunc ResolveResString(resLOG_ABOUTTOREMOVEAPP), vbInformation Or vbOKOnly, gstrTitle
  2024.             
  2025.             Err = 0
  2026.             '
  2027.             ' Ready to run the installer.  Determine if this is a
  2028.             ' silent uninstall or not.
  2029.             '
  2030.             If gfSilent Then
  2031.                 strSilent = gstrSilentLog
  2032.             Else
  2033.                 strSilent = vbNullString
  2034.             End If
  2035.             
  2036.             Shell GetAppRemovalCmdLine(gstrAppRemovalEXE, gstrAppRemovalLog, strSilent, gfSMS, nErrorLevel, True), vbNormalFocus
  2037.             If Err Then
  2038.                 MsgError Error$ & vbLf & vbLf & ResolveResString(resLOG_CANTRUNAPPREMOVER), vbExclamation Or vbOKOnly, gstrTitle
  2039.             End If
  2040.  
  2041.             'Since the app removal program will attempt to delete this program and all of our runtime
  2042.             'files, we should exit as soon as possible (otherwise the app remover will not be
  2043.             'able to remove these files)
  2044.         End If
  2045.         
  2046.         'Note: We do not delete the logfile if an error occurs.
  2047.         'The application removal EXE will do that if needed.
  2048.         
  2049.     End If
  2050.     
  2051.     Unload frmSetup1
  2052.  
  2053.     If gfSMS = True Then
  2054.         WriteMIF gstrMIFFile, gfSMSStatus, gstrSMSDescription
  2055.     End If
  2056.  
  2057.     'Try the reboot (if necessary)...
  2058.     If fNeedReboot Then RebootSystem
  2059.     'End the program
  2060.     If (intExitCode = gintRET_FINISHEDSUCCESS) Then
  2061.         ExitProcess iSUCCESS
  2062.     Else
  2063.         ExitProcess iFAIL
  2064.     End If
  2065. End Sub
  2066.  
  2067. '-----------------------------------------------------------
  2068. ' FUNCTION: ProcessCommandLine
  2069. '
  2070. ' Processes the command-line arguments
  2071. '
  2072. ' OUT: Fills in the passed-in byref parameters as appropriate
  2073. '-----------------------------------------------------------
  2074. '
  2075. Sub ProcessCommandLine(ByVal strCommand As String, ByRef fSilent As Boolean, ByRef strSilentLog As String, ByRef fSMS As Boolean, ByRef strMIFFile As String, ByRef strSrcPath As String, ByRef strAppRemovalLog As String, ByRef strAppRemovalEXE As String)
  2076.     Dim fErr As Boolean
  2077.     Dim intAnchor As Integer
  2078.     
  2079.     Const strTemp$ = ""
  2080.     
  2081.     strSrcPath = ""
  2082.     strAppRemovalLog = ""
  2083.     
  2084.     strCommand = Trim$(strCommand)
  2085.     
  2086.     '
  2087.     ' First, check to see if this is supposed to be a silent
  2088.     ' install (/s on the command line followed by
  2089.     ' a log file name) and set global variables appropriately.
  2090.     '
  2091.     ' If you are designing a silent install, the /s
  2092.     ' command line parameter should be added to the setup.exe
  2093.     ' command.  It will automatically be passed to setup1 as the
  2094.     ' first parameter.
  2095.     '
  2096.     ' The filename that follows the /s parameter must
  2097.     ' include the full path name.
  2098.     '
  2099.     intAnchor = InStr(LCase(strCommand), gstrSwitchPrefix2 & gstrSILENTSWITCH)
  2100.     If intAnchor > 0 Then
  2101.         fSilent = True
  2102.         strCommand = Trim(Mid(strCommand, intAnchor + 2))
  2103.         strSilentLog = strExtractFilenameArg(strCommand, fErr)
  2104.         If fErr Then GoTo BadCommandLine
  2105.     Else
  2106.         fSilent = False
  2107.     End If
  2108.     fSMS = False
  2109.     
  2110.     '
  2111.     ' We expect to find the source directory,
  2112.     ' name/path of the logfile, and name/path
  2113.     ' of the app removal executable, separated only by
  2114.     ' spaces
  2115.     '
  2116.     strSrcPath = strExtractFilenameArg(strCommand, fErr)
  2117.     If fErr Then GoTo BadCommandLine
  2118.     
  2119.     strAppRemovalLog = strExtractFilenameArg(strCommand, fErr)
  2120.     If fErr Then GoTo BadCommandLine
  2121.     
  2122.  
  2123.     strAppRemovalEXE = strExtractFilenameArg(strCommand, fErr)
  2124.     If fErr Then GoTo BadCommandLine
  2125.         
  2126.     ' Both the app removal logfile and executable must exist
  2127.     If Not FileExists(strAppRemovalLog) Then
  2128.         GoTo BadAppRemovalLog
  2129.     End If
  2130.     
  2131.     If Not FileExists(strAppRemovalEXE) Then
  2132.         GoTo BadAppRemovalEXE
  2133.     End If
  2134.     
  2135.     ' Last check:  There should be nothing else on the command line
  2136.     strCommand = Trim$(strCommand)
  2137.     If strCommand <> "" Then
  2138.         GoTo BadCommandLine
  2139.     End If
  2140.     
  2141.     Exit Sub
  2142.     
  2143. BadAppRemovalLog:
  2144.     MsgError ResolveResString(resCANTFINDAPPREMOVALLOG, "|1", strAppRemovalLog), vbExclamation Or vbOKOnly, gstrTitle
  2145.     ExitSetup frmSetup1, gintRET_FATAL
  2146.     
  2147. BadAppRemovalEXE:
  2148.     MsgError ResolveResString(resCANTFINDAPPREMOVALEXE, "|1", strAppRemovalEXE), vbExclamation Or vbOKOnly, gstrTitle
  2149.     ExitSetup frmSetup1, gintRET_FATAL
  2150.     
  2151. BadCommandLine:
  2152.     MsgError ResolveResString(resBADCOMMANDLINE), vbExclamation Or vbOKOnly, gstrTitle
  2153.     ExitSetup frmSetup1, gintRET_FATAL
  2154. End Sub
  2155.  
  2156. '-----------------------------------------------------------
  2157. ' FUNCTION: GetDrivesAllocUnit
  2158. '
  2159. ' Gets the minimum file size allocation unit for the
  2160. ' specified drive
  2161. '
  2162. ' IN: [strDrive] - Drive to get allocation unit for
  2163. '
  2164. ' Returns: minimum allocation unit of drive, or -1 if
  2165. '          this value couldn't be determined
  2166. '-----------------------------------------------------------
  2167. '
  2168. Function GetDrivesAllocUnit(ByVal strDrive As String) As Long
  2169.     Dim strCurDrive As String
  2170.     Dim lAllocUnit As Long
  2171.  
  2172.     On Error Resume Next
  2173.  
  2174.     '
  2175.     'Save current drive
  2176.     '
  2177.     strCurDrive = Left$(CurDir$, 2)
  2178.  
  2179.     '
  2180.     'append a colon to the end of the drivespec if none supplied
  2181.     '
  2182.     If InStr(strDrive, gstrCOLON) = 0 Or Len(strDrive) > 2 Then
  2183.         strDrive = Left$(strDrive, 1) & gstrCOLON
  2184.     End If
  2185.  
  2186.     '
  2187.     'Change to the drive to determine the allocation unit for.  The AllocUnit()
  2188.     'API returns this value for the current drive only
  2189.     '
  2190.     ChDrive strDrive
  2191.  
  2192.     '
  2193.     'If there was an error accessing the specified drive, flag error return.
  2194.     'It is also possible for the AllocUnit() API to return -1 on other failure
  2195.     '
  2196.     If Err <> 0 Or (strDrive <> Left$(CurDir$, 2)) Then
  2197.         lAllocUnit = -1
  2198.     Else
  2199.         Dim lRet As Long
  2200.         Dim lBytes As Long, lSect As Long, lClust As Long, lTot As Long
  2201.         
  2202.         lRet = GetDiskFreeSpace(vbNullString, lSect, lBytes, lClust, lTot)
  2203.         lAllocUnit = lSect * lBytes
  2204.         If Err <> 0 Then
  2205.             lAllocUnit = -1
  2206.         End If
  2207.     End If
  2208.  
  2209.     If lAllocUnit = -1 Then
  2210.         MsgError Error$ & vbLf & vbLf & ResolveResString(resALLOCUNIT) & strDrive, vbExclamation, gstrTitle
  2211.         If gfSMS Then
  2212.             ExitSetup frmSetup1, gintRET_FATAL
  2213.         End If
  2214.     End If
  2215.  
  2216.     GetDrivesAllocUnit = lAllocUnit
  2217.  
  2218.     '
  2219.     'Restore to original drive
  2220.     '
  2221.     ChDrive strCurDrive
  2222.  
  2223.     Err = 0
  2224. End Function
  2225.  
  2226. '-----------------------------------------------------------
  2227. ' FUNCTION: GetFileName
  2228. '
  2229. ' Return the filename portion of a path
  2230. '
  2231. '-----------------------------------------------------------
  2232. '
  2233. Function GetFileName(ByVal strPath As String) As String
  2234.     Dim strFilename As String
  2235.     Dim iSep As Integer
  2236.     
  2237.     strFilename = strPath
  2238.     Do
  2239.         iSep = InStr(strFilename, gstrSEP_DIR)
  2240.         If iSep = 0 Then iSep = InStr(strFilename, gstrCOLON)
  2241.         If iSep = 0 Then
  2242.             GetFileName = strFilename
  2243.             Exit Function
  2244.         Else
  2245.             strFilename = Right(strFilename, Len(strFilename) - iSep)
  2246.         End If
  2247.     Loop
  2248. End Function
  2249.  
  2250. '-----------------------------------------------------------
  2251. ' FUNCTION: GetFileSize
  2252. '
  2253. ' Determine the size (in bytes) of the specified file
  2254. '
  2255. ' IN: [strFileName] - name of file to get size of
  2256. '
  2257. ' Returns: size of file in bytes, or -1 if an error occurs
  2258. '-----------------------------------------------------------
  2259. '
  2260. Function GetFileSize(strFilename As String) As Long
  2261.     On Error Resume Next
  2262.  
  2263.     GetFileSize = FileLen(strFilename)
  2264.  
  2265.     If Err > 0 Then
  2266.         GetFileSize = -1
  2267.         Err = 0
  2268.     End If
  2269. End Function
  2270.  
  2271. '-----------------------------------------------------------
  2272. ' FUNCTION: GetAppRemovalCmdLine
  2273. '
  2274. ' Returns the correct command-line arguments (including
  2275. ' path to the executable for use in calling the
  2276. ' application removal executable)
  2277. '
  2278. ' IN: [strAppRemovalEXE] - Full path/filename of the app removal EXE
  2279. '     [strAppRemovalLog] - Full path/filename of the app removal logfile
  2280. '     [strSilentLog] - Full path/filename of the file to log messages to when in silent mode.
  2281. '                       If this is an empty string then silent mode is turned off for uninstall.
  2282. '     [fSMS] - Boolean.  If True, we have been doing an SMS install and must tell the Uninstaller
  2283. '              to also do an SMS uninstall.  SMS is the Microsoft Systems Management Server.
  2284. '     [nErrorLevel] - Error level:
  2285. '                        APPREMERR_NONE - no error
  2286. '                        APPREMERR_FATAL - fatal error
  2287. '                        APPREMERR_NONFATAL - non-fatal error, user chose to abort
  2288. '                        APPREMERR_USERCANCEL - user chose to cancel (no error)
  2289. '     [fWaitForParent] - True if the application removal utility should wait
  2290. '                        for the parent (this process) to finish before starting
  2291. '                        to remove files.  Otherwise it may not be able to remove
  2292. '                        this process' executable file, depending upon timing.
  2293. '                        Defaults to False if not specified.
  2294. '-----------------------------------------------------------
  2295. '
  2296. Function GetAppRemovalCmdLine(ByVal strAppRemovalEXE As String, ByVal strAppRemovalLog, ByVal strSilentLog As String, ByVal fSMS As Boolean, ByVal nErrorLevel As Integer, Optional fWaitForParent As Boolean = False)
  2297.     Dim strEXE As String
  2298.     Dim strLog As String
  2299.     Dim strSilent As String
  2300.     Dim strErrLevel As String
  2301.     Dim strForce As String
  2302.     Dim strWait As String
  2303.     Dim strSMS As String
  2304.  
  2305.     strEXE = AddQuotesToFN(strAppRemovalEXE)
  2306.     strLog = "-n " & """" & GetLongPathName(strAppRemovalLog) & """"
  2307.     If gfSilent And strSilentLog <> vbNullString Then
  2308.         strSilent = "/s " & """" & strSilentLog & """"
  2309.     Else
  2310.         strSilent = vbNullString
  2311.     End If
  2312.     
  2313.     strSMS = IIf(fSMS, " /q ", vbNullString)
  2314.     
  2315.     strErrLevel = IIf(nErrorLevel <> APPREMERR_NONE, "-e " & Format(nErrorLevel), "")
  2316.     If nErrorLevel <> APPREMERR_NONE Then
  2317.         strForce = " -f"
  2318.     End If
  2319.     If fWaitForParent Then
  2320.         Dim curProcessId As Currency
  2321.         Dim Wrap As Currency
  2322.         Dim lProcessId As Long
  2323.         Dim cProcessId As Currency
  2324.         
  2325.         Wrap = 2 * (CCur(&H7FFFFFFF) + 1)
  2326.  
  2327.         'Always print as an unsigned long
  2328.         lProcessId = GetCurrentProcessId()
  2329.         cProcessId = lProcessId
  2330.         If cProcessId < 0 Then cProcessId = cProcessId + Wrap
  2331.  
  2332.         strWait = " -w " & str(cProcessId)
  2333.     End If
  2334.     
  2335.     GetAppRemovalCmdLine = strEXE & " " & strLog & " " & strSilent & " " & strSMS & strErrLevel & strForce & strWait
  2336. End Function
  2337.  
  2338. '-----------------------------------------------------------
  2339. ' FUNCTION: IncrementRefCount
  2340. '
  2341. ' Increments the reference count on a file in the registry
  2342. ' so that it may properly be removed if the user chooses
  2343. ' to remove this application.
  2344. '
  2345. ' IN: [strFullPath] - FULL path/filename of the file
  2346. '     [fFileAlreadyExisted] - indicates whether the given
  2347. '                             file already existed on the
  2348. '                             hard drive
  2349. '-----------------------------------------------------------
  2350. '
  2351. Sub IncrementRefCount(ByVal strFullPath As String, ByVal fFileAlreadyExisted As Boolean)
  2352.     Dim strSharedDLLsKey As String
  2353.     strSharedDLLsKey = RegPathWinCurrentVersion() & "\SharedDLLs"
  2354.     
  2355.     'We must always use the LFN for the filename, so that we can uniquely
  2356.     'and accurately identify the file in the registry.
  2357.     strFullPath = GetLongPathName(strFullPath)
  2358.     
  2359.     'Get the current reference count for this file
  2360.     Dim fSuccess As Boolean
  2361.     Dim hKey As Long
  2362.     fSuccess = RegCreateKey(HKEY_LOCAL_MACHINE, strSharedDLLsKey, "", hKey)
  2363.     If fSuccess Then
  2364.         Dim lCurRefCount As Long
  2365.         If Not RegQueryRefCount(hKey, strFullPath, lCurRefCount) Then
  2366.             'No current reference count for this file
  2367.             If fFileAlreadyExisted Then
  2368.                 'If there was no reference count, but the file was found
  2369.                 'on the hard drive, it means one of two things:
  2370.                 '  1) This file is shipped with the operating system
  2371.                 '  2) This file was installed by an older setup program
  2372.                 '     that does not do reference counting
  2373.                 'In either case, the correct conservative thing to do
  2374.                 'is assume that the file is needed by some application,
  2375.                 'which means it should have a reference count of at
  2376.                 'least 1.  This way, our application removal program
  2377.                 'will not delete this file.
  2378.                 lCurRefCount = 1
  2379.  
  2380.             Else
  2381.                 lCurRefCount = 0
  2382.             End If
  2383.         End If
  2384.         
  2385.         'Increment the count in the registry
  2386.         fSuccess = RegSetNumericValue(hKey, strFullPath, lCurRefCount + 1, False)
  2387.         If Not fSuccess Then
  2388.             GoTo DoErr
  2389.         End If
  2390.         RegCloseKey hKey
  2391.     Else
  2392.         GoTo DoErr
  2393.     End If
  2394.     
  2395.     Exit Sub
  2396.     
  2397. DoErr:
  2398.     'An error message should have already been shown to the user
  2399.     Exit Sub
  2400. End Sub
  2401.  
  2402. '-----------------------------------------------------------
  2403. ' FUNCTION: InitDiskInfo
  2404. '
  2405. ' Called before calculating disk space to initialize
  2406. ' values used/determined when calculating disk space
  2407. ' required.
  2408. '-----------------------------------------------------------
  2409. '
  2410. Sub InitDiskInfo()
  2411.     '
  2412.     'Initialize "table" of drives used and disk space array
  2413.     '
  2414.     gstrDrivesUsed = vbNullString
  2415.     Erase gsDiskSpace
  2416.  
  2417.     mlTotalToCopy = 0
  2418.  
  2419.     '
  2420.     'Get drive/directory for temporary files
  2421.     '
  2422.     mstrConcatDrive = UCase$(Environ$(gstrTMP_DIR))
  2423.     If mstrConcatDrive = vbNullString Then
  2424.         mstrConcatDrive = UCase$(Environ$(gstrTEMP_DIR))
  2425.     End If
  2426.     AddDirSep mstrConcatDrive
  2427.  
  2428.     If mstrConcatDrive <> vbNullString Then
  2429.         If CheckDrive(mstrConcatDrive, ResolveResString(resTEMPDRIVE)) = False Then
  2430.             mstrConcatDrive = vbNullString
  2431.         Else
  2432.             '
  2433.             'If we found a temp drive and the drive is "ready", add it to the
  2434.             'table of drives used
  2435.             '
  2436.             gstrDrivesUsed = Left$(mstrConcatDrive, 1)
  2437.             ReDim Preserve gsDiskSpace(1)
  2438.             gsDiskSpace(1).lAvail = GetDiskSpaceFree(mstrConcatDrive)
  2439.             gsDiskSpace(1).lMinAlloc = GetDrivesAllocUnit(mstrConcatDrive)
  2440.         End If
  2441.     End If
  2442. End Sub
  2443.  
  2444. '-----------------------------------------------------------
  2445. ' FUNCTION: IsDisplayNameUnique
  2446. '
  2447. ' Determines whether a given display name for registering
  2448. '   the application removal executable is unique or not.  This
  2449. '   display name is the title which is presented to the
  2450. '   user in Windows 95's control panel Add/Remove Programs
  2451. '   applet.
  2452. '
  2453. ' IN: [hkeyAppRemoval] - open key to the path in the registry
  2454. '                       containing application removal entries
  2455. '     [strDisplayName] - the display name to test for uniqueness
  2456. '
  2457. ' Returns: True if the given display name is already in use,
  2458. '          False if otherwise
  2459. '-----------------------------------------------------------
  2460. '
  2461. Function IsDisplayNameUnique(ByVal hkeyAppRemoval As Long, ByVal strDisplayName As String) As Boolean
  2462.     Dim lIdx As Long
  2463.     Dim strSubkey As String
  2464.     Dim strDisplayNameExisting As String
  2465.     Const strKEY_DISPLAYNAME$ = "DisplayName"
  2466.     
  2467.     IsDisplayNameUnique = True
  2468.     
  2469.     lIdx = 0
  2470.     Do
  2471.         Select Case RegEnumKey(hkeyAppRemoval, lIdx, strSubkey)
  2472.             Case ERROR_NO_MORE_ITEMS
  2473.                 'No more keys - must be unique
  2474.                 Exit Do
  2475.             Case ERROR_SUCCESS
  2476.                 'We have a key to some application removal program.  Compare its
  2477.                 '  display name with ours
  2478.                 Dim hkeyExisting As Long
  2479.                 
  2480.                 If RegOpenKey(hkeyAppRemoval, strSubkey, hkeyExisting) Then
  2481.                     If RegQueryStringValue(hkeyExisting, strKEY_DISPLAYNAME, strDisplayNameExisting) Then
  2482.                         If strDisplayNameExisting = strDisplayName Then
  2483.                             'There is a match to an existing display name
  2484.                             IsDisplayNameUnique = False
  2485.                             RegCloseKey hkeyExisting
  2486.                             Exit Do
  2487.                         End If
  2488.                     End If
  2489.                     RegCloseKey hkeyExisting
  2490.                 End If
  2491.             Case Else
  2492.                 'Error, we must assume it's unique.  An error will probably
  2493.                 '  occur later when trying to add to the registry
  2494.                 Exit Do
  2495.             'End Case
  2496.         End Select
  2497.         lIdx = lIdx + 1
  2498.     Loop
  2499. End Function
  2500.  
  2501. '-----------------------------------------------------------
  2502. ' FUNCTION: IsNewerVer
  2503. '
  2504. ' Compares two file version structures and determines
  2505. ' whether the source file version is newer (greater) than
  2506. ' the destination file version.  This is used to determine
  2507. ' whether a file needs to be installed or not
  2508. '
  2509. ' IN: [sSrcVer] - source file version information
  2510. '     [sDestVer] - dest file version information
  2511. '
  2512. ' Returns: True if source file is newer than dest file,
  2513. '          False if otherwise
  2514. '-----------------------------------------------------------
  2515. '
  2516. Function IsNewerVer(sSrcVer As VERINFO, sDestVer As VERINFO) As Integer
  2517.     IsNewerVer = False
  2518.  
  2519.     With sSrcVer
  2520.      If .FileVerPart1 > sDestVer.FileVerPart1 Then GoTo INVNewer
  2521.      If .FileVerPart1 < sDestVer.FileVerPart1 Then GoTo INVOlder
  2522.      
  2523.      If .FileVerPart2 > sDestVer.FileVerPart2 Then GoTo INVNewer
  2524.      If .FileVerPart2 < sDestVer.FileVerPart2 Then GoTo INVOlder
  2525.     
  2526.      If .FileVerPart3 > sDestVer.FileVerPart3 Then GoTo INVNewer
  2527.      If .FileVerPart3 < sDestVer.FileVerPart3 Then GoTo INVOlder
  2528.      
  2529.      If .FileVerPart4 > sDestVer.FileVerPart4 Then GoTo INVNewer
  2530.     End With
  2531.     With sSrcVer
  2532.         If (.FileVerPart1 = sDestVer.FileVerPart1) And _
  2533.             (.FileVerPart2 = sDestVer.FileVerPart2) And _
  2534.             (.FileVerPart3 = sDestVer.FileVerPart3) And _
  2535.             (.FileVerPart4 = sDestVer.FileVerPart4) Then GoTo INVNewer
  2536.     End With
  2537.     GoTo INVOlder
  2538.  
  2539. INVNewer:
  2540.     IsNewerVer = True
  2541. INVOlder:
  2542. End Function
  2543.  
  2544. '-----------------------------------------------------------
  2545. ' FUNCTION: IsValidDestDir
  2546. '
  2547. ' Determines whether or not the destination directory
  2548. ' specifed in the "DefaultDir" key of the [Setup] section
  2549. ' in SETUP.LST or a destination dir entered by the user
  2550. ' is not a subdirectory of the source directory.
  2551. '
  2552. ' Notes: [gstrSrcPath] - points to the source directory
  2553. '        [strDestDir] - points to the dest directory
  2554. '
  2555. ' Returns: True if dest dir is a valid location, False
  2556. '          otherwise
  2557. '-----------------------------------------------------------
  2558. '
  2559. Function IsValidDestDir(strDestDir As String) As Integer
  2560.     Dim strMsg As String
  2561.  
  2562.     '
  2563.     ' Both of these paths, strDestDir and gstrSrcPath, are *always*
  2564.     ' in the format 'X:\' or 'X:\DIRNAME\'.
  2565.     '
  2566.     If InStr(strDestDir, gstrSrcPath) > 0 Then
  2567.         IsValidDestDir = False
  2568.         strMsg = ResolveResString(resDIRSPECIFIED) & vbLf & strDestDir & vbLf & ResolveResString(resSAMEASSRC)
  2569.         MsgFunc strMsg, vbOKOnly Or vbExclamation, gstrTitle
  2570.     Else
  2571.         IsValidDestDir = True
  2572.     End If
  2573. End Function
  2574.  
  2575. '-----------------------------------------------------------
  2576. ' FUNCTION: MakePath
  2577. '
  2578. ' Creates the specified directory path
  2579. '
  2580. ' IN: [strDirName] - name of the dir path to make
  2581. '     [fAllowIgnore] - whether or not to allow the user to
  2582. '                      ignore any encountered errors.  If
  2583. '                      false, the function only returns
  2584. '                      if successful.  If missing, this
  2585. '                      defaults to True.
  2586. '
  2587. ' Returns: True if successful, False if error and the user
  2588. '          chose to ignore.  (The function does not return
  2589. '          if the user selects ABORT/CANCEL on an error.)
  2590. '-----------------------------------------------------------
  2591. '
  2592. Public Function MakePath(ByVal strDir As String, Optional ByVal fAllowIgnore As Boolean = True) As Boolean
  2593.     Do
  2594.         If MakePathAux(strDir) Then
  2595.             MakePath = True
  2596.             Exit Function
  2597.         Else
  2598.             Dim strMsg As String
  2599.             Dim iRet As Integer
  2600.             
  2601.             strMsg = ResolveResString(resMAKEDIR) & vbLf & strDir
  2602.             iRet = MsgError(strMsg, IIf(fAllowIgnore, vbAbortRetryIgnore, vbRetryCancel) Or vbExclamation Or vbDefaultButton2, gstrSETMSG)
  2603.             '
  2604.             ' if we are running silent then we
  2605.             ' can't continue.  Previous MsgError
  2606.             ' took care of write silent log entry.
  2607.             '
  2608.             If gfNoUserInput = True Then
  2609.                 ExitSetup frmCopy, gintRET_FATAL
  2610.             End If
  2611.             
  2612.             Select Case iRet
  2613.                 Case vbAbort, vbCancel
  2614.                     ExitSetup frmCopy, gintRET_ABORT
  2615.                 Case vbIgnore
  2616.                     MakePath = False
  2617.                     Exit Function
  2618.                 Case vbRetry
  2619.                 'End Case
  2620.             End Select
  2621.         End If
  2622.     Loop
  2623. End Function
  2624.  
  2625. '----------------------------------------------------------
  2626. ' SUB: MoveAppRemovalFiles
  2627. '
  2628. ' Moves the app removal logfile to the application directory,
  2629. ' and registers the app removal executable with the operating
  2630. ' system.
  2631. '----------------------------------------------------------
  2632. Sub MoveAppRemovalFiles(ByVal strGroupName As String)
  2633.     Dim strNewAppRemovalLogName As String
  2634.     Dim lCount As Long
  2635.     Dim sCab As String
  2636.     Dim sTemp As String
  2637.     
  2638.     lCount = 0
  2639.     'Get rid of the cab file in the windows dir (if it exists).
  2640.     Do
  2641.         If gintCabs = 1 Then
  2642.             sCab = gstrWinDir
  2643.             AddDirSep sCab
  2644.             sCab = sCab & BaseName(GetShortPathName(gsCABNAME))
  2645.             If FileExists(sCab) Then Kill sCab
  2646.             Exit Do
  2647.         End If
  2648.         lCount = lCount + 1
  2649.         sCab = gstrWinDir
  2650.         AddDirSep sCab
  2651.         sTemp = Left(gsCABNAME, Len(gsCABNAME) - 5) & CStr(lCount) & gstrSEP_EXT & gsINI_CABNAME
  2652.         sCab = sCab & BaseName(sTemp)
  2653.         If FileExists(sCab) Then
  2654.             Kill sCab
  2655.         Else
  2656.             Exit Do
  2657.         End If
  2658.     Loop
  2659.     'Get rid of the temp dir
  2660.     'Bug fix for #6-34583
  2661.     KillTempFolder
  2662.     'Find a unique name for the app removal logfile in the
  2663.     'application directory
  2664.     
  2665.     '...First try the default extension
  2666.     strNewAppRemovalLogName = gstrDestDir & mstrFILE_APPREMOVALLOGBASE & mstrFILE_APPREMOVALLOGEXT
  2667.     If FileExists(strNewAppRemovalLogName) Then
  2668.         '...Next try incrementing integral extensions
  2669.         Dim iExt As Integer
  2670.         Do
  2671.             If iExt > 999 Then
  2672.                 GoTo CopyErr
  2673.             End If
  2674.             
  2675.  
  2676.             strNewAppRemovalLogName = gstrDestDir & mstrFILE_APPREMOVALLOGBASE & gstrSEP_EXT & Format(iExt, "000")
  2677.             If Not FileExists(strNewAppRemovalLogName) Then
  2678.                 Exit Do 'Unique name was found
  2679.             Else
  2680.                 iExt = iExt + 1
  2681.             End If
  2682.         Loop
  2683.     End If
  2684.     
  2685.     
  2686.     
  2687.     On Error GoTo CopyErr
  2688.     FileCopy gstrAppRemovalLog, strNewAppRemovalLogName
  2689.     
  2690.     'Now we need to start logging in the new logfile, so that the
  2691.     'creation of the application removal icon under NT gets logged.
  2692.     EnableLogging strNewAppRemovalLogName
  2693.     
  2694.     On Error GoTo 0
  2695.     If Not RegisterAppRemovalEXE(gstrAppRemovalEXE, strNewAppRemovalLogName, strGroupName) Then
  2696.         If TreatAsWin95() Then
  2697.             MsgError ResolveResString(resCANTREGISTERAPPREMOVER), vbExclamation Or vbOKOnly, gstrTitle
  2698.         Else
  2699.             MsgError ResolveResString(resCANTCREATEAPPREMOVALICON), vbExclamation Or vbOKOnly, gstrTitle
  2700.         End If
  2701.         ExitSetup frmSetup1, gintRET_FATAL
  2702.     End If
  2703.     
  2704.     'Now we can delete the original logfile, since we no longer have a reference
  2705.     'to it, and start using the new logfile
  2706.     On Error Resume Next
  2707.     Kill gstrAppRemovalLog
  2708.     
  2709.     'This temporary app removal logfile should no longer be used
  2710.     gstrAppRemovalLog = strNewAppRemovalLogName
  2711.     gfAppRemovalFilesMoved = True
  2712.     
  2713.     Exit Sub
  2714.     
  2715. CleanUpOnErr:
  2716.     On Error Resume Next
  2717.     Kill strNewAppRemovalLogName
  2718.     On Error GoTo 0
  2719.     MsgError ResolveResString(resCANTCOPYLOG, "|1", gstrAppRemovalLog), vbExclamation Or vbOKOnly, gstrTitle
  2720.     ExitSetup Screen.ActiveForm, gintRET_FATAL
  2721.     
  2722. CopyErr:
  2723.     Resume CleanUpOnErr
  2724. End Sub
  2725. '-----------------------------------------------------------
  2726. ' SUB: KillTempFolder
  2727. ' BUG FIX #6-34583
  2728. '
  2729. ' Deletes the temporary files stored in the temp folder
  2730. '
  2731. Private Sub KillTempFolder()
  2732.  
  2733.     Const sWILD As String = "*.*"
  2734.     Dim sFile As String
  2735.     
  2736.     sFile = Dir(gsTEMPDIR & sWILD)
  2737.     While sFile <> vbNullString
  2738.         SetAttr gsTEMPDIR & sFile, vbNormal
  2739.         Kill gsTEMPDIR & sFile
  2740.         sFile = Dir
  2741.     Wend
  2742.     RmDir gsTEMPDIR
  2743. End Sub
  2744.  
  2745. '-----------------------------------------------------------
  2746. ' SUB: ParseDateTime
  2747. '
  2748. ' Same as CDate with a string argument, except that it
  2749. ' ignores the current localization settings.  This is
  2750. ' important because SETUP.LST always uses the same
  2751. ' format for dates.
  2752. '
  2753. ' IN: [strDate] - string representing the date in
  2754. '                 the format mm/dd/yy or mm/dd/yyyy
  2755. ' OUT: The date which strDate represents
  2756. '-----------------------------------------------------------
  2757. '
  2758. Function ParseDateTime(ByVal strDateTime As String) As Date
  2759. Dim Var As Variant
  2760.     Var = strDateTime
  2761.     If 0 = VariantChangeTypeEx(VarPtr(Var), VarPtr(Var), &H409, 0, vbDate) Then
  2762.         ParseDateTime = Var
  2763.     Else
  2764.         'Raise same error as CDate
  2765.         Err.Raise 13
  2766.     End If
  2767. End Function
  2768.  
  2769. '-----------------------------------------------------------
  2770. ' SUB: PerformDDE
  2771. '
  2772. ' Performs a Program Manager DDE operation as specified
  2773. ' by the intDDE flag and the passed in parameters.
  2774. ' Possible operations are:
  2775. '
  2776. '   mintDDE_ITEMADD:  Add an icon to the active group
  2777. '   mintDDE_GRPADD:   Create a program manager group
  2778. '
  2779. ' IN: [frm] - form containing a label named 'lblDDE'
  2780. '     [strGroup] - name of group to create or insert icon
  2781. '     [strTitle] - title of icon or group
  2782. '     [strCmd] - command line for icon/item to add
  2783. '     [intDDE] - ProgMan DDE action to perform
  2784. '-----------------------------------------------------------
  2785. '
  2786. Sub PerformDDE(frm As Form, ByVal strGroup As String, ByVal strCmd As String, ByVal strTitle As String, ByVal intDDE As Integer, ByVal fLog As Boolean)
  2787.     Const strCOMMA$ = ","
  2788.     Const strRESTORE$ = ", 1)]"
  2789.     Const strACTIVATE$ = ", 5)]"
  2790.     Const strENDCMD$ = ")]"
  2791.     Const strSHOWGRP$ = "[ShowGroup("
  2792.     Const strADDGRP$ = "[CreateGroup("
  2793.     Const strREPLITEM$ = "[ReplaceItem("
  2794.     Const strADDITEM$ = "[AddItem("
  2795.  
  2796.     Dim intIdx As Integer        'loop variable
  2797.  
  2798.     SetMousePtr vbHourglass
  2799.  
  2800.     '
  2801.     'Initialize for DDE Conversation with Windows Program Manager in
  2802.     'manual mode (.LinkMode = 2) where destination control is not auto-
  2803.     'matically updated.  Set DDE timeout for 10 seconds.  The loop around
  2804.     'DoEvents() is to allow time for the DDE Execute to be processsed.
  2805.     '
  2806.  
  2807.     Dim intRetry As Integer
  2808.     For intRetry = 1 To 20
  2809.         On Error Resume Next
  2810.         frm.lblDDE.LinkTopic = "PROGMAN|PROGMAN"
  2811.         If Err = 0 Then
  2812.             Exit For
  2813.         End If
  2814.         DoEvents
  2815.     Next intRetry
  2816.         
  2817.     frm.lblDDE.LinkMode = 2
  2818.     For intIdx = 1 To 10
  2819.       DoEvents
  2820.     Next
  2821.     frm.lblDDE.LinkTimeout = 100
  2822.  
  2823.     On Error Resume Next
  2824.  
  2825.     If Err = 0 Then
  2826.         Select Case intDDE
  2827.             Case mintDDE_ITEMADD
  2828.                 '
  2829.                 ' The item will be created in the group titled strGroup
  2830.                 '
  2831.                 ' Write the action to the logfile
  2832.                 '
  2833.                 If fLog Then
  2834.                     NewAction gstrKEY_PROGMANITEM, """" & strUnQuoteString(strGroup) & """" & ", " & """" & strUnQuoteString(strTitle) & """"
  2835.                 End If
  2836.                 '
  2837.                 ' Force the group strGroup to be the active group.  Additem only
  2838.                 ' puts icons in the active group.
  2839.                 '
  2840.                 #If 0 Then
  2841.                     frm.lblDDE.LinkExecute strSHOWGRP & strGroup & strACTIVATE
  2842.                 #Else
  2843.                     ' strShowGRP doesn't seem to work if ProgMan is minimized.
  2844.                     '  : strADDGRP does the trick fine, though, and it doesn't matter if it already exists.
  2845.                     frm.lblDDE.LinkExecute strADDGRP & strGroup & strENDCMD
  2846.                 #End If
  2847.                 frm.lblDDE.LinkExecute strREPLITEM & strTitle & strENDCMD
  2848.                 Err = 0
  2849.                 frm.lblDDE.LinkExecute strADDITEM & strCmd & strCOMMA & strTitle & String$(3, strCOMMA) & strENDCMD
  2850.             Case mintDDE_GRPADD
  2851.                 '
  2852.                 ' Write the action to the logfile
  2853.                 '
  2854.                 If fLog Then
  2855.                     NewAction gstrKEY_PROGMANGROUP, """" & strUnQuoteString(strGroup) & """"
  2856.                 End If
  2857.                 frm.lblDDE.LinkExecute strADDGRP & strGroup & strENDCMD
  2858.                 frm.lblDDE.LinkExecute strSHOWGRP & strGroup & strRESTORE
  2859.             'End Case
  2860.         End Select
  2861.     End If
  2862.  
  2863.     
  2864.     '
  2865.     'Disconnect DDE Link
  2866.     '
  2867.  
  2868.     frm.lblDDE.LinkMode = 0
  2869.     frm.lblDDE.LinkTopic = ""
  2870.  
  2871.  
  2872.     SetMousePtr gintMOUSE_DEFAULT
  2873.  
  2874.     If fLog Then
  2875.         CommitAction
  2876.     End If
  2877.     
  2878.     
  2879.     Err = 0
  2880. End Sub
  2881.  
  2882. '-----------------------------------------------------------
  2883. ' SUB: PromptForNextDisk
  2884. '
  2885. ' If the source media is removable or a network connection,
  2886. ' prompts the user to insert the specified disk number
  2887. ' containing the filename which is used to determine that
  2888. ' the correct disk is inserted.
  2889. '
  2890. ' IN: [intDiskNum] - disk number to insert
  2891. '     [strDetectFile] - file to search for to ensure that
  2892. '                       the correct disk was inserted
  2893. '
  2894. ' Notes: [gstrSrcPath] - used to identify the source drive
  2895. '-----------------------------------------------------------
  2896. '
  2897. Sub PromptForNextDisk(ByVal intDiskNum As Integer, ByVal strDetectFile As String)
  2898.     Static intDrvType As Integer
  2899.  
  2900.     Dim intRC As Integer
  2901.     Dim strMsg As String
  2902.     Dim strDrive As String
  2903.     Dim strMultDirBaseName As String
  2904.     Dim strDetectPath As String
  2905.  
  2906.     On Error Resume Next
  2907.  
  2908.     strMultDirBaseName = ResolveResString(resCOMMON_MULTDIRBASENAME)
  2909.     '
  2910.     'Get source drive and, if we haven't yet determined it, get the
  2911.     'source drive type
  2912.     '
  2913.     
  2914.     strDrive = Left$(gstrSrcPath, 2)
  2915.     If intDrvType = 0 Then
  2916.         If IsUNCName(strDrive) Then
  2917.             intDrvType = intDRIVE_REMOTE
  2918.             strDrive = gstrSrcPath
  2919.         Else
  2920.             intDrvType = GetDriveType(Asc(strDrive) - 65)
  2921.         End If
  2922.     End If
  2923.  
  2924.     While SrcFileMissing(gstrSrcPath, strDetectFile, intDiskNum) = True
  2925.         Select Case intDrvType
  2926.             Case 0, intDRIVE_REMOVABLE, intDRIVE_CDROM
  2927.                 strMsg = ResolveResString(resINSERT) & vbLf & ResolveResString(resDISK) & Format$(intDiskNum)
  2928.                 strMsg = strMsg & ResolveResString(resINTO) & strDrive
  2929.             Case intDRIVE_REMOTE
  2930.                 strMsg = ResolveResString(resCHKCONNECT) & strDrive
  2931.             Case intDRIVE_FIXED
  2932.                 If DirExists(gstrSrcPath & strMultDirBaseName & Format(intDiskNum)) = True Then
  2933.                     strDetectPath = gstrSrcPath & strMultDirBaseName & Format(intDiskNum)
  2934.                 Else
  2935.                     strDetectPath = gstrSrcPath
  2936.                 End If
  2937.                 strMsg = ResolveResString(resCOMMON_CANTFINDSRCFILE, "|1", strDetectPath & gstrSEP_DIR & strDetectFile)
  2938.             'End Case
  2939.         End Select
  2940.  
  2941.         Beep
  2942.         intRC = MsgFunc(strMsg, vbOKCancel Or vbExclamation, gstrSETMSG)
  2943.         '
  2944.         ' We should always fail if in silent or sms mode.
  2945.         '
  2946.         If intRC = vbCancel Or gfNoUserInput Then
  2947.             ExitSetup frmCopy, gintRET_EXIT
  2948.         End If
  2949.     Wend
  2950.  
  2951.     gintCurrentDisk = intDiskNum
  2952. End Sub
  2953. Function SrcFileMissing(ByVal strSrcDir As String, ByVal strSrcFile As String, ByVal intDiskNum As Integer) As Boolean
  2954. '-----------------------------------------------------------
  2955. ' FUNCTION: SrcFileMissing
  2956. '
  2957. ' Tries to locate the file strSrcFile by first looking
  2958. ' in the strSrcDir directory, then in the DISK(x+1)
  2959. ' directory if it exists.
  2960. '
  2961. ' IN: [strSrcDir] - Directory/Path where file should be.
  2962. '     [strSrcFile] - File we are looking for.
  2963. '     [intDiskNum] - Disk number we are expecting file
  2964. '                    to be on.
  2965. '
  2966. ' Returns: True if file not found; otherwise, false
  2967. '-----------------------------------------------------------
  2968.     Dim fFound As Boolean
  2969.     Dim strMultDirBaseName As String
  2970.     
  2971.     fFound = False
  2972.     
  2973.     AddDirSep strSrcDir
  2974.     '
  2975.     ' First check to see if it's in the main src directory.
  2976.     ' This would happen if someone copied the contents of
  2977.     ' all the floppy disks to a single directory on the
  2978.     ' hard drive.  We should allow this to work.
  2979.     '
  2980.     ' This test would also let us know if the user inserted
  2981.     ' the wrong floppy disk or if a network connection is
  2982.     ' unavailable.
  2983.     '
  2984.     If FileExists(strSrcDir & strSrcFile) = True Then
  2985.         fFound = True
  2986.         GoTo doneSFM
  2987.     End If
  2988.     '
  2989.     ' Next try the DISK(x) subdirectory of the main src
  2990.     ' directory.  This would happen if the floppy disks
  2991.     ' were copied into directories named DISK1, DISK2,
  2992.     ' DISK3,..., DISKN, etc.
  2993.     '
  2994.     strMultDirBaseName = ResolveResString(resCOMMON_MULTDIRBASENAME)
  2995.     If FileExists(strSrcDir & ".." & gstrSEP_DIR & strMultDirBaseName & Format(intDiskNum) & gstrSEP_DIR & strSrcFile) = True Then
  2996.         fFound = True
  2997.         GoTo doneSFM
  2998.     End If
  2999.     
  3000. doneSFM:
  3001.     SrcFileMissing = Not fFound
  3002. End Function
  3003. '-----------------------------------------------------------
  3004. ' FUNCTION: ReadIniFile
  3005. '
  3006. ' Reads a value from the specified section/key of the
  3007. ' specified .INI file
  3008. '
  3009. ' IN: [strIniFile] - name of .INI file to read
  3010. '     [strSection] - section where key is found
  3011. '     [strKey] - name of key to get the value of
  3012. '
  3013. ' Returns: non-zero terminated value of .INI file key
  3014. '-----------------------------------------------------------
  3015. '
  3016. Function ReadIniFile(ByVal strIniFile As String, ByVal strsection As String, ByVal strKey As String) As String
  3017.     Dim strBuffer As String
  3018.     Dim intPos As Integer
  3019.  
  3020.     '
  3021.     'If successful read of .INI file, strip any trailing zero returned by the Windows API GetPrivateProfileString
  3022.     '
  3023.     strBuffer = Space$(gintMAX_SIZE)
  3024.     
  3025.     If GetPrivateProfileString(strsection, strKey, vbNullString, strBuffer, gintMAX_SIZE, strIniFile) > 0 Then
  3026.         ReadIniFile = RTrim$(StripTerminator(strBuffer))
  3027.     Else
  3028.         ReadIniFile = vbNullString
  3029.     End If
  3030. End Function
  3031.  
  3032. '-----------------------------------------------------------
  3033. ' SUB: ReadSetupFileLine
  3034. '
  3035. ' Reads the requested 'FileX=' key from the specified
  3036. ' section of the setup information file (SETUP.LST).
  3037. '
  3038. ' IN: [strSection] - name of section to read from SETUP.LST,
  3039. '                    Ex: "Files"
  3040. '     [intFileNum] - file number index to read
  3041. '
  3042. ' OUT: [sFile] - FILEINFO Type variable that, after parsing,
  3043. '                holds the information for the file
  3044. '                described.
  3045. '
  3046. ' Returns: True if the requested info was successfully read,
  3047. '          False otherwise
  3048. '
  3049. ' Notes: Lines in the setup information file have the
  3050. '        following format:
  3051. '
  3052. '        #,[SPLIT],SrcName,DestName,DestDir,Register,
  3053. '        Date,Size,Version
  3054. '
  3055. '        [#] - disk number where this file is located
  3056. '        [SPLIT] - optional, determines whether this is
  3057. '                  an extent of a split file.  The last
  3058. '                  extent does not specify this key
  3059. '        [SrcName] - filename on the installation media
  3060. '        [DestName] - file name to use when copied
  3061. '
  3062. '        (For split files, the following info is required only
  3063. '        for the *first* extent)
  3064. '
  3065. '        [DestDir] - dirname or macro specifying destdir
  3066. '        [Register] - reginfo file name or macro specifying
  3067. '                     file registration action
  3068. '        [Date] - date of the source file
  3069. '        [Size] - size of the source file
  3070. '        [Version] - optional, version number string
  3071. '        [Reserved] - Must be empty, else error!
  3072. '        [ProgIcon] - Caption for icon, if there is one.
  3073. '        [ProgCmdLine] - Command line for icon, if there is one.
  3074. '-----------------------------------------------------------
  3075. '
  3076. Function ReadSetupFileLine(ByVal strsection As String, ByVal intFileNum As Integer, sFile As FILEINFO) As Integer
  3077.     Static strSplitName As String
  3078.     Const CompareBinary = 0
  3079.  
  3080.     Dim strLine As String
  3081.     Dim strMsg As String
  3082.     Dim intOffset As Integer
  3083.     Dim intAnchor As Integer
  3084.     Dim fDone As Integer
  3085.     Dim fErr As Boolean
  3086.     Dim strVersion As String
  3087.     Dim strFilename As String
  3088.  
  3089.     ReadSetupFileLine = False
  3090.  
  3091.     sFile.fSystem = False
  3092.     sFile.fShared = False
  3093.     
  3094.     '
  3095.     ' Read the requested line, if unable to read it (strLine = vbnullstring) then exit
  3096.     '
  3097.     strLine = ReadIniFile(gstrSetupInfoFile, strsection, gstrINI_FILE & Format$(intFileNum))
  3098.     If strLine = vbNullString Then
  3099.         Exit Function
  3100.     End If
  3101.  
  3102.     '
  3103.     'source file name, ensure it's not a UNC name
  3104.     '
  3105.     intAnchor = 1
  3106.     sFile.strSrcName = strExtractFilenameItem(strLine, intAnchor, fErr)
  3107.     If fErr Then GoTo RSFLError
  3108.     If IsUNCName(sFile.strSrcName) = True Then GoTo RSFLError
  3109.     intAnchor = intAnchor + 1 'Skip past the comma
  3110.     
  3111.     '
  3112.     'dest file name, ensure it's not a UNC name
  3113.     '
  3114.     If Left(sFile.strSrcName, 1) = gstrSEP_AMPERSAND Then
  3115.         sFile.strDestName = Right(sFile.strSrcName, Len(sFile.strSrcName) - 1)
  3116.     Else
  3117.         sFile.strDestName = sFile.strSrcName
  3118.     End If
  3119.     strFilename = GetFileName(sFile.strDestName)
  3120.  
  3121.     '
  3122.     'parse and resolve destination directory
  3123.     '
  3124.     intOffset = intGetNextFldOffset(intAnchor, strLine, gstrCOMMA, CompareBinary)
  3125.     If intOffset > 0 Then
  3126.         Dim strInitialDestDir As String
  3127.         strInitialDestDir = Mid$(strLine, intAnchor, intOffset - intAnchor)
  3128.         If InStr(strInitialDestDir, gstrWINSYSDESTSYSFILE) Then
  3129.             sFile.fSystem = True
  3130.         End If
  3131.         If InStr(strInitialDestDir, gstrDAODEST) Then
  3132.             '
  3133.             ' Special case for DAO destinations.  If there
  3134.             ' are any DAO files, we need to add special
  3135.             ' DAO reg info later.  gfRegDAO tells us to do that.
  3136.             '
  3137.             gfRegDAO = True
  3138.         End If
  3139.         sFile.strDestDir = ResolveDestDir(strInitialDestDir)
  3140.         If sFile.strDestDir <> "?" Then
  3141.             sFile.strDestDir = ResolveDir(sFile.strDestDir, False, False)
  3142.             If sFile.strDestDir = vbNullString Or IsUNCName(sFile.strDestDir) Then
  3143.                 GoTo RSFLError
  3144.             End If
  3145.         End If
  3146.     Else
  3147.         GoTo RSFLError
  3148.     End If
  3149.  
  3150.     '
  3151.     'file registration information
  3152.     '
  3153.     intAnchor = intOffset + 1
  3154.     intOffset = intGetNextFldOffset(intAnchor, strLine, gstrCOMMA, CompareBinary)
  3155.     If intOffset > 0 Then
  3156.         sFile.strRegister = Mid$(strLine, intAnchor, intOffset - intAnchor)
  3157.     Else
  3158.         GoTo RSFLError
  3159.     End If
  3160.  
  3161.     '
  3162.     'Extract file share type
  3163.     '
  3164.     intAnchor = intOffset + 1
  3165.     intOffset = intGetNextFldOffset(intAnchor, strLine, gstrCOMMA, CompareBinary)
  3166.     sFile.fShared = False
  3167.     If intOffset > 0 Then
  3168.         Dim strShareType As String
  3169.         strShareType = Mid$(strLine, intAnchor, intOffset - intAnchor)
  3170.         Select Case strShareType
  3171.             Case mstrPRIVATEFILE
  3172.                 sFile.fShared = False
  3173.             Case mstrSHAREDFILE
  3174.                 If sFile.fSystem Then
  3175.                     'A file cannot be both system and shared
  3176.                     GoTo RSFLError
  3177.                 End If
  3178.                 
  3179.                 sFile.fShared = True
  3180.             Case Else
  3181.                 GoTo RSFLError
  3182.             'End Case
  3183.         End Select
  3184.     End If
  3185.     
  3186.     '
  3187.     'Extract file date and convert to a date variant
  3188.     '
  3189.     intAnchor = intOffset + 1
  3190.     intOffset = intGetNextFldOffset(intAnchor, strLine, gstrCOMMA, CompareBinary)
  3191.     If intOffset > 0 Then
  3192.         On Error GoTo RSFLError
  3193.         sFile.varDate = ParseDateTime(Mid$(strLine, intAnchor, intOffset - intAnchor))
  3194.         On Error GoTo 0
  3195.     End If
  3196.  
  3197.     '
  3198.     'Get file size
  3199.     '
  3200.     intAnchor = intOffset + 1
  3201.     intOffset = intGetNextFldOffset(intAnchor, strLine, gstrCOMMA, CompareBinary)
  3202.     If intOffset > 0 Then
  3203.         sFile.lFileSize = Val(Mid$(strLine, intAnchor, intOffset - intAnchor))
  3204.     Else
  3205.         GoTo RSFLError
  3206.     End If
  3207.  
  3208.     '
  3209.     ' Get the version number, otherwise flag that there is no version info
  3210.     '
  3211.     intAnchor = intOffset + 1
  3212.     If intOffset > 0 Then
  3213.         strVersion = Trim(Right$(strLine, Len(strLine) - intOffset))
  3214.         If strVersion = "" Then
  3215.             sFile.sVerInfo.FileVerPart1 = gintNOVERINFO
  3216.         Else
  3217.             PackVerInfo strVersion, sFile.sVerInfo
  3218.         End If
  3219.     Else
  3220.         GoTo RSFLError
  3221.     End If
  3222.     
  3223. RSFLDone:
  3224.     ReadSetupFileLine = True
  3225.     Exit Function
  3226.  
  3227. RSFLError:
  3228.     strMsg = gstrSetupInfoFile & vbLf & vbLf & ResolveResString(resINVLINE) & vbLf & vbLf
  3229.     strMsg = strMsg & ResolveResString(resSECTNAME) & strsection & vbLf & strLine
  3230.     MsgError strMsg, vbCritical, gstrTitle
  3231.     ExitSetup frmSetup1, gintRET_FATAL
  3232. End Function
  3233.  
  3234. '-----------------------------------------------------------
  3235. ' SUB: ReadSetupRemoteLine
  3236. '
  3237. ' Reads the requested 'RemoteX=' key from the specified
  3238. ' section of the setup information file (SETUP.LST).
  3239. '
  3240. ' IN: [strSection] - name of section to read from SETUP.LST,
  3241. '                    Ex: "Files"
  3242. '     [intFileNum] - remote number index to read
  3243. '
  3244. ' OUT: [rInfo] - REGINFO Type variable that, after parsing,
  3245. '                holds the information for the line
  3246. '                described.
  3247. '
  3248. ' Returns: True if the requested info was successfully read,
  3249. '          False otherwise
  3250. '
  3251. ' Notes: Remote server lines in the setup information file
  3252. '        have the following format:
  3253. '
  3254. '        address,protocol,authentication-level
  3255. '
  3256. '        [address] - network address of the server, if known
  3257. '        [protocol] - network protocol name, if known
  3258. '        [authentication level] - authentication level (or 0 for default)
  3259. '-----------------------------------------------------------
  3260. '
  3261. Function ReadSetupRemoteLine(ByVal strsection As String, ByVal intFileNum As Integer, rInfo As REGINFO) As Integer
  3262.     Dim strLine As String
  3263.     Dim strMsg As String
  3264.     Dim intAnchor As Integer
  3265.     Dim intOffset As Integer
  3266.     Dim fErr As Boolean
  3267.  
  3268.     ReadSetupRemoteLine = False
  3269.  
  3270.     '
  3271.     'Read the requested line, if unable to read it (strLine = vbnullstring) then exit
  3272.     '
  3273.     strLine = ReadIniFile(gstrSetupInfoFile, strsection, gstrINI_REMOTE & Format$(intFileNum))
  3274.     If strLine = vbNullString Then
  3275.         Exit Function
  3276.     End If
  3277.  
  3278.     '
  3279.     'Get the network address
  3280.     '
  3281.     intAnchor = 1
  3282.     fErr = False
  3283.     If Mid$(strLine, intAnchor, 1) = gstrCOMMA Then
  3284.         rInfo.strNetworkAddress = ""
  3285.     Else
  3286.         rInfo.strNetworkAddress = strExtractFilenameItem(strLine, intAnchor, fErr)
  3287.     End If
  3288.     If fErr Then GoTo RSRLError
  3289.     intAnchor = intAnchor + 1 'Skip past the comma
  3290.  
  3291.     '
  3292.     'Get the network protocol
  3293.     '
  3294.     If Mid$(strLine, intAnchor, 1) = gstrCOMMA Then
  3295.         rInfo.strNetworkProtocol = ""
  3296.     Else
  3297.         rInfo.strNetworkProtocol = strExtractFilenameItem(strLine, intAnchor, fErr)
  3298.     End If
  3299.     If fErr Then GoTo RSRLError
  3300.     intAnchor = intAnchor + 1 'Skip past the comma
  3301.  
  3302.     '
  3303.     'Get the authentication level (must be a single digit
  3304.     '  in the range 0..6)
  3305.     '
  3306.     Const intMaxAuthentication = 6
  3307.     Dim strAuthentication As String
  3308.     
  3309.     strAuthentication = Mid$(strLine, intAnchor, 1)
  3310.     If Len(strAuthentication) <> 1 Then GoTo RSRLError
  3311.     If (Asc(strAuthentication) < Asc("0")) Or (Asc(strAuthentication) > Asc("9")) Then GoTo RSRLError
  3312.     rInfo.intAuthentication = Val(strAuthentication)
  3313.     If rInfo.intAuthentication > intMaxAuthentication Then GoTo RSRLError
  3314.     '
  3315.     ' Is this dcom or remote automation?
  3316.     '
  3317.     intAnchor = InStr(intAnchor + 1, strLine, gstrCOMMA)
  3318.     If intAnchor > 0 Then
  3319.         rInfo.fDCOM = (Trim(Mid$(strLine, intAnchor + 1)) = gstrDCOM)
  3320.     End If
  3321.     
  3322.     ReadSetupRemoteLine = True
  3323.     Exit Function
  3324.  
  3325. RSRLError:
  3326.     strMsg = gstrSetupInfoFile & vbLf & vbLf & ResolveResString(resINVLINE) & vbLf & vbLf
  3327.     strMsg = strMsg & ResolveResString(resSECTNAME) & strsection & vbLf & strLine
  3328.     MsgError strMsg, vbCritical, gstrTitle
  3329.     ExitSetup frmSetup1, gintRET_FATAL
  3330. End Function
  3331.  
  3332. '-----------------------------------------------------------
  3333. ' FUNCTION: RegCloseKey
  3334. '
  3335. ' Closes an open registry key.
  3336. '
  3337. ' Returns: True on success, else False.
  3338. '-----------------------------------------------------------
  3339. '
  3340. Function RegCloseKey(ByVal hKey As Long) As Boolean
  3341.     Dim lResult As Long
  3342.     
  3343.     On Error GoTo 0
  3344.     lResult = OSRegCloseKey(hKey)
  3345.     RegCloseKey = (lResult = ERROR_SUCCESS)
  3346. End Function
  3347.  
  3348. '-----------------------------------------------------------
  3349. ' FUNCTION: RegCreateKey
  3350. '
  3351. ' Opens (creates if already exists) a key in the system registry.
  3352. '
  3353. ' IN: [hkey] - The HKEY parent.
  3354. '     [lpszSubKeyPermanent] - The first part of the subkey of
  3355. '         'hkey' that will be created or opened.  The application
  3356. '         removal utility (32-bit only) will never delete any part
  3357. '         of this subkey.  May NOT be an empty string ("").
  3358. '     [lpszSubKeyRemovable] - The subkey of hkey\lpszSubKeyPermanent
  3359. '         that will be created or opened.  If the application is
  3360. '         removed (32-bit only), then this entire subtree will be
  3361. '         deleted, if it is empty at the time of application removal.
  3362. '         If this parameter is an empty string (""), then the entry
  3363. '         will not be logged.
  3364. '
  3365. ' OUT: [phkResult] - The HKEY of the newly-created or -opened key.
  3366. '
  3367. ' Returns: True if the key was created/opened OK, False otherwise
  3368. '   Upon success, phkResult is set to the handle of the key.
  3369. '
  3370. '-----------------------------------------------------------
  3371. Function RegCreateKey(ByVal hKey As Long, ByVal lpszSubKeyPermanent As String, ByVal lpszSubKeyRemovable As String, phkResult As Long) As Boolean
  3372.     Dim lResult As Long
  3373.     Dim strHkey As String
  3374.     Dim fLog As Boolean
  3375.     Dim strSubKeyFull As String
  3376.  
  3377.     On Error GoTo 0
  3378.  
  3379.     If lpszSubKeyPermanent = "" Then
  3380.         RegCreateKey = False 'Error: lpszSubKeyPermanent must not = ""
  3381.         Exit Function
  3382.     End If
  3383.     
  3384.     If Left$(lpszSubKeyRemovable, 1) = "\" Then
  3385.         lpszSubKeyRemovable = Mid$(lpszSubKeyRemovable, 2)
  3386.     End If
  3387.  
  3388.     If lpszSubKeyRemovable = "" Then
  3389.         fLog = False
  3390.     Else
  3391.         fLog = True
  3392.     End If
  3393.     
  3394.     If lpszSubKeyRemovable <> "" Then
  3395.         strSubKeyFull = lpszSubKeyPermanent & "\" & lpszSubKeyRemovable
  3396.     Else
  3397.         strSubKeyFull = lpszSubKeyPermanent
  3398.     End If
  3399.     strHkey = strGetHKEYString(hKey)
  3400.  
  3401.     If fLog Then
  3402.         NewAction _
  3403.           gstrKEY_REGKEY, _
  3404.           """" & strHkey & "\" & lpszSubKeyPermanent & """" _
  3405.             & ", " & """" & lpszSubKeyRemovable & """"
  3406.     End If
  3407.  
  3408.     lResult = OSRegCreateKey(hKey, strSubKeyFull, phkResult)
  3409.     If lResult = ERROR_SUCCESS Then
  3410.         RegCreateKey = True
  3411.         If fLog Then
  3412.             CommitAction
  3413.         End If
  3414.         AddHkeyToCache phkResult, strHkey & "\" & strSubKeyFull
  3415.     Else
  3416.         RegCreateKey = False
  3417.         MsgError ResolveResString(resERR_REG), vbOKOnly Or vbExclamation, gstrTitle
  3418.         If fLog Then
  3419.             AbortAction
  3420.         End If
  3421.         If gfNoUserInput Then
  3422.             ExitSetup frmSetup1, gintRET_FATAL
  3423.         End If
  3424.     End If
  3425. End Function
  3426.  
  3427. '-----------------------------------------------------------
  3428. ' FUNCTION: RegDeleteKey
  3429. '
  3430. ' Deletes an existing key in the system registry.
  3431. '
  3432. ' Returns: True on success, False otherwise
  3433. '-----------------------------------------------------------
  3434. '
  3435. Function RegDeleteKey(ByVal hKey As Long, ByVal lpszSubKey As String) As Boolean
  3436.     Dim lResult As Long
  3437.     
  3438.     On Error GoTo 0
  3439.     lResult = OSRegDeleteKey(hKey, lpszSubKey)
  3440.     RegDeleteKey = (lResult = ERROR_SUCCESS)
  3441. End Function
  3442.  
  3443. '-----------------------------------------------------------
  3444. ' SUB: RegEdit
  3445. '
  3446. ' Calls REGEDIT to add the information in the specifed file
  3447. ' to the system registry.  If your .REG file requires path
  3448. ' information based upon the destination directory given by
  3449. ' the user, then you will need to write and call a .REG fixup
  3450. ' routine before performing the registration below.
  3451. '
  3452. ' WARNING: Use of this functionality under Win32 is not recommended,
  3453. ' WARNING: because the application removal utility does not support
  3454. ' WARNING: undoing changes that occur as a result of calling
  3455. ' WARNING: REGEDIT on an arbitrary .REG file.
  3456. ' WARNING: Instead, it is recommended that you use the RegCreateKey(),
  3457. ' WARNING: RegOpenKey(), RegSetStringValue(), etc. functions in
  3458. ' WARNING: this module instead.  These make entries to the
  3459. ' WARNING: application removal logfile, thus enabling application
  3460. ' WARNING: removal to undo such changes.
  3461. '
  3462. ' IN: [strRegFile] - name of file containing reg. info
  3463. '-----------------------------------------------------------
  3464. '
  3465. Sub RegEdit(ByVal strRegFile As String)
  3466.     Const strREGEDIT$ = "REGEDIT /S "
  3467.  
  3468.     Dim fShellOK As Integer
  3469.  
  3470.     On Error Resume Next
  3471.  
  3472.     If FileExists(strRegFile) = True Then
  3473.         'Because regedit is a 16-bit application, it does not accept
  3474.         'double quotes around the filename.  Thus, if strRegFile
  3475.         'contains spaces, the only way to get this to work is to pass
  3476.         'regedit the short pathname version of the filename.
  3477.         strRegFile = GetShortPathName(strRegFile)
  3478.         
  3479.         fShellOK = SyncShell(strREGEDIT & strRegFile, INFINITE, , True)
  3480.         frmSetup1.Refresh
  3481.     Else
  3482.         MsgError ResolveResString(resCANTFINDREGFILE, "|1", strRegFile), vbExclamation Or vbOKOnly, gstrTitle
  3483.         ExitSetup frmSetup1, gintRET_FATAL
  3484.     End If
  3485.  
  3486.     Err = 0
  3487. End Sub
  3488.  
  3489. ' FUNCTION: RegEnumKey
  3490. '
  3491. ' Enumerates through the subkeys of an open registry
  3492. ' key (returns the "i"th subkey of hkey, if it exists)
  3493. '
  3494. ' Returns:
  3495. '   ERROR_SUCCESS on success.  strSubkeyName is set to the name of the subkey.
  3496. '   ERROR_NO_MORE_ITEMS if there are no more subkeys (32-bit only)
  3497. '   anything else - error
  3498. '
  3499. Function RegEnumKey(ByVal hKey As Long, ByVal i As Long, strKeyName As String) As Long
  3500.     Dim strResult As String
  3501.     
  3502.     strResult = String(300, " ")
  3503.     RegEnumKey = OSRegEnumKey(hKey, i, strResult, Len(strResult))
  3504.     strKeyName = StripTerminator(strResult)
  3505. End Function
  3506. '-----------------------------------------------------------
  3507. ' SUB: RegisterDAO
  3508. '
  3509. ' Special keys need to be added to the registry if
  3510. ' DAO is installed.  This routine adds those keys.
  3511. '
  3512. ' Note, these keys will not be uninstalled.
  3513. '
  3514. Sub RegisterDAO()
  3515.     Const strDAOKey = "CLSID\{F7A9C6E0-EFF2-101A-8185-00DD01108C6B}"
  3516.     Const strDAOKeyVal = "OLE 2.0 Link"
  3517.     Const strDAOInprocHandlerKey = "CLSID\{F7A9C6E0-EFF2-101A-8185-00DD01108C6B}\InprocHandler"
  3518.     Const strDAOInprocHandlerKeyVal = "ole2.dll"
  3519.     Const strDAOProgIDKey = "CLSID\{F7A9C6E0-EFF2-101A-8185-00DD01108C6B}\ProgID"
  3520.     Const strDAOProgIDKeyVal = "Access.OLE2Link"
  3521.     
  3522.     Dim hKey As Long
  3523.     
  3524.     If Not RegCreateKey(HKEY_CLASSES_ROOT, strDAOKey, "", hKey) Then
  3525.         '
  3526.         ' RegCreateKey displays an error if something goes wrong.
  3527.         '
  3528.         GoTo REGDAOError
  3529.     End If
  3530.     '
  3531.     ' Set the key's value
  3532.     '
  3533.     If Not RegSetStringValue(hKey, "", strDAOKeyVal, False) Then
  3534.         '
  3535.         ' RegSetStringValue displays an error if something goes wrong.
  3536.         '
  3537.         GoTo REGDAOError
  3538.     End If
  3539.     '
  3540.     ' Close the key
  3541.     '
  3542.     RegCloseKey hKey
  3543.     '
  3544.     ' Repeat the same process for the other two keys.
  3545.     '
  3546.     If Not RegCreateKey(HKEY_CLASSES_ROOT, strDAOInprocHandlerKey, "", hKey) Then GoTo REGDAOError
  3547.     If Not RegSetStringValue(hKey, "", strDAOInprocHandlerKeyVal, False) Then GoTo REGDAOError
  3548.     RegCloseKey hKey
  3549.     
  3550.     If Not RegCreateKey(HKEY_CLASSES_ROOT, strDAOProgIDKey, "", hKey) Then GoTo REGDAOError
  3551.     If Not RegSetStringValue(hKey, "", strDAOProgIDKeyVal, False) Then GoTo REGDAOError
  3552.     RegCloseKey hKey
  3553.  
  3554.     Exit Sub
  3555.         
  3556. REGDAOError:
  3557.     '
  3558.     ' Error messages should have already been displayed.
  3559.     '
  3560.     ExitSetup frmSetup1, gintRET_FATAL
  3561.         
  3562. End Sub
  3563. '-----------------------------------------------------------
  3564. ' SUB: RegisterFiles
  3565. '
  3566. ' Loop through the list (array) of files to register that
  3567. ' was created in the CopySection function and register
  3568. ' each file therein as required
  3569. '
  3570. ' Notes: msRegInfo() array created by CopySection function
  3571. '-----------------------------------------------------------
  3572. '
  3573. Sub RegisterFiles()
  3574.     Const strEXT_EXE$ = "EXE"
  3575.  
  3576.     Dim intIdx As Integer
  3577.     Dim intLastIdx As Integer
  3578.     Dim strFilename As String
  3579.     Dim strMsg As String
  3580.     Dim sDrive As String, sPath As String
  3581.     On Error Resume Next
  3582.  
  3583.     '
  3584.     'Get number of items to register, if none then we can get out of here
  3585.     '
  3586.     intLastIdx = UBound(msRegInfo)
  3587.     If Err > 0 Then
  3588.         GoTo RFCleanup
  3589.     End If
  3590.  
  3591.     For intIdx = 0 To intLastIdx
  3592.         strFilename = msRegInfo(intIdx).strFilename
  3593.  
  3594.         If Extension(msRegInfo(intIdx).strRegister) = gsEXT_REG Then
  3595.             If BaseName(msRegInfo(intIdx).strFilename) = BaseName(msRegInfo(intIdx).strRegister) Then
  3596.                 Kill msRegInfo(intIdx).strRegister
  3597.             End If
  3598.             GoTo GoodToGo
  3599.         End If
  3600.         Select Case msRegInfo(intIdx).strRegister
  3601.             Case mstrDLLSELFREGISTER
  3602.                 Dim intDllSelfRegRet As Integer
  3603.                 Dim intErrRes As Integer
  3604.                 Const FAIL_OLE = 2
  3605.                 Const FAIL_LOAD = 3
  3606.                 Const FAIL_ENTRY = 4
  3607.                 Const FAIL_REG = 5
  3608.                 
  3609.                 NewAction gstrKEY_DLLSELFREGISTER, """" & strFilename & """"
  3610.                 
  3611. RetryDllSelfReg:
  3612.                 sDrive = CurDir
  3613.                 sPath = CurDir
  3614.                 ChDrive GetPathName(strFilename)
  3615.                 ChDir GetPathName(strFilename)
  3616.                 Err = 0
  3617.                 intErrRes = 0
  3618.                 intDllSelfRegRet = DLLSelfRegister(strFilename)
  3619.                 If (Err <> 49) And (Err <> 0) Then
  3620.                     intErrRes = resCOMMON_CANTREGUNEXPECTED
  3621.                 Else
  3622.                     Select Case intDllSelfRegRet
  3623.                         Case 0
  3624.                             'Good - everything's okay
  3625.                         Case FAIL_OLE
  3626.                             intErrRes = resCOMMON_CANTREGOLE
  3627.                         Case FAIL_LOAD
  3628.                             intErrRes = resCOMMON_CANTREGLOAD
  3629.                         Case FAIL_ENTRY
  3630.                             intErrRes = resCOMMON_CANTREGENTRY
  3631.                         Case FAIL_REG
  3632.                             intErrRes = resCOMMON_CANTREGREG
  3633.                         Case Else
  3634.                             intErrRes = resCOMMON_CANTREGUNEXPECTED
  3635.                         'End Case
  3636.                     End Select
  3637.                 End If
  3638.                 ChDrive sDrive
  3639.                 ChDir sPath
  3640.                 If intErrRes Then
  3641.                     'There was some kind of error
  3642.                     
  3643.                     'Log the more technical version of the error message -
  3644.                     'this would be too confusing to show to the end user
  3645.                     LogError ResolveResString(intErrRes, "|1", strFilename)
  3646.                     
  3647.                     'Now show a general error message to the user
  3648. AskWhatToDo:
  3649.                     strMsg = ResolveResString(resCOMMON_CANTREG, "|1", strFilename)
  3650.                     
  3651.                     Select Case MsgError(strMsg, vbExclamation Or vbAbortRetryIgnore, gstrTitle)
  3652.                         Case vbAbort:
  3653.                             ExitSetup frmSetup1, gintRET_ABORT
  3654.                             GoTo AskWhatToDo
  3655.                         Case vbRetry:
  3656.                             GoTo RetryDllSelfReg
  3657.                         Case vbIgnore:
  3658.                             AbortAction
  3659.                         'End Case
  3660.                     End Select
  3661.                 Else
  3662.                     CommitAction
  3663.                 End If
  3664.             Case mstrEXESELFREGISTER
  3665.                 '
  3666.                 'Only self register EXE files
  3667.                 '
  3668.                 If Extension(strFilename) = strEXT_EXE Then
  3669.                     NewAction gstrKEY_EXESELFREGISTER, """" & strFilename & """"
  3670.                     Err = 0
  3671.                     ExeSelfRegister strFilename
  3672.                     If Err Then
  3673.                         AbortAction
  3674.                     Else
  3675.                         CommitAction
  3676.                     End If
  3677.                 End If
  3678.             Case mstrREMOTEREGISTER
  3679.                 NewAction gstrKEY_REMOTEREGISTER, """" & strFilename & """"
  3680.                 Err = 0
  3681.                 RemoteRegister strFilename, msRegInfo(intIdx)
  3682.                 If Err Then
  3683.                     AbortAction
  3684.                 Else
  3685.                     CommitAction
  3686.                 End If
  3687.             Case mstrTLBREGISTER
  3688.                 NewAction gstrKEY_TLBREGISTER, """" & strFilename & """"
  3689.                 '
  3690.                 ' Call vb6stkit.dll's RegisterTLB export which calls
  3691.                 ' LoadTypeLib and RegisterTypeLib.
  3692.                 '
  3693. RetryTLBReg:
  3694.                 If Not RegisterTLB(strFilename) Then
  3695.                     '
  3696.                     ' Registration of the TLB file failed.
  3697.                     '
  3698.                     LogError ResolveResString(resCOMMON_CANTREGTLB, "|1", strFilename)
  3699. TLBAskWhatToDo:
  3700.                     strMsg = ResolveResString(resCOMMON_CANTREGTLB, "|1", strFilename)
  3701.                     
  3702.                     Select Case MsgError(strMsg, vbExclamation Or vbAbortRetryIgnore, gstrTitle)
  3703.                         Case vbAbort:
  3704.                             ExitSetup frmSetup1, gintRET_ABORT
  3705.                             GoTo TLBAskWhatToDo
  3706.                         Case vbRetry:
  3707.                             GoTo RetryTLBReg
  3708.                         Case vbIgnore:
  3709.                             AbortAction
  3710.                         'End Case
  3711.                     End Select
  3712.                 Else
  3713.                     CommitAction
  3714.                 End If
  3715.             Case mstrVBLREGISTER
  3716.                 '
  3717.                 ' RegisterVBLFile takes care of logging, etc.
  3718.                 '
  3719.  
  3720.                 RegisterVBLFile strFilename
  3721.             Case Else
  3722.                 RegEdit msRegInfo(intIdx).strRegister
  3723.             'End Case
  3724.         End Select
  3725. GoodToGo:
  3726.     Next
  3727.  
  3728.  
  3729.     Erase msRegInfo
  3730.  
  3731. RFCleanup:
  3732.     Err = 0
  3733. End Sub
  3734. '-----------------------------------------------------------
  3735. ' SUB: RegisterLicenses
  3736. '
  3737. ' Find all the setup.lst license entries and register
  3738. ' them.
  3739. '-----------------------------------------------------------
  3740. '
  3741. Sub RegisterLicenses()
  3742.     Const strINI_LICENSES = "Licenses"
  3743.     Const strREG_LICENSES = "Licenses"
  3744.     Dim iLic As Integer
  3745.     Dim strLine As String
  3746.     Dim strLicKey As String
  3747.     Dim strLicVal As String
  3748.     Dim iCommaPos As Integer
  3749.     Dim strMsg As String
  3750.     Dim hkeyLicenses As Long
  3751.     Const strCopyright$ = "Licensing: Copying the keys may be a violation of established copyrights."
  3752.  
  3753.     'Make sure the Licenses key exists
  3754.     If Not RegCreateKey(HKEY_CLASSES_ROOT, strREG_LICENSES, "", hkeyLicenses) Then
  3755.         'RegCreateKey will have already displayed an error message
  3756.         '  if something's wrong
  3757.         ExitSetup frmSetup1, gintRET_FATAL
  3758.     End If
  3759.     If Not RegSetStringValue(hkeyLicenses, "", strCopyright, False) Then
  3760.         RegCloseKey hkeyLicenses
  3761.         ExitSetup frmSetup1, gintRET_FATAL
  3762.     End If
  3763.     RegCloseKey hkeyLicenses
  3764.     
  3765.     iLic = 1
  3766.     Do
  3767.         strLine = ReadIniFile(gstrSetupInfoFile, strINI_LICENSES, gstrINI_LICENSE & iLic)
  3768.         If strLine = vbNullString Then
  3769.             '
  3770.             ' We've got all the licenses.
  3771.             '
  3772.             Exit Sub
  3773.         End If
  3774.         strLine = strUnQuoteString(strLine)
  3775.         '
  3776.         ' We have a license, parse it and register it.
  3777.         '
  3778.         iCommaPos = InStr(strLine, gstrCOMMA)
  3779.         If iCommaPos = 0 Then
  3780.             '
  3781.             ' Looks like the setup.lst file is corrupt.  There should
  3782.             ' always be a comma in the license information that separates
  3783.             ' the license key from the license value.
  3784.             '
  3785.             GoTo RLError
  3786.         End If
  3787.         strLicKey = Left(strLine, iCommaPos - 1)
  3788.         strLicVal = Mid(strLine, iCommaPos + 1)
  3789.         
  3790.         RegisterLicense strLicKey, strLicVal
  3791.         
  3792.         iLic = iLic + 1
  3793.     Loop While strLine <> vbNullString
  3794.     Exit Sub
  3795.         
  3796. RLError:
  3797.     strMsg = gstrSetupInfoFile & vbLf & vbLf & ResolveResString(resINVLINE) & vbLf & vbLf
  3798.     strMsg = strMsg & ResolveResString(resSECTNAME) & strINI_LICENSES & vbLf & strLine
  3799.     MsgError strMsg, vbCritical, gstrTitle
  3800.     ExitSetup frmSetup1, gintRET_FATAL
  3801. End Sub
  3802. '-----------------------------------------------------------
  3803. ' SUB: RegisterLicense
  3804. '
  3805. ' Register license information given the key and default
  3806. ' value.  License information always goes into
  3807. ' HKEY_CLASSES_ROOT\Licenses.
  3808. '-----------------------------------------------------------
  3809. '
  3810. Sub RegisterLicense(strLicKey As String, strLicVal As String)
  3811.     Const strREG_LICENSES = "Licenses"
  3812.     Dim hKey As Long
  3813.     '
  3814.     ' Create the key
  3815.     '
  3816.     If Not RegCreateKey(HKEY_CLASSES_ROOT, strREG_LICENSES, strLicKey, hKey) Then
  3817.         '
  3818.         ' RegCreateKey displays an error if something goes wrong.
  3819.         '
  3820.         GoTo REGError
  3821.     End If
  3822.     '
  3823.     ' Set the key's value
  3824.     '
  3825.     If Not RegSetStringValue(hKey, "", strLicVal, True) Then
  3826.         '
  3827.         ' RegSetStringValue displays an error if something goes wrong.
  3828.         '
  3829.         GoTo REGError
  3830.     End If
  3831.     '
  3832.     ' Close the key
  3833.     '
  3834.     RegCloseKey hKey
  3835.  
  3836.     Exit Sub
  3837.         
  3838. REGError:
  3839.     '
  3840.     ' Error messages should have already been displayed.
  3841.     '
  3842.     ExitSetup frmSetup1, gintRET_FATAL
  3843. End Sub
  3844. '-----------------------------------------------------------
  3845. ' SUB: RegisterVBLFile
  3846. '
  3847. ' Register license information in a VB License (vbl) file.
  3848. ' Basically, parse out the license info and then call
  3849. ' RegisterLicense.
  3850. '
  3851. ' If strVBLFile is not a valid VBL file, nothing is
  3852. ' registered.
  3853. '-----------------------------------------------------------
  3854. '
  3855. Sub RegisterVBLFile(strVBLFile As String)
  3856.     Dim strLicKey As String
  3857.     Dim strLicVal As String
  3858.     
  3859.     GetLicInfoFromVBL strVBLFile, strLicKey, strLicVal
  3860.     If strLicKey <> vbNullString Then
  3861.         RegisterLicense strLicKey, strLicVal
  3862.     End If
  3863. End Sub
  3864.  
  3865. '----------------------------------------------------------
  3866. ' SUB: RegisterAppRemovalEXE
  3867. '
  3868. ' Registers the application removal program (Windows 95 only)
  3869. ' or else places an icon for it in the application directory.
  3870. '
  3871. ' Returns True on success, False otherwise.
  3872. '----------------------------------------------------------
  3873. Function RegisterAppRemovalEXE(ByVal strAppRemovalEXE As String, ByVal strAppRemovalLog As String, ByVal strGroupName As String) As Boolean
  3874.     On Error GoTo Err
  3875.     
  3876.     Const strREGSTR_VAL_AppRemoval_APPNAMELINE = "ApplicationName"
  3877.     Const strREGSTR_VAL_AppRemoval_DISPLAYNAME = "DisplayName"
  3878.     Const strREGSTR_VAL_AppRemoval_COMMANDLINE = "UninstallString"
  3879.     Const strREGSTR_VAL_AppRemoval_APPTOUNINSTALL = "AppToUninstall"
  3880.     
  3881.     
  3882.     Dim strREGSTR_PATH_UNINSTALL As String
  3883.     strREGSTR_PATH_UNINSTALL = RegPathWinCurrentVersion() & "\Uninstall"
  3884.     
  3885.     'The command-line for the application removal executable is simply the path
  3886.     'for the installation logfile
  3887.     Dim strAppRemovalCmdLine As String
  3888.     strAppRemovalCmdLine = GetAppRemovalCmdLine(strAppRemovalEXE, strAppRemovalLog, vbNullString, False, APPREMERR_NONE)
  3889.     '
  3890.     ' Make sure that the Removal command line (including path, filename, commandline args, etc.
  3891.     ' is not longer than the max allowed, which is _MAX_PATH.
  3892.     '
  3893.     If Not fCheckFNLength(strAppRemovalCmdLine) Then
  3894.         Dim strMsg As String
  3895.         strMsg = ResolveResString(resCANTCREATEICONPATHTOOLONG) & vbLf & vbLf & ResolveResString(resCHOOSENEWDEST) & vbLf & vbLf & strAppRemovalCmdLine
  3896.         Call MsgError(strMsg, vbOKOnly, gstrSETMSG)
  3897.         ExitSetup frmCopy, gintRET_FATAL
  3898.         Exit Function
  3899.     End If
  3900.     '
  3901.     ' Create registry entries to tell Windows where the app removal executable is,
  3902.     ' how it should be displayed to the user, and what the command-line arguments are
  3903.     '
  3904.     Dim iAppend As Integer
  3905.     Dim fOk As Boolean
  3906.     Dim hkeyAppRemoval As Long
  3907.     Dim hkeyOurs As Long
  3908.     Dim i As Integer
  3909.     
  3910.     'Go ahead and create a key to the main Uninstall branch
  3911.     If Not RegCreateKey(HKEY_LOCAL_MACHINE, strREGSTR_PATH_UNINSTALL, "", hkeyAppRemoval) Then
  3912.         GoTo Err
  3913.     End If
  3914.     
  3915.     'We need a unique key.  This key is never shown to the end user.  We will use a key of
  3916.     'the form 'ST5UNST #xxx'
  3917.     Dim strAppRemovalKey As String
  3918.     Dim strAppRemovalKeyBase As String
  3919.     Dim hkeyTest As Long
  3920.     strAppRemovalKeyBase = mstrFILE_APPREMOVALLOGBASE$ & " #"
  3921.     iAppend = 1
  3922.     
  3923.     Do
  3924.         strAppRemovalKey = strAppRemovalKeyBase & Format(iAppend)
  3925.         If RegOpenKey(hkeyAppRemoval, strAppRemovalKey, hkeyTest) Then
  3926.             'This key already exists.  But we need a unique key.
  3927.             RegCloseKey hkeyTest
  3928.         Else
  3929.             'We've found a key that doesn't already exist.  Use it.
  3930.             Exit Do
  3931.         End If
  3932.         
  3933.         iAppend = iAppend + 1
  3934.     Loop
  3935.     
  3936.     '
  3937.     ' We also need a unique displayname.  This name is
  3938.     ' the only means the user has to identify the application
  3939.     ' to remove
  3940.     '
  3941.     Dim strDisplayName As String
  3942.     strDisplayName = gstrAppName 'First try... Application name
  3943.     If Not IsDisplayNameUnique(hkeyAppRemoval, strDisplayName) Then
  3944.         'Second try... Add path
  3945.         strDisplayName = strDisplayName & " (" & gstrDestDir & ")"
  3946.         If Not IsDisplayNameUnique(hkeyAppRemoval, strDisplayName) Then
  3947.             'Subsequent tries... Append a unique integer
  3948.             Dim strDisplayNameBase As String
  3949.             
  3950.             strDisplayNameBase = strDisplayName
  3951.             iAppend = 3
  3952.             Do
  3953.                 strDisplayName = strDisplayNameBase & " #" & Format(iAppend)
  3954.                 If IsDisplayNameUnique(hkeyAppRemoval, strDisplayName) Then
  3955.                     Exit Do
  3956.                 Else
  3957.                     iAppend = iAppend + 1
  3958.                 End If
  3959.             Loop
  3960.         End If
  3961.     End If
  3962.     
  3963.     'Go ahead and fill in entries for the app removal executable
  3964.     If Not RegCreateKey(hkeyAppRemoval, strAppRemovalKey, "", hkeyOurs) Then
  3965.         GoTo Err
  3966.     End If
  3967.     If Not RegSetStringValue(hkeyOurs, strREGSTR_VAL_AppRemoval_APPNAMELINE, gstrAppExe, False) Then
  3968.         GoTo Err
  3969.     End If
  3970.     If Not RegSetStringValue(hkeyOurs, strREGSTR_VAL_AppRemoval_DISPLAYNAME, strDisplayName, False) Then
  3971.         GoTo Err
  3972.     End If
  3973.     If Not RegSetStringValue(hkeyOurs, strREGSTR_VAL_AppRemoval_COMMANDLINE, strAppRemovalCmdLine, False) Then
  3974.         GoTo Err
  3975.     End If
  3976.     If gstrAppToUninstall = vbNullString Then gstrAppToUninstall = gstrAppExe
  3977.     If Not RegSetStringValue(hkeyOurs, strREGSTR_VAL_AppRemoval_APPTOUNINSTALL, gstrAppToUninstall, False) Then
  3978.         GoTo Err
  3979.     End If
  3980.     If Not TreatAsWin95() Then
  3981.         '
  3982.         ' Under NT3.51, we simply place an icon to the app removal EXE in the program manager
  3983.         '
  3984.         If fMainGroupWasCreated Then
  3985.             CreateProgManItem frmSetup1, strGroupName, strAppRemovalCmdLine, ResolveResString(resAPPREMOVALICONNAME, "|1", gstrAppName)
  3986.         Else
  3987.             'If you get this message, it means that you incorrectly customized Form_Load().
  3988.             'Under 32-bits and NT 3.51, a Program Manager group must always be created.
  3989.             MsgError ResolveResString(resNOFOLDERFORICON, "|1", strAppRemovalEXE), vbOKOnly Or vbExclamation, gstrTitle
  3990.             ExitSetup frmSetup1, gintRET_FATAL
  3991.         End If
  3992.     End If
  3993.     
  3994.     RegCloseKey hkeyAppRemoval
  3995.     RegCloseKey hkeyOurs
  3996.     
  3997.     RegisterAppRemovalEXE = True
  3998.     Exit Function
  3999.     
  4000. Err:
  4001.     If hkeyOurs Then
  4002.         RegCloseKey hkeyOurs
  4003.         RegDeleteKey hkeyAppRemoval, strAppRemovalKey
  4004.     End If
  4005.     If hkeyAppRemoval Then
  4006.         RegCloseKey hkeyAppRemoval
  4007.     End If
  4008.     
  4009.     RegisterAppRemovalEXE = False
  4010.     Exit Function
  4011. End Function
  4012.  
  4013. '-----------------------------------------------------------
  4014. ' FUNCTION: RegOpenKey
  4015. '
  4016. ' Opens an existing key in the system registry.
  4017. '
  4018. ' Returns: True if the key was opened OK, False otherwise
  4019. '   Upon success, phkResult is set to the handle of the key.
  4020. '-----------------------------------------------------------
  4021. '
  4022. Function RegOpenKey(ByVal hKey As Long, ByVal lpszSubKey As String, phkResult As Long) As Boolean
  4023.     Dim lResult As Long
  4024.     Dim strHkey As String
  4025.  
  4026.     On Error GoTo 0
  4027.  
  4028.     strHkey = strGetHKEYString(hKey)
  4029.  
  4030.     lResult = OSRegOpenKey(hKey, lpszSubKey, phkResult)
  4031.     If lResult = ERROR_SUCCESS Then
  4032.         RegOpenKey = True
  4033.         AddHkeyToCache phkResult, strHkey & "\" & lpszSubKey
  4034.     Else
  4035.         RegOpenKey = False
  4036.     End If
  4037. End Function
  4038. '----------------------------------------------------------
  4039. ' FUNCTION: RegPathWinPrograms
  4040. '
  4041. ' Returns the name of the registry key
  4042. ' "\HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders"
  4043. '----------------------------------------------------------
  4044. Function RegPathWinPrograms() As String
  4045.     RegPathWinPrograms = RegPathWinCurrentVersion() & "\Explorer\Shell Folders"
  4046. End Function
  4047.  
  4048. '----------------------------------------------------------
  4049. ' FUNCTION: RegPathWinCurrentVersion
  4050. '
  4051. ' Returns the name of the registry key
  4052. ' "\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion"
  4053. '----------------------------------------------------------
  4054. Function RegPathWinCurrentVersion() As String
  4055.     RegPathWinCurrentVersion = "SOFTWARE\Microsoft\Windows\CurrentVersion"
  4056. End Function
  4057.  
  4058. '----------------------------------------------------------
  4059. ' FUNCTION: RegQueryIntValue
  4060. '
  4061. ' Retrieves the integer data for a named
  4062. ' (strValueName = name) or unnamed (strValueName = "")
  4063. ' value within a registry key.  If the named value
  4064. ' exists, but its data is not a REG_DWORD, this function
  4065. ' fails.
  4066. '
  4067. ' NOTE: There is no 16-bit version of this function.
  4068. '
  4069. ' Returns: True on success, else False.
  4070. '   On success, lData is set to the numeric data value
  4071. '
  4072. '----------------------------------------------------------
  4073. Function RegQueryNumericValue(ByVal hKey As Long, ByVal strValueName As String, lData As Long) As Boolean
  4074.     Dim lResult As Long
  4075.     Dim lValueType As Long
  4076.     Dim lBuf As Long
  4077.     Dim lDataBufSize As Long
  4078.     
  4079.     RegQueryNumericValue = False
  4080.     
  4081.     On Error GoTo 0
  4082.     
  4083.     ' Get length/data type
  4084.     lDataBufSize = 4
  4085.         
  4086.     lResult = OSRegQueryValueEx(hKey, strValueName, 0&, lValueType, lBuf, lDataBufSize)
  4087.     If lResult = ERROR_SUCCESS Then
  4088.         If lValueType = REG_DWORD Then
  4089.             lData = lBuf
  4090.             RegQueryNumericValue = True
  4091.         End If
  4092.     End If
  4093. End Function
  4094.  
  4095. ' FUNCTION: RegQueryStringValue
  4096. '
  4097. ' Retrieves the string data for a named
  4098. ' (strValueName = name) or unnamed (strValueName = "")
  4099. ' value within a registry key.  If the named value
  4100. ' exists, but its data is not a string, this function
  4101. ' fails.
  4102. '
  4103. ' NOTE: For 16-bits, strValueName MUST be "" (but the
  4104. ' NOTE: parameter is left in for source code compatability)
  4105. '
  4106. ' Returns: True on success, else False.
  4107. '   On success, strData is set to the string data value
  4108. '
  4109. Function RegQueryStringValue(ByVal hKey As Long, ByVal strValueName As String, strData As String) As Boolean
  4110.     Dim lResult As Long
  4111.     Dim lValueType As Long
  4112.     Dim strBuf As String
  4113.     Dim lDataBufSize As Long
  4114.     
  4115.     RegQueryStringValue = False
  4116.     On Error GoTo 0
  4117.     ' Get length/data type
  4118.     lResult = OSRegQueryValueEx(hKey, strValueName, 0&, lValueType, ByVal 0&, lDataBufSize)
  4119.     If lResult = ERROR_SUCCESS Then
  4120.         If lValueType = REG_SZ Then
  4121.             strBuf = String(lDataBufSize, " ")
  4122.             lResult = OSRegQueryValueEx(hKey, strValueName, 0&, 0&, ByVal strBuf, lDataBufSize)
  4123.             If lResult = ERROR_SUCCESS Then
  4124.                 RegQueryStringValue = True
  4125.                 strData = StripTerminator(strBuf)
  4126.             End If
  4127.         End If
  4128.     End If
  4129. End Function
  4130.  
  4131. '----------------------------------------------------------
  4132. ' FUNCTION: RegQueryRefCount
  4133. '
  4134. ' Retrieves the data inteded as a reference count for a
  4135. ' particular value within a registry key.  Although
  4136. ' REG_DWORD is the preferred way of storing reference
  4137. ' counts, it is possible that some installation programs
  4138. ' may incorrect use a string or binary value instead.
  4139. ' This routine accepts the data whether it is a string,
  4140. ' a binary value or a DWORD (Long).
  4141. '
  4142. ' NOTE: There is no 16-bit version of this function.
  4143. '
  4144. ' Returns: True on success, else False.
  4145. '   On success, lrefcount is set to the numeric data value
  4146. '
  4147. '----------------------------------------------------------
  4148. Function RegQueryRefCount(ByVal hKey As Long, ByVal strValueName As String, lRefCount As Long) As Boolean
  4149.     Dim lResult As Long
  4150.     Dim lValueType As Long
  4151.     Dim lBuf As Long
  4152.     Dim lDataBufSize As Long
  4153.  
  4154.     RegQueryRefCount = False
  4155.  
  4156.     On Error GoTo 0
  4157.  
  4158.     ' Get length/data type
  4159.     lDataBufSize = 4
  4160.  
  4161.     lResult = OSRegQueryValueEx(hKey, strValueName, 0&, lValueType, lBuf, lDataBufSize)
  4162.     If lResult = ERROR_SUCCESS Then
  4163.         Select Case lValueType
  4164.             Case REG_DWORD
  4165.                 lRefCount = lBuf
  4166.                 RegQueryRefCount = True
  4167.             Case REG_BINARY
  4168.                 If lDataBufSize = 4 Then
  4169.                     lRefCount = lBuf
  4170.                     RegQueryRefCount = True
  4171.                 End If
  4172.             Case REG_SZ
  4173.                 Dim strRefCount As String
  4174.                 
  4175.                 If RegQueryStringValue(hKey, strValueName, strRefCount) Then
  4176.                     lRefCount = Val(strRefCount)
  4177.                     RegQueryRefCount = True
  4178.                 End If
  4179.             'End Case
  4180.         End Select
  4181.     End If
  4182. End Function
  4183.  
  4184. ' FUNCTION: RegSetNumericValue
  4185. '
  4186. ' Associates a named (strValueName = name) or unnamed (strValueName = "")
  4187. '   value with a registry key.
  4188. '
  4189. ' If fLog is missing or is True, then this action is logged in the logfile,
  4190. ' and the value will be deleted by the application removal utility if the
  4191. ' user choose to remove the installed application.
  4192. '
  4193. ' NOTE: There is no 16-bit version of this function.
  4194. '
  4195. ' Returns: True on success, else False.
  4196. '
  4197. Function RegSetNumericValue(ByVal hKey As Long, ByVal strValueName As String, ByVal lData As Long, Optional ByVal fLog As Boolean = True) As Boolean
  4198.     Dim lResult As Long
  4199.     Dim strHkey As String
  4200.  
  4201.     On Error GoTo 0
  4202.     
  4203.     strHkey = strGetHKEYString(hKey)
  4204.     
  4205.     If fLog Then
  4206.         NewAction _
  4207.           gstrKEY_REGVALUE, _
  4208.           """" & strHkey & """" _
  4209.             & ", " & """" & strValueName & """"
  4210.     End If
  4211.  
  4212.     lResult = OSRegSetValueNumEx(hKey, strValueName, 0, REG_DWORD, lData, 4)
  4213.     If lResult = ERROR_SUCCESS Then
  4214.         RegSetNumericValue = True
  4215.         If fLog Then
  4216.             CommitAction
  4217.         End If
  4218.     Else
  4219.         RegSetNumericValue = False
  4220.         MsgError ResolveResString(resERR_REG), vbOKOnly Or vbExclamation, gstrTitle
  4221.         If fLog Then
  4222.             AbortAction
  4223.         End If
  4224.         If gfNoUserInput Then
  4225.             ExitSetup frmSetup1, gintRET_FATAL
  4226.         End If
  4227.     End If
  4228. End Function
  4229.  
  4230. ' FUNCTION: RegSetStringValue
  4231. '
  4232. ' Associates a named (strValueName = name) or unnamed (strValueName = "")
  4233. '   value with a registry key.
  4234. '
  4235. ' If fLog is missing or is True, then this action is logged in the
  4236. ' logfile, and the value will be deleted by the application removal
  4237. ' utility if the user choose to remove the installed application.
  4238. '
  4239. ' Returns: True on success, else False.
  4240. '
  4241. Function RegSetStringValue(ByVal hKey As Long, ByVal strValueName As String, ByVal strData As String, Optional ByVal fLog As Boolean = True) As Boolean
  4242.     Dim lResult As Long
  4243.     Dim strHkey As String
  4244.     
  4245.     On Error GoTo 0
  4246.     
  4247.     If hKey = 0 Then
  4248.         Exit Function
  4249.     End If
  4250.     
  4251.     strHkey = strGetHKEYString(hKey)
  4252.  
  4253.     If fLog Then
  4254.         NewAction _
  4255.           gstrKEY_REGVALUE, _
  4256.           """" & strHkey & """" _
  4257.             & ", " & """" & strValueName & """"
  4258.     End If
  4259.  
  4260.     lResult = OSRegSetValueEx(hKey, strValueName, 0&, REG_SZ, ByVal strData, LenB(StrConv(strData, vbFromUnicode)) + 1)
  4261.     'lResult = OSRegSetValueEx(hKey, strValueName, 0&, REG_SZ, ByVal strData, Len(strData) + 1)
  4262.     
  4263.     If lResult = ERROR_SUCCESS Then
  4264.         RegSetStringValue = True
  4265.         If fLog Then
  4266.             CommitAction
  4267.         End If
  4268.     Else
  4269.         RegSetStringValue = False
  4270.         MsgError ResolveResString(resERR_REG), vbOKOnly Or vbExclamation, gstrTitle
  4271.         If fLog Then
  4272.             AbortAction
  4273.         End If
  4274.         If gfNoUserInput Then
  4275.             ExitSetup frmSetup1, gintRET_FATAL
  4276.         End If
  4277.     End If
  4278. End Function
  4279.  
  4280. '-----------------------------------------------------------
  4281. ' SUB: RemoteRegister
  4282. '
  4283. ' Synchronously run the client registration utility on the
  4284. ' given remote server registration file in order to set it
  4285. ' up properly in the registry.
  4286. '
  4287. ' IN: [strFileName] - .EXE file to register
  4288.  
  4289. '-----------------------------------------------------------
  4290. '
  4291. Sub RemoteRegister(ByVal strFilename As String, rInfo As REGINFO)
  4292.     Const strClientRegistrationUtility$ = "CLIREG32.EXE"
  4293.     Const strAddressSwitch = " /s "
  4294.     Const strProtocolSwitch = " /p "
  4295.     Const strSilentSwitch = " /q "
  4296.     Const strNoLogoSwitch = " /nologo "
  4297.     Const strAuthenticationSwitch = " /a "
  4298.     Const strTypelibSwitch = " /t "
  4299.     Const strDCOMSwitch = " /d "
  4300.     Const strEXT_REMOTE$ = "VBR"
  4301.     Const strEXT_REMOTETLB$ = "TLB"
  4302.  
  4303.     Dim strAddress As String
  4304.     Dim strProtocol As String
  4305.     Dim intAuthentication As Integer
  4306.     Dim strCmdLine As String
  4307.     Dim fShell As Integer
  4308.     Dim strMatchingTLB As String
  4309.     Dim fDCOM As Boolean
  4310.  
  4311.     'Find the name of the matching typelib file.  This should have already
  4312.     'been installed to the same directory as the .VBR file.
  4313.     strMatchingTLB = strFilename
  4314.     If Right$(strMatchingTLB, Len(strEXT_REMOTE)) = strEXT_REMOTE Then
  4315.         strMatchingTLB = Left$(strMatchingTLB, Len(strMatchingTLB) - Len(strEXT_REMOTE))
  4316.     End If
  4317.     strMatchingTLB = strMatchingTLB & strEXT_REMOTETLB
  4318.  
  4319.     strAddress = rInfo.strNetworkAddress
  4320.     strProtocol = rInfo.strNetworkProtocol
  4321.     intAuthentication = rInfo.intAuthentication
  4322.     fDCOM = rInfo.fDCOM
  4323.     frmRemoteServerDetails.GetServerDetails strFilename, strAddress, strProtocol, fDCOM
  4324.     frmMessage.Refresh
  4325.     strCmdLine = _
  4326.       strClientRegistrationUtility _
  4327.       & strAddressSwitch & """" & strAddress & """" _
  4328.       & IIf(fDCOM, " ", strProtocolSwitch & strProtocol) _
  4329.       & IIf(fDCOM, " ", strAuthenticationSwitch & Format$(intAuthentication) & " ") _
  4330.       & strNoLogoSwitch _
  4331.       & strTypelibSwitch & """" & strMatchingTLB & """" & " " _
  4332.       & IIf(fDCOM, strDCOMSwitch, "") _
  4333.       & IIf(gfNoUserInput, strSilentSwitch, "") _
  4334.       & """" & strFilename & """"
  4335.       
  4336.     '
  4337.     'Synchronously shell out and run the utility with the correct switches
  4338.     '
  4339.     fShell = SyncShell(strCmdLine, INFINITE, , False)
  4340.  
  4341.     If Not fShell Then
  4342.         MsgError ResolveResString(resCANTRUNPROGRAM, "|1", strClientRegistrationUtility), vbOKOnly Or vbExclamation, gstrTitle, gintRET_FATAL
  4343.         ExitSetup frmSetup1, gintRET_FATAL
  4344.     End If
  4345. End Sub
  4346.  
  4347. '-----------------------------------------------------------
  4348. ' SUB: RemoveShellLink
  4349. '
  4350. ' Removes a link in either Start>Programs or any of its
  4351.  
  4352. ' immediate subfolders in the Windows 95 shell.
  4353. '
  4354. ' IN: [strFolderName] - text name of the immediate folder
  4355. '                       in which the link to be removed
  4356. '                       currently exists, or else the
  4357. '                       empty string ("") to indicate that
  4358. '                       the link can be found directly in
  4359. '                       the Start>Programs menu.
  4360. '     [strLinkName] - text caption for the link
  4361. '
  4362. ' This action is never logged in the app removal logfile.
  4363. '
  4364. ' PRECONDITION: strFolderName has already been created and is
  4365. '               an immediate subfolder of Start>Programs, if it
  4366. '               is not equal to ""
  4367. '-----------------------------------------------------------
  4368. '
  4369. Sub RemoveShellLink(ByVal strFolderName As String, ByVal strLinkName As String)
  4370.     Dim fSuccess As Boolean
  4371.     
  4372.     ReplaceDoubleQuotes strFolderName
  4373.     ReplaceDoubleQuotes strLinkName
  4374.     
  4375.     fSuccess = OSfRemoveShellLink(strFolderName, strLinkName)
  4376. End Sub
  4377.  
  4378. '-----------------------------------------------------------
  4379. ' FUNCTION: ResolveDestDir
  4380. '
  4381. ' Given a destination directory string, equate any macro
  4382. ' portions of the string to their runtime determined
  4383. ' actual locations and return a string reflecting the
  4384. ' actual path.
  4385. '
  4386. ' IN: [strDestDir] - string containing directory macro info
  4387. '                    and/or actual dir path info
  4388. '
  4389. '     [fAssumeDir] - boolean that if true, causes this routine
  4390. '                    to assume that strDestDir contains a dir
  4391. '                    path.  If a directory isn't given it will
  4392. '                    make it the application path.  If false,
  4393. '                    this routine will return strDestDir as
  4394. '                    is after performing expansion.  Set this
  4395. '                    to False when you are not sure it is a
  4396. '                    directory but you want to expand macros
  4397. '                    if it contains any.  E.g., If this is a
  4398. '                    command line parameter, you can't be
  4399. '                    certain if it refers to a path.  In this
  4400. '                    case, set fAssumeDir = False.  Default
  4401. '                    is True.
  4402. '
  4403. ' Return: A string containing the resolved dir name
  4404. '-----------------------------------------------------------
  4405. '
  4406. Function ResolveDestDir(ByVal strDestDir As String, Optional fAssumeDir As Boolean = True) As String
  4407.     Const strMACROSTART$ = "$("
  4408.     Const strMACROEND$ = ")"
  4409.  
  4410.     Dim intPos As Integer
  4411.     Dim strResolved As String
  4412.     Dim hKey As Long
  4413.     Dim strPathsKey As String
  4414.     Dim fQuoted As Boolean
  4415.     
  4416.     strPathsKey = RegPathWinCurrentVersion()
  4417.     strDestDir = Trim(strDestDir)
  4418.     '
  4419.     ' If strDestDir is quoted when passed to this routine, it
  4420.     ' should be quoted when it's returned.  The quotes need
  4421.     ' to be temporarily removed, though, for processing.
  4422.     '
  4423.     If Left(strDestDir, 1) = gstrQUOTE Then
  4424.         fQuoted = True
  4425.         strDestDir = strUnQuoteString(strDestDir)
  4426.     End If
  4427.     '
  4428.     ' We take the first part of destdir, and if its $( then we need to get the portion
  4429.     ' of destdir up to and including the last paren.  We then test against this for
  4430.     ' macro expansion.  If no ) is found after finding $(, then must assume that it's
  4431.     ' just a normal file name and do no processing.  Only enter the case statement
  4432.     ' if strDestDir starts with $(.
  4433.     '
  4434.     If Left$(strDestDir, 2) = strMACROSTART Then
  4435.         intPos = InStr(strDestDir, strMACROEND)
  4436.  
  4437.         Select Case Left$(strDestDir, intPos)
  4438.             Case gstrAPPDEST
  4439.                 If gstrDestDir <> vbNullString Then
  4440.  
  4441.                     strResolved = gstrDestDir
  4442.                 Else
  4443.                     strResolved = "?"
  4444.                 End If
  4445.             Case gstrWINDEST
  4446.                 strResolved = gstrWinDir
  4447.             Case gstrFONTDEST
  4448.                 strResolved = gstrFontDir
  4449.             Case gstrWINSYSDEST, gstrWINSYSDESTSYSFILE
  4450.                 strResolved = gstrWinSysDir
  4451.             Case gstrPROGRAMFILES
  4452.                 If TreatAsWin95() Then
  4453.                     Const strProgramFilesKey = "ProgramFilesDir"
  4454.     
  4455.                     If RegOpenKey(HKEY_LOCAL_MACHINE, strPathsKey, hKey) Then
  4456.                         RegQueryStringValue hKey, strProgramFilesKey, strResolved
  4457.                         RegCloseKey hKey
  4458.                     End If
  4459.                 End If
  4460.     
  4461.                 If strResolved = "" Then
  4462.                     'If not otherwise set, let strResolved be the root of the first fixed disk
  4463.                     strResolved = strRootDrive()
  4464.                 End If
  4465.             Case gstrCOMMONFILES
  4466.                 'First determine the correct path of Program Files\Common Files, if under Win95
  4467.                 strResolved = strGetCommonFilesPath()
  4468.                 If strResolved = "" Then
  4469.                     'If not otherwise set, let strResolved be the Windows directory
  4470.                     strResolved = gstrWinDir
  4471.                 End If
  4472.             Case gstrCOMMONFILESSYS
  4473.                 'First determine the correct path of Program Files\Common Files, if under Win95
  4474.                 Dim strCommonFiles As String
  4475.                 
  4476.                 strCommonFiles = strGetCommonFilesPath()
  4477.                 If strCommonFiles <> "" Then
  4478.                     'Okay, now just add \System, and we're done
  4479.                     strResolved = strCommonFiles & "System\"
  4480.                 Else
  4481.                     'If Common Files isn't in the registry, then map the
  4482.                     'entire macro to the Windows\{system,system32} directory
  4483.                     strResolved = gstrWinSysDir
  4484.                 End If
  4485.             Case gstrDAODEST
  4486.                 strResolved = strGetDAOPath()
  4487.             Case Else
  4488.                 intPos = 0
  4489.             'End Case
  4490.         End Select
  4491.     End If
  4492.     
  4493.     If intPos <> 0 Then
  4494.         AddDirSep strResolved
  4495.     End If
  4496.  
  4497.     If fAssumeDir = True Then
  4498.         If intPos = 0 Then
  4499.             '
  4500.             'if no drive spec, and doesn't begin with any root path indicator ("\"),
  4501.             'then we assume that this destination is relative to the app dest dir
  4502.             '
  4503.             If Mid$(strDestDir, 2, 1) <> gstrCOLON Then
  4504.                 If Left$(strDestDir, 1) <> gstrSEP_DIR Then
  4505.                     strResolved = gstrDestDir
  4506.                 End If
  4507.             End If
  4508.         Else
  4509.             If Mid$(strDestDir, intPos + 1, 1) = gstrSEP_DIR Then
  4510.                 intPos = intPos + 1
  4511.             End If
  4512.         End If
  4513.     End If
  4514.  
  4515.     If fQuoted = True Then
  4516.         ResolveDestDir = strQuoteString(strResolved & Mid$(strDestDir, intPos + 1), True, False)
  4517.     Else
  4518.         ResolveDestDir = strResolved & Mid$(strDestDir, intPos + 1)
  4519.     End If
  4520. End Function
  4521. '-----------------------------------------------------------
  4522. ' FUNCTION: ResolveDestDirs
  4523. '
  4524. ' Given a space delimited string, this routine finds all
  4525. ' Destination directory macros and expands them by making
  4526. ' repeated calls to ResolveDestDir.  See ResolveDestDir.
  4527. '
  4528. ' Note that the macro must immediately follow a space (or
  4529. ' a space followed by a quote) delimiter or else it will
  4530. ' be ignored.
  4531. '
  4532. ' Note that this routine does not assume that each item
  4533. ' in the delimited string is actually a directory path.
  4534. ' Therefore, the last parameter in the call to ResolveDestDir,
  4535. ' below, is false.
  4536. '
  4537. ' IN: [str] - string containing directory macro(s) info
  4538. '             and/or actual dir path info
  4539. '
  4540. ' Return: str with destdir macros expanded.
  4541. '-----------------------------------------------------------
  4542. '
  4543. Function ResolveDestDirs(str As String)
  4544.     Dim intAnchor As Integer
  4545.     Dim intOffset As Integer
  4546.     Dim strField As String
  4547.     Dim strExpField As String
  4548.     Dim strExpanded As String
  4549.     
  4550.     If Len(Trim(strUnQuoteString(str))) = 0 Then
  4551.         ResolveDestDirs = str
  4552.         Exit Function
  4553.     End If
  4554.         
  4555.     intAnchor = 1
  4556.     strExpanded = ""
  4557.     
  4558.     Do
  4559.         intOffset = intGetNextFldOffset(intAnchor, str, " ")
  4560.         If intOffset = 0 Then intOffset = Len(str) + 1
  4561.         strField = Mid(str, intAnchor, intOffset - intAnchor)
  4562.         strExpField = ResolveDestDir(strField, False)
  4563.         strExpanded = strExpanded & strExpField & " "
  4564.         intAnchor = intOffset + 1
  4565.     Loop While intAnchor < Len(str)
  4566.     
  4567.     ResolveDestDirs = Trim(strExpanded)
  4568. End Function
  4569. '-----------------------------------------------------------
  4570. ' FUNCTION: ResolveDir
  4571. '
  4572. ' Given a pathname, resolve it to its smallest form.  If
  4573. ' the pathname is invalid, then optionally warn the user.
  4574. '
  4575. ' IN: [strPathName] - pathname to resolve
  4576. '     [fMustExist] - enforce that the path actually exists
  4577. '     [fWarn] - If True, warn user upon invalid path
  4578. '
  4579. ' Return: A string containing the resolved dir name
  4580. '-----------------------------------------------------------
  4581. '
  4582. Function ResolveDir(ByVal strPathName As String, fMustExist As Integer, fWarn As Integer) As String
  4583.     Dim strMsg As String
  4584.     Dim fInValid As Integer
  4585.     Dim strUnResolvedPath As String
  4586.     Dim strResolvedPath As String
  4587.     Dim strIgnore As String
  4588.     Dim cbResolved As Long
  4589.  
  4590.     On Error Resume Next
  4591.  
  4592.     fInValid = False
  4593.     '
  4594.     'If the pathname is a UNC name (16-bit only), or if it's in actuality a file name, then it's invalid
  4595.     '
  4596.     If FileExists(strPathName) = True Then
  4597.         fInValid = True
  4598.         GoTo RDContinue
  4599.     End If
  4600.  
  4601.     strUnResolvedPath = strPathName
  4602.  
  4603.     If InStr(3, strUnResolvedPath, gstrSEP_DIR) > 0 Then
  4604.  
  4605.         strResolvedPath = Space(gintMAX_PATH_LEN * 2)
  4606.         cbResolved = GetFullPathName(strUnResolvedPath, gintMAX_PATH_LEN, strResolvedPath, strIgnore)
  4607.         If cbResolved = 0 Then
  4608.             '
  4609.             ' The path couldn't be resolved.  If we can actually
  4610.             ' switch to the directory we want, continue anyway.
  4611.             '
  4612.             ChDir strUnResolvedPath
  4613.             AddDirSep strUnResolvedPath
  4614.             If Err > 0 Then
  4615.                 Err = 0
  4616.                 ChDir strUnResolvedPath
  4617.                 If Err > 0 Then
  4618.                     fInValid = True
  4619.                 Else
  4620.                     strResolvedPath = strUnResolvedPath
  4621.                 End If
  4622.             Else
  4623.                 strResolvedPath = strUnResolvedPath
  4624.             End If
  4625.         Else
  4626.             '
  4627.             ' GetFullPathName returned us a NULL terminated string in
  4628.             ' strResolvedPath.  Remove the NULL.
  4629.             '
  4630.             strResolvedPath = StripTerminator(strResolvedPath)
  4631.             If CheckDrive(strResolvedPath, gstrTitle) = False Then
  4632.                 fInValid = True
  4633.             Else
  4634.                 AddDirSep strResolvedPath
  4635.                 If fMustExist = True Then
  4636.                     Err = 0
  4637.                     
  4638.                     Dim strDummy As String
  4639.                     strDummy = Dir$(strResolvedPath & "*.*")
  4640.                     
  4641.                     If Err > 0 Then
  4642.                         strMsg = ResolveResString(resNOTEXIST) & vbLf & vbLf
  4643.                         fInValid = True
  4644.                     End If
  4645.                 End If
  4646.             End If
  4647.         End If
  4648.     Else
  4649.         fInValid = True
  4650.     End If
  4651.  
  4652. RDContinue:
  4653.     If fInValid = True Then
  4654.         If fWarn = True Then
  4655.             strMsg = strMsg & ResolveResString(resDIRSPECIFIED) & vbLf & vbLf & strPathName & vbLf & vbLf
  4656.             strMsg = strMsg & ResolveResString(resDIRINVALID)
  4657.             MsgError strMsg, vbOKOnly Or vbExclamation, ResolveResString(resDIRINVNAME)
  4658.             If gfNoUserInput Then
  4659.                 ExitSetup frmSetup1, gintRET_FATAL
  4660.             End If
  4661.         End If
  4662.  
  4663.         ResolveDir = vbNullString
  4664.     Else
  4665.         ResolveDir = strResolvedPath
  4666.     End If
  4667.  
  4668.     Err = 0
  4669. End Function
  4670.  
  4671. '-----------------------------------------------------------
  4672. ' SUB: RestoreProgMan
  4673. '
  4674. ' Restores Windows Program Manager
  4675. '-----------------------------------------------------------
  4676. '
  4677. Sub RestoreProgMan()
  4678.     Const strPMTITLE$ = "Program Manager"
  4679.  
  4680.     On Error Resume Next
  4681.  
  4682.     'Try the localized name first
  4683.     AppActivate ResolveResString(resPROGRAMMANAGER)
  4684.     
  4685.     If Err Then
  4686.         'If that doesn't work, try the English name
  4687.         AppActivate strPMTITLE
  4688.     End If
  4689.  
  4690.     Err = 0
  4691. End Sub
  4692.  
  4693. '-----------------------------------------------------------
  4694. ' SUB: ShowPathDialog
  4695. '
  4696. ' Display form to allow user to get either a source or
  4697. ' destination path
  4698. '
  4699. ' IN: [strPathRequest] - determines whether to ask for the
  4700. '                        source or destination pathname.
  4701. '                        gstrDIR_SRC for source path
  4702. '                        gstrDIR_DEST for destination path
  4703. '-----------------------------------------------------------
  4704. '
  4705. Sub ShowPathDialog(ByVal strPathRequest As String)
  4706.     frmSetup1.Tag = strPathRequest
  4707.  
  4708.     '
  4709.     'frmPath.Form_Load() reads frmSetup1.Tag to determine whether
  4710.     'this is a request for the source or destination path
  4711.     '
  4712.     frmPath.Show vbModal
  4713.  
  4714.     If strPathRequest = gstrDIR_SRC Then
  4715.         gstrSrcPath = frmSetup1.Tag
  4716.     Else
  4717.         If gfRetVal = gintRET_CONT Then
  4718.             gstrDestDir = frmSetup1.Tag
  4719.         End If
  4720.     End If
  4721. End Sub
  4722.  
  4723. '-----------------------------------------------------------
  4724. ' FUNCTION: strExtractFilenameArg
  4725. '
  4726. ' Extracts a quoted or unquoted filename from a string
  4727. '   containing command-line arguments
  4728. '
  4729. ' IN: [str] - string containing a filename.  This filename
  4730. '             begins at the first character, and continues
  4731. '             to the end of the string or to the first space
  4732. '             or switch character, or, if the string begins
  4733. '             with a double quote, continues until the next
  4734. '             double quote
  4735. ' OUT: Returns the filename, without quotes
  4736. '      str is set to be the remainder of the string after
  4737. '      the filename and quote (if any)
  4738. '
  4739. '-----------------------------------------------------------
  4740. '
  4741. Function strExtractFilenameArg(str As String, fErr As Boolean)
  4742.     Dim strFilename As String
  4743.     
  4744.     str = Trim$(str)
  4745.     
  4746.     Dim iEndFilenamePos As Integer
  4747.     If Left$(str, 1) = """" Then
  4748.         ' Filenames is surrounded by quotes
  4749.         iEndFilenamePos = InStr(2, str, """") ' Find matching quote
  4750.         If iEndFilenamePos > 0 Then
  4751.             strFilename = Mid$(str, 2, iEndFilenamePos - 2)
  4752.             str = Right$(str, Len(str) - iEndFilenamePos)
  4753.         Else
  4754.             fErr = True
  4755.             Exit Function
  4756.         End If
  4757.     Else
  4758.         ' Filename continues until next switch or space or quote
  4759.         Dim iSpacePos As Integer
  4760.         Dim iSwitch1 As Integer
  4761.         Dim iSwitch2 As Integer
  4762.         Dim iQuote As Integer
  4763.         
  4764.         iSpacePos = InStr(str, " ")
  4765.         iSwitch2 = InStr(str, gstrSwitchPrefix2)
  4766.         iQuote = InStr(str, """")
  4767.         
  4768.         If iSpacePos = 0 Then iSpacePos = Len(str) + 1
  4769.         If iSwitch1 = 0 Then iSwitch1 = Len(str) + 1
  4770.         If iSwitch2 = 0 Then iSwitch2 = Len(str) + 1
  4771.         If iQuote = 0 Then iQuote = Len(str) + 1
  4772.         
  4773.         iEndFilenamePos = iSpacePos
  4774.         If iSwitch2 < iEndFilenamePos Then iEndFilenamePos = iSwitch2
  4775.         If iQuote < iEndFilenamePos Then iEndFilenamePos = iQuote
  4776.         
  4777.         strFilename = Left$(str, iEndFilenamePos - 1)
  4778.         If iEndFilenamePos > Len(str) Then
  4779.             str = ""
  4780.         Else
  4781.             str = Right(str, Len(str) - iEndFilenamePos + 1)
  4782.         End If
  4783.     End If
  4784.     
  4785.     strFilename = Trim$(strFilename)
  4786.     If strFilename = "" Then
  4787.         fErr = True
  4788.         Exit Function
  4789.     End If
  4790.     
  4791.     fErr = False
  4792.     strExtractFilenameArg = strFilename
  4793.     str = Trim$(str)
  4794. End Function
  4795.  
  4796.  
  4797.  
  4798. '-----------------------------------------------------------
  4799. ' SUB: UpdateStatus
  4800. '
  4801. ' "Fill" (by percentage) inside the PictureBox and also
  4802. ' display the percentage filled
  4803. '
  4804. ' IN: [pic] - PictureBox used to bound "fill" region
  4805. '     [sngPercent] - Percentage of the shape to fill
  4806. '     [fBorderCase] - Indicates whether the percentage
  4807. '        specified is a "border case", i.e. exactly 0%
  4808. '        or exactly 100%.  Unless fBorderCase is True,
  4809. '        the values 0% and 100% will be assumed to be
  4810. '        "close" to these values, and 1% and 99% will
  4811. '        be used instead.
  4812. '
  4813. ' Notes: Set AutoRedraw property of the PictureBox to True
  4814. '        so that the status bar and percentage can be auto-
  4815. '        matically repainted if necessary
  4816. '-----------------------------------------------------------
  4817. '
  4818. Sub UpdateStatus(pic As PictureBox, ByVal sngPercent As Single, Optional ByVal fBorderCase As Boolean = False)
  4819.     Dim strPercent As String
  4820.     Dim intX As Integer
  4821.     Dim intY As Integer
  4822.     Dim intWidth As Integer
  4823.     Dim intHeight As Integer
  4824.  
  4825.     'For this to work well, we need a white background and any color foreground (blue)
  4826.     Const colBackground = &HFFFFFF ' white
  4827.     Const colForeground = &H800000 ' dark blue
  4828.  
  4829.     pic.ForeColor = colForeground
  4830.     pic.BackColor = colBackground
  4831.     
  4832.     '
  4833.     'Format percentage and get attributes of text
  4834.     '
  4835.     Dim intPercent
  4836.     intPercent = Int(100 * sngPercent + 0.5)
  4837.     
  4838.     'Never allow the percentage to be 0 or 100 unless it is exactly that value.  This
  4839.     'prevents, for instance, the status bar from reaching 100% until we are entirely done.
  4840.     If intPercent = 0 Then
  4841.         If Not fBorderCase Then
  4842.             intPercent = 1
  4843.         End If
  4844.     ElseIf intPercent = 100 Then
  4845.         If Not fBorderCase Then
  4846.             intPercent = 99
  4847.         End If
  4848.     End If
  4849.     
  4850.     strPercent = Format$(intPercent) & "%"
  4851.     intWidth = pic.TextWidth(strPercent)
  4852.     intHeight = pic.TextHeight(strPercent)
  4853.  
  4854.     '
  4855.     'Now set intX and intY to the starting location for printing the percentage
  4856.     '
  4857.     intX = pic.Width / 2 - intWidth / 2
  4858.     intY = pic.Height / 2 - intHeight / 2
  4859.  
  4860.     '
  4861.     'Need to draw a filled box with the pics background color to wipe out previous
  4862.     'percentage display (if any)
  4863.     '
  4864.     pic.DrawMode = 13 ' Copy Pen
  4865.     pic.Line (intX, intY)-Step(intWidth, intHeight), pic.BackColor, BF
  4866.  
  4867.     '
  4868.     'Back to the center print position and print the text
  4869.     '
  4870.     pic.CurrentX = intX
  4871.     pic.CurrentY = intY
  4872.     pic.Print strPercent
  4873.  
  4874.     '
  4875.     'Now fill in the box with the ribbon color to the desired percentage
  4876.     'If percentage is 0, fill the whole box with the background color to clear it
  4877.     'Use the "Not XOR" pen so that we change the color of the text to white
  4878.     'wherever we touch it, and change the color of the background to blue
  4879.     'wherever we touch it.
  4880.     '
  4881.     pic.DrawMode = 10 ' Not XOR Pen
  4882.     If sngPercent > 0 Then
  4883.         pic.Line (0, 0)-(pic.Width * sngPercent, pic.Height), pic.ForeColor, BF
  4884.     Else
  4885.         pic.Line (0, 0)-(pic.Width, pic.Height), pic.BackColor, BF
  4886.     End If
  4887.  
  4888.     pic.Refresh
  4889. End Sub
  4890.  
  4891. '-----------------------------------------------------------
  4892. ' FUNCTION: WriteAccess
  4893. '
  4894. ' Determines whether there is write access to the specified
  4895. ' directory.
  4896. '
  4897. ' IN: [strDirName] - directory to check for write access
  4898. '
  4899. ' Returns: True if write access, False otherwise
  4900. '-----------------------------------------------------------
  4901. '
  4902. Function WriteAccess(ByVal strDirName As String) As Integer
  4903.     Dim intFileNum As Integer
  4904.  
  4905.     On Error Resume Next
  4906.  
  4907.     AddDirSep strDirName
  4908.  
  4909.     intFileNum = FreeFile
  4910.     Open strDirName & mstrCONCATFILE For Output As intFileNum
  4911.  
  4912.     WriteAccess = IIf(Err, False, True)
  4913.     
  4914.     Close intFileNum
  4915.  
  4916.     Kill strDirName & mstrCONCATFILE
  4917.  
  4918.     Err = 0
  4919. End Function
  4920. '-----------------------------------------------------------
  4921. ' FUNCTION: WriteMIF
  4922. '
  4923. ' If this is a SMS install, this routine writes the
  4924. ' failed MIF status file if something goes wrong or
  4925. ' a successful MIF if everything installs correctly.
  4926. '
  4927. ' The MIF file requires a special format specified
  4928. ' by SMS.  Currently, this routine implements the
  4929. ' minimum requirements.  The hardcoded strings below
  4930. ' that are written to the MIF should be written
  4931. ' character by character as they are; except that
  4932. ' status message should change depending on the
  4933. ' circumstances of the install.  DO NOT LOCALIZE
  4934. ' anything except the status message.
  4935. '
  4936. ' IN: [strMIFFilename] - The name of the MIF file.
  4937. '                        Passed in to setup1 by
  4938. '                        setup.exe.  It is probably
  4939. '                        named <appname>.mif where
  4940. '                        <appname> is the name of the
  4941. '                        application you are installing.
  4942. '
  4943. '     [fStatus] - False to write a failed MIF (i.e. setup
  4944. '                 failed); True to write a successful MIF.
  4945. '
  4946. '     [strSMSDescription] - This is the description string
  4947. '                           to be written to the MIF file.
  4948. '                           It cannot be longer than 255
  4949. '                           characters and cannot contain
  4950. '                           carriage returns and/or line
  4951. '                           feeds.  This routine will
  4952. '                           enforce these requirements.
  4953. '
  4954. ' Note, when running in SMS mode, there is no other way
  4955. ' to display a message to the user than to write it to
  4956. ' the MIF file.  Displaying a MsgBox will cause the
  4957. ' computer to appear as if it has hung.  Therefore, this
  4958. ' routine makes no attempt to display an error message.
  4959. '
  4960. '-----------------------------------------------------------
  4961. '
  4962. Sub WriteMIF(ByVal strMIFFilename As String, ByVal fStatus As Boolean, ByVal strSMSDescription As String)
  4963.     Const strSUCCESS = """SUCCESS"""                 ' Cannot be localized as per SMS
  4964.     Const strFAILED = """FAILED"""                   ' Cannot be localized as per SMS
  4965.     
  4966.     Dim fn As Integer
  4967.     Dim intOffset As Integer
  4968.     Dim fOpened As Boolean
  4969.         
  4970.     fOpened = False
  4971.         
  4972.     On Error GoTo WMIFFAILED  ' If we fail, we just return without doing anything
  4973.                               ' because there is no way to inform the user while
  4974.                               ' in SMS mode.
  4975.  
  4976.     '
  4977.     ' If the description string is greater than 255 characters,
  4978.     ' truncate it.  Required my SMS.
  4979.     '
  4980.     strSMSDescription = Left(strSMSDescription, MAX_SMS_DESCRIP)
  4981.     '
  4982.     ' Remove any carriage returns or line feeds and replace
  4983.     ' them with spaces.  The message must be a single line.
  4984.     '
  4985.     For intOffset = 1 To Len(strSMSDescription)
  4986.         If (Mid(strSMSDescription, intOffset, 1) = Chr(10)) Or (Mid(strSMSDescription, intOffset, 1) = Chr(13)) Then
  4987.             Mid(strSMSDescription, intOffset, 1) = " "
  4988.         End If
  4989.     Next intOffset
  4990.     '
  4991.     ' Open the MIF file for append, but first delete any existing
  4992.     ' ones with the same name.  Note, that setup.exe passed a
  4993.     ' unique name so if there is one with this name already in
  4994.     ' on the disk, it was put there by setup.exe.
  4995.     '
  4996.     If FileExists(strMIFFilename) Then
  4997.         Kill strMIFFilename
  4998.     End If
  4999.     
  5000.     fn = FreeFile
  5001.     Open strMIFFilename For Append As fn
  5002.     fOpened = True
  5003.     '
  5004.     ' We are ready to write the actual MIF file
  5005.     ' Note, none of the string below are supposed
  5006.     ' to be localized.
  5007.     '
  5008.     Print #fn, "Start Component"
  5009.         Print #fn, Tab; "Name = ""Workstation"""
  5010.         Print #fn, Tab; "Start Group"
  5011.             Print #fn, Tab; Tab; "Name = ""InstallStatus"""
  5012.             Print #fn, Tab; Tab; "ID = 1"
  5013.             Print #fn, Tab; Tab; "Class = ""MICROSOFT|JOBSTATUS|1.0"""
  5014.             Print #fn, Tab; Tab; "Start Attribute"
  5015.                 Print #fn, Tab; Tab; Tab; "Name = ""Status"""
  5016.                 Print #fn, Tab; Tab; Tab; "ID = 1"
  5017.                 Print #fn, Tab; Tab; Tab; "Type = String(16)"
  5018.                 Print #fn, Tab; Tab; Tab; "Value = "; IIf(fStatus, strSUCCESS, strFAILED)
  5019.             Print #fn, Tab; Tab; "End Attribute"
  5020.             Print #fn, Tab; Tab; "Start Attribute"
  5021.                 Print #fn, Tab; Tab; Tab; "Name = ""Description"""
  5022.                 Print #fn, Tab; Tab; Tab; "ID = 2"
  5023.                 Print #fn, Tab; Tab; Tab; "Type = String(256)"
  5024.                 Print #fn, Tab; Tab; Tab; "Value = "; strSMSDescription
  5025.             Print #fn, Tab; Tab; "End Attribute"
  5026.         Print #fn, Tab; "End Group"
  5027.     Print #fn, "End Component"
  5028.  
  5029.     Close fn
  5030.     '
  5031.     ' Success
  5032.     '
  5033.     Exit Sub
  5034.  
  5035. WMIFFAILED:
  5036.     '
  5037.     ' At this point we are unable to create the MIF file.
  5038.     ' Since we are running under SMS there is no one to
  5039.     ' tell, so we don't generate an error message at all.
  5040.     '
  5041.     If fOpened = True Then
  5042.         Close fn
  5043.     End If
  5044.     Exit Sub
  5045. End Sub
  5046.  
  5047. 'Adds or replaces an HKEY to the list of HKEYs in cache.
  5048. 'Note that it is not necessary to remove keys from
  5049. 'this list.
  5050. Private Sub AddHkeyToCache(ByVal hKey As Long, ByVal strHkey As String)
  5051.     Dim intIdx As Integer
  5052.     
  5053.     intIdx = intGetHKEYIndex(hKey)
  5054.     If intIdx < 0 Then
  5055.         'The key does not already exist.  Add it to the end.
  5056.         On Error Resume Next
  5057.         ReDim Preserve hkeyCache(0 To UBound(hkeyCache) + 1)
  5058.         If Err Then
  5059.             'If there was an error, it means the cache was empty.
  5060.             On Error GoTo 0
  5061.             ReDim hkeyCache(0 To 0)
  5062.         End If
  5063.         On Error GoTo 0
  5064.  
  5065.         intIdx = UBound(hkeyCache)
  5066.     Else
  5067.         'The key already exists.  It will be replaced.
  5068.     End If
  5069.  
  5070.     hkeyCache(intIdx).hKey = hKey
  5071.     hkeyCache(intIdx).strHkey = strHkey
  5072. End Sub
  5073.  
  5074. 'Given a predefined HKEY, return the text string representing that
  5075. 'key, or else return "".
  5076. Private Function strGetPredefinedHKEYString(ByVal hKey As Long) As String
  5077.     Select Case hKey
  5078.         Case HKEY_CLASSES_ROOT
  5079.             strGetPredefinedHKEYString = "HKEY_CLASSES_ROOT"
  5080.         Case HKEY_CURRENT_USER
  5081.             strGetPredefinedHKEYString = "HKEY_CURRENT_USER"
  5082.         Case HKEY_LOCAL_MACHINE
  5083.             strGetPredefinedHKEYString = "HKEY_LOCAL_MACHINE"
  5084.         Case HKEY_USERS
  5085.             strGetPredefinedHKEYString = "HKEY_USERS"
  5086.         'End Case
  5087.     End Select
  5088. End Function
  5089.  
  5090. 'Given an HKEY, return the text string representing that
  5091. 'key.
  5092. Private Function strGetHKEYString(ByVal hKey As Long) As String
  5093.     Dim strKey As String
  5094.  
  5095.     'Is the hkey predefined?
  5096.     strKey = strGetPredefinedHKEYString(hKey)
  5097.     If strKey <> "" Then
  5098.         strGetHKEYString = strKey
  5099.         Exit Function
  5100.     End If
  5101.     
  5102.     'It is not predefined.  Look in the cache.
  5103.     Dim intIdx As Integer
  5104.     intIdx = intGetHKEYIndex(hKey)
  5105.     If intIdx >= 0 Then
  5106.         strGetHKEYString = hkeyCache(intIdx).strHkey
  5107.     Else
  5108.         strGetHKEYString = ""
  5109.     End If
  5110. End Function
  5111.  
  5112. 'Searches the cache for the index of the given HKEY.
  5113. 'Returns the index if found, else returns -1.
  5114. Private Function intGetHKEYIndex(ByVal hKey As Long) As Integer
  5115.     Dim intUBound As Integer
  5116.     
  5117.     On Error Resume Next
  5118.     intUBound = UBound(hkeyCache)
  5119.     If Err Then
  5120.         'If there was an error accessing the ubound of the array,
  5121.         'then the cache is empty
  5122.         GoTo NotFound
  5123.     End If
  5124.     On Error GoTo 0
  5125.  
  5126.     Dim intIdx As Integer
  5127.     For intIdx = 0 To intUBound
  5128.         If hkeyCache(intIdx).hKey = hKey Then
  5129.             intGetHKEYIndex = intIdx
  5130.             Exit Function
  5131.         End If
  5132.     Next intIdx
  5133.     
  5134. NotFound:
  5135.     intGetHKEYIndex = -1
  5136. End Function
  5137.  
  5138. 'Returns the location of the Program Files\Common Files path, if
  5139. 'it is present in the registry.  Otherwise, returns "".
  5140. Public Function strGetCommonFilesPath() As String
  5141.     Dim hKey As Long
  5142.     Dim strPath As String
  5143.     
  5144.     If TreatAsWin95() Then
  5145.         Const strCommonFilesKey = "CommonFilesDir"
  5146.  
  5147.         If RegOpenKey(HKEY_LOCAL_MACHINE, RegPathWinCurrentVersion(), hKey) Then
  5148.             RegQueryStringValue hKey, strCommonFilesKey, strPath
  5149.             RegCloseKey hKey
  5150.         End If
  5151.     End If
  5152.  
  5153.     If strPath <> "" Then
  5154.         AddDirSep strPath
  5155.     End If
  5156.     
  5157.     strGetCommonFilesPath = strPath
  5158. End Function
  5159. 'Returns the location of the "Windows\Start Menu\Programs" Files path, if
  5160. 'it is present in the registry.  Otherwise, returns "".
  5161. Public Function strGetProgramsFilesPath() As String
  5162.     Dim hKey As Long
  5163.     Dim strPath As String
  5164.     
  5165.     strPath = ""
  5166.     If TreatAsWin95() Then
  5167.         Const strProgramsKey = "Programs"
  5168.  
  5169.         If RegOpenKey(HKEY_CURRENT_USER, RegPathWinPrograms(), hKey) Then
  5170.             RegQueryStringValue hKey, strProgramsKey, strPath
  5171.             RegCloseKey hKey
  5172.         End If
  5173.     End If
  5174.  
  5175.     If strPath <> "" Then
  5176.         AddDirSep strPath
  5177.     End If
  5178.     
  5179.     strGetProgramsFilesPath = strPath
  5180. End Function
  5181.  
  5182. 'Returns the directory where DAO is or should be installed.  If the
  5183. 'key does not exist in the registry, it is created.  For instance, under
  5184. 'NT 3.51 this location is normally 'C:\WINDOWS\MSAPPS\DAO'
  5185. Private Function strGetDAOPath() As String
  5186.     Const strMSAPPS$ = "MSAPPS\"
  5187.     Const strDAO3032$ = "DAO350.DLL"
  5188.     
  5189.     'first look in the registry
  5190.     Const strKey = "SOFTWARE\Microsoft\Shared Tools\DAO350"
  5191.     Const strValueName = "Path"
  5192.     Dim hKey As Long
  5193.     Dim strPath As String
  5194.  
  5195.     If RegOpenKey(HKEY_LOCAL_MACHINE, strKey, hKey) Then
  5196.         RegQueryStringValue hKey, strValueName, strPath
  5197.         RegCloseKey hKey
  5198.     End If
  5199.  
  5200.     If strPath <> "" Then
  5201.         strPath = GetPathName(strPath)
  5202.         AddDirSep strPath
  5203.         strGetDAOPath = strPath
  5204.         Exit Function
  5205.     End If
  5206.     
  5207.     'It's not yet in the registry, so we need to decide
  5208.     'where the directory should be, and then need to place
  5209.     'that location in the registry.
  5210.  
  5211.     If TreatAsWin95() Then
  5212.         'For Win95, use "Common Files\Microsoft Shared\DAO"
  5213.         strPath = strGetCommonFilesPath() & ResolveResString(resMICROSOFTSHARED) & "DAO\"
  5214.     Else
  5215.         'Otherwise use Windows\MSAPPS\DAO
  5216.         strPath = gstrWinDir & strMSAPPS & "DAO\"
  5217.     End If
  5218.     
  5219.     'Place this information in the registry (note that we point to DAO3032.DLL
  5220.     'itself, not just to the directory)
  5221.     If RegCreateKey(HKEY_LOCAL_MACHINE, strKey, "", hKey) Then
  5222.         RegSetStringValue hKey, strValueName, strPath & strDAO3032, False
  5223.         RegCloseKey hKey
  5224.     End If
  5225.  
  5226.     strGetDAOPath = strPath
  5227. End Function
  5228.  
  5229. ' Replace all double quotes with single quotes
  5230. Public Sub ReplaceDoubleQuotes(str As String)
  5231.     Dim i As Integer
  5232.     
  5233.     For i = 1 To Len(str)
  5234.         If Mid$(str, i, 1) = """" Then
  5235.             Mid$(str, i, 1) = "'"
  5236.         End If
  5237.     Next i
  5238. End Sub
  5239.  
  5240. 'Get the path portion of a filename
  5241. Function GetPathName(ByVal strFilename As String) As String
  5242.     Dim sPath As String
  5243.     Dim sFile As String
  5244.     
  5245.     SeparatePathAndFileName strFilename, sPath, sFile
  5246.     
  5247.     GetPathName = sPath
  5248. End Function
  5249. 'Determines if a character is a path separator (\ or /).
  5250. Public Function IsSeparator(Character As String) As Boolean
  5251.     Select Case Character
  5252.         Case gstrSEP_DIR
  5253.             IsSeparator = True
  5254.         Case gstrSEP_DIRALT
  5255.             IsSeparator = True
  5256.     End Select
  5257. End Function
  5258. 'Given a fully qualified filename, returns the path portion and the file
  5259. '   portion.
  5260. Public Sub SeparatePathAndFileName(FullPath As String, ByRef Path As String, _
  5261.     ByRef FileName As String)
  5262.  
  5263.     Dim nSepPos As Long
  5264.     Dim sSEP As String
  5265.  
  5266.     nSepPos = Len(FullPath)
  5267.     sSEP = Mid$(FullPath, nSepPos, 1)
  5268.     Do Until IsSeparator(sSEP)
  5269.         nSepPos = nSepPos - 1
  5270.         If nSepPos = 0 Then Exit Do
  5271.         sSEP = Mid$(FullPath, nSepPos, 1)
  5272.     Loop
  5273.  
  5274.     Select Case nSepPos
  5275.         Case 0
  5276.             'Separator was not found.
  5277.             Path = CurDir$
  5278.             FileName = FullPath
  5279.         Case Else
  5280.             Path = Left$(FullPath, nSepPos - 1)
  5281.             FileName = Mid$(FullPath, nSepPos + 1)
  5282.     End Select
  5283. End Sub
  5284.  
  5285. 'Returns the path to the root of the first fixed disk
  5286. Function strRootDrive() As String
  5287.     Dim intDriveNum As Integer
  5288.     
  5289.     For intDriveNum = 0 To Asc("Z") - Asc("A") - 1
  5290.         If GetDriveType(intDriveNum) = intDRIVE_FIXED Then
  5291.             strRootDrive = Chr$(Asc("A") + intDriveNum) & gstrCOLON & gstrSEP_DIR
  5292.             Exit Function
  5293.         End If
  5294.     Next intDriveNum
  5295.     
  5296.     strRootDrive = "C:\"
  5297. End Function
  5298.  
  5299. 'Returns "" if the path is not complete, or is a UNC pathname
  5300. Function strGetDriveFromPath(ByVal strPath As String) As String
  5301.     If Len(strPath) < 2 Then
  5302.         Exit Function
  5303.     End If
  5304.     
  5305.     If Mid$(strPath, 2, 1) <> gstrCOLON Then
  5306.         Exit Function
  5307.     End If
  5308.     
  5309.     strGetDriveFromPath = Mid$(strPath, 1, 1) & gstrCOLON & gstrSEP_DIR
  5310. End Function
  5311.  
  5312. Public Function fValidFilename(strFilename As String) As Boolean
  5313. '
  5314. ' This routine verifies that strFileName is a valid file name.
  5315. ' It checks that its length is less than the max allowed
  5316. ' and that it doesn't contain any invalid characters..
  5317. '
  5318.     If Not fCheckFNLength(strFilename) Then
  5319.         '
  5320.         ' Name is too long.
  5321.         '
  5322.         fValidFilename = False
  5323.         Exit Function
  5324.     End If
  5325.     '
  5326.     ' Search through the list of invalid filename characters and make
  5327.     ' sure none of them are in the string.
  5328.     '
  5329.     Dim iInvalidChar As Integer
  5330.     Dim iFilename As Integer
  5331.     Dim strInvalidChars As String
  5332.     
  5333.     strInvalidChars = ResolveResString(resCOMMON_INVALIDFILECHARS)
  5334.     
  5335.     For iInvalidChar = 1 To Len(strInvalidChars)
  5336.         If InStr(strFilename, Mid$(strInvalidChars, iInvalidChar, 1)) <> 0 Then
  5337.             fValidFilename = False
  5338.             Exit Function
  5339.         End If
  5340.     Next iInvalidChar
  5341.     
  5342.     fValidFilename = True
  5343.     
  5344. End Function
  5345. Public Function fValidNTGroupName(strGroupName) As Boolean
  5346. '
  5347. ' This routine verifies that strGroupName is a valid group name.
  5348. ' It checks that its length is less than the max allowed
  5349. ' and that it doesn't contain any invalid characters.
  5350. '
  5351.     If Len(strGroupName) > gintMAX_GROUPNAME_LEN Then
  5352.         fValidNTGroupName = False
  5353.         Exit Function
  5354.     End If
  5355.     '
  5356.     ' Search through the list of invalid filename characters and make
  5357.     ' sure none of them are in the string.
  5358.     '
  5359.     Dim iInvalidChar As Integer
  5360.     Dim iFilename As Integer
  5361.     Dim strInvalidChars As String
  5362.     
  5363.     strInvalidChars = ResolveResString(resGROUPINVALIDCHARS)
  5364.     
  5365.     For iInvalidChar = 1 To Len(strInvalidChars)
  5366.         If InStr(strGroupName, Mid$(strInvalidChars, iInvalidChar, 1)) <> 0 Then
  5367.             fValidNTGroupName = False
  5368.             Exit Function
  5369.         End If
  5370.     Next iInvalidChar
  5371.     
  5372.     fValidNTGroupName = True
  5373.     
  5374. End Function
  5375. '-----------------------------------------------------------
  5376. ' SUB: CountGroups
  5377. '
  5378. ' Determines how many groups must be installed by counting
  5379. ' them in the setup information file (SETUP.LST)
  5380. '-----------------------------------------------------------
  5381. '
  5382. Function CountGroups(ByVal strsection As String) As Integer
  5383.     Dim intIdx As Integer
  5384.     Dim sGroup As String
  5385.     
  5386.     intIdx = 0
  5387.     Do
  5388.         sGroup = ReadIniFile(gstrSetupInfoFile, strsection, gsGROUP & CStr(intIdx))
  5389.         If sGroup <> vbNullString Then 'Found a group
  5390.             intIdx = intIdx + 1
  5391.         Else
  5392.             Exit Do
  5393.         End If
  5394.     Loop
  5395.     CountGroups = intIdx
  5396. End Function
  5397. '-----------------------------------------------------------
  5398. ' SUB: GetGroup
  5399. '
  5400. ' Returns the Groupname specified by Index
  5401. '-----------------------------------------------------------
  5402. '
  5403. Function GetGroup(ByVal strsection As String, ByVal index As Integer)
  5404.     GetGroup = ReadIniFile(gstrSetupInfoFile, strsection, gsGROUP & CStr(index))
  5405. End Function
  5406. '-----------------------------------------------------------
  5407. ' SUB: SetGroup
  5408. '
  5409. ' Sets Groupname specified by Index
  5410. '-----------------------------------------------------------
  5411. '
  5412. Sub SetGroup(ByVal strsection As String, ByVal index As Integer, ByVal sGroupName As String)
  5413.     Const iBuf As Integer = 2048
  5414.     Const sEQUAL As String * 1 = "="
  5415.     Dim sGroup As String
  5416.     Dim sNames As String, ret As Long
  5417.     sGroup = ReadIniFile(gstrSetupInfoFile, strsection, gsGROUP & CStr(index))
  5418.     sNames = Space$(iBuf)
  5419.     ret = GetPrivateProfileSection(sGroup, sNames, iBuf, gstrSetupInfoFile)
  5420.     If ret = 0 Then 'We have nothing in this section, just quit.
  5421.         Exit Sub
  5422.     End If
  5423.     sNames = Left$(sNames, ret - 1)
  5424.     'We now have the Group name, modify the icons in that group
  5425.     Dim lCount As Long, sKEY As String, sValue As String, fKey As Boolean
  5426.     fKey = True
  5427.     For lCount = 1 To Len(sNames)
  5428.         If (Mid$(sNames, lCount, 1) = sEQUAL) Then
  5429.             fKey = False
  5430.         ElseIf (Asc(Mid$(sNames, lCount, 1)) = 0) Or (Len(sNames) = lCount) Then
  5431.             If Len(sNames) = lCount Then
  5432.                 If fKey Then
  5433.                     sKEY = sKEY & Mid$(sNames, lCount, 1)
  5434.                 Else
  5435.                     sValue = sValue & Mid$(sNames, lCount, 1)
  5436.                 End If
  5437.             End If
  5438.             If Len(sKEY) <> 0 Then
  5439.                 Call WritePrivateProfileString(sGroupName, sKEY, sValue, gstrSetupInfoFile)
  5440.             End If
  5441.             sKEY = vbNullString
  5442.             sValue = vbNullString
  5443.             fKey = True
  5444.         Else
  5445.             If fKey Then
  5446.                 sKEY = sKEY & Mid$(sNames, lCount, 1)
  5447.             Else
  5448.                 sValue = sValue & Mid$(sNames, lCount, 1)
  5449.             End If
  5450.         End If
  5451.     Next
  5452.     Call WritePrivateProfileString(strsection, gsGROUP & CStr(index), sGroupName, gstrSetupInfoFile)
  5453. End Sub
  5454. '-----------------------------------------------------------
  5455. ' SUB: GetPrivate
  5456. '
  5457. ' Returns the the value of whether the group is private specified by Index
  5458. '-----------------------------------------------------------
  5459. '
  5460. Function GetPrivate(ByVal strsection As String, ByVal index As Integer) As Boolean
  5461.     GetPrivate = CBool(ReadIniFile(gstrSetupInfoFile, strsection, gsPRIVATE & CStr(index)))
  5462. End Function
  5463. Function GetStart(ByVal strsection As String, ByVal index As Integer) As Boolean
  5464.     GetStart = ReadIniFile(gstrSetupInfoFile, strsection, gsPARENT & CStr(index)) = gsSTARTMENUKEY
  5465. End Function
  5466.  
  5467. '-----------------------------------------------------------
  5468. ' SUB: CountIcons
  5469. '
  5470. ' Determines how many icons must be installed by counting
  5471. ' them in the setup information file (SETUP.LST)
  5472. '-----------------------------------------------------------
  5473. '
  5474. Function CountIcons(ByVal strsection As String) As Integer
  5475.     Dim intIdx As Integer
  5476.     Dim cIcons As Integer
  5477.     Dim sGroup As String
  5478.     Dim oCol As New Collection
  5479.     
  5480.     intIdx = 0
  5481.     cIcons = 0
  5482.     Do
  5483.         sGroup = ReadIniFile(gstrSetupInfoFile, strsection, gsGROUP & CStr(intIdx))
  5484.         If sGroup <> vbNullString Then 'Found a group
  5485.             oCol.Add sGroup
  5486.             intIdx = intIdx + 1
  5487.         Else
  5488.             Exit Do
  5489.         End If
  5490.     Loop
  5491.     Dim sGName As String
  5492.     Dim vGroup As Variant
  5493.     For Each vGroup In oCol
  5494.         intIdx = 1
  5495.         Do
  5496.             sGName = ReadIniFile(gstrSetupInfoFile, vGroup, gsICON & CStr(intIdx))
  5497.             If sGName <> vbNullString Then
  5498.                 cIcons = cIcons + 1
  5499.                 intIdx = intIdx + 1
  5500.             Else
  5501.                 Exit Do
  5502.             End If
  5503.         Loop
  5504.     Next
  5505.     CountIcons = cIcons
  5506.     
  5507. End Function
  5508. '-----------------------------------------------------------
  5509. ' SUB: CreateIcons
  5510. '
  5511. ' Walks through the list of files in SETUP.LST and creates
  5512. ' Icons in the Program Group for files needed it.
  5513. '-----------------------------------------------------------
  5514. '
  5515. Sub CreateIcons(ByVal strsection As String)
  5516.     Dim intIdx As Integer
  5517.     Dim sFile As FILEINFO
  5518.     Dim strProgramIconTitle As String
  5519.     Dim strProgramIconCmdLine As String
  5520.     Dim strProgramPath As String
  5521.     Dim strProgramArgs As String
  5522.     Dim intAnchor As Integer
  5523.     Dim intOffset As Integer
  5524.     Dim strGroup As String
  5525.     Dim sGroup As String
  5526.     Dim oCol As New Collection
  5527.     Const CompareBinary = 0
  5528.     '
  5529.     'For each file in the specified section, read info from the setup info file
  5530.     '
  5531.     intIdx = 0
  5532.     Do
  5533.         sGroup = ReadIniFile(gstrSetupInfoFile, strsection, gsGROUP & CStr(intIdx))
  5534.         If sGroup <> vbNullString Then 'Found a group
  5535.             oCol.Add sGroup
  5536.             intIdx = intIdx + 1
  5537.         Else
  5538.             Exit Do
  5539.         End If
  5540.     Loop
  5541.     Dim sGName As String
  5542.     Dim vGroup As Variant
  5543.     For Each vGroup In oCol
  5544.         intIdx = 0
  5545.         Do
  5546.             intIdx = intIdx + 1
  5547.             sGName = ReadIniFile(gstrSetupInfoFile, vGroup, gsICON & CStr(intIdx))
  5548.             If sGName <> vbNullString Then
  5549.                 '
  5550.                 ' Get the Icon's caption and command line
  5551.                 '
  5552.                 strProgramIconTitle = ReadIniFile(gstrSetupInfoFile, vGroup, gsTITLE & CStr(intIdx))
  5553.                 strProgramIconCmdLine = ReadIniFile(gstrSetupInfoFile, vGroup, gsICON & CStr(intIdx))
  5554.                 strGroup = vGroup
  5555.                 '
  5556.                 ' if the ProgramIcon is specified, then we create an icon,
  5557.                 ' otherwise we don't.
  5558.                 '
  5559.                 If Trim(strUnQuoteString(strProgramIconTitle)) <> vbNullString Then
  5560.                     '
  5561.                     ' If the command line is not specified in SETUP.LST and the icon
  5562.                     ' is, then use the files destination path as the command line.  In
  5563.                     ' this case there are no parameters.
  5564.                     '
  5565.                     If Trim(strUnQuoteString(strProgramIconCmdLine)) = "" Then
  5566.                         strProgramPath = sFile.strDestDir & gstrSEP_DIR & sFile.strDestName
  5567.                         strProgramArgs = ""
  5568.                     Else
  5569.                         '
  5570.                         ' Parse the command line, to determine what is the exe, etc. and what
  5571.                         ' are the parameters.  The first space that is not contained within
  5572.                         ' quotes, marks the end of the exe, etc..  Everything afterwards are
  5573.                         ' parameters/arguments for the exe.  NOTE: It is important that if
  5574.                         ' the exe is contained within quotes that the parameters not be
  5575.                         ' contained within the same quotes.  The arguments can themselves
  5576.                         ' each be inside quotes as long as they are not in the same quotes
  5577.                         ' with the exe.
  5578.                         '
  5579.                         intAnchor = 1
  5580.                         intOffset = intGetNextFldOffset(intAnchor, strProgramIconCmdLine, " ", CompareBinary)
  5581.                         If intOffset = 0 Then intOffset = Len(strProgramIconCmdLine) + 1
  5582.                         strProgramPath = Trim(Left(strProgramIconCmdLine, intOffset - 1))
  5583.                         '
  5584.                         ' Got the exe, now the parameters.
  5585.                         '
  5586.                         strProgramArgs = Trim(Mid(strProgramIconCmdLine, intOffset + 1))
  5587.                     End If
  5588.                     '
  5589.                     ' Expand all the Destination Directory macros that are embedded in the
  5590.                     ' Program Path and the Arguments'
  5591.                     '
  5592.                     strProgramPath = ResolveDestDir(strProgramPath)
  5593.                     strProgramArgs = ResolveDestDirs(strProgramArgs)
  5594.                     '
  5595.                     ' Finally, we have everything we need, create the icon.
  5596.                     '
  5597.                     Dim fPrivate As Boolean, sParent As String
  5598.                     Dim intIdx2 As Integer
  5599.                     
  5600.                     intIdx2 = 0
  5601.                     Do
  5602.                         sGroup = ReadIniFile(gstrSetupInfoFile, gsICONGROUP, gsGROUP & CStr(intIdx2))
  5603.                         If sGroup = strGroup Then 'Found the group
  5604.                             If IsWindows95 Then
  5605.                                 fPrivate = True
  5606.                             Else
  5607.                                 fPrivate = GetPrivate(gsICONGROUP, intIdx2)
  5608.                             End If
  5609.                             If GetStart(gsICONGROUP, intIdx2) Then
  5610.                                 sParent = gsSTARTMENUKEY
  5611.                             Else
  5612.                                 sParent = gsPROGMENUKEY
  5613.                             End If
  5614.                             Exit Do
  5615.                         End If
  5616.                         intIdx2 = intIdx2 + 1
  5617.                     Loop
  5618.                     CreateOSLink frmSetup1, strGroup, strProgramPath, strProgramArgs, strProgramIconTitle, fPrivate, sParent
  5619.                 ElseIf Trim(strUnQuoteString(strProgramIconCmdLine)) <> vbNullString Then
  5620.                     '
  5621.                     ' This file contained specified a command line in SETUP.LST but no icon.
  5622.                     ' This is an error.  Let the user know and skip this icon or abort.
  5623.         
  5624.                     '
  5625.                     If gfNoUserInput Or MsgWarning(ResolveResString(resICONMISSING, "|1", sFile.strDestName), vbYesNo Or vbExclamation, gstrSETMSG) = vbNo Then
  5626.                         ExitSetup frmSetup1, gintRET_FATAL
  5627.                     End If
  5628.                 End If
  5629.             Else
  5630.                 Exit Do
  5631.             End If
  5632.         Loop
  5633.     Next
  5634. End Sub
  5635.  
  5636. Public Function RebootSystem() As Boolean
  5637.     Dim ret As Long
  5638.     Dim hToken As Long
  5639.     Dim tkp As TOKEN_PRIVILEGES
  5640.     Dim tkpOld As TOKEN_PRIVILEGES
  5641.     Dim fOkReboot As Boolean
  5642.     Const sSHUTDOWN As String = "SeShutdownPrivilege"
  5643.     'Check to see if we are running on Windows NT
  5644.     If IsWindowsNT() Then
  5645.         'We are running windows NT.  We need to do some security checks/modifications
  5646.         'to ensure we have the token that allows us to reboot.
  5647.         If OpenProcessToken(GetCurrentProcess(), _
  5648.                 TOKEN_ADJUST_PRIVILEGES Or TOKEN_QUERY, hToken) Then
  5649.             ret = LookupPrivilegeValue(vbNullString, sSHUTDOWN, tkp.Privileges(0).pLuid)
  5650.             tkp.PrivilegeCount = 1
  5651.             tkp.Privileges(0).Attributes = SE_PRIVILEGE_ENABLED
  5652.             fOkReboot = AdjustTokenPrivileges(hToken, 0, tkp, LenB(tkpOld), tkpOld, ret)
  5653.         End If
  5654.     Else
  5655.         'We are running Win95/98.  Nothing needs to be done.
  5656.         fOkReboot = True
  5657.     End If
  5658.     If fOkReboot Then RebootSystem = (ExitWindowsEx(EWX_REBOOT, 0) <> 0)
  5659. End Function
  5660.  
  5661. Private Function GetFileTime(ByVal aDate As Date) As FileTime
  5662.     Dim lTemp As SYSTEMTIME
  5663.     Dim lTime As FileTime
  5664.     
  5665.     VariantTimeToSystemTime aDate, lTemp
  5666.     SystemTimeToFileTime lTemp, lTime
  5667.     LocalFileTimeToFileTime lTime, GetFileTime
  5668. End Function
  5669.  
  5670.