home *** CD-ROM | disk | FTP | other *** search
/ Windows Game Programming for Dummies (2nd Edition) / WinGamProgFD.iso / pc / DirectX SDK / DXSDK / samples / Multimedia / VBSamples / Misc / DXSetup / common.bas < prev    next >
Encoding:
BASIC Source File  |  2001-10-08  |  72.0 KB  |  2,034 lines

  1. Attribute VB_Name = "basCommon"
  2. Option Explicit
  3.  
  4. '
  5. ' Public Constants
  6. '
  7.  
  8. Public Const gstrSEP_DIR$ = "\"                         ' Directory separator character
  9. Public Const gstrAT$ = "@"
  10. Public Const gstrSEP_DRIVE$ = ":"                       ' Driver separater character, e.g., C:\
  11. Public Const gstrSEP_DIRALT$ = "/"                      ' Alternate directory separator character
  12. Public Const gstrSEP_EXT$ = "."                         ' Filename extension separator character
  13. Public Const gstrSEP_URLDIR$ = "/"                      ' Separator for dividing directories in URL addresses.
  14.  
  15. Public Const gstrCOLON$ = ":"
  16. Public Const gstrSwitchPrefix2 = "/"
  17. Public Const gstrCOMMA$ = ","
  18. Public Const gstrDECIMAL$ = "."
  19. Public Const gstrQUOTE$ = """"
  20. Public Const gstrASSIGN$ = "="
  21. Public Const gstrINI_PROTOCOL = "Protocol"
  22. 'This should remain uppercase
  23. Public Const gstrDCOM = "DCOM"
  24.  
  25. Public Const gintMAX_SIZE% = 255                        'Maximum buffer size
  26. Public Const gintMAX_PATH_LEN% = 260                    ' Maximum allowed path length including path, filename,
  27.                                                         ' and command line arguments for NT (Intel) and Win95.
  28.  
  29. Public Const intDRIVE_REMOVABLE% = 2                    'Constants for GetDriveType
  30. Public Const intDRIVE_FIXED% = 3
  31. Public Const intDRIVE_REMOTE% = 4
  32. Public Const intDRIVE_CDROM% = 5
  33.  
  34. Public Const gintNOVERINFO% = 32767                     'flag indicating no version info
  35.  
  36. 'File names
  37. Public Const gstrFILE_SETUP$ = "SETUP.LST"              'Name of setup information file
  38.  
  39. 'Share type macros for files
  40. Public Const mstrPRIVATEFILE = vbNullString
  41. Public Const mstrSHAREDFILE = "$(Shared)"
  42.  
  43. 'INI File keys
  44. Public Const gstrINI_FILES$ = "Setup1 Files"                           'default section to install
  45. Public Const gstrINI_SETUP$ = "Setup"
  46. Public Const gstrINI_COLOR$ = "Color"
  47. Public Const gstrINI_BOOT$ = "Bootstrap"
  48. Public Const gstrINI_APPNAME$ = "Title"
  49. Public Const gstrINI_CABS$ = "Cabs"
  50. Public Const gstrINI_APPDIR$ = "DefaultDir"
  51. Public Const gstrINI_APPEXE$ = "AppExe"
  52. Public Const gstrINI_APPPATH$ = "AppPath"
  53. Public Const gstrINI_FORCEUSEDEFDEST = "ForceUseDefDir"
  54. Public Const gstrINI_CABNAME$ = "CabFile"
  55. Public Const gsPRIVATE As String = "PrivateGroup"
  56.  
  57. 'This should remain uppercase.
  58. Public Const gstrEXT_DEP$ = "DEP"
  59.  
  60. 'Setup information file macros
  61. Public Const gstrAPPDEST$ = "$(AppPath)"
  62. Public Const gstrWINDEST$ = "$(WinPath)"
  63. Public Const gstrFONTDEST$ = "$(Font)"
  64. Public Const gstrWINSYSDEST$ = "$(WinSysPath)"
  65. Public Const gstrWINSYSDESTSYSFILE$ = "$(WinSysPathSysFile)"
  66. Public Const gstrPROGRAMFILES$ = "$(ProgramFiles)"
  67. Public Const gstrCOMMONFILES$ = "$(CommonFiles)"
  68. Public Const gstrCOMMONFILESSYS$ = "$(CommonFilesSys)"
  69. Public Const gstrDAODEST$ = "$(MSDAOPath)"
  70.  
  71. Public Const gsZERO As String = "0"
  72.  
  73. 'MsgError() Constants
  74. Public Const MSGERR_ERROR = 1
  75. Public Const MSGERR_WARNING = 2
  76.  
  77. 'Shell Constants
  78. Public Const NORMAL_PRIORITY_CLASS      As Long = &H20&
  79. Public Const INFINITE                   As Long = -1&
  80.  
  81. Public Const STATUS_WAIT_0              As Long = &H0
  82. Public Const WAIT_OBJECT_0              As Long = STATUS_WAIT_0
  83.  
  84. 'GetLocaleInfo constants
  85. Public Const LOCALE_FONTSIGNATURE = &H58&           ' font signature
  86.  
  87. Public Const TCI_SRCFONTSIG = 3
  88.  
  89. Public Const LANG_CHINESE = &H4
  90. Public Const SUBLANG_CHINESE_TRADITIONAL = &H1           ' Chinese (Taiwan)
  91. Public Const SUBLANG_CHINESE_SIMPLIFIED = &H2            ' Chinese (PR China)
  92. Public Const CHARSET_CHINESESIMPLIFIED = 134
  93. Public Const CHARSET_CHINESEBIG5 = 136
  94.  
  95. Public Const LANG_JAPANESE = &H11
  96. Public Const CHARSET_SHIFTJIS = 128
  97.  
  98. Public Const LANG_KOREAN = &H12
  99. Public Const SUBLANG_KOREAN = &H1                        ' Korean (Extended Wansung)
  100. Public Const SUBLANG_KOREAN_JOHAB = &H2                  ' Korean (Johab)
  101. Public Const CHARSET_HANGEUL = 129
  102.  
  103. Public Type STARTUPINFO
  104.     cb              As Long
  105.     lpReserved      As Long
  106.     lpDesktop       As Long
  107.     lpTitle         As Long
  108.     dwX             As Long
  109.     dwY             As Long
  110.     dwXSize         As Long
  111.     dwYSize         As Long
  112.     dwXCountChars   As Long
  113.     dwYCountChars   As Long
  114.     dwFillAttribute As Long
  115.     dwFlags         As Long
  116.     wShowWindow     As Integer
  117.     cbReserved2     As Integer
  118.     lpReserved2     As Long
  119.     hStdInput       As Long
  120.     hStdOutput      As Long
  121.     hStdError       As Long
  122. End Type
  123.  
  124. Public Type PROCESS_INFORMATION
  125.     hProcess    As Long
  126.     hThread     As Long
  127.     dwProcessID As Long
  128.     dwThreadID  As Long
  129. End Type
  130.  
  131. Private Type OFSTRUCT
  132.     cBytes As Byte
  133.     fFixedDisk As Byte
  134.     nErrCode As Integer
  135.     nReserved1 As Integer
  136.     nReserved2 As Integer
  137.     szPathName As String * 256
  138. End Type
  139.  
  140. Public Type VERINFO                                            'Version FIXEDFILEINFO
  141.     'There is data in the following two dwords, but it is for Windows internal
  142.     '   use and we should ignore it
  143.     Ignore(1 To 8) As Byte
  144.     'Signature As Long
  145.     'StrucVersion As Long
  146.     FileVerPart2 As Integer
  147.     FileVerPart1 As Integer
  148.     FileVerPart4 As Integer
  149.     FileVerPart3 As Integer
  150.     ProductVerPart2 As Integer
  151.     ProductVerPart1 As Integer
  152.     ProductVerPart4 As Integer
  153.     ProductVerPart3 As Integer
  154.     FileFlagsMask As Long 'VersionFileFlags
  155.     FileFlags As Long 'VersionFileFlags
  156.     FileOS As Long 'VersionOperatingSystemTypes
  157.     FileType As Long
  158.     FileSubtype As Long 'VersionFileSubTypes
  159.     'I've never seen any data in the following two dwords, so I'll ignore them
  160.     Ignored(1 To 8) As Byte 'DateHighPart As Long, DateLowPart As Long
  161. End Type
  162.  
  163. Private Type PROTOCOL
  164.     strName As String
  165.     strFriendlyName As String
  166. End Type
  167.  
  168. Private Type OSVERSIONINFO 'for GetVersionEx API call
  169.     dwOSVersionInfoSize As Long
  170.     dwMajorVersion As Long
  171.     dwMinorVersion As Long
  172.     dwBuildNumber As Long
  173.     dwPlatformId As Long
  174.     szCSDVersion As String * 128
  175. End Type
  176.  
  177. Private Type LOCALESIGNATURE
  178.     lsUsb(3)          As Long
  179.     lsCsbDefault(1)   As Long
  180.     lsCsbSupported(1) As Long
  181. End Type
  182. Private Type FONTSIGNATURE
  183.     fsUsb(3) As Long
  184.     fsCsb(1) As Long
  185. End Type
  186. Private Type CHARSETINFO
  187.     ciCharset As Long
  188.     ciACP     As Long
  189.     fs        As FONTSIGNATURE
  190. End Type
  191.  
  192. '
  193. ' Public variables used for silent and SMS installation
  194. '
  195. Public gfSilent As Boolean                              ' Whether or not we are doing a silent install
  196. Public gstrSilentLog As String                          ' filename for output during silent install.
  197. #If SMS Then
  198. Public gfSMS As Boolean                                 ' Whether or not we are doing an SMS silent install
  199. Public gstrMIFFile As String                            ' status output file for SMS
  200. Public gfSMSStatus As Boolean                           ' status of SMS installation
  201. Public gstrSMSDescription As String                     ' description string written to MIF file for SMS installation
  202. Public gfDontLogSMS As Boolean                          ' Prevents MsgFunc from being logged to SMS (e.g., for confirmation messasges)
  203. Public Const MAX_SMS_DESCRIP = 255                      ' SMS does not allow description strings longer than 255 chars.
  204. #End If
  205.  
  206. 'Note: Silent mode is untested and unsupported, but it's still there and still works.
  207. Public gfNoUserInput As Boolean                         ' True if either gfSMS or gfSilent is True
  208.  
  209. 'Variables for caching font values
  210. Private msFont As String                   ' the cached name of the font
  211. Private mnFont As Integer                  ' the cached size of the font
  212. Private mnCharset As Integer               ' the cached charset of the font
  213.  
  214. Public Const gsSTARTMENUKEY As String = "$(Start Menu)"
  215. Public Const gsPROGMENUKEY As String = "$(Programs)"
  216. Public Const gsPARENT As String = "Parent"
  217.  
  218. '
  219. 'List of available protocols
  220. '
  221. Public gProtocol() As PROTOCOL
  222. Public gcProtocols As Integer
  223. '
  224. ' MDAC_TYP.exe is a self extracting exe
  225. ' that installs data access.
  226. '
  227. Public gfMDag As Boolean
  228. Public Const gstrFILE_MDAG = "mdac_typ.exe"
  229. Public Const gstrFILE_MDAGARGS = " /q:a /c:""setup.exe /QN1"""
  230. Public gstrMDagInstallPath As String
  231.  
  232. ' DirectX Redist File Names
  233. Public Const gstrFILE_DSETUP As String = "dsetup.dll"
  234. Public Const gstrFILE_DSETUP32 As String = "dsetup32.dll"
  235. Public Const gstrFILE_CFGMGR32 As String = "cfgmgr32.dll"
  236. Public Const gstrFILE_DIRECTXCAB As String = "DirectX.Cab"
  237. Public Const gstrFILE_DXSETUP As String = "DXSetup.exe"
  238. Public Const gstrFILE_SETUPAPIDLL As String = "setupapi.dll"
  239. Public Const gstrFILE_BDACAB As String = "bda.cab"
  240. Public Const gstrFILE_BDANTCAB As String = "bdant.cab"
  241. Public Const gstrFILE_DXNTCAB As String = "dxnt.cab"
  242. Public Const gstrFILE_WAMSETUP As String = "wamsetup.exe"
  243.  
  244. '
  245. 'API/DLL Declarations for 32 bit SetupToolkit
  246. '
  247. Public Declare Function DLLSelfRegister Lib "vb6stkit.dll" (ByVal lpDllName As String) As Integer
  248. Public Declare Function RegisterTLB Lib "vb6stkit.dll" (ByVal lpTLBName As String) As Integer
  249. Public Declare Function OSfCreateShellLink Lib "vb6stkit.dll" Alias "fCreateShellLink" (ByVal lpstrFolderName As String, ByVal lpstrLinkName As String, ByVal lpstrLinkPath As String, ByVal lpstrLinkArguments As String, ByVal fPrivate As Long, ByVal sParent As String) As Long
  250.  
  251. Private Declare Function GetLocaleInfoLS Lib "Kernel32" Alias "GetLocaleInfoA" (ByVal Locale As Long, ByVal LCType As Long, lpLCData As LOCALESIGNATURE, ByVal cchData As Long) As Long
  252. Private Declare Function TranslateCharsetInfo Lib "gdi32" (lpSrc As Long, lpcs As CHARSETINFO, ByVal dwFlags As Long) As Long
  253.  
  254. Private Declare Sub CopyMemory Lib "Kernel32" Alias "RtlMoveMemory" (pDest As Any, pSource As Any, ByVal ByteLen As Long)
  255. Private Declare Function WaitForSingleObject Lib "Kernel32" (ByVal hProcess As Long, ByVal dwMilliseconds As Long) As Long
  256. Private Declare Function InputIdle Lib "user32" Alias "WaitForInputIdle" (ByVal hProcess As Long, ByVal dwMilliseconds As Long) As Long
  257. Private Declare Function CreateProcessA Lib "Kernel32" (ByVal lpApplicationName As Long, ByVal lpCommandLine As String, ByVal lpProcessAttributes As Long, ByVal lpThreadAttributes As Long, ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, ByVal lpEnvironment As Long, ByVal lpCurrentDirectory As Long, lpStartupInfo As STARTUPINFO, lpProcessInformation As PROCESS_INFORMATION) As Long
  258. Public Declare Function GetDiskFreeSpace Lib "Kernel32" Alias "GetDiskFreeSpaceA" (ByVal lpRootPathName As String, lpSectorsPerCluster As Long, lpBytesPerSector As Long, lpNumberOfFreeClusters As Long, lpTtoalNumberOfClusters As Long) As Long
  259. Public Declare Function GetFullPathName Lib "Kernel32" Alias "GetFullPathNameA" (ByVal lpFilename As String, ByVal nBufferLength As Long, ByVal lpBuffer As String, ByRef lpFilePart As Long) As Long
  260. Public Declare Function GetPrivateProfileString Lib "Kernel32" Alias "GetPrivateProfileStringA" (ByVal lpApplicationName As String, ByVal lpKeyName As Any, ByVal lpDefault As String, ByVal lpReturnedString As String, ByVal lSize As Long, ByVal lpFilename As String) As Long
  261. Public Declare Function WritePrivateProfileString Lib "Kernel32" Alias "WritePrivateProfileStringA" (ByVal lpApplicationName As Any, ByVal lpKeyName As Any, ByVal lpString As Any, ByVal lplFilename As String) As Long
  262. Public Declare Function GetPrivateProfileSection Lib "Kernel32" Alias "GetPrivateProfileSectionA" (ByVal lpAppName As String, ByVal lpReturnedString As String, ByVal nSize As Long, ByVal lpFilename As String) As Long
  263. Private Declare Function GetWindowsDirectory Lib "Kernel32" Alias "GetWindowsDirectoryA" (ByVal lpBuffer As String, ByVal nSize As Long) As Long
  264. Private Declare Function GetSystemDirectory Lib "Kernel32" Alias "GetSystemDirectoryA" (ByVal lpBuffer As String, ByVal nSize As Long) As Long
  265. Private Declare Function GetDriveType32 Lib "Kernel32" Alias "GetDriveTypeA" (ByVal strWhichDrive As String) As Long
  266. Public Declare Function GetTempPath Lib "Kernel32" Alias "GetTempPathA" (ByVal nBufferLength As Long, ByVal lpBuffer As String) As Long
  267. Public Declare Function SendMessageString Lib "user32" Alias "SendMessageA" (ByVal hWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, ByVal lParam As String) As Long
  268. Public Const LB_FINDSTRINGEXACT = &H1A2
  269. Public Const LB_ERR = (-1)
  270.  
  271. Private Declare Function GetUserDefaultLCID Lib "Kernel32" () As Long
  272. Private Declare Function GetLocaleInfoA Lib "Kernel32" (ByVal lLCID As Long, ByVal lLCTYPE As Long, ByVal strLCData As String, ByVal lDataLen As Long) As Long
  273.  
  274. Public Declare Function VerInstallFile Lib "version.dll" Alias "VerInstallFileA" (ByVal Flags&, ByVal SrcName$, ByVal DestName$, ByVal SrcDir$, ByVal DestDir$, ByVal CurrDir As Any, ByVal TmpName$, lpTmpFileLen&) As Long
  275. Private Declare Function GetFileVersionInfoSize Lib "version.dll" Alias "GetFileVersionInfoSizeA" (ByVal sFile As String, lpLen As Long) As Long
  276. Private Declare Function GetFileVersionInfo Lib "version.dll" Alias "GetFileVersionInfoA" (ByVal sFile As String, ByVal lpIgnored As Long, ByVal lpSize As Long, ByVal lpBuf As Long) As Long
  277. Private Declare Function VerQueryValue Lib "version.dll" Alias "VerQueryValueA" (ByVal lpBuf As Long, ByVal szReceive As String, lpBufPtr As Long, lLen As Long) As Long
  278. Private Declare Function OSGetShortPathName Lib "Kernel32" Alias "GetShortPathNameA" (ByVal lpszLongPath As String, ByVal lpszShortPath As String, ByVal cchBuffer As Long) As Long
  279. Private Declare Function GetVersionEx Lib "Kernel32" Alias "GetVersionExA" (lpVersionInformation As OSVERSIONINFO) As Long
  280.  
  281. ' Reboot system code
  282. Public Const EWX_REBOOT = 2
  283. Public Declare Function ExitWindowsEx Lib "user32" (ByVal uFlags As Long, ByVal dwReserved As Long) As Long
  284.  
  285. 'Public constants used for string replacements
  286. Public Const gstrPIPE1 As String = "|1"
  287. Public Const gstrPIPE2 As String = "|2"
  288.  
  289. '----------------------------------------------------------
  290. ' FUNCTION: GetWinPlatform
  291. ' Get the current windows platform.
  292. ' ---------------------------------------------------------
  293. Public Function GetWinPlatform() As Long
  294.     Dim osvi As OSVERSIONINFO
  295.  
  296.     osvi.dwOSVersionInfoSize = Len(osvi)
  297.     If GetVersionEx(osvi) = 0 Then
  298.         Exit Function
  299.     End If
  300.     GetWinPlatform = osvi.dwPlatformId
  301. End Function
  302.  
  303. '-----------------------------------------------------------
  304. ' SUB: AddDirSep
  305. ' Add a trailing directory path separator (back slash) to the
  306. ' end of a pathname unless one already exists
  307. '
  308. ' IN/OUT: [strPathName] - path to add separator to
  309. '-----------------------------------------------------------
  310. '
  311. Public Sub AddDirSep(strPathName As String)
  312.     strPathName = RTrim$(strPathName)
  313.     If Right$(strPathName, Len(gstrSEP_URLDIR)) <> gstrSEP_URLDIR Then
  314.         If Right$(strPathName, Len(gstrSEP_DIR)) <> gstrSEP_DIR Then
  315.             strPathName = strPathName & gstrSEP_DIR
  316.         End If
  317.     End If
  318. End Sub
  319.  
  320. '-----------------------------------------------------------
  321. ' SUB: RemoveDirSep
  322. ' Removes a trailing directory path separator (back slash)
  323. ' at the end of a pathname if one exists
  324. '
  325. ' IN/OUT: [strPathName] - path to remove separator from
  326. '-----------------------------------------------------------
  327. '
  328. Public Sub RemoveDirSep(strPathName As String)
  329.     Select Case Right$(strPathName, 1)
  330.     Case gstrSEP_DIR, gstrSEP_DIRALT
  331.         strPathName = Left$(strPathName, Len(strPathName) - 1)
  332.     End Select
  333. End Sub
  334.  
  335. '-----------------------------------------------------------
  336. ' FUNCTION: FileExists
  337. ' Determines whether the specified file exists
  338. '
  339. ' IN: [strPathName] - file to check for
  340. '
  341. ' Returns: True if file exists, False otherwise
  342. '-----------------------------------------------------------
  343. '
  344. Public Function FileExists(ByVal strPathName As String) As Boolean
  345.     Dim intFileNum As Integer
  346.  
  347.     On Error Resume Next
  348.  
  349.     '
  350.     ' If the string is quoted, remove the quotes.
  351.     '
  352.     strPathName = strUnQuoteString(strPathName)
  353.     '
  354.     'Remove any trailing directory separator character
  355.     '
  356.     If Right$(strPathName, 1) = gstrSEP_DIR Then
  357.         strPathName = Left$(strPathName, Len(strPathName) - 1)
  358.     End If
  359.  
  360.     '
  361.     'Attempt to open the file, return value of this function is False
  362.     'if an error occurs on open, True otherwise
  363.     '
  364.     intFileNum = FreeFile
  365.     Open strPathName For Input As intFileNum
  366.  
  367.     FileExists = (Err.Number = 0)
  368.  
  369.     Close intFileNum
  370.  
  371.     Err.Clear
  372. End Function
  373.  
  374. '-----------------------------------------------------------
  375. ' FUNCTION: FileInUse
  376. ' Determines whether the specified file is currently in use
  377. '
  378. ' IN: [strPathName] - file to check for
  379. '
  380. ' Returns: True if file exists and is in use, False otherwise
  381. '-----------------------------------------------------------
  382. '
  383. Public Function FileInUse(ByVal strPathName As String) As Boolean
  384.     Dim hFile As Long
  385.     
  386.     On Error Resume Next
  387.     '
  388.     ' If the string is quoted, remove the quotes.
  389.     '
  390.     strPathName = strUnQuoteString(strPathName)
  391.     '
  392.     'Remove any trailing directory separator character
  393.     '
  394.     If Right$(strPathName, 1) = gstrSEP_DIR Then
  395.         strPathName = Left$(strPathName, Len(strPathName) - 1)
  396.     End If
  397.  
  398.     hFile = CreateFile(strPathName, GENERIC_WRITE, 0, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL Or FILE_FLAG_WRITE_THROUGH, 0)
  399.     
  400.     If hFile = INVALID_HANDLE_VALUE Then
  401.         FileInUse = Err.LastDllError = ERROR_SHARING_VIOLATION
  402.     Else
  403.         CloseHandle hFile
  404.     End If
  405.     Err.Clear
  406. End Function
  407.  
  408. '-----------------------------------------------------------
  409. ' FUNCTION: DirExists
  410. '
  411. ' Determines whether the specified directory name exists.
  412. ' This function is used (for example) to determine whether
  413. ' an installation floppy is in the drive by passing in
  414. ' something like 'A:\'.
  415. '
  416. ' IN: [strDirName] - name of directory to check for
  417. '
  418. ' Returns: True if the directory exists, False otherwise
  419. '-----------------------------------------------------------
  420. '
  421. Public Function DirExists(ByVal strDirName As String) As Boolean
  422.     On Error Resume Next
  423.  
  424.     DirExists = (GetAttr(strDirName) And vbDirectory) = vbDirectory
  425.  
  426.     Err.Clear
  427. End Function
  428.  
  429. '-----------------------------------------------------------
  430. ' FUNCTION: GetDriveType
  431. ' Determine whether a disk is fixed, removable, etc. by
  432. ' calling Windows GetDriveType()
  433. '-----------------------------------------------------------
  434. '
  435. Public Function GetDriveType(ByVal intDriveNum As Integer) As Integer
  436.     '
  437.     ' This function expects an integer drive number in Win16 or a string in Win32
  438.     '
  439.     Dim strDriveName As String
  440.  
  441.     strDriveName = Chr$(Asc("A") + intDriveNum) & gstrSEP_DRIVE & gstrSEP_DIR
  442.     GetDriveType = CInt(GetDriveType32(strDriveName))
  443. End Function
  444.  
  445. '-----------------------------------------------------------
  446. ' FUNCTION: ReadProtocols
  447. ' Reads the allowable protocols from the specified file.
  448. '
  449. ' IN: [strInputFilename] - INI filename from which to read the protocols
  450. '     [strINISection] - Name of the INI section
  451. '-----------------------------------------------------------
  452. Public Function ReadProtocols(ByVal strInputFilename As String, ByVal strINISection As String) As Boolean
  453.     Dim intIdx As Integer
  454.     Dim strInfo As String
  455.     Dim intOffset As Integer
  456.  
  457.     Erase gProtocol
  458.     gcProtocols = 0
  459.  
  460.     Do
  461.         intIdx = intIdx + 1
  462.         strInfo = ReadIniFile(strInputFilename, strINISection, gstrINI_PROTOCOL & CStr(intIdx))
  463.         If Len(strInfo) = 0 Then
  464.             ReadProtocols = True
  465.             Exit Function
  466.         End If
  467.         intOffset = InStr(strInfo, gstrCOMMA)
  468.         If intOffset = 0 Then
  469.             'Something is invalid. Exit and return False.
  470.             Exit Function
  471.         End If
  472.         'The "ugly" name will be first on the line
  473.         ReDim Preserve gProtocol(intIdx)
  474.         gcProtocols = intIdx
  475.         gProtocol(intIdx).strName = Left$(strInfo, intOffset - 1)
  476.         
  477.         '... followed by the friendly name
  478.         gProtocol(intIdx).strFriendlyName = Mid$(strInfo, intOffset + 1)
  479.         If (Len(gProtocol(intIdx).strName) = 0) Or (Len(gProtocol(intIdx).strFriendlyName) = 0) Then
  480.             'Something is invalid. Exit and return False.
  481.             Exit Function
  482.         End If
  483.     Loop
  484. End Function
  485.  
  486. '-----------------------------------------------------------
  487. ' FUNCTION: ResolveResString
  488. ' Reads resource and replaces given macros with given values
  489. '
  490. ' Example, given a resource number 14:
  491. '    "Could not read '|1' in drive |2"
  492. '   The call
  493. '     ResolveResString(14, gstrPIPE1, "TXTFILE.TXT", gstrPIPE2, "A:")
  494. '   would return the string
  495. '     "Could not read 'TXTFILE.TXT' in drive A:"
  496. '
  497. ' IN: [resID] - resource identifier
  498. '     [varReplacements] - pairs of macro/replacement value
  499. '-----------------------------------------------------------
  500. '
  501. Public Function ResolveResString(ByVal resID As Integer, ParamArray varReplacements() As Variant) As String
  502.     Dim intMacro As Integer
  503.     Dim strResString As String
  504.     Dim strMacro As String
  505.     Dim strValue As String
  506.  
  507.     strResString = LoadResString(resID)
  508.  
  509.     ' For each macro/value pair passed in...
  510.     For intMacro = LBound(varReplacements) To UBound(varReplacements) - 1 Step 2
  511.         strMacro = varReplacements(intMacro)
  512.         strValue = varReplacements(intMacro + 1)
  513.  
  514.         'Replace all occurrences of strMacro with strValue
  515.         strResString = Replace$(strResString, strMacro, strValue)
  516.     Next intMacro
  517.  
  518.     ResolveResString = strResString
  519. End Function
  520. '-----------------------------------------------------------
  521. ' SUB: GetLicInfoFromVBL
  522. ' Parses a VBL file name and extracts the license key for
  523. ' the registry and license information.
  524. '
  525. ' IN: [strVBLFile] - must be a valid VBL.
  526. '
  527. ' OUT: [strLicKey] - registry key to write license info to.
  528. '                    This key will be added to
  529. '                    HKEY_CLASSES_ROOT\Licenses.  It is a
  530. '                    guid.
  531. ' OUT: [strLicVal] - license information.  Usually in the
  532. '                    form of a string of cryptic characters.
  533. '-----------------------------------------------------------
  534. '
  535. Public Sub GetLicInfoFromVBL(strVBLFile As String, strLicKey As String, strLicVal As String)
  536.     Const strLICKEYBASE = "HKEY_CLASSES_ROOT\Licenses\"
  537.     Dim fn As Integer
  538.     Dim strTemp As String
  539.     Dim posEqual As Integer
  540.     Dim fLicFound As Boolean
  541.     
  542.     fn = FreeFile
  543.     Open strVBLFile For Input Access Read Lock Read Write As #fn
  544.     '
  545.     ' Read through the file until we find a line that starts with strLICKEYBASE
  546.     '
  547.     Do Until EOF(fn)
  548.         Line Input #fn, strTemp
  549.         strTemp = Trim$(strTemp)
  550.         ' Avoid Option Compare Text and use explicit UCase comparisons because there
  551.         ' is a Unicode character (&H818F) which is equal to a path separator when
  552.         ' using Option Compare Text.
  553.         If InStr(1, UCase$(strTemp), UCase$(strLICKEYBASE)) = 1 Then
  554.             '
  555.             ' We've got the line we want.
  556.             '
  557.             fLicFound = True
  558.             Exit Do
  559.         End If
  560.     Loop
  561.  
  562.     Close fn
  563.  
  564.     If fLicFound Then
  565.         '
  566.         ' Parse the data on this line to split out the
  567.         ' key and the license info.  The line should be
  568.         ' the form of:
  569.         ' "HKEY_CLASSES_ROOT\Licenses\<lickey> = <licval>"
  570.         '
  571.         ' First, get rid of the HKEY_CLASSES_ROOT\Licenses\ part.
  572.         strTemp = Mid$(strTemp, Len(strLICKEYBASE) + 1)
  573.         
  574.         posEqual = InStr(strTemp, gstrASSIGN)
  575.         If posEqual > 0 Then
  576.             strLicKey = Trim$(Left$(strTemp, posEqual - 1))
  577.             strLicVal = Trim$(Mid$(strTemp, posEqual + 1))
  578.         End If
  579.     Else
  580.         strLicKey = vbNullString
  581.         strLicVal = vbNullString
  582.     End If
  583. End Sub
  584.  
  585. '-----------------------------------------------------------
  586. ' FUNCTION GetLongPathName
  587. '
  588. ' Retrieve the long pathname version of a path possibly
  589. '   containing short subdirectory and/or file names
  590. '-----------------------------------------------------------
  591. '
  592. Public Function GetLongPathName(ByVal strShortPath As String) As String
  593.     MakeLongPath strShortPath
  594.     GetLongPathName = strShortPath
  595. End Function
  596.  
  597. '-----------------------------------------------------------
  598. ' FUNCTION GetShortPathName
  599. '
  600. ' Retrieve the short pathname version of a path possibly
  601. '   containing long subdirectory and/or file names
  602. '-----------------------------------------------------------
  603. '
  604. Function GetShortPathName(ByVal strLongPath As String) As String
  605.     Const cchBuffer = 300
  606.     Dim strShortPath As String
  607.     Dim lResult As Long
  608.  
  609.     strShortPath = String$(cchBuffer, 0)
  610.     lResult = OSGetShortPathName(strLongPath, strShortPath, cchBuffer)
  611.     If lResult = 0 Then
  612.         'Just use the long name as this is usually good enough
  613.         GetShortPathName = strLongPath
  614.     Else
  615.         GetShortPathName = StringFromBuffer(strShortPath)
  616.     End If
  617. End Function
  618.  
  619. '-----------------------------------------------------------
  620. ' FUNCTION: GetDefMsgBoxButton
  621. ' Decode the flags passed to the MsgBox function to
  622. ' determine what the default button is.  Use this
  623. ' for silent installs.
  624. '
  625. ' IN: [intFlags] - Flags passed to MsgBox
  626. '
  627. ' Returns: VB defined number for button
  628. '               vbOK        1   OK button pressed.
  629. '               vbCancel    2   Cancel button pressed.
  630. '               vbAbort     3   Abort button pressed.
  631. '               vbRetry     4   Retry button pressed.
  632. '               vbIgnore    5   Ignore button pressed.
  633. '               vbYes       6   Yes button pressed.
  634. '               vbNo        7   No button pressed.
  635. '-----------------------------------------------------------
  636. '
  637. Private Function GetDefMsgBoxButton(intFlags) As Integer
  638.     '
  639.     ' First determine the ordinal of the default
  640.     ' button on the message box.
  641.     '
  642.     Dim intButtonNum As Integer
  643.  
  644.     If (intFlags And vbDefaultButton2) = vbDefaultButton2 Then
  645.         intButtonNum = 2
  646.     ElseIf (intFlags And vbDefaultButton3) = vbDefaultButton3 Then
  647.         intButtonNum = 3
  648.     Else
  649.         intButtonNum = 1
  650.     End If
  651.     '
  652.     ' Now determine the type of message box we are dealing
  653.     ' with and return the default button.
  654.     '
  655.     If (intFlags And vbRetryCancel) = vbRetryCancel Then
  656.         If intButtonNum = 1 Then
  657.             GetDefMsgBoxButton = vbRetry
  658.         Else
  659.             GetDefMsgBoxButton = vbCancel
  660.         End If
  661.     ElseIf (intFlags And vbYesNoCancel) = vbYesNoCancel Then
  662.         Select Case intButtonNum
  663.             Case 1
  664.                 GetDefMsgBoxButton = vbYes
  665.             Case 2
  666.                 GetDefMsgBoxButton = vbNo
  667.             Case 3
  668.                 GetDefMsgBoxButton = vbCancel
  669.         End Select
  670.     ElseIf (intFlags And vbOKCancel) = vbOKCancel Then
  671.         If intButtonNum = 1 Then
  672.             GetDefMsgBoxButton = vbOK
  673.         Else
  674.             GetDefMsgBoxButton = vbCancel
  675.         End If
  676.     ElseIf (intFlags And vbAbortRetryIgnore) = vbAbortRetryIgnore Then
  677.         Select Case intButtonNum
  678.             Case 1
  679.                 GetDefMsgBoxButton = vbAbort
  680.             Case 2
  681.                 GetDefMsgBoxButton = vbRetry
  682.             Case 3
  683.                 GetDefMsgBoxButton = vbIgnore
  684.         End Select
  685.     ElseIf (intFlags And vbYesNo) = vbYesNo Then
  686.         If intButtonNum = 1 Then
  687.             GetDefMsgBoxButton = vbYes
  688.         Else
  689.             GetDefMsgBoxButton = vbNo
  690.         End If
  691.     Else
  692.         GetDefMsgBoxButton = vbOK
  693.     End If
  694. End Function
  695. '-----------------------------------------------------------
  696. ' FUNCTION: GetDiskSpaceFree
  697. ' Get the amount of free disk space for the specified drive
  698. '
  699. ' IN: [strDrive] - drive to check space for
  700. '
  701. ' Returns: Amount of free disk space, or -1 if an error occurs
  702. '-----------------------------------------------------------
  703. '
  704. Public Function GetDiskSpaceFree(ByVal strDrive As String) As Long
  705.     Dim lRet As Long
  706.     Dim lBytes As Long
  707.     Dim lSect As Long
  708.     Dim lClust As Long
  709.     Dim lTot As Long
  710.  
  711.     On Error Resume Next
  712.  
  713.     'Start by assuming failure
  714.     GetDiskSpaceFree = -1
  715.     If GetDrive(strDrive, strDrive) Then
  716.         lRet = GetDiskFreeSpace(strDrive, lSect, lBytes, lClust, lTot)
  717.         If Err.Number = 0 Then
  718.             If lRet <> 0 Then
  719.                 'Return the total number of bytes
  720.                 GetDiskSpaceFree = lBytes * lSect * lClust
  721.                 If Err.Number <> 0 Then
  722.                     'Assume we overflowed when multiplying. Return max long.
  723.                     GetDiskSpaceFree = &H7FFFFFFF
  724.                 End If
  725.             End If
  726.         End If
  727.     End If
  728.  
  729.     Err.Clear
  730. End Function
  731.  
  732. '-----------------------------------------------------------
  733. ' FUNCTION: GetWindowsSysDir
  734. '
  735. ' Calls the windows API to get the windows\SYSTEM directory
  736. ' and ensures that a trailing dir separator is present
  737. '
  738. ' Returns: The windows\SYSTEM directory
  739. '-----------------------------------------------------------
  740. '
  741. Public Function GetWindowsSysDir() As String
  742.     Dim strBuf As String
  743.  
  744.     strBuf = Space$(gintMAX_SIZE)
  745.     '
  746.     'Get the system directory and then trim the buffer to the exact length
  747.     'returned and add a dir sep (backslash) if the API didn't return one
  748.     '
  749.     If GetSystemDirectory(strBuf, gintMAX_SIZE) Then
  750.         GetWindowsSysDir = StringFromBuffer(strBuf)
  751.         AddDirSep GetWindowsSysDir
  752.     End If
  753. End Function
  754.  
  755. '-----------------------------------------------------------
  756. ' FUNCTION: IsWindows95
  757. '
  758. ' Returns true if this program is running under Windows 95
  759. '   or successor
  760. '-----------------------------------------------------------
  761. '
  762. Public Function IsWindows95() As Boolean
  763.     Const dwMask95 = &H1&
  764.     IsWindows95 = (GetWinPlatform() And dwMask95)
  765. End Function
  766.  
  767. '-----------------------------------------------------------
  768. ' FUNCTION: IsWindowsNT
  769. '
  770. ' Returns true if this program is running under Windows NT
  771. '-----------------------------------------------------------
  772. '
  773. Public Function IsWindowsNT() As Boolean
  774.     Const dwMaskNT = &H2&
  775.     IsWindowsNT = (GetWinPlatform() And dwMaskNT)
  776. End Function
  777.  
  778. '-----------------------------------------------------------
  779. ' FUNCTION: IsUNCName
  780. '
  781. ' Determines whether the pathname specified is a UNC name.
  782. ' UNC (Universal Naming Convention) names are typically
  783. ' used to specify machine resources, such as remote network
  784. ' shares, named pipes, etc.  An example of a UNC name is
  785. ' "\\SERVER\SHARE\FILENAME.EXT".
  786. '
  787. ' IN: [strPathName] - pathname to check
  788. '
  789. ' Returns: True if pathname is a UNC name, False otherwise
  790. '-----------------------------------------------------------
  791. '
  792. Public Function IsUNCName(ByVal strPathName As String) As Integer
  793.     Const strUNCNAME$ = "\\//\"        'so can check for \\, //, \/, /\
  794.  
  795.     IsUNCName = ((InStr(strUNCNAME, Left$(strPathName, 2)) > 0) And _
  796.                  (Len(strPathName) > 1))
  797. End Function
  798. '-----------------------------------------------------------
  799. ' FUNCTION: LogSilentMsg
  800. '
  801. ' If this is a silent install, this routine writes
  802. ' a message to the gstrSilentLog file.
  803. '
  804. ' IN: [strMsg] - The message
  805. '
  806. ' Normally, this routine is called inlieu of displaying
  807. ' a MsgBox and strMsg is the same message that would
  808. ' have appeared in the MsgBox
  809.  
  810. '-----------------------------------------------------------
  811. '
  812. Public Sub LogSilentMsg(strMsg As String)
  813.     Dim fn As Integer
  814.  
  815.     If Not gfSilent Then Exit Sub
  816.  
  817.     On Error Resume Next
  818.  
  819.     fn = FreeFile
  820.     Open gstrSilentLog For Append As fn
  821.     Print #fn, strMsg
  822.     Close fn
  823.  
  824.     Err.Clear
  825. End Sub
  826. '-----------------------------------------------------------
  827. ' FUNCTION: LogSMSMsg
  828. '
  829. ' If this is a SMS install, this routine appends
  830. ' a message to the gstrSMSDescription string.  This
  831. ' string will later be written to the SMS status
  832. ' file (*.MIF) when the installation completes (success
  833. ' or failure).
  834. '
  835. ' Note that if gfSMS = False, not message will be logged.
  836. ' Therefore, to prevent some messages from being logged
  837. ' (e.g., confirmation only messages), temporarily set
  838. ' gfSMS = False.
  839. '
  840. ' IN: [strMsg] - The message
  841. '
  842. ' Normally, this routine is called inlieu of displaying
  843. ' a MsgBox and strMsg is the same message that would
  844. ' have appeared in the MsgBox
  845. '-----------------------------------------------------------
  846. '
  847. #If SMS Then
  848. Public Sub LogSMSMsg(strMsg As String)
  849.     If gfDontLogSMS Then
  850.         ' We were told to ignore this message. Do so, but clear the ignore
  851.         ' flag.
  852.         gfDontLogSMS = False
  853.     Else
  854.         If Not gfSMS Then Exit Sub
  855.         '
  856.         ' Append the message.  Note that the total
  857.         ' length cannot be more than 255 characters, so
  858.         ' truncate anything after that.
  859.         '
  860.         gstrSMSDescription = Left$(gstrSMSDescription & strMsg, MAX_SMS_DESCRIP)
  861.     End If
  862. End Sub
  863. #End If
  864. '-----------------------------------------------------------
  865. ' FUNCTION: MakePathAux
  866. '
  867. ' Creates the specified directory path.
  868. '
  869. ' IN: [strDirName] - name of the dir path to make
  870. '
  871. ' Returns: True if successful, False if error.
  872. '-----------------------------------------------------------
  873. '
  874. Public Function MakePathAux(ByVal strDirName As String) As Boolean
  875.     Dim strPath As String
  876.     Dim intOffset As Integer
  877.     Dim intAnchor As Integer
  878.     Dim strOldPath As String
  879.  
  880.     On Error Resume Next
  881.  
  882.     '
  883.     'Add trailing backslash
  884.     '
  885.     AddDirSep strDirName
  886.  
  887.     strOldPath = CurDir$
  888.     '
  889.     'Loop and make each subdir of the path separately.
  890.     '
  891.     'Start with the first backslash after the drive portion. This is the last
  892.     '   character of the output from GetDrive.
  893.     GetDrive strDirName, strPath
  894.     intAnchor = Len(strPath)
  895.     Do
  896.         intOffset = InStr(intAnchor + 1, strDirName, gstrSEP_DIR)
  897.         intAnchor = intOffset
  898.  
  899.         If intAnchor > 0 Then
  900.             strPath = Left$(strDirName, intOffset - 1)
  901.             ' Determine if this directory already exists
  902.             If Not DirExists(strPath) Then
  903.                 ' We must create this directory
  904.                 Err.Clear
  905. #If LOGGING Then
  906.                 NewAction gstrKEY_CREATEDIR, gstrQUOTE & strPath & gstrQUOTE
  907. #End If
  908.                 MkDir strPath
  909. #If LOGGING Then
  910.                 If Err.Number <> 0 Then
  911.                     LogError ResolveResString(resMAKEDIR, gstrPIPE1, strPath)
  912.                     AbortAction
  913.                     GoTo Done
  914.                 Else
  915.                     CommitAction
  916.                 End If
  917. #End If
  918.             End If
  919.         End If
  920.     Loop Until intAnchor = 0
  921.  
  922.     MakePathAux = True
  923. Done:
  924.     Err.Clear
  925. End Function
  926.  
  927. '-----------------------------------------------------------
  928. ' FUNCTION: MsgError
  929. '
  930. ' Forces mouse pointer to default, calls VB's MsgBox
  931. ' function, and logs this error and (32-bit only)
  932. ' writes the message and the user's response to the
  933. ' logfile (32-bit only)
  934. '
  935. ' IN: [strMsg] - message to display
  936. '     [intFlags] - MsgBox function type flags
  937. '     [strCaption] - caption to use for message box
  938. '     [intLogType] (optional) - The type of logfile entry to make.
  939. '                   By default, creates an error entry.  Use
  940. '                   the MsgWarning() function to create a warning.
  941. '                   Valid types as MSGERR_ERROR and MSGERR_WARNING
  942. '
  943. ' Returns: Result of MsgBox function
  944. '-----------------------------------------------------------
  945. '
  946. Public Function MsgError(ByVal strMsg As String, ByVal intFlags As Integer, ByVal strCaption As String, Optional ByVal intLogType As Integer = MSGERR_ERROR) As Integer
  947.     Dim iRet As Integer
  948.     Dim strID As String
  949.     Dim strLogMsg As String
  950.  
  951.     iRet = MsgFunc(strMsg, intFlags, strCaption)
  952.     MsgError = iRet
  953. #If LOGGING Then
  954.     ' We need to log this error and decode the user's response.
  955.     Select Case iRet
  956.         Case vbOK
  957.             strID = ResolveResString(resLOG_vbok)
  958.         Case vbCancel
  959.             strID = ResolveResString(resLOG_vbCancel)
  960.         Case vbAbort
  961.             strID = ResolveResString(resLOG_vbabort)
  962.         Case vbRetry
  963.             strID = ResolveResString(resLOG_vbretry)
  964.         Case vbIgnore
  965.             strID = ResolveResString(resLOG_vbignore)
  966.         Case vbYes
  967.             strID = ResolveResString(resLOG_vbyes)
  968.         Case vbNo
  969.             strID = ResolveResString(resLOG_vbno)
  970.         Case Else
  971.             strID = ResolveResString(resLOG_IDUNKNOWN)
  972.     End Select
  973.  
  974.     strLogMsg = strMsg & vbLf & "(" & ResolveResString(resLOG_USERRESPONDEDWITH, gstrPIPE1, strID) & ")"
  975.     On Error Resume Next
  976.     Select Case intLogType
  977.         Case MSGERR_WARNING
  978.             LogWarning strLogMsg
  979.         Case MSGERR_ERROR
  980.             LogError strLogMsg
  981.         Case Else
  982.             LogError strLogMsg
  983.     End Select
  984.     Err.Clear
  985. #End If
  986. End Function
  987.  
  988. '-----------------------------------------------------------
  989. ' FUNCTION: MsgFunc
  990. '
  991. ' Forces mouse pointer to default and calls VB's MsgBox
  992. ' function.  See also MsgError.
  993. '
  994. ' IN: [strMsg] - message to display
  995. '     [intFlags] - MsgBox function type flags
  996. '     [strCaption] - caption to use for message box
  997. ' Returns: Result of MsgBox function
  998. '-----------------------------------------------------------
  999. '
  1000. Public Function MsgFunc(ByVal strMsg As String, ByVal intFlags As Integer, ByVal strCaption As String) As Integer
  1001.     Dim intOldPointer As Integer
  1002.  
  1003.     If gfNoUserInput Then
  1004.         MsgFunc = GetDefMsgBoxButton(intFlags)
  1005.         If gfSilent Then
  1006.             LogSilentMsg strMsg
  1007.         End If
  1008. #If SMS Then
  1009.         If gfSMS Then
  1010.             LogSMSMsg strMsg
  1011.         End If
  1012. #End If
  1013.     Else
  1014.         intOldPointer = Screen.MousePointer
  1015.         Screen.MousePointer = vbDefault
  1016.         MsgFunc = MsgBox(strMsg, intFlags, strCaption)
  1017.         Screen.MousePointer = intOldPointer
  1018.     End If
  1019. End Function
  1020.  
  1021. '-----------------------------------------------------------
  1022. ' FUNCTION: MsgWarning
  1023. '
  1024. ' Forces mouse pointer to default, calls VB's MsgBox
  1025. ' function, and logs this error and (32-bit only)
  1026. ' writes the message and the user's response to the
  1027. ' logfile (32-bit only)
  1028. '
  1029. ' IN: [strMsg] - message to display
  1030. '     [intFlags] - MsgBox function type flags
  1031. '     [strCaption] - caption to use for message box
  1032. '
  1033. ' Returns: Result of MsgBox function
  1034. '-----------------------------------------------------------
  1035. '
  1036. Public Function MsgWarning(ByVal strMsg As String, ByVal intFlags As Integer, ByVal strCaption As String) As Integer
  1037.     MsgWarning = MsgError(strMsg, intFlags, strCaption, MSGERR_WARNING)
  1038. End Function
  1039.  
  1040. '-----------------------------------------------------------
  1041. ' SUB: SetFormFont
  1042. '
  1043. ' Walks through all controls on specified form and
  1044. ' sets Font a font chosen according to the system locale
  1045. '
  1046. ' IN: [frm] - Form whose control fonts need to be set.
  1047. '-----------------------------------------------------------
  1048. '
  1049. Public Sub SetFormFont(frm As Form)
  1050.     Dim ctl As Control
  1051.     Dim fntSize As Integer
  1052.     Dim fntName As String
  1053.     Dim fntCharset As Integer
  1054.     Dim oFont As StdFont
  1055.     
  1056.     ' some controls may fail, so we will do a resume next...
  1057.     '
  1058.     On Error Resume Next
  1059.     
  1060.     ' get the font name, size, and charset
  1061.     '
  1062.     GetFontInfo fntName, fntSize, fntCharset
  1063.  
  1064.     'Create a new font object
  1065.     Set oFont = New StdFont
  1066.     With oFont
  1067.         .Name = fntName
  1068.         .Size = fntSize
  1069.         .Charset = fntCharset
  1070.     End With
  1071.     ' Set the form's font
  1072.     Set frm.Font = oFont
  1073.     '
  1074.     ' loop through each control and try to set its font property
  1075.     ' this may fail, but our error handling is shut off
  1076.     '
  1077.     For Each ctl In frm.Controls
  1078.         Set ctl.Font = oFont
  1079.     Next
  1080.     '
  1081.     ' get out, reset error handling
  1082.     '
  1083.     Err.Clear
  1084. End Sub
  1085.  
  1086. '-----------------------------------------------------------
  1087. ' SUB:  GetFontInfo
  1088. '
  1089. ' Gets the best font to use according the current system's
  1090. ' locale.
  1091. '
  1092. ' OUT:  [sFont] - name of font
  1093. '       [nFont] - size of font
  1094. '       [nCharset] - character set of font to use
  1095. '-----------------------------------------------------------
  1096. Private Sub GetFontInfo(sFont As String, nFont As Integer, nCharSet As Integer)
  1097.     Dim LCID    As Integer
  1098.     Dim PLangId As Integer
  1099.     Dim SLangId As Integer
  1100.  
  1101.     ' if font is set, used the cached values
  1102.     If Len(msFont) > 0 Then
  1103.         sFont = msFont
  1104.         nFont = mnFont
  1105.         nCharSet = mnCharset
  1106.         Exit Sub
  1107.     End If
  1108.  
  1109.     ' font hasn't been set yet, need to get it now...
  1110.     LCID = GetUserDefaultLCID                   ' get current LCID
  1111.     PLangId = PRIMARYLANGID(LCID)               ' get LCID's Primary language id
  1112.     SLangId = SUBLANGID(LCID)                   ' get LCID's Sub language id
  1113.  
  1114.     Select Case PLangId                         ' determine primary language id
  1115.     Case LANG_CHINESE
  1116.         If (SLangId = SUBLANG_CHINESE_TRADITIONAL) Then
  1117.             sFont = ChrW$(&H65B0) & ChrW$(&H7D30) & ChrW$(&H660E) & ChrW$(&H9AD4)   ' New Ming-Li
  1118.             nFont = 9
  1119.             nCharSet = CHARSET_CHINESEBIG5
  1120.         ElseIf (SLangId = SUBLANG_CHINESE_SIMPLIFIED) Then
  1121.             sFont = ChrW$(&H5B8B) & ChrW$(&H4F53)
  1122.             nFont = 9
  1123.             nCharSet = CHARSET_CHINESESIMPLIFIED
  1124.         End If
  1125.     Case LANG_JAPANESE
  1126.         sFont = ChrW$(&HFF2D) & ChrW$(&HFF33) & ChrW$(&H20) & ChrW$(&HFF30) & _
  1127.                 ChrW$(&H30B4) & ChrW$(&H30B7) & ChrW$(&H30C3) & ChrW$(&H30AF)
  1128.         nFont = 9
  1129.         nCharSet = CHARSET_SHIFTJIS
  1130.     Case LANG_KOREAN
  1131.         If (SLangId = SUBLANG_KOREAN) Then
  1132.             sFont = ChrW$(&HAD74) & ChrW$(&HB9BC)
  1133.         ElseIf (SLangId = SUBLANG_KOREAN_JOHAB) Then
  1134.             sFont = ChrW$(&HAD74) & ChrW$(&HB9BC)
  1135.         End If
  1136.         nFont = 9
  1137.         nCharSet = CHARSET_HANGEUL
  1138.     Case Else
  1139.         sFont = "Tahoma"
  1140.         If Not IsFontSupported(sFont) Then
  1141.             'Tahoma is not on this machine.  This condition is very probably since
  1142.             'this is a setup program that may be run on a clean machine
  1143.             'Try Arial
  1144.             sFont = "Arial"
  1145.             If Not IsFontSupported(sFont) Then
  1146.                 'Arial isn't even on the machine.  This is an unusual situation that
  1147.                 'is caused by deliberate removal
  1148.                 'Try system
  1149.                 sFont = "System"
  1150.                 'If system isn't supported, allow the default font to be used
  1151.                 IsFontSupported sFont
  1152.                 'If "System" is not supported, "IsFontSupported" will have
  1153.                 'output the default font in sFont
  1154.             End If
  1155.         End If
  1156.         nFont = 8
  1157.         ' set the charset for the users default system Locale
  1158.         nCharSet = GetUserCharset
  1159.     End Select
  1160.     msFont = sFont
  1161.     mnFont = nFont
  1162.     mnCharset = nCharSet
  1163. '-------------------------------------------------------
  1164. End Sub
  1165. '-------------------------------------------------------
  1166.  
  1167. '------------------------------------------------------------
  1168. '- Language Functions...
  1169. '------------------------------------------------------------
  1170. Private Function PRIMARYLANGID(ByVal LCID As Integer) As Integer
  1171.     PRIMARYLANGID = (LCID And &H3FF)
  1172. End Function
  1173. Private Function SUBLANGID(ByVal LCID As Integer) As Integer
  1174.     SUBLANGID = (LCID / (2 ^ 10))
  1175. End Function
  1176.  
  1177. '-----------------------------------------------------------
  1178. ' Function: GetUserCharset
  1179. '
  1180. ' Get's the default user character set
  1181. '
  1182. ' OS: Win 95 & NT 4 or newer
  1183. '-----------------------------------------------------------
  1184. Private Function GetUserCharset() As Integer
  1185.     Dim ls  As LOCALESIGNATURE                              ' local signature struct.
  1186.     Dim ci  As CHARSETINFO                                  ' character set info struct.
  1187.     Dim rc  As Long                                         ' return code
  1188.  
  1189.     ' get locale signature based on the USER's Default LCID.
  1190.     rc = GetLocaleInfoLS(GetUserDefaultLCID, LOCALE_FONTSIGNATURE, ls, Len(ls))
  1191.     If (rc > 0) Then                                        ' if success
  1192.         ls.lsCsbDefault(1) = 0                              ' zero out bits
  1193.  
  1194.         ' translate charset info from locale fontsignature.
  1195.         rc = TranslateCharsetInfo(ls.lsCsbDefault(0), ci, TCI_SRCFONTSIG)
  1196.         If rc <> 0 Then GetUserCharset = ci.ciCharset       ' return charset
  1197.     End If
  1198. End Function
  1199.  
  1200. '-----------------------------------------------------------
  1201. ' Function: IsFontSupported
  1202. '
  1203. ' Validates a font name to make sure it is supported by
  1204. ' on the current system.
  1205. '
  1206. ' IN/OUT: [sFontName] - name of font to check, will also]
  1207. '         be set to the default font name if the provided
  1208. '         one is not supported.
  1209. '-----------------------------------------------------------
  1210. Private Function IsFontSupported(sFontName As String) As Boolean
  1211.     Dim oFont As StdFont
  1212.  
  1213.     On Error Resume Next
  1214.  
  1215.     Set oFont = New StdFont
  1216.     oFont.Name = sFontName
  1217.     ' Check to see whether the font name passed in was valid by seeing
  1218.     ' if the property got set.
  1219.     IsFontSupported = (UCase$(oFont.Name) = UCase$(sFontName))
  1220.     ' Whatever happens, return a valid font name in this ByRef parameter.
  1221.     sFontName = oFont.Name
  1222.  
  1223.     Err.Clear
  1224. End Function
  1225.  
  1226. '-----------------------------------------------------------
  1227. ' SUB: SetMousePtr
  1228. '
  1229. ' Provides a way to set the mouse pointer only when the
  1230. ' pointer state changes.  For every HOURGLASS call, there
  1231. ' should be a corresponding DEFAULT call.  Other types of
  1232. ' mouse pointers are set explicitly.
  1233. '
  1234. ' IN: [intMousePtr] - type of mouse pointer desired
  1235. '-----------------------------------------------------------
  1236. '
  1237. Public Sub SetMousePtr(intMousePtr As Integer)
  1238.     Static intPtrState As Integer
  1239.  
  1240.     Select Case intMousePtr
  1241.         Case vbHourglass
  1242.             intPtrState = intPtrState + 1
  1243.         Case vbDefault
  1244.             intPtrState = intPtrState - 1
  1245.             If intPtrState < 0 Then
  1246.                 intPtrState = 0
  1247.             End If
  1248.         Case Else
  1249.             Screen.MousePointer = intMousePtr
  1250.             Exit Sub
  1251.     End Select
  1252.  
  1253.     If intPtrState > 0 Then
  1254.         Screen.MousePointer = vbHourglass
  1255.     Else
  1256.         Screen.MousePointer = vbDefault
  1257.     End If
  1258. End Sub
  1259.  
  1260. '-----------------------------------------------------------
  1261. ' FUNCTION: GetFileVerStruct
  1262. '
  1263. ' Gets the file version information into a VERINFO TYPE
  1264. ' variable
  1265. '
  1266. ' IN: [strFilename] - name of file to get version info for
  1267. '     [fIsRemoteServerSupportFile] - whether or not this file is
  1268. '          a remote ActiveX component support file (.VBR)
  1269. '          (Enterprise edition only).  If missing, False is assumed.
  1270. ' OUT: [sVerInfo] - VERINFO Type to fill with version info
  1271. '
  1272. ' Returns: True if version info found, False otherwise
  1273. '-----------------------------------------------------------
  1274. '
  1275. Public Function GetFileVerStruct(ByVal sFile As String, sVer As VERINFO, Optional ByVal fIsRemoteServerSupportFile As Boolean = False) As Boolean
  1276.     Const sEXE As String = "\"
  1277.     Dim lVerSize As Long
  1278.     Dim lTemp As Long
  1279.     Dim lRet As Long
  1280.     Dim bInfo() As Byte
  1281.     Dim lpBuffer As Long
  1282.     Dim fFoundVer As Boolean
  1283.  
  1284.     If fIsRemoteServerSupportFile Then
  1285.         GetFileVerStruct = GetRemoteSupportFileVerStruct(sFile, sVer)
  1286.         fFoundVer = True
  1287.     Else
  1288.         '
  1289.         'Get the size of the file version info, allocate a buffer for it, and get the
  1290.         'version info.  Next, we query the Fixed file info portion, where the internal
  1291.         'file version used by the Windows VerInstallFile API is kept.  We then copy
  1292.         'the fixed file info into a VERINFO structure.
  1293.         '
  1294.         lVerSize = GetFileVersionInfoSize(sFile, lTemp)
  1295.         ReDim bInfo(lVerSize)
  1296.         If lVerSize > 0 Then
  1297.             lRet = GetFileVersionInfo(sFile, lTemp, lVerSize, VarPtr(bInfo(0)))
  1298.             If lRet <> 0 Then
  1299.                 lRet = VerQueryValue(VarPtr(bInfo(0)), sEXE, lpBuffer, lVerSize)
  1300.                 If lRet <> 0 Then
  1301.                     CopyMemory sVer, ByVal lpBuffer, lVerSize
  1302.                     fFoundVer = True
  1303.                     GetFileVerStruct = True
  1304.                 End If
  1305.             End If
  1306.         End If
  1307.     End If
  1308.     If Not fFoundVer Then
  1309.         '
  1310.         ' We were unsuccessful in finding the version info from the file.
  1311.         ' One possibility is that this is a dependency file.
  1312.         '
  1313.         If UCase$(Extension(sFile)) = gstrEXT_DEP Then 'gstrEXT_DEP is uppercase.
  1314.             GetFileVerStruct = GetDepFileVerStruct(sFile, sVer)
  1315.         End If
  1316.     End If
  1317. End Function
  1318. '-----------------------------------------------------------
  1319. ' FUNCTION: GetFileDescription
  1320. '
  1321. ' Gets the file description information.
  1322. '
  1323. ' IN: [strFilename] - name of file to get description of.
  1324. '
  1325. ' Returns: Description (vbNullString if not found)
  1326. '-----------------------------------------------------------
  1327. '
  1328. Public Function GetFileDescription(ByVal sFile As String) As String
  1329.     Const sEXE As String = "\FileDescription"
  1330.     Dim lVerSize As Long
  1331.     Dim lTemp As Long
  1332.     Dim lRet As Long
  1333.     Dim bInfo() As Byte
  1334.     Dim lpBuffer As Long
  1335.     Dim sDesc As String
  1336.     Dim sKEY As String
  1337.     '
  1338.     'Get the size of the file version info, allocate a buffer for it, and get the
  1339.     'version info.  Next, we query the Fixed file info portion, where the internal
  1340.     'file version used by the Windows VerInstallFile API is kept.  We then copy
  1341.     'the info into a string.
  1342.     '
  1343.     lVerSize = GetFileVersionInfoSize(sFile, lTemp)
  1344.     ReDim bInfo(lVerSize)
  1345.     If lVerSize > 0 Then
  1346.         lRet = GetFileVersionInfo(sFile, lTemp, lVerSize, VarPtr(bInfo(0)))
  1347.         If lRet <> 0 Then
  1348.             sKEY = GetNLSKey(bInfo)
  1349.             lRet = VerQueryValue(VarPtr(bInfo(0)), sKEY & sEXE, lpBuffer, lVerSize)
  1350.             If lRet <> 0 Then
  1351.                 sDesc = Space$(lVerSize)
  1352.                 lstrcpyn sDesc, lpBuffer, lVerSize
  1353.                 GetFileDescription = sDesc
  1354.             End If
  1355.         End If
  1356.     End If
  1357. End Function
  1358. Private Function GetNLSKey(byteVerData() As Byte) As String
  1359.     Const strTRANSLATION$ = "\VarFileInfo\Translation"
  1360.     Const strSTRINGFILEINFO$ = "\StringFileInfo\"
  1361.     Const strDEFAULTNLSKEY$ = "040904E4"
  1362.     Const LOCALE_IDEFAULTLANGUAGE& = &H9&
  1363.     Const LOCALE_IDEFAULTCODEPAGE& = &HB&
  1364.  
  1365.     Static strLANGCP As String
  1366.  
  1367.     Dim lpBufPtr As Long
  1368.     Dim strNLSKey As String
  1369.     Dim fGotNLSKey As Integer
  1370.     Dim intOffset As Integer
  1371.     Dim lVerSize As Long
  1372.     Dim lTmp As Long
  1373.     Dim lBufLen As Long
  1374.     Dim lLCID As Long
  1375.     Dim strTmp As String
  1376.  
  1377.     On Error GoTo GNLSKCleanup
  1378.  
  1379.     If VerQueryValue(VarPtr(byteVerData(0)), strTRANSLATION, lpBufPtr, lVerSize) <> 0 Then ' (Pass byteVerData array via reference to first element)
  1380.         If Len(strLANGCP) = 0 Then
  1381.             lLCID = GetUserDefaultLCID()
  1382.             If lLCID > 0 Then
  1383.                 strTmp = Space$(8)
  1384.  
  1385.                 GetLocaleInfoA lLCID, LOCALE_IDEFAULTCODEPAGE, strTmp, 8
  1386.                 strLANGCP = StringFromBuffer(strTmp)
  1387.                 Do While Len(strLANGCP) < 4
  1388.                     strLANGCP = gsZERO & strLANGCP
  1389.                 Loop
  1390.  
  1391.                 GetLocaleInfoA lLCID, LOCALE_IDEFAULTLANGUAGE, strTmp, 8
  1392.                 strLANGCP = StringFromBuffer(strTmp) & strLANGCP
  1393.                 Do While Len(strLANGCP) < 8
  1394.                     strLANGCP = gsZERO & strLANGCP
  1395.                 Loop
  1396.             End If
  1397.         End If
  1398.  
  1399.         If VerQueryValue(VarPtr(byteVerData(0)), strLANGCP, lTmp, lBufLen) <> 0 Then
  1400.             strNLSKey = strLANGCP
  1401.         Else
  1402.             For intOffset = 0 To lVerSize - 1 Step 4
  1403.                 CopyMemory lTmp, ByVal lpBufPtr + intOffset, 4
  1404.                 strTmp = Hex$(lTmp)
  1405.                 Do While Len(strTmp) < 8
  1406.                     strTmp = gsZERO & strTmp
  1407.                 Loop
  1408.  
  1409.                 strNLSKey = strSTRINGFILEINFO & Right$(strTmp, 4) & Left$(strTmp, 4)
  1410.  
  1411.                 If VerQueryValue(VarPtr(byteVerData(0)), strNLSKey, lTmp, lBufLen) <> 0 Then
  1412.                     fGotNLSKey = True
  1413.                     Exit For
  1414.                 End If
  1415.             Next
  1416.  
  1417.             If Not fGotNLSKey Then
  1418.                 strNLSKey = strSTRINGFILEINFO & strDEFAULTNLSKEY
  1419.                 If VerQueryValue(VarPtr(byteVerData(0)), strNLSKey, lTmp, lBufLen) <> 0 Then
  1420.                     fGotNLSKey = True
  1421.                 End If
  1422.             End If
  1423.         End If
  1424.     End If
  1425.  
  1426. GNLSKCleanup:
  1427.     If fGotNLSKey Then
  1428.         GetNLSKey = strNLSKey
  1429.     End If
  1430. End Function
  1431. '-----------------------------------------------------------
  1432. ' FUNCTION: GetDepFileVerStruct
  1433. '
  1434. ' Gets the file version information from a dependency
  1435. ' file (*.dep).  Such files do not have a Windows version
  1436. ' stamp, but they do have an internal version stamp that
  1437. ' we can look for.
  1438. '
  1439. ' IN: [strFilename] - name of dep file to get version info for
  1440. ' OUT: [sVerInfo] - VERINFO Type to fill with version info
  1441. '
  1442. ' Returns: True if version info found, False otherwise
  1443. '-----------------------------------------------------------
  1444. '
  1445. Private Function GetDepFileVerStruct(ByVal strFilename As String, sVerInfo As VERINFO) As Boolean
  1446.     Const strVersionKey = "Version="
  1447.     Const strVersionKeyUCase = "VERSION="
  1448.     Dim cchVersionKey As Integer
  1449.     Dim iFile As Integer
  1450.     Dim strLine As String
  1451.     Dim strVersion As String
  1452.  
  1453.     cchVersionKey = Len(strVersionKey)
  1454.     sVerInfo.FileVerPart1 = gintNOVERINFO
  1455.  
  1456.     On Error GoTo Failed
  1457.  
  1458.     iFile = FreeFile
  1459.  
  1460.     Open strFilename For Input Access Read Lock Read Write As #iFile
  1461.  
  1462.     ' Loop through each line, looking for the key
  1463.     Do Until EOF(iFile)
  1464.         Line Input #iFile, strLine
  1465.         strLine = UCase$(strLine)
  1466.         If Left$(strLine, cchVersionKey) = strVersionKeyUCase Then
  1467.             ' We've found the version key.  Copy everything after the equals sign
  1468.             strVersion = Mid$(strLine, cchVersionKey + 1)
  1469.  
  1470.             'Parse and store the version information
  1471.             PackVerInfo strVersion, sVerInfo
  1472.  
  1473.             GetDepFileVerStruct = True
  1474.             Exit Do
  1475.         End If
  1476.     Loop
  1477.  
  1478. Failed:
  1479.     Close iFile
  1480. End Function
  1481.  
  1482. '-----------------------------------------------------------
  1483. ' FUNCTION: GetRemoteSupportFileVerStruct
  1484. '
  1485. ' Gets the file version information of a remote ActiveX component
  1486. ' support file into a VERINFO TYPE variable (Enterprise
  1487. ' Edition only).  Such files do not have a Windows version
  1488. ' stamp, but they do have an internal version stamp that
  1489. ' we can look for.
  1490. '
  1491. ' IN: [strFilename] - name of file to get version info for
  1492. ' OUT: [sVerInfo] - VERINFO Type to fill with version info
  1493. '
  1494. ' Returns: True if version info found, False otherwise
  1495. '-----------------------------------------------------------
  1496. '
  1497. Private Function GetRemoteSupportFileVerStruct(ByVal strFilename As String, sVerInfo As VERINFO) As Boolean
  1498.     Const strVersionKey = "Version="
  1499.     Const strVersionKeyUCase = "VERSION="
  1500.     Dim cchVersionKey As Integer
  1501.     Dim iFile As Integer
  1502.     Dim strLine As String
  1503.     Dim strVersion As String
  1504.  
  1505.     cchVersionKey = Len(strVersionKey)
  1506.     sVerInfo.FileVerPart1 = gintNOVERINFO
  1507.     
  1508.     On Error GoTo Failed
  1509.     
  1510.     iFile = FreeFile
  1511.  
  1512.     Open strFilename For Input Access Read Lock Read Write As #iFile
  1513.  
  1514.     ' Loop through each line, looking for the key
  1515.     Do Until EOF(iFile)
  1516.         Line Input #iFile, strLine
  1517.         strLine = UCase$(strLine)
  1518.         If Left$(strLine, cchVersionKey) = strVersionKeyUCase Then
  1519.             ' We've found the version key.  Copy everything after the equals sign
  1520.             strVersion = Mid$(strLine, cchVersionKey + 1)
  1521.  
  1522.             'Parse and store the version information
  1523.             PackVerInfo strVersion, sVerInfo
  1524.  
  1525.             'Convert the format 1.2.3 from the .VBR into
  1526.             '1.2.0.3, which is really want we want
  1527.             sVerInfo.FileVerPart4 = sVerInfo.FileVerPart3
  1528.             sVerInfo.FileVerPart3 = 0
  1529.  
  1530.             GetRemoteSupportFileVerStruct = True
  1531.             Exit Do
  1532.         End If
  1533.     Loop
  1534.  
  1535. Failed:
  1536.     Close iFile
  1537. End Function
  1538. '-----------------------------------------------------------
  1539. ' FUNCTION: GetWindowsFontDir
  1540. '
  1541. ' Calls the windows API to get the windows font directory
  1542. ' and ensures that a trailing dir separator is present
  1543. '
  1544. ' Returns: The windows font directory
  1545. '-----------------------------------------------------------
  1546. '
  1547. Public Function GetWindowsFontDir() As String
  1548.     Dim oMalloc As IVBMalloc
  1549.     Dim sPath   As String
  1550.     Dim IDL     As Long
  1551.  
  1552.     ' Fill the item id list with the pointer of each folder item, rtns 0 on success
  1553.     If SHGetSpecialFolderLocation(0, sfidFONTS, IDL) = NOERROR Then
  1554.         sPath = String$(gintMAX_PATH_LEN, 0)
  1555.         SHGetPathFromIDListA IDL, sPath
  1556.         SHGetMalloc oMalloc
  1557.         oMalloc.Free IDL
  1558.         GetWindowsFontDir = StringFromBuffer(sPath)
  1559.     End If
  1560.     AddDirSep GetWindowsFontDir
  1561. End Function
  1562.  
  1563. '-----------------------------------------------------------
  1564. ' FUNCTION: GetWindowsDir
  1565. '
  1566. ' Calls the windows API to get the windows directory and
  1567. ' ensures that a trailing dir separator is present
  1568. '
  1569. ' Returns: The windows directory
  1570. '-----------------------------------------------------------
  1571. '
  1572. Public Function GetWindowsDir() As String
  1573.     Dim strBuf As String
  1574.  
  1575.     strBuf = Space$(gintMAX_SIZE)
  1576.     '
  1577.     'Get the windows directory and then trim the buffer to the exact length
  1578.     'returned and add a dir sep (backslash) if the API didn't return one
  1579.     '
  1580.     If GetWindowsDirectory(strBuf, gintMAX_SIZE) Then
  1581.         GetWindowsDir = StringFromBuffer(strBuf)
  1582.         AddDirSep GetWindowsDir
  1583.     End If
  1584. End Function
  1585.  
  1586. '-----------------------------------------------------------
  1587. ' FUNCTION: ExtractFilenameItem
  1588. '
  1589. ' Extracts a quoted or unquoted filename from a string.
  1590. '
  1591. ' IN: [str] - string to parse for a filename.
  1592. '     [intAnchor] - index in str at which the filename begins.
  1593. '             The filename continues to the end of the string
  1594. '             or up to the next comma in the string, or, if
  1595. '             the filename is enclosed in quotes, until the
  1596. '             next double quote.
  1597. ' OUT: Returns the filename, without quotes.
  1598. '      [intAnchor] is set to the comma, or else one character
  1599. '             past the end of the string
  1600. '      [fErr] is set to True if a parsing error is discovered
  1601. '
  1602. '-----------------------------------------------------------
  1603. '
  1604. Public Function strExtractFilenameItem(ByVal str As String, intAnchor As Integer, fErr As Boolean) As String
  1605.     Dim iEndFilenamePos As Integer
  1606.     Dim strFilename As String
  1607.     Dim iCommaPos As Integer
  1608.  
  1609.     Do While Mid$(str, intAnchor, 1) = " "
  1610.         intAnchor = intAnchor + 1
  1611.     Loop
  1612.  
  1613.     If Mid$(str, intAnchor, 1) = gstrQUOTE Then
  1614.         ' Filename is surrounded by quotes
  1615.         iEndFilenamePos = InStr(intAnchor + 1, str, gstrQUOTE) ' Find matching quote
  1616.         If iEndFilenamePos > 0 Then
  1617.             strFilename = Mid$(str, intAnchor + 1, iEndFilenamePos - 1 - intAnchor)
  1618.             intAnchor = iEndFilenamePos + 1
  1619.             Do While Mid$(str, intAnchor, 1) = " "
  1620.                 intAnchor = intAnchor + 1
  1621.             Loop
  1622.             If intAnchor <= Len(str) Then
  1623.                 ' If there are any more characters, then the next character
  1624.                 ' must be a comma.
  1625.                 If Mid$(str, intAnchor, 1) <> gstrCOMMA Then
  1626.                     fErr = True
  1627.                     Exit Function
  1628.                 End If
  1629.             End If
  1630.         Else
  1631.             fErr = True
  1632.             Exit Function
  1633.         End If
  1634.     Else
  1635.         ' Filename continues until next comma or end of string
  1636.         iCommaPos = InStr(intAnchor, str, gstrCOMMA)
  1637.         If iCommaPos = 0 Then
  1638.             iCommaPos = Len(str) + 1
  1639.         End If
  1640.         iEndFilenamePos = iCommaPos
  1641.  
  1642.         strFilename = Mid$(str, intAnchor, iEndFilenamePos - intAnchor)
  1643.         intAnchor = iCommaPos
  1644.     End If
  1645.  
  1646.     strFilename = Trim$(strFilename)
  1647.     If Len(strFilename) = 0 Then
  1648.         fErr = True
  1649.         Exit Function
  1650.     End If
  1651.  
  1652.     fErr = False
  1653.     strExtractFilenameItem = strFilename
  1654. End Function
  1655.  
  1656. '-----------------------------------------------------------
  1657. ' FUNCTION: Extension
  1658. '
  1659. ' Extracts the extension portion of a file/path name
  1660. '
  1661. ' IN: [strFilename] - file/path to get the extension of
  1662. '
  1663. ' Returns: The extension if one exists, else vbNullString
  1664. '-----------------------------------------------------------
  1665. '
  1666. Public Function Extension(ByVal strFilename As String) As String
  1667.     Dim intDotPos As Integer
  1668.     Dim intSepPos As Integer
  1669.  
  1670.     intDotPos = InStrRev(strFilename, gstrSEP_EXT)
  1671.     If intDotPos > 0 Then
  1672.         'We've found a dot. Now make sure there is no '\' after it.
  1673.         intSepPos = InStr(intDotPos + 1, strFilename, gstrSEP_DIR)
  1674.         If intSepPos = 0 Then
  1675.             'There is no '\' after the dot. Make sure there is also no '/'.
  1676.             intSepPos = InStr(intDotPos + 1, strFilename, gstrSEP_DIRALT)
  1677.             If intSepPos = 0 Then
  1678.                 'The dot has no '\' or '/' after it, so it is good.
  1679.                 Extension = Mid$(strFilename, intDotPos + 1)
  1680.             End If
  1681.         End If
  1682.     End If
  1683. End Function
  1684. Public Function BaseName(sPathAndFile As String) As String
  1685.     '
  1686.     ' Strip the path from the file name, and just return the FileName
  1687.     ' Wraps the SeparatePathAndFileName from DWTools
  1688.     '
  1689.     Dim sFile As String
  1690.  
  1691.     SeparatePathAndFileName sPathAndFile, , sFile
  1692.  
  1693.     BaseName = sFile
  1694. End Function
  1695. 'Given a fully qualified filename, returns the path portion and the file
  1696. '   portion.
  1697. Public Sub SeparatePathAndFileName(FullPath As String, _
  1698.     Optional ByRef Path As String, _
  1699.     Optional ByRef FileName As String)
  1700.  
  1701.     Dim nSepPos As Long
  1702.     Dim nSepPos2 As Long
  1703.     Dim fUsingDriveSep As Boolean
  1704.  
  1705.     nSepPos = InStrRev(FullPath, gstrSEP_DIR)
  1706.     nSepPos2 = InStrRev(FullPath, gstrSEP_DIRALT)
  1707.     If nSepPos2 > nSepPos Then
  1708.         nSepPos = nSepPos2
  1709.     End If
  1710.     nSepPos2 = InStrRev(FullPath, gstrSEP_DRIVE)
  1711.     If nSepPos2 > nSepPos Then
  1712.         nSepPos = nSepPos2
  1713.         fUsingDriveSep = True
  1714.     End If
  1715.  
  1716.     If nSepPos = 0 Then
  1717.         'Separator was not found.
  1718.         Path = CurDir$
  1719.         FileName = FullPath
  1720.     Else
  1721.         If fUsingDriveSep Then
  1722.             Path = Left$(FullPath, nSepPos)
  1723.         Else
  1724.             Path = Left$(FullPath, nSepPos - 1)
  1725.         End If
  1726.         FileName = Mid$(FullPath, nSepPos + 1)
  1727.     End If
  1728. End Sub
  1729.  
  1730. '-----------------------------------------------------------
  1731. ' SUB: PackVerInfo
  1732. '
  1733. ' Parses a file version number string of the form
  1734. ' x[.x[.x[.x]]] and assigns the extracted numbers to the
  1735. ' appropriate elements of a VERINFO type variable.
  1736. ' Examples of valid version strings are '3.11.0.102',
  1737. ' '3.11', '3', etc.
  1738. '
  1739. ' IN: [strVersion] - version number string
  1740. '
  1741. ' OUT: [sVerInfo] - VERINFO type variable whose elements
  1742. '                   are assigned the appropriate numbers
  1743. '                   from the version number string
  1744. '-----------------------------------------------------------
  1745. '
  1746. Public Sub PackVerInfo(ByVal strVersion As String, sVerInfo As VERINFO)
  1747.     Dim intOffset As Integer
  1748.     Dim intAnchor As Integer
  1749.  
  1750.     On Error GoTo PVIError
  1751.  
  1752.     intOffset = InStr(strVersion, gstrDECIMAL)
  1753.     If intOffset = 0 Then
  1754.         sVerInfo.FileVerPart1 = Val(strVersion)
  1755.         GoTo PVIMSLo
  1756.     Else
  1757.         sVerInfo.FileVerPart1 = Val(Left$(strVersion, intOffset - 1))
  1758.         intAnchor = intOffset + 1
  1759.     End If
  1760.  
  1761.     intOffset = InStr(intAnchor, strVersion, gstrDECIMAL)
  1762.     If intOffset = 0 Then
  1763.         sVerInfo.FileVerPart2 = Val(Mid$(strVersion, intAnchor))
  1764.         GoTo PVILSHi
  1765.     Else
  1766.         sVerInfo.FileVerPart2 = Val(Mid$(strVersion, intAnchor, intOffset - intAnchor))
  1767.         intAnchor = intOffset + 1
  1768.     End If
  1769.  
  1770.     intOffset = InStr(intAnchor, strVersion, gstrDECIMAL)
  1771.     If intOffset = 0 Then
  1772.         sVerInfo.FileVerPart3 = Val(Mid$(strVersion, intAnchor))
  1773.         GoTo PVILSLo
  1774.     Else
  1775.         sVerInfo.FileVerPart3 = Val(Mid$(strVersion, intAnchor, intOffset - intAnchor))
  1776.         intAnchor = intOffset + 1
  1777.     End If
  1778.  
  1779.     intOffset = InStr(intAnchor, strVersion, gstrDECIMAL)
  1780.     If intOffset = 0 Then
  1781.         sVerInfo.FileVerPart4 = Val(Mid$(strVersion, intAnchor))
  1782.     Else
  1783.         sVerInfo.FileVerPart4 = Val(Mid$(strVersion, intAnchor, intOffset - intAnchor))
  1784.     End If
  1785.  
  1786.     Exit Sub
  1787.  
  1788. PVIError:
  1789.     sVerInfo.FileVerPart1 = 0
  1790. PVIMSLo:
  1791.     sVerInfo.FileVerPart2 = 0
  1792. PVILSHi:
  1793.     sVerInfo.FileVerPart3 = 0
  1794. PVILSLo:
  1795.     sVerInfo.FileVerPart4 = 0
  1796. End Sub
  1797.  
  1798. Public Function strQuoteString(strUnQuotedString As String, Optional vForce As Boolean = False, Optional vTrim As Boolean = True)
  1799. '
  1800. ' This routine adds quotation marks around an unquoted string, by default.  If the string is already quoted
  1801. ' it returns without making any changes unless vForce is set to True (vForce defaults to False) except that white
  1802. ' space before and after the quotes will be removed unless vTrim is False.  If the string contains leading or
  1803. ' trailing white space it is trimmed unless vTrim is set to False (vTrim defaults to True).
  1804. '
  1805.     Dim strQuotedString As String
  1806.  
  1807.     strQuotedString = strUnQuotedString
  1808.     '
  1809.     ' Trim$ the string if necessary
  1810.     '
  1811.     If vTrim Then
  1812.         strQuotedString = Trim$(strQuotedString)
  1813.     End If
  1814.     '
  1815.     ' See if the string is already quoted
  1816.     '
  1817.     If Not vForce Then
  1818.         If Left$(strQuotedString, 1) = gstrQUOTE Then
  1819.             If Right$(strQuotedString, 1) = gstrQUOTE Then
  1820.                 '
  1821.                 ' String is already quoted.  We are done.
  1822.                 '
  1823.                 GoTo DoneQuoteString
  1824.             End If
  1825.         End If
  1826.     End If
  1827.     '
  1828.     ' Add the quotes
  1829.     '
  1830.     strQuotedString = gstrQUOTE & strQuotedString & gstrQUOTE
  1831. DoneQuoteString:
  1832.     strQuoteString = strQuotedString
  1833. End Function
  1834. Public Function strUnQuoteString(ByVal strQuotedString As String)
  1835. '
  1836. ' This routine tests to see if strQuotedString is wrapped in quotation
  1837. ' marks, and, if so, remove them.
  1838. '
  1839.     strQuotedString = Trim$(strQuotedString)
  1840.  
  1841.     If Mid$(strQuotedString, 1, 1) = gstrQUOTE Then
  1842.         If Right$(strQuotedString, 1) = gstrQUOTE Then
  1843.             '
  1844.             ' It's quoted.  Get rid of the quotes.
  1845.             '
  1846.             strQuotedString = Mid$(strQuotedString, 2, Len(strQuotedString) - 2)
  1847.         End If
  1848.     End If
  1849.     strUnQuoteString = strQuotedString
  1850. End Function
  1851. Public Function fCheckFNLength(strFilename As String) As Boolean
  1852. '
  1853. ' This routine verifies that the length of the filename strFilename is valid.
  1854. ' Under NT (Intel) and Win95 it can be up to 259 (gintMAX_PATH_LEN-1) characters
  1855. ' long.  This length must include the drive, path, filename, commandline
  1856. ' arguments and quotes (if the string is quoted).
  1857. '
  1858.     fCheckFNLength = (Len(strFilename) < gintMAX_PATH_LEN)
  1859. End Function
  1860. Public Function intGetNextFldOffset(ByVal intAnchor As Integer, strList As String, strDelimit As String) As Integer
  1861. '
  1862. ' This routine reads from a strDelimit separated list, strList, and locates the next
  1863. ' item in the list following intAnchor.  Basically it finds the next
  1864. ' occurance of strDelimit that is not inside quotes.  If strDelimit is not
  1865. ' found the routine returns 0.  Note intAnchor must be outside of quotes
  1866. ' or this routine will return incorrect results.
  1867. '
  1868. ' strDelimit is typically a comma.
  1869. '
  1870. ' If there is an error this routine returns -1.
  1871. '
  1872.     Dim intQuote As Integer
  1873.     Dim intDelimit As Integer
  1874.  
  1875.     If intAnchor = 0 Then intAnchor = 1
  1876.  
  1877.     intQuote = InStr(intAnchor, strList, gstrQUOTE)
  1878.     intDelimit = InStr(intAnchor, strList, strDelimit)
  1879.     
  1880.     If intQuote > 0 Then
  1881.         If intQuote < intDelimit Then
  1882.             '
  1883.             ' A quote appeared before the next delimiter.  This
  1884.             ' means we might be inside quotes.  We still need to check
  1885.             ' if the closing quote comes after the delmiter or not.
  1886.             '
  1887.             intAnchor = intQuote + 1
  1888.             intQuote = InStr(intAnchor, strList, gstrQUOTE)
  1889.             If intQuote > intDelimit Then
  1890.                 '
  1891.                 ' The delimiter was inside quotes.  Therefore, ignore it.
  1892.                 ' The next delimiter after the closing quote must be outside
  1893.                 ' of quotes or else we have a corrupt file.
  1894.                 '
  1895.                 intAnchor = intQuote + 1
  1896.                 intDelimit = InStr(intAnchor, strList, strDelimit)
  1897.                 '
  1898.                 ' Sanity check.  Make sure there is not another quote before
  1899.                 ' the delimiter we just found.
  1900.                 '
  1901.                 If intDelimit > 0 Then
  1902.                     intQuote = InStr(intAnchor, strList, gstrQUOTE)
  1903.                     If intQuote > 0 Then
  1904.                         If intQuote < intDelimit Then
  1905.                             '
  1906.                             ' Something is wrong.  We've encountered a stray
  1907.                             ' quote.  Means the string is probably corrupt.
  1908.                             '
  1909.                             intDelimit = -1 ' Error
  1910.                         End If
  1911.                     End If
  1912.                 End If
  1913.             End If
  1914.         End If
  1915.     End If
  1916.     intGetNextFldOffset = intDelimit
  1917. End Function
  1918. Public Function LongPath(Path As String) As String
  1919.     Dim oDesktop As IVBShellFolder
  1920.     Dim nEaten As Long
  1921.     Dim pIdl As Long
  1922.     Dim sPath As String
  1923.     Dim oMalloc As IVBMalloc
  1924.  
  1925.     If Len(Path) > 0 Then
  1926.         SHGetDesktopFolder oDesktop
  1927.         oDesktop.ParseDisplayName 0, 0, Path, nEaten, pIdl, 0
  1928.         sPath = String$(gintMAX_PATH_LEN, 0)
  1929.         SHGetPathFromIDListA pIdl, sPath
  1930.         SHGetMalloc oMalloc
  1931.         oMalloc.Free pIdl
  1932.         LongPath = StringFromBuffer(sPath)
  1933.     End If
  1934. End Function
  1935.  
  1936. 'Try to convert a path to its long filename equivalent, but leave it unaltered
  1937. '   if we fail.
  1938. Public Sub MakeLongPath(Path As String)
  1939.     On Error Resume Next
  1940.     Path = LongPath(Path)
  1941. End Sub
  1942.  
  1943. Public Function StringFromBuffer(Buffer As String) As String
  1944.     Dim nPos As Long
  1945.  
  1946.     nPos = InStr(Buffer, vbNullChar)
  1947.     If nPos > 0 Then
  1948.         StringFromBuffer = Left$(Buffer, nPos - 1)
  1949.     Else
  1950.         StringFromBuffer = Buffer
  1951.     End If
  1952. End Function
  1953.  
  1954. ''==============================================================================
  1955. ''Code flow routines:
  1956.  
  1957. Public Function SyncShell(CommandLine As String, Optional Timeout As Long, _
  1958.     Optional WaitForInputIdle As Boolean, Optional Hide As Boolean = False) As Boolean
  1959.  
  1960.     Dim hProcess As Long
  1961.  
  1962.     Dim ret As Long
  1963.     Dim nMilliseconds As Long
  1964.  
  1965.     If Timeout > 0 Then
  1966.         nMilliseconds = Timeout
  1967.     Else
  1968.         nMilliseconds = INFINITE
  1969.     End If
  1970.  
  1971.     hProcess = StartProcess(CommandLine, Hide)
  1972.  
  1973.     If WaitForInputIdle Then
  1974.         'Wait for the shelled application to finish setting up its UI:
  1975.         ret = InputIdle(hProcess, nMilliseconds)
  1976.     Else
  1977.         'Wait for the shelled application to terminate:
  1978.         ret = WaitForSingleObject(hProcess, nMilliseconds)
  1979.     End If
  1980.  
  1981.     CloseHandle hProcess
  1982.  
  1983.     'Return True if the application finished. Otherwise it timed out or erred.
  1984.     SyncShell = (ret = WAIT_OBJECT_0)
  1985. End Function
  1986.  
  1987. Public Function StartProcess(CommandLine As String, Optional Hide As Boolean = False) As Long
  1988.     Const STARTF_USESHOWWINDOW As Long = &H1
  1989.     Const SW_HIDE As Long = 0
  1990.     
  1991.     Dim proc As PROCESS_INFORMATION
  1992.     Dim Start As STARTUPINFO
  1993.  
  1994.     'Initialize the STARTUPINFO structure:
  1995.     Start.cb = Len(Start)
  1996.     If Hide Then
  1997.         Start.dwFlags = STARTF_USESHOWWINDOW
  1998.         Start.wShowWindow = SW_HIDE
  1999.     End If
  2000.     'Start the shelled application:
  2001.     CreateProcessA 0&, CommandLine, 0&, 0&, 1&, _
  2002.         NORMAL_PRIORITY_CLASS, 0&, 0&, Start, proc
  2003.  
  2004.     StartProcess = proc.hProcess
  2005. End Function
  2006. Public Function CheckDataAccess() As Boolean
  2007.     Dim i As Integer
  2008.     Dim udtFile As FILEINFO
  2009.     Dim sTarget As String
  2010.  
  2011.     sTarget = UCase$(gstrAT & gstrFILE_MDAG)
  2012.     i = 1
  2013.     Do While ReadSetupFileLine(gstrINI_FILES, i, udtFile)
  2014.         If UCase$(udtFile.strSrcName) = sTarget Then 'This is mdac_typ
  2015.             CheckDataAccess = True
  2016.             Exit Function
  2017.         End If
  2018.         i = i + 1
  2019.     Loop
  2020. End Function
  2021. Public Sub InstallDataAccess()
  2022.     Dim sTarget As String
  2023.  
  2024.     'Create the folder if it doesn't exist already.
  2025.     If Not (DirExists(gsTEMPDIR)) Then
  2026.         MkDir gsTEMPDIR
  2027.     End If
  2028.     sTarget = gsTEMPDIR & gstrFILE_MDAG
  2029.     ExtractFileFromCab gsCABFULLNAME, gstrAT & gstrFILE_MDAG, sTarget, gintCabs, gstrSrcPath
  2030.     If FileExists(sTarget) Then
  2031.         SyncShell sTarget & gstrFILE_MDAGARGS, INFINITE
  2032.     End If
  2033. End Sub
  2034.