home *** CD-ROM | disk | FTP | other *** search
/ Liren Large Software Subsidy 10 / 10.iso / l / l391 / 2.ddi / SETUP.BA$ / SETUP.bin
Encoding:
Text File  |  1992-08-19  |  33.2 KB  |  1,040 lines

  1. ' ------------------------------------------------------------------------
  2. ' Visual Basic for MS-DOS Setup Toolkit
  3. '
  4. ' SETUP.BAS - startup file for Setup program.
  5. '
  6. ' The Setup Toolkit is designed to be modified by you to
  7. ' create Setup programs for your applications.  The toolkit
  8. ' is provided in source code form and with only minor
  9. ' modifications, you can change the text that is displayed
  10. ' to the user and the files that are installed.  With more
  11. ' work, you can add custom features to the Setup Toolkit.
  12. '
  13. ' To change messages and files to install, simply modify the
  14. ' values found in the InitSetup procedure in SETUP.BAS.
  15. '
  16. ' The default Visual Basic library (VBDOS.LIB) and
  17. ' Quick library (VBDOS.QLB) are required to create
  18. ' and run your Setup program.  Use the /L command-line
  19. ' switch to use VBDOS.QLB in the programming environment.
  20. '
  21. ' Copyright (C) 1982-1992 Microsoft Corporation
  22. '
  23. ' You have a royalty-free right to use, modify, reproduce
  24. ' and distribute the sample applications and toolkits provided with
  25. ' Visual Basic for MS-DOS (and/or any modified version)
  26. ' in any way you find useful, provided that you agree that
  27. ' Microsoft has no warranty, obligations or liability for
  28. ' any of the sample applications or toolkits.
  29. ' ------------------------------------------------------------------------
  30.  
  31. ' Include files containing declarations for called procedures
  32. '$INCLUDE: 'VBDOS.BI'
  33.  
  34. OPTION BASE 1
  35.  
  36. ' Setup toolkit forms.
  37. '$FORM frmMessage
  38. '$FORM frmPath
  39. '$FORM frmOption
  40. '$FORM frmStatus
  41.  
  42. ' Constant definitions.
  43. CONST FALSE = 0
  44. CONST TRUE = NOT FALSE
  45. CONST LEFT_JUSTIFY = 0
  46. CONST RIGHT_JUSTIFY = 1
  47. CONST CENTERED = 2
  48.  
  49.  
  50. TYPE TheFilesArray
  51.     FileName AS STRING * 12
  52.     InstallOption AS INTEGER
  53.     Disk AS INTEGER
  54. END TYPE
  55.  
  56. ' Procedure declarations.
  57. DECLARE SUB CenterForm (x AS FORM)
  58. DECLARE FUNCTION CopyFile (BYVAL SourcePath AS STRING, BYVAL DestinationPath AS STRING, BYVAL FileName AS STRING) AS INTEGER
  59. DECLARE FUNCTION CreatePath (BYVAL DestPath$) AS INTEGER
  60. DECLARE FUNCTION FileExists (path$) AS INTEGER
  61. DECLARE FUNCTION GetDiskSpaceFree (Drive AS STRING) AS LONG
  62. DECLARE FUNCTION GetDrivesAllocUnit (Drive AS STRING) AS LONG
  63. DECLARE FUNCTION GetFileSize (source$) AS LONG
  64. DECLARE SUB InitSetup ()
  65. DECLARE FUNCTION IsValidPath (DestPath$, BYVAL DefaultDrive$) AS INTEGER
  66. DECLARE FUNCTION PromptForNextDisk (wDiskNum AS INTEGER, FileToLookFor$) AS INTEGER
  67. DECLARE FUNCTION SetFileDateTime (SourceFile AS STRING, DestinationFile AS STRING) AS INTEGER
  68. DECLARE SUB Setup ()
  69. DECLARE SUB ShowMessageDialog (MessageText$, MessageAlignment%, outButton$, fUseTwoButtons%)
  70. DECLARE SUB ShowOptionDialog (title$, NumOptions%, OptionName() AS STRING, HelpTxt$, outButton$)
  71. DECLARE SUB ShowPathDialog (title$, caption1$, caption2$, DefaultDrive$, defaultDir$, SourcePath$, outButton$)
  72. DECLARE SUB ShowStatusDialog (title$, totalBytes AS LONG)
  73. DECLARE SUB UpdateStatus (FileLen AS LONG)
  74.  
  75.  
  76. DIM SHARED DestPath$                    ' User supplied destination path.
  77. DIM SHARED DefaultDrive$                ' Default destination drive.
  78. DIM SHARED SourcePath$                  ' User supplied source path.
  79. DIM SHARED TotalBytesNeeded  AS LONG    ' The number of bytes that all of your files will use.
  80. DIM SHARED Choice() AS INTEGER          ' Dim this array, it will be REDIM'ed later.
  81. DIM SHARED OptionName() AS STRING       ' Dim this array, it will be REDIM'ed later.
  82. DIM SHARED DialogCaption$               ' This is the caption displayed in the Title of all the dialogs (forms).
  83. DIM SHARED DefaultDestDrive$            ' This is the default destination drive where files will be copied to.
  84. DIM SHARED DefaultDestDir$              ' This is the default destination drive where files will be copied to.
  85. DIM SHARED NumOptions%                  ' This is if you would like to allow a user to install part of your application.
  86. DIM SHARED NumOfDisks%                  ' Number of Disks that your setup application expects.
  87. DIM SHARED NumOfFiles%                  ' Number of Files that will be installed.
  88. DIM SHARED IntroMessage$                ' This is the message used in the opening/welcome screen.
  89. DIM SHARED IntroMessageAlignment%       ' The alignment of the text in the intro screen.
  90. DIM SHARED AppName$                     ' The name of this program.
  91. DIM SHARED ExitMessage$                 ' This is the message used in the exit/closing screen.
  92. DIM SHARED ExitMessageAlignment%        ' The alignment of the text in the exit screen.
  93. DIM SHARED HelpTxt$                     ' Help text for Option screen.
  94. DIM SHARED InstallFiles() AS TheFilesArray  ' List of files to install.
  95.  
  96.  
  97. ' Create your own Setup program by changing
  98. ' initialization information in this procedure.
  99. CALL InitSetup
  100.  
  101.  
  102. ' Perform setup.
  103. CALL Setup
  104.  
  105.  
  106. END
  107.  
  108. ' Positions a form just above center on the screen.
  109. '
  110. SUB CenterForm (x AS FORM)
  111.   
  112.     Screen.Mousepointer = 11
  113.     x.top = (Screen.height * .85) / 2 - x.height / 2
  114.     x.Left = Screen.Width / 2 - x.Width / 2
  115.     Screen.Mousepointer = 0
  116.  
  117. END SUB
  118.  
  119. ' Copies file Filename from SourcePath to DestinationPath.
  120. '
  121. ' Returns FALSE if an error occurs, otherwise returns TRUE.
  122. '
  123. FUNCTION CopyFile (BYVAL SourcePath AS STRING, BYVAL DestinationPath AS STRING, BYVAL FileName AS STRING) AS INTEGER
  124.     DIM Index AS INTEGER
  125.     DIM FileLength AS LONG
  126.     DIM LeftOver AS LONG
  127.     DIM FileData AS STRING
  128.     DIM NumBlocks AS LONG
  129.     DIM x AS INTEGER
  130.  
  131.     Screen.Mousepointer = 11
  132.     IF RIGHT$(SourcePath$, 1) <> "\" THEN
  133.         SourcePath$ = SourcePath$ + "\"            'Add ending \ symbols to path variables
  134.     END IF
  135.     IF RIGHT$(DestinationPath$, 1) <> "\" THEN
  136.         DestinationPath$ = DestinationPath$ + "\"   'Add ending \ symbols to path variables
  137.     END IF
  138.     
  139.     'Update status dialog info
  140.     '
  141.     frmStatus.lblCopyFrom.Caption = "Source file: " + CHR$(10) + CHR$(13) + UCASE$(SourcePath$ + FileName$)
  142.     frmStatus.lLblCopyFrom.REFRESH
  143.     frmStatus.lblCopyTo.Caption = "Destination file: " + CHR$(10) + CHR$(13) + UCASE$(DestinationPath$ + FileName$)
  144.     frmStatus.lblCopyTo.REFRESH
  145.  
  146.     IF NOT FileExists(SourcePath$ + FileName$) THEN
  147.         MSGBOX ERROR$(75) + ": """ + SourcePath$ + FileName$ + """", 0, DialogCaption$
  148.         GOTO ErrorCopy
  149.     END IF
  150.     
  151.     ON LOCAL ERROR GOTO ErrorCopy
  152.  
  153.  
  154.     'Copy the file
  155.     '
  156.     CONST BlockSize = 32768
  157.     
  158.     OPEN SourcePath$ + FileName$ FOR BINARY ACCESS READ AS #1
  159.  
  160.     OPEN DestinationPath$ + FileName$ FOR OUTPUT AS #2
  161.     CLOSE #2
  162.  
  163.     OPEN DestinationPath$ + FileName$ FOR BINARY AS #2
  164.     
  165.     FileLength = LOF(1)
  166.     
  167.     NumBlocks = FileLength \ BlockSize
  168.     LeftOver = FileLength MOD BlockSize
  169.     
  170.     FileData = STRING$(LeftOver, 32)
  171.     
  172.     GET #1, , FileData
  173.     PUT #2, , FileData
  174.     
  175.     FileData = STRING$(BlockSize / 2, 32)
  176.  
  177.     FOR Index = 1 TO (NumBlocks * 2)
  178.         GET #1, , FileData
  179.         PUT #2, , FileData
  180.     NEXT Index
  181.     
  182.     FileData = ""    ' Free up String Allocation
  183.     CLOSE #1, #2
  184.     UpdateStatus FileLength
  185.     x = SetFileDateTime(SourcePath$ + FileName$, DestinationPath$ + FileName$)
  186.  
  187. SkipCopy:
  188.     CopyFile = TRUE
  189. ExitCopyFile:
  190.     Screen.Mousepointer = 0
  191.     EXIT FUNCTION
  192.     
  193. ErrorCopy:
  194.     CopyFile = FALSE
  195.     CLOSE
  196.     Screen.Mousepointer = 0
  197.     EXIT FUNCTION
  198. END FUNCTION
  199.  
  200. ' Create the path contained in DestPath$.
  201. ' First char must be drive letter, followed by
  202. ' a ":\" followed by the path, if any.
  203. '
  204. FUNCTION CreatePath (BYVAL DestPath$) AS INTEGER
  205.     DIM BackPos AS INTEGER
  206.     DIM ForePos AS INTEGER
  207.     DIM temp$
  208.  
  209.     Screen.Mousepointer = 11
  210.  
  211.     IF RIGHT$(DestPath$, 1) <> "\" THEN
  212.         DestPath$ = DestPath$ + "\"        'Add slash to end of path if not there already
  213.     END IF
  214.       
  215.  
  216.     'Change to the root dir of the drive
  217.     '
  218.     ON ERROR RESUME NEXT
  219.     CHDRIVE DestPath$
  220.     IF ERR <> 0 THEN GOTO errorOut
  221.     CHDIR "\"
  222.  
  223.     'Attempt to make each directory, then change to it
  224.     '
  225.     BackPos = 3
  226.     ForePos = INSTR(4, DestPath$, "\")
  227.     DO WHILE ForePos <> 0
  228.         temp$ = MID$(DestPath$, BackPos + 1, ForePos - BackPos - 1)
  229.  
  230.         ERR = 0
  231.         MKDIR temp$
  232.         IF ERR <> 0 AND ERR <> 75 THEN GOTO errorOut
  233.  
  234.         ERR = 0
  235.         CHDIR temp$
  236.         IF ERR <> 0 THEN GOTO errorOut
  237.  
  238.         BackPos = ForePos
  239.         ForePos = INSTR(BackPos + 1, DestPath$, "\")
  240.     LOOP
  241.          
  242.     CreatePath = TRUE
  243.     Screen.Mousepointer = 0
  244.     EXIT FUNCTION
  245.          
  246. errorOut:
  247.     Screen.Mousepointer = 0
  248.     MSGBOX "Can't create destination directory.", 0, DialogCaption$
  249.     CreatePath = FALSE
  250.     
  251. END FUNCTION
  252.  
  253. ' Check for the existence of a file by attempting an OPEN.
  254. '
  255. FUNCTION FileExists (path$) AS INTEGER
  256.     DIM x AS INTEGER
  257.  
  258.     x = FREEFILE
  259.  
  260.     ON ERROR RESUME NEXT
  261.     OPEN path$ FOR INPUT AS x
  262.     IF ERR = 0 THEN
  263.         FileExists = TRUE
  264.     ELSE
  265.         FileExists = FALSE
  266.     END IF
  267.     CLOSE x
  268.  
  269. END FUNCTION
  270.  
  271. ' Get free disk space for the specified drive.
  272. '
  273. ' Note: CALL INTERRUPT is used to obtain free disk
  274. ' space which requires VBDOS.QLB (VBDOS.LIB) to
  275. ' be loaded.
  276. '
  277. FUNCTION GetDiskSpaceFree (Drive AS STRING) AS LONG
  278.     DIM CurDrv$
  279.     DIM regs AS RegType                    ' Define registers
  280.     DIM SectorsInCluster AS LONG
  281.     DIM BytesInSector AS LONG
  282.     DIM ClustersInDrive AS LONG
  283.     DIM ClustersAvailable AS LONG
  284.  
  285.     CurDrv$ = CURDIR$
  286.     CHDRIVE Drive$
  287.  
  288.     ' Get current drive info; set up input and do system call
  289.     regs.ax = &H1900
  290.     CALL INTERRUPT(&H21, regs, regs)
  291.  
  292.     ' Convert drive info to readable form
  293.     Drive$ = CHR$((regs.ax AND &HFF) + 65) + ":"
  294.  
  295.     ' Get disk free space; set up input values and do system call
  296.     regs.ax = &H3600
  297.     regs.dx = ASC(UCASE$(Drive$)) - 64
  298.     CALL INTERRUPT(&H21, regs, regs)
  299.  
  300.     ' Decipher the results
  301.     SectorsInCluster = regs.ax
  302.     BytesInSector = regs.cx
  303.     IF regs.dx >= 0 THEN
  304.         ClustersInDrive = regs.dx
  305.     ELSE
  306.         ClustersInDrive = regs.dx + 65536
  307.     END IF
  308.     IF regs.bx >= 0 THEN
  309.         ClustersAvailable = regs.bx
  310.     ELSE
  311.         ClustersAvailable = regs.bx + 65536
  312.     END IF
  313.     GetDiskSpaceFree = ClustersAvailable * SectorsInCluster * BytesInSector
  314.     CHDRIVE CurDrv$
  315. END FUNCTION
  316.  
  317. ' Get the disk Allocation unit for the current drive.
  318. '
  319. ' Note: CALL INTERRUPT is used to obtain free disk
  320. ' space which requires VBDOS.QLB (VBDOS.LIB) to
  321. ' be loaded.
  322. '
  323. FUNCTION GetDrivesAllocUnit (Drive AS STRING) AS LONG
  324.     DIM CurDrv$
  325.     DIM regs AS RegType                    ' Define registers
  326.     DIM SectorsInCluster AS LONG
  327.     DIM BytesInSector AS LONG
  328.     DIM ClustersInDrive AS LONG
  329.     DIM ClustersAvailable AS LONG
  330.  
  331.     CurDrv$ = CURDIR$
  332.     CHDRIVE Drive$
  333.  
  334.     ' Get current drive info; set up input and do system call
  335.     regs.ax = &H1900
  336.     CALL INTERRUPT(&H21, regs, regs)
  337.  
  338.     ' Convert drive info to readable form
  339.     Drive$ = CHR$((regs.ax AND &HFF) + 65) + ":"
  340.  
  341.     ' Get drive allocation unit; set up input values and do system call
  342.     regs.ax = &H1C00
  343.     regs.dx = ASC(UCASE$(Drive$)) - 64
  344.     CALL INTERRUPT(&H21, regs, regs)
  345.  
  346.     ' Decipher the results
  347.     SectorsInCluster = regs.ax AND &HFF
  348.     BytesInSector = regs.cx
  349.     IF regs.dx >= 0 THEN
  350.         ClustersInDrive = regs.dx
  351.     ELSE
  352.         ClustersInDrive = regs.dx + 65536
  353.     END IF
  354.     IF regs.bx >= 0 THEN
  355.         ClustersAvailable = regs.bx
  356.     ELSE
  357.         ClustersAvailable = regs.bx + 65536
  358.     END IF
  359.     GetDrivesAllocUnit = SectorsInCluster * BytesInSector
  360.     CHDRIVE CurDrv$
  361. END FUNCTION
  362.  
  363. ' Get the size of the Source$ file.
  364. '
  365. FUNCTION GetFileSize (source$) AS LONG
  366.     DIM x AS INTEGER
  367.  
  368.     x = FREEFILE
  369.     OPEN source$ FOR BINARY ACCESS READ AS x
  370.     GetFileSize = LOF(x)
  371.     CLOSE x
  372. END FUNCTION
  373.  
  374. ' Setup initialization routine.
  375. ' Defines setup messages and files to install.
  376. '
  377. SUB InitSetup ()
  378.  
  379.     '******************************************
  380.     ' Modify the colors, messages and files to
  381.     ' install listed here to create a Setup
  382.     ' program for your application.
  383.     '******************************************
  384.  
  385.  
  386.     ' Name of the application to be installed.
  387.     AppName$ = "Sample Program"
  388.     DialogCaption$ = AppName$ + " Setup"               ' This is the caption displayed in the Title of all the dialogs (forms).
  389.  
  390.     ' Setup colors and display characteristics.
  391.     Screen.controlpanel(5) = 1      ' Set up default DESKTOP_BACKCOLOR.
  392.     Screen.controlpanel(6) = 7      ' Set up default DESKTOP_FORECOLOR.
  393.     Screen.controlpanel(7) = 176    ' Set up default DESKTOP_PATTERN as the block character.
  394.     Screen.controlpanel(16) = 5     ' Set up default TITLEBAR_BACKCOLOR.
  395.     Screen.controlpanel(17) = 15    ' Set up default TITLEBAR_FORECOLOR.
  396.  
  397.  
  398.     ' Introduction message.  Displayed before files are installed.
  399.     IntroMessageAlignment% = LEFT_JUSTIFY   ' Alignment can be CENTERED, LEFT_JUSTIFY, or RIGHT_JUSTIFY.
  400.     IntroMessage$ = "Setup program for '" + AppName$ + "'." + CHR$(13)
  401.     IntroMessage$ = IntroMessage$ + CHR$(13)
  402.     IntroMessage$ = IntroMessage$ + "The Setup Toolkit is designed to be modified by you to" + CHR$(13)
  403.     IntroMessage$ = IntroMessage$ + "create Setup programs for your applications.  The toolkit" + CHR$(13)
  404.     IntroMessage$ = IntroMessage$ + "is provided in source code form and with only minor" + CHR$(13)
  405.     IntroMessage$ = IntroMessage$ + "modifications, you can change the text that is displayed" + CHR$(13)
  406.     IntroMessage$ = IntroMessage$ + "to the user and the files that are installed.  With more" + CHR$(13)
  407.     IntroMessage$ = IntroMessage$ + "work, you can add custom features to the Setup Toolkit." + CHR$(13)
  408.     IntroMessage$ = IntroMessage$ + CHR$(13)
  409.     IntroMessage$ = IntroMessage$ + "To change messages and files to install, simply modify the" + CHR$(13)
  410.     IntroMessage$ = IntroMessage$ + "values found in the InitSetup procedure in SETUP.BAS."
  411.  
  412.  
  413.     ' Exit message.  Displayed after files are installed.
  414.     ExitMessageAlignment% = LEFT_JUSTIFY    ' Alignment can be CENTERED, LEFT_JUSTIFY, or RIGHT_JUSTIFY.
  415.     ExitMessage$ = AppName$ + " installation is complete."
  416.     
  417.  
  418.     ' Default destination for files to be installed.
  419.     DefaultDestDrive$ = "C:"
  420.     DefaultDestDir$ = "C:\TESTAPP"
  421.  
  422.  
  423.     ' Available disk space required to install your files (in bytes).
  424.     TotalBytesNeeded = 49000
  425.  
  426.  
  427.     ' Number of install options.
  428.     NumOptions% = 3
  429.     REDIM OptionName(1 TO NumOptions%) AS STRING    ' Dimension array to hold option text.
  430.  
  431.     ' Install option text displayed to use on options screen.
  432.     OptionName(1) = "Install Setup Toolkit .BAS file"
  433.     OptionName(2) = "Install Setup Toolkit .FRM files"
  434.     OptionName(3) = "Install Setup Toolkit .MAK file"
  435.  
  436.     ' Help text for install options.
  437.     HelpTxt$ = "Select the program components you want to install."
  438.  
  439.     ' Number of distribution disks.
  440.     NumOfDisks% = 1
  441.  
  442.     ' Total number of files to install.
  443.     NumOfFiles% = 6
  444.  
  445.     ' Files to install.
  446.     ' This array holds information needed to install files.
  447.     ' It is an array of a user-defined type that contains
  448.     ' the filename, the install option that the file is
  449.     ' associated with, and the disk the file is located on.
  450.     ' Files that are on disk 1 should be listed first with files
  451.     ' on disk 2 listed next and so on.
  452.  
  453.     ' Note, the number of files to install listed here
  454.     ' should match value assigned to NumOfFiles% above.
  455.  
  456.     REDIM InstallFiles(NumOfFiles%) AS TheFilesArray
  457.  
  458.     InstallFiles(1).FileName = "Setup.bas"
  459.     InstallFiles(1).InstallOption = 1
  460.     InstallFiles(1).Disk = 1
  461.  
  462.     InstallFiles(2).FileName = "SetupMsg.frm"
  463.     InstallFiles(2).InstallOption = 2
  464.     InstallFiles(2).Disk = 1
  465.  
  466.     InstallFiles(3).FileName = "SetupOpt.frm"
  467.     InstallFiles(3).InstallOption = 2
  468.     InstallFiles(3).Disk = 1
  469.  
  470.     InstallFiles(4).FileName = "SetupPth.frm"
  471.     InstallFiles(4).InstallOption = 2
  472.     InstallFiles(4).Disk = 1
  473.  
  474.     InstallFiles(5).FileName = "SetupSts.frm"
  475.     InstallFiles(5).InstallOption = 2
  476.     InstallFiles(5).Disk = 1
  477.  
  478.     InstallFiles(6).FileName = "Setup.mak"
  479.     InstallFiles(6).InstallOption = 3
  480.     InstallFiles(6).Disk = 1
  481.  
  482. END SUB
  483.  
  484. ' Determines if specified path is valid.
  485. '
  486. ' If DestPath$ does not include a drive specification,
  487. ' DefaultDrive$ is used.
  488. '
  489. ' When IsValidPath is finished, DestPath$ has the following
  490. ' format "X:\dir\dir\dir\"
  491. '
  492. ' Returns TRUE if path is valid, otherwise returns FALSE.
  493. '
  494. FUNCTION IsValidPath (DestPath$, BYVAL DefaultDrive$) AS INTEGER
  495.     DIM temp$
  496.     DIM Drive$
  497.     DIM LegalChar$
  498.     DIM BackPos AS INTEGER
  499.     DIM ForePos AS INTEGER
  500.     DIM I AS INTEGER
  501.     DIM PeriodPos AS INTEGER
  502.     DIM Length AS INTEGER
  503.  
  504.     DestPath$ = RTRIM$(LTRIM$(DestPath$))       'Remove left and right spaces
  505.     
  506.     'Check Default Drive Parameter
  507.     '
  508.     IF RIGHT$(DefaultDrive$, 1) <> ":" OR LEN(DefaultDrive$) <> 2 THEN
  509.         MSGBOX "Bad default drive parameter specified in IsValidPath Function.  You passed, """ + DefaultDrive$ + """.  Must be one drive letter and "":"" (i.e. ""C:"").", 0, DialogCaption$
  510.         GOTO parseErr
  511.     END IF
  512.     
  513.  
  514.     IF LEFT$(DestPath$, 1) = "\" THEN
  515.         DestPath$ = DefaultDrive$ + DestPath$    'Insert default drive if path begins with root backslash
  516.     END IF
  517.     
  518.     ON ERROR RESUME NEXT
  519.     temp$ = DIR$(DestPath$)
  520.     IF ERR <> 0 THEN
  521.         GOTO parseErr                             ' check for invalid characters
  522.     END IF
  523.     
  524.  
  525.     ' Check for wildcard characters and spaces
  526.     '
  527.     IF (INSTR(DestPath$, "*") <> 0) GOTO parseErr
  528.     IF (INSTR(DestPath$, "?") <> 0) GOTO parseErr
  529.     IF (INSTR(DestPath$, " ") <> 0) GOTO parseErr
  530.      
  531.     
  532.     IF MID$(DestPath$, 2, 1) <> CHR$(58) THEN GOTO parseErr   'Make Sure colon is in second char position
  533.     
  534.  
  535.     'Insert root backslash if needed
  536.     '
  537.     IF LEN(DestPath$) > 2 THEN
  538.         IF RIGHT$(LEFT$(DestPath$, 3), 1) <> "\" THEN
  539.             DestPath$ = LEFT$(DestPath$, 2) + "\" + RIGHT$(DestPath$, LEN(DestPath$) - 2)
  540.         END IF
  541.     END IF
  542.  
  543.     Drive$ = LEFT$(DestPath$, 1)
  544.     CHDRIVE (Drive$)                                          ' Try to change to the dest drive
  545.     IF ERR <> 0 THEN GOTO parseErr
  546.     
  547.     IF RIGHT$(DestPath$, 1) <> "\" THEN
  548.         DestPath$ = DestPath$ + "\"                        'Add final \
  549.     END IF
  550.     
  551.  
  552.     'Root dir is a valid dir
  553.     '
  554.     IF LEN(DestPath$) = 3 THEN
  555.         IF RIGHT$(DestPath$, 2) = ":\" THEN
  556.             GOTO ParseOK
  557.         END IF
  558.     END IF
  559.     
  560.  
  561.     IF INSTR(DestPath$, "\\") <> 0 THEN GOTO parseErr          'Check for repeated Slash
  562.     
  563.     'Check for illegal directory names
  564.     '
  565.     LegalChar$ = "!#$%&'()-0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`{}~."
  566.     BackPos = 3
  567.     ForePos = INSTR(4, DestPath$, "\")
  568.     DO
  569.         temp$ = MID$(DestPath$, BackPos + 1, ForePos - BackPos - 1)
  570.     
  571.         'Test for illegal characters
  572.         '
  573.         FOR I = 1 TO LEN(temp$)
  574.             IF INSTR(LegalChar$, UCASE$(MID$(temp$, I, 1))) = 0 THEN GOTO parseErr
  575.         NEXT I
  576.  
  577.         'Check combinations of periods and lengths
  578.         '
  579.         PeriodPos = INSTR(temp$, ".")
  580.         Length = LEN(temp$)
  581.         IF PeriodPos = 0 THEN
  582.             IF Length > 8 THEN GOTO parseErr                         'Base too long
  583.         ELSE
  584.            IF PeriodPos > 9 THEN GOTO parseErr                      'Base too long
  585.            IF Length > PeriodPos + 3 THEN GOTO parseErr             'Extension too long
  586.            IF INSTR(PeriodPos + 1, temp$, ".") <> 0 THEN GOTO parseErr'Two periods not allowed
  587.         END IF
  588.              
  589.         BackPos = ForePos
  590.         ForePos = INSTR(BackPos + 1, DestPath$, "\")
  591.     LOOP UNTIL ForePos = 0
  592.  
  593. ParseOK:
  594.     IsValidPath = TRUE
  595.     EXIT FUNCTION
  596.  
  597. parseErr:
  598.     IsValidPath = FALSE
  599. END FUNCTION
  600.  
  601. ' Prompt for the next disk.
  602. ' Use the FileToLookFor$ argument to verify that the
  603. ' proper disk (disk number wDiskNum) was inserted.
  604. '
  605. FUNCTION PromptForNextDisk (wDiskNum AS INTEGER, FileToLookFor$) AS INTEGER
  606.     DIM Ready AS INTEGER
  607.     DIM temp$
  608.     DIM x AS INTEGER
  609.  
  610.     Ready = FALSE
  611.     ON ERROR RESUME NEXT
  612.     temp$ = DIR$(FileToLookFor$)                    'Test for file
  613.     
  614.     'If not found, start loop
  615.     '
  616.     IF ERR <> 0 OR LEN(temp$) = 0 THEN
  617.         WHILE NOT Ready
  618.             BEEP
  619.             x = MSGBOX("Please insert disk #" + FORMAT$(wDiskNum%), 1, DialogCaption$)   'Put up msg box
  620.             IF x = 2 THEN
  621.                 '-------------------------------
  622.                 'User hit cancel, abort the copy
  623.                 '-------------------------------
  624.                 PromptForNextDisk = FALSE
  625.                 GOTO ExitProc
  626.             ELSE
  627.                 '----------------------------------------
  628.                 'User hits OK, try to find the file again
  629.                 '----------------------------------------
  630.                 temp$ = DIR$(FileToLookFor$)
  631.                 IF ERR = 0 AND LEN(temp$) <> 0 THEN
  632.                     PromptForNextDisk = TRUE
  633.                     Ready = TRUE
  634.                 END IF
  635.             END IF
  636.         WEND
  637.     ELSE
  638.         PromptForNextDisk = TRUE
  639.     END IF
  640.  
  641. ExitProc:
  642.  
  643. END FUNCTION
  644.  
  645. ' Set SourceFile's Date and Time to match
  646. ' DestinationFile's Date and Time
  647. '
  648. ' Note: CALL INTERRUPTX is used to obtain free disk
  649. ' space which requires VBDOS.QLB (VBDOS.LIB) to
  650. ' be loaded.
  651. '
  652. FUNCTION SetFileDateTime (SourceFile AS STRING, DestinationFile AS STRING) AS INTEGER
  653.     DIM regs1 AS RegTypeX                    ' Define registers
  654.     DIM regs2 AS RegTypeX                    ' Define registers
  655.     DIM fh1%, fh2%
  656.     DIM Result%
  657.  
  658.     ' Get SourceFile date and time
  659.     fh1% = FREEFILE
  660.     OPEN SourceFile FOR INPUT AS fh1%        ' Open file and get handle
  661.  
  662.     regs1.ax = &H5700
  663.     regs1.bx = FILEATTR(fh1%, 2)
  664.     CALL INTERRUPTX(&H21, regs1, regs1)   ' Get date and time information
  665.     Result% = regs1.flags AND 1
  666.     
  667.     CLOSE fh1%                                ' Close file
  668.  
  669.     ' Get DestinationFile date and time
  670.     fh2% = FREEFILE
  671.     OPEN DestinationFile FOR INPUT AS fh2%    ' Open file and get handle
  672.  
  673.     regs2.ax = &H5701
  674.     regs2.bx = FILEATTR(fh2%, 2)
  675.     regs2.cx = regs1.cx
  676.     regs2.dx = regs1.dx
  677.     CALL INTERRUPTX(&H21, regs2, regs2)   ' Set date and time information
  678.     Result% = regs2.flags AND 1
  679.  
  680.     CLOSE fh2%                                ' Close file
  681.  
  682.     SetFileDateTime = -1
  683. END FUNCTION
  684.  
  685. ' Control procedure for the setup process.
  686. '
  687. SUB Setup ()
  688.  
  689.     DIM DestSpaceFree AS LONG                    ' Dim disk space variable as Long Integer.
  690.     DIM DefaultSrcDrive$                         ' This is the default source drive where files will be copied from.
  691.     DIM DefaultSrcDir$                           ' This is the default source directory where files will be copied from.
  692.     DIM outButton$                               ' outButton$ is a global variable used to detect if the user is cancelling Setup.
  693.     DIM caption1$                                ' Used by ShowPathDialog
  694.     DIM caption2$                                ' Used by ShowPathDialog
  695.     DIM DestDrive$                               ' Destination drive letter
  696.     DIM SourceDrive$                             ' Source drive letter
  697.     DIM DestAllocUnit AS LONG                    ' Destination drive's allocation unit size, or the minimum disk space that a file will use.
  698.     DIM I%, J%, K%                               ' Loop counters.
  699.     DIM Message$                                 ' Message for errors.
  700.     DIM FileToFind$                              ' The file to look for on the next disk.
  701.  
  702.     '----------------------------------------------------
  703.     ' Set Destination variables with default values
  704.     '----------------------------------------------------
  705.     DefaultSrcDrive$ = LEFT$(CURDIR$, 2)         ' This is the default source drive where files will be copied from.
  706.     DefaultSrcDir$ = CURDIR$                     ' This is the default source directory where files will be copied from.
  707.  
  708.     '------------------------------------------------------
  709.     ' Show opening screen with specified text and alignment
  710.     '------------------------------------------------------
  711.     ShowMessageDialog IntroMessage$, IntroMessageAlignment%, outButton$, TRUE
  712.  
  713.     IF outButton$ = "exit" THEN GOTO ErrorSetup
  714.  
  715.     '----------------------------------------------------
  716.     ' Get Source path
  717.     '----------------------------------------------------
  718. GetSourcePath:
  719.     caption1$ = "Specify source directory containing the " + AppName$ + " files."
  720.     caption2$ = "Source:"
  721.  
  722.     ShowPathDialog DialogCaption$, caption1$, caption2$, DefaultSrcDrive$, DefaultSrcDir$, SourcePath$, outButton$
  723.  
  724.     IF outButton$ = "exit" THEN GOTO ErrorSetup
  725.  
  726.     '------------------------------
  727.     ' Check that SourcePath$ exists
  728.     '------------------------------
  729.     ON ERROR RESUME NEXT
  730.     IF LEN(SourcePath$) > 3 AND RIGHT$(SourcePath$, 1) = "\" THEN
  731.         SourcePath$ = LEFT$(SourcePath$, LEN(SourcePath$) - 1)
  732.     END IF
  733.     CHDRIVE (SourcePath$)
  734.     CHDIR (SourcePath$)
  735.     IF ERR <> 0 THEN
  736.         MSGBOX ERROR$(ERR) + ": " + SourcePath$, 0, DialogCaption$
  737.         ERR = 0
  738.         GOTO GetSourcePath
  739.     END IF
  740.     ON ERROR GOTO 0
  741.  
  742.     IF RIGHT$(SourcePath$, 1) <> "\" THEN
  743.         SourcePath$ = SourcePath$ + "\"
  744.     END IF
  745.  
  746.     '--------------------
  747.     ' Get Destination Path
  748.     '--------------------
  749.     caption1$ = "Specify destination directory for " + AppName$ + ".  The directory will be created if it does not exist."
  750.     caption2$ = "Destination:"
  751.  
  752.     ShowPathDialog DialogCaption$, caption1$, caption2$, DefaultDestDrive$, DefaultDestDir$, DestPath$, outButton$
  753.  
  754.     IF outButton$ = "exit" THEN GOTO ErrorSetup
  755.  
  756.  
  757.     '---------------------------------
  758.     'Get Drive Letters of directories
  759.     '---------------------------------
  760.     DestDrive$ = UCASE$(LEFT$(DestPath$, 1))
  761.     SourceDrive$ = UCASE$(LEFT$(SourcePath$, 1))
  762.  
  763.     '---------------------------------
  764.     'Compute free disk space variable
  765.     '---------------------------------
  766.     DestSpaceFree = GetDiskSpaceFree(DestDrive$)
  767.  
  768.     '---------------------------------
  769.     'Compute disk allocation unit size
  770.     '---------------------------------
  771.     DestAllocUnit = GetDrivesAllocUnit(DestDrive$)
  772.  
  773.     '-----------------------------------------
  774.     'Check for enough disk space.
  775.     '-----------------------------------------
  776.     IF DestSpaceFree < TotalBytesNeeded THEN
  777.         MSGBOX "There is not enough disk space on drive " + DestDrive$ + CHR$(13) + "An additional " + FORMAT$(TotalBytesNeeded - DestSpaceFree) + " bytes are needed.", 0, DialogCaption$
  778.         GOTO ErrorSetup
  779.     END IF
  780.  
  781.  
  782.     '----------------------------
  783.     'Create destination directory
  784.     '----------------------------
  785.     IF NOT CreatePath(DestPath$) THEN GOTO ErrorSetup
  786.  
  787.  
  788.     '-------------------------------------------------------------
  789.     ' Sample Option Dialog.  This code and dialog is useful if you
  790.     ' would like to give the user the option of installing certain
  791.     ' features or sections of your product.  The Option dialog will
  792.     ' size itself depending on the number of  options.  If you use
  793.     ' the Option dialog, then you will not need to use the copy file
  794.     ' section below.
  795.     '--------------------------------------------------------------
  796.     IF NumOptions% <> 0 THEN
  797.         REDIM Choice(1 TO NumOptions%) AS INTEGER
  798.  
  799.         ShowOptionDialog DialogCaption$, NumOptions%, OptionName(), HelpTxt$, outButton$
  800.  
  801.         IF outButton$ = "exit" THEN
  802.             GOTO ErrorSetup
  803.         END IF
  804.     END IF
  805.  
  806.     '-----------------------------------------------------------
  807.     ' Show Status Dialog -- This stays up while copying files
  808.     ' It is required by the CopyFile routine
  809.     '-----------------------------------------------------------
  810.     ShowStatusDialog DialogCaption$, TotalBytesNeeded
  811.  
  812.  
  813.     '-----------------------------------------------------------
  814.     ' Copy files, using the PROMPTFORNEXTDISK when you need to have the
  815.     ' user insert the next diskette.  See the appropriate procedure
  816.     ' to understand their arguments.
  817.     '-----------------------------------------------------------
  818.     FOR I% = 1 TO NumOfDisks%                            ' Loop thru for each disk.
  819.         FOR J% = 1 TO NumOptions%                        ' Loop thru for each Option section.
  820.         IF Choice(J%) <> 0 THEN
  821.             FOR K% = 1 TO NumOfFiles%                    ' Loop thru for each file.
  822.                 IF InstallFiles(K%).InstallOption = J% AND InstallFiles(K%).Disk = I% THEN ' If the file pointed to in the array belongs to the current Option section, then copy that file.
  823.                     IF NOT CopyFile(SourcePath$, DestPath$, InstallFiles(K%).FileName) THEN
  824.                       ShowMessageDialog "Could not copy file " + InstallFiles(K%).FileName, LEFT_JUSTIFY, outButton$, TRUE
  825.                       IF outButton$ = "exit" THEN GOTO ErrorSetup
  826.                     END IF
  827.                 END IF
  828.             NEXT K%
  829.         END IF
  830.         NEXT J%
  831.         
  832.         IF NumOfDisks% > 1 THEN                          ' If more than one disk has been specifed, then prompt for next disk.
  833.             FileToFind$ = ""
  834.             FOR K% = 1 TO NumOfFiles%
  835.                 IF InstallFiles(K%).Disk = I% + 1 THEN
  836.                     FileToFind$ = InstallFiles(K%).FileName
  837.                     EXIT FOR
  838.                 END IF
  839.             NEXT K%
  840.             IF FileToFind$ <> "" THEN
  841.                 IF NOT PromptForNextDisk(I% + 1, SourcePath$ + FileToFind$) THEN GOTO ErrorSetup
  842.             END IF
  843.         END IF
  844.     NEXT I%
  845.  
  846.     '--------------------------------------------------
  847.     'File Copying is over, so unload the status dialog
  848.     '--------------------------------------------------
  849.     UpdateStatus TotalBytesNeeded
  850.     UNLOAD frmStatus
  851.  
  852.  
  853.     '------------------
  854.     'Show Final message
  855.     '------------------
  856.     '------------------------------------------------------
  857.     ' Show closing screen with specified text and alignment
  858.     '------------------------------------------------------
  859.     ShowMessageDialog ExitMessage$, ExitMessageAlignment%, outButton$, FALSE
  860.  
  861.     GOTO ExitNow:
  862.  
  863. ErrorSetup:
  864.     MSGBOX AppName$ + " is not properly installed on your system." + CHR$(13) + "Run setup again to install " + AppName$ + ".", 0, DialogCaption$
  865.  
  866. ExitNow:
  867.     SYSTEM
  868.  
  869. END SUB
  870.  
  871. ' Display setup messages to the user.
  872. '
  873. SUB ShowMessageDialog (MessageText$, MessageAlignment%, outButton$, fUseTwoButtons%)
  874.     LOAD frmMessage
  875.  
  876.     frmMessage.Caption = DialogCaption$
  877.     frmMessage.LblMessage.Caption = MessageText$
  878.     frmMessage.LblMessage.Alignment = MessageAlignment%
  879.     frmMessage.LblMessage.height = frmMessage.TEXTHEIGHT(frmMessage.LblMessage.Caption)
  880.     frmMessage.LblMessage.Width = frmMessage.TEXTWIDTH(frmMessage.LblMessage.Caption)
  881.     frmMessage.LblMessage.Left = 2
  882.     frmMessage.LblMessage.top = 1
  883.  
  884.     frmMessage.Width = frmMessage.LblMessage.Width + 8
  885.     frmMessage.height = frmMessage.LblMessage.height + 7
  886.  
  887.     IF fUseTwoButtons% THEN
  888.         frmMessage.CmdContinue.top = frmMessage.ScaleHeight - 3
  889.         frmMessage.CmdContinue.Left = (frmMessage.ScaleWidth - (frmMessage.CmdContinue.Width + frmMessage.CmdExit.Width + 3)) / 2
  890.         frmMessage.CmdContinue.Cancel = TRUE
  891.  
  892.         frmMessage.CmdExit.top = frmMessage.ScaleHeight - 3
  893.         frmMessage.CmdExit.Left = frmMessage.CmdContinue.Left + frmMessage.CmdContinue.Width + 2
  894.     ELSE
  895.         frmMessage.CmdContinue.top = frmMessage.ScaleHeight - 3
  896.         frmMessage.CmdContinue.Left = (frmMessage.ScaleWidth - (frmMessage.CmdContinue.Width)) / 2
  897.         frmMessage.CmdContinue.Cancel = TRUE
  898.     END IF
  899.     CenterForm frmMessage
  900.  
  901.     frmMessage.SHOW 1
  902.  
  903.     outButton$ = frmMessage.LblMessage.tag
  904.     UNLOAD frmMessage
  905. END SUB
  906.  
  907. ' Display setup options to the user.
  908. '
  909. SUB ShowOptionDialog (title$, NumOptions%, OptionName() AS STRING, HelpTxt$, outButton$)
  910.     DIM x AS INTEGER
  911.     DIM MaxTxtWidth AS INTEGER
  912.     DIM Tmp%
  913.  
  914.     Screen.Mousepointer = 11
  915.     LOAD frmOption
  916.     frmOption.Caption = title$
  917.     frmOption.tag = HelpTxt$
  918.     MaxTxtWidth = 0
  919.     FOR x = 1 TO NumOptions%
  920.         IF x <> 1 THEN LOAD frmOption.ChkOption(x)
  921.         frmOption.ChkOption(x).Visible = TRUE
  922.         frmOption.ChkOption(x).Caption = OptionName(x)
  923.         frmOption.ChkOption(x).Width = frmOption.TEXTWIDTH(frmOption.ChkOption(x).Caption) + 4  ' The 4 is padding for the checkbox
  924.         IF frmOption.ChkOption(x).Width > MaxTxtWidth THEN MaxTxtWidth = frmOption.ChkOption(x).Width
  925.         IF x <> 1 THEN
  926.             frmOption.ChkOption(x).top = frmOption.ChkOption(x - 1).top + frmOption.ChkOption(x - 1).height
  927.             frmOption.height = frmOption.height + frmOption.ChkOption(x).height
  928.         END IF
  929.     NEXT x
  930.     IF MaxTxtWidth > 70 THEN MaxTxtWidth = 70
  931.     IF MaxTxtWidth > frmOption.Width THEN
  932.         frmOption.Width = MaxTxtWidth + 8
  933.         frmOption.CmdContinue.Left = (frmOption.Width - frmOption.CmdContinue.Width * 3 - 4) / 2
  934.         frmOption.CmdExit.Left = frmOption.CmdContinue.Left + frmOption.CmdContinue.Width + 2
  935.         frmOption.CmdHelp.Left = frmOption.CmdExit.Left + frmOption.CmdExit.Width + 2
  936.     END IF
  937.     frmOption.CmdContinue.top = frmOption.height - (frmOption.CmdContinue.height + 2)
  938.     frmOption.CmdExit.top = frmOption.CmdContinue.top
  939.     frmOption.CmdHelp.top = frmOption.CmdContinue.top
  940.  
  941.     Screen.Mousepointer = 0
  942.  
  943.     frmOption.ChkOption(0).tabindex = 0
  944.     CenterForm frmOption
  945.  
  946.     frmOption.SHOW 1
  947.  
  948.     FOR x = 1 TO NumOptions%
  949.         IF frmOption.ChkOption(x).Value THEN
  950.             Choice(x) = TRUE
  951.         ELSE
  952.             Choice(x) = FALSE
  953.         END IF
  954.     NEXT x
  955.     outButton$ = frmOption.LblMessage.tag
  956.     UNLOAD frmOption
  957. END SUB
  958.  
  959. ' Display path dialog to user.
  960. '
  961. SUB ShowPathDialog (title$, caption1$, caption2$, DefaultDrive$, defaultDir$, SourcePath$, outButton$)
  962.     Screen.Mousepointer = 11
  963.     LOAD frmPath
  964.     frmPath.Caption = title$
  965.     frmPath.LblMessage.Caption = caption1$
  966.     frmPath.LblPrompt.Caption = caption2$
  967.     frmPath.LblInDrive.tag = DefaultDrive$
  968.     frmPath.TxtPath.text = defaultDir$
  969.     frmPath.TxtPath.Selstart = 0
  970.     frmPath.TxtPath.Sellength = LEN(defaultDir$)
  971.     CenterForm frmPath
  972.     Screen.Mousepointer = 0
  973.  
  974.     frmPath.SHOW 1
  975.  
  976.     SourcePath$ = frmPath.LblOutPath.tag
  977.     outButton$ = frmPath.LblMessage.tag
  978.     UNLOAD frmPath
  979. END SUB
  980.  
  981. ' Display setup status to user.
  982. '
  983. SUB ShowStatusDialog (title$, totalBytes AS LONG)
  984.     LOAD frmStatus
  985.     frmStatus.Caption = title$
  986.     frmStatus.LblTotal.tag = STR$(totalBytes)
  987.     CenterForm frmStatus
  988.     frmStatus.SHOW
  989. END SUB
  990.  
  991. ' Update the status bar on setup status form (frmStatus).
  992. '
  993. SUB UpdateStatus (FileLen AS LONG)
  994.     STATIC Position
  995.     DIM estTotal AS LONG
  996.     DIM poslen%
  997.     CONST Pic1Width = 33
  998.     DIM tempstr$
  999.     DIM fillstr$
  1000.  
  1001.     ON LOCAL ERROR GOTO UpdateStatusErr
  1002.  
  1003.     estTotal = VAL(frmStatus.LblTotal.tag)
  1004.     IF estTotal = FALSE THEN
  1005.         estTotal = 10000000
  1006.     END IF
  1007.  
  1008.     Position = Position + (Pic1Width * CSNG((FileLen) / estTotal))
  1009.     IF Position > Pic1Width THEN
  1010.         Position = Pic1Width
  1011.     END IF
  1012.     
  1013.     frmStatus.PicStatus.CLS
  1014.  
  1015.     poslen% = LEN(STR$(INT(Position)))
  1016.     tempstr$ = STRING$(Position, CHR$(219))
  1017.     IF LEN(tempstr$) < frmStatus.PicStatus.Width THEN
  1018.         fillstr$ = STRING$(frmStatus.PicStatus.Width - LEN(tempstr$), CHR$(0))
  1019.         tempstr$ = tempstr$ + fillstr$
  1020.     END IF
  1021.     
  1022.     ' When the status reaches the label, set the
  1023.     ' Labels' backcolor to match the picture's
  1024.     ' forecolor
  1025.     '
  1026.     IF Position > 14 THEN
  1027.         frmStatus.LblPercent.BackColor = frmStatus.PicStatus.ForeColor
  1028.     END IF
  1029.  
  1030.     frmStatus.LblPercent.Caption = LTRIM$(STR$(INT((Position / Pic1Width) * 100))) + "%"
  1031.     frmStatus.LblPercent.REFRESH
  1032.  
  1033.     frmStatus.PicStatus.PRINT tempstr$
  1034.     frmStatus.PicStatus.REFRESH
  1035.  
  1036. UpdateStatusErr:
  1037.     EXIT SUB
  1038. END SUB
  1039.  
  1040.