|
| 1 | +// This installer script references Bill Stewart's UninsIS DLL. |
| 2 | +// see https://github.com/Bill-Stewart/UninsIS |
| 3 | +// With this installer, the user can (re)install the DLL in a location |
| 4 | +// of their choice (DisableDirPage=no). If the user had already |
| 5 | +// installed an older (or even same or newer) version compared to the one |
| 6 | +// user is currently installing, and then decides to change the |
| 7 | +// install location, this installer (with help from UninsIS) will first |
| 8 | +// uninstall the previously installed version from the old location, |
| 9 | +// so that there is only one version on the system at any one point in time. |
| 10 | +// |
| 11 | +#define AppName "twinBASIC Userform Converter" |
| 12 | +#define AppGUID "{ECF26FA9-EE79-104A-ADD2-5A0D56287D2A}" |
| 13 | +#define AppPublisher "GCUser99" |
| 14 | +#define AppURL "https://github.com/GCuser99/VBA-UserForm-to-twinBASIC" |
| 15 | +#define AppHelpURL "https://github.com/GCuser99/VBA-UserForm-to-twinBASIC#quick-how-to-use" |
| 16 | +#define InstallerName "tBUserformConverterSetup" |
| 17 | +#define DLL64FilePath "..\Build\tBUserFormConverter_win64.dll" |
| 18 | +#define DLL32FilePath "..\Build\tBUserFormConverter_win32.dll" |
| 19 | +#define LicenseFilePath "..\..\..\GitHub\VBA-UserForm-to-twinBASIC\LICENSE.txt" |
| 20 | +#define TestFolderPath "..\test_documents" |
| 21 | +#define LogoFilePath "..\logo\logo_setup.bmp" |
| 22 | +#define RequirementsFilePath ".\readme.rtf" |
| 23 | +#define SetupOutputFolderPath "..\..\..\GitHub\VBA-UserForm-to-twinBASIC\dist" |
| 24 | +#define AppVersion GetVersionNumbersString(DLL64FilePath) |
| 25 | +// The following definition points to the path of |
| 26 | +// Bill Stewart's UninsIS.dll |
| 27 | +#define UninstallDLLFilePath ".\UninsIS\UninsIS-1.0.1\UninsIS.dll" |
| 28 | + |
| 29 | +[Setup] |
| 30 | + |
| 31 | +AppId={{#AppGUID} |
| 32 | +AppName={#AppName} |
| 33 | +AppVersion={#AppVersion} |
| 34 | +AppPublisher={#AppPublisher} |
| 35 | +AppPublisherURL={#AppURL} |
| 36 | +AppSupportURL={#AppURL} |
| 37 | +AppUpdatesURL={#AppURL} |
| 38 | +; Set default install location |
| 39 | +DefaultDirName={localappdata}\{#AppName} |
| 40 | +DefaultGroupName={#AppName} |
| 41 | +; Remove the following line to run in administrative |
| 42 | +; install mode (install for all users.) |
| 43 | +PrivilegesRequired=lowest |
| 44 | +OutputBaseFilename={#InstallerName} |
| 45 | +LicenseFile={#LicenseFilePath} |
| 46 | +Compression=lzma |
| 47 | +SolidCompression=yes |
| 48 | +WizardStyle=modern |
| 49 | +SetupLogging=yes |
| 50 | +; Uninstallable determines if Inno Setup's |
| 51 | +; automatic uninstaller is to be included in |
| 52 | +; the installation folder - this must be set to |
| 53 | +; "yes" for PrepareToInstall code to function |
| 54 | +; correctly |
| 55 | +Uninstallable=yes |
| 56 | +ArchitecturesAllowed=x64 |
| 57 | +ArchitecturesInstallIn64BitMode=x64 |
| 58 | +WizardImageFile={#LogoFilePath} |
| 59 | +DisableWelcomePage=no |
| 60 | +DisableProgramGroupPage=yes |
| 61 | +InfoBeforeFile={#RequirementsFilePath} |
| 62 | +; DisableDirPage must be set to "no" to allow |
| 63 | +; User to select a different install location |
| 64 | +; if updating |
| 65 | +DisableDirPage=no |
| 66 | +OutputDir={#SetupOutputFolderPath} |
| 67 | + |
| 68 | +[Languages] |
| 69 | + |
| 70 | +Name: "english"; MessagesFile: "compiler:Default.isl" |
| 71 | + |
| 72 | +[Components] |
| 73 | + |
| 74 | +Name: "pkg_core"; Description: "Userform Converter ActiveX Dll"; Types: full compact custom; Flags: fixed; |
| 75 | +Name: "pkg_docs"; Description: "UserForm Test Files"; Types: full compact custom; |
| 76 | + |
| 77 | +[Messages] |
| 78 | + |
| 79 | +FinishedLabel=Setup has finished installing [name] on your computer. A shortcut to the Install folder can be found on your Desktop. |
| 80 | + |
| 81 | +[Files] |
| 82 | +Source: {#DLL64FilePath}; DestDir: {app}; Flags: ignoreversion regserver ; Check: InstallX64; Components: pkg_core; |
| 83 | +Source: {#DLL32FilePath}; DestDir: {app}; Flags: ignoreversion regserver ; Check: InstallX32; Components: pkg_core; |
| 84 | +Source: {#TestFolderPath}\dlgAllMSControls.frm; DestDir: {app}\examples; Flags: ignoreversion; Components: pkg_docs; |
| 85 | +Source: {#TestFolderPath}\dlgAllMSControls.frx; DestDir: {app}\examples; Flags: ignoreversion; Components: pkg_docs; |
| 86 | +Source: {#TestFolderPath}\readme.txt; DestDir: {app}\examples; Flags: ignoreversion; Components: pkg_docs; |
| 87 | +Source: {#LicenseFilePath} ; DestDir: "{app}"; Flags: ignoreversion ; Components: pkg_core; |
| 88 | +Source: {#RequirementsFilePath} ; DestDir: "{app}"; Flags: ignoreversion ; Components: pkg_core; |
| 89 | +; Source: "Readme.txt"; DestDir: "{app}"; Flags: isreadme |
| 90 | +; For importing DLL functions at setup |
| 91 | +Source: {#UninstallDLLFilePath}; Flags: dontcopy |
| 92 | + |
| 93 | +[Icons] |
| 94 | +Name: "{autodesktop}\tBUserFormConverter - Shortcut"; Filename: "{app}" |
| 95 | +Name: "{app}\GitHub help documentation"; Filename: "{#AppHelpURL}" |
| 96 | + |
| 97 | +[Code] |
| 98 | +const |
| 99 | + AppPathsKey = 'SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths'; |
| 100 | + OFFICE_UNKNOWN_BIT = -1; |
| 101 | + OFFICE_32_BIT = 0; |
| 102 | + OFFICE_64_BIT = 6; |
| 103 | +
|
| 104 | +// Office version detection notes |
| 105 | +// Only if 2007 then we can rule out 64 bit. 2010, 2013, 2016, |
| 106 | +// and 365 all have both 32 and 64 bit versions |
| 107 | +// MS not supporting 2013 after April 2023 |
| 108 | +
|
| 109 | +// first get the path to executables: |
| 110 | +// HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\excel.exe |
| 111 | +// HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\MSACCESS.EXE |
| 112 | +// The default key value of above should yield something like: |
| 113 | +// C:\Program Files\Microsoft Office\root\Office16\MSACCESS.EXE |
| 114 | +// C:\Program Files\Microsoft Office\root\Office16\EXCEL.EXE |
| 115 | +// then use GetBinaryType to discover bitness |
| 116 | +
|
| 117 | +Function GetBinaryType(ApplicationName: string; var BinaryType: Integer): Boolean; |
| 118 | + external 'GetBinaryTypeW@kernel32.dll stdcall'; |
| 119 | +
|
| 120 | +// The function below is based on: |
| 121 | +// https://stackoverflow.com/questions/47431008/getting-the-version-and-platform-of-office-application-from-windows-registry#47443674 |
| 122 | +Function GetOfficeBitness():Integer; |
| 123 | + Var OfficeApps: array[1..5] of string; |
| 124 | + Var i: Integer; |
| 125 | + Var OfficeAppPath: String; |
| 126 | + Var BinaryType: Integer; |
| 127 | + Var KeyFound: Boolean; |
| 128 | + Begin |
| 129 | + //find the Office app binary path from Registry - |
| 130 | + //first try Excel, and if that fails then Access, then Word, etc |
| 131 | + //once path is found, then use API to determine bitness of app |
| 132 | + Result := OFFICE_UNKNOWN_BIT; |
| 133 | +
|
| 134 | + OfficeApps[1]:= 'excel.exe'; |
| 135 | + OfficeApps[2]:= 'MSACESS.EXE'; |
| 136 | + OfficeApps[3]:= 'Winword.exe'; |
| 137 | + OfficeApps[4]:= 'powerpnt.exe'; |
| 138 | + OfficeApps[5]:= 'OUTLOOK.exe'; |
| 139 | +
|
| 140 | + For i:=1 To High(OfficeApps) Do |
| 141 | + Begin |
| 142 | + KeyFound := RegQueryStringValue(HKLM, AppPathsKey + '\' + OfficeApps[i], '', OfficeAppPath); |
| 143 | + If KeyFound Then Break; |
| 144 | + End; |
| 145 | + |
| 146 | + If KeyFound Then |
| 147 | + Begin |
| 148 | + // find the bitness of the application binary |
| 149 | + if GetBinaryType(OfficeAppPath, BinaryType) Then Result:=BinaryType; |
| 150 | + End; |
| 151 | + End; |
| 152 | +
|
| 153 | +Function InstallX64(): Boolean; |
| 154 | + Begin |
| 155 | + Result := (GetOfficeBitness = OFFICE_64_BIT); |
| 156 | + End; |
| 157 | +
|
| 158 | +Function InstallX32: Boolean; |
| 159 | + Begin |
| 160 | + Result := IsWin64 And (GetOfficeBitness = OFFICE_32_BIT); |
| 161 | + End; |
| 162 | +
|
| 163 | +// The following code is for implementation of Bill Stewart's |
| 164 | +// UninsIS DLL - see https://github.com/Bill-Stewart/UninsIS |
| 165 | +// Import IsISPackageInstalled() function from UninsIS.dll at setup time |
| 166 | +Function DLLIsISPackageInstalled(AppId: string; Is64BitInstallMode, |
| 167 | + IsAdminInstallMode: DWORD): DWORD; |
| 168 | + external 'IsISPackageInstalled@files:UninsIS.dll stdcall setuponly'; |
| 169 | +
|
| 170 | +// Import CompareISPackageVersion() function from UninsIS.dll at setup time |
| 171 | +Function DLLCompareISPackageVersion(AppId, InstallingVersion: string; |
| 172 | + Is64BitInstallMode, IsAdminInstallMode: DWORD): LongInt; |
| 173 | + external 'CompareISPackageVersion@files:UninsIS.dll stdcall setuponly'; |
| 174 | +
|
| 175 | +// Import UninstallISPackage() function from UninsIS.dll at setup time |
| 176 | +Function DLLUninstallISPackage(AppId: string; Is64BitInstallMode, |
| 177 | + IsAdminInstallMode: DWORD): DWORD; |
| 178 | + external 'UninstallISPackage@files:UninsIS.dll stdcall setuponly'; |
| 179 | +
|
| 180 | +// Wrapper for UninsIS.dll IsISPackageInstalled() function |
| 181 | +// Returns true if package is detected as installed, or false otherwise |
| 182 | +Function IsISPackageInstalled(): Boolean; |
| 183 | + Begin |
| 184 | + result := DLLIsISPackageInstalled('{#AppGUID}', // AppId |
| 185 | + DWORD(Is64BitInstallMode()), // Is64BitInstallMode |
| 186 | + DWORD(IsAdminInstallMode())) = 1; // IsAdminInstallMode |
| 187 | + If result Then |
| 188 | + Log('UninsIS.dll - Package detected as installed') |
| 189 | + Else |
| 190 | + Log('UninsIS.dll - Package not detected as installed'); |
| 191 | + End; |
| 192 | +
|
| 193 | +// Wrapper for UninsIS.dll CompareISPackageVersion() function |
| 194 | +// Returns: |
| 195 | +// < 0 if version we are installing is < installed version |
| 196 | +// 0 if version we are installing is = installed version |
| 197 | +// > 0 if version we are installing is > installed version |
| 198 | +Function CompareISPackageVersion(): LongInt; |
| 199 | + Begin |
| 200 | + result := DLLCompareISPackageVersion('{#AppGUID}', // AppId |
| 201 | + '{#AppVersion}', // InstallingVersion |
| 202 | + DWORD(Is64BitInstallMode()), // Is64BitInstallMode |
| 203 | + DWORD(IsAdminInstallMode())); // IsAdminInstallMode |
| 204 | + If result < 0 Then |
| 205 | + Log('UninsIS.dll - This version {#AppVersion} older than installed version') |
| 206 | + Else If result = 0 Then |
| 207 | + Log('UninsIS.dll - This version {#AppVersion} same as installed version') |
| 208 | + Else |
| 209 | + Log('UninsIS.dll - This version {#AppVersion} newer than installed version'); |
| 210 | + End; |
| 211 | +
|
| 212 | +// Wrapper for UninsIS.dll UninstallISPackage() function |
| 213 | +// Returns 0 for success, non-zero for failure |
| 214 | +Function UninstallISPackage(): DWORD; |
| 215 | + Begin |
| 216 | + result := DLLUninstallISPackage('{#AppGUID}', // AppId |
| 217 | + DWORD(Is64BitInstallMode()), // Is64BitInstallMode |
| 218 | + DWORD(IsAdminInstallMode())); // IsAdminInstallMode |
| 219 | + If result = 0 Then |
| 220 | + Log('UninsIS.dll - Installed package uninstall completed successfully') |
| 221 | + Else |
| 222 | + Log('UninsIS.dll - installed package uninstall did not complete successfully'); |
| 223 | + End; |
| 224 | +
|
| 225 | +Function PrepareToInstall(Var NeedsRestart: Boolean): string; |
| 226 | + Var oldInstallDir: String; newInstallDir: String; |
| 227 | + Var isDifferentLocation: Boolean; |
| 228 | + Var isDifferentVersion: Boolean; |
| 229 | + Var isInstalled: Boolean; |
| 230 | + Var alwaysUninstall: Boolean; |
| 231 | + Begin |
| 232 | + // set alwaysUninstall to true if uninstall |
| 233 | + // should run regardless of version and location compare |
| 234 | + alwaysUninstall := false; |
| 235 | + isDifferentLocation := false; |
| 236 | + isDifferentVersion := false; |
| 237 | +
|
| 238 | + oldInstallDir := WizardForm.PrevAppDir; |
| 239 | + newInstallDir := ExpandConstant('{app}'); |
| 240 | + |
| 241 | + If oldInstallDir <> newInstallDir Then isDifferentLocation := true; |
| 242 | +
|
| 243 | + If CompareISPackageVersion() <> 0 Then isDifferentVersion := true; |
| 244 | +
|
| 245 | + isInstalled := IsISPackageInstalled() |
| 246 | +
|
| 247 | + If isInstalled Then |
| 248 | + Begin |
| 249 | + If isDifferentLocation Or isDifferentVersion Or alwaysUninstall Then |
| 250 | + UninstallISPackage(); |
| 251 | + End; |
| 252 | +
|
| 253 | + result := ''; |
| 254 | + End; |
| 255 | +
|
| 256 | +// Check if the OS/Office requirements have been met - if not warn user |
| 257 | +Function InitializeSetup(): Boolean; |
| 258 | + Var OfficeBitness: Integer; |
| 259 | + Var answer: Integer; |
| 260 | + Begin |
| 261 | + if Not IsWin64 then |
| 262 | + Begin |
| 263 | + answer := MsgBox('Setup has determined that your OS is not 64-bit Windows, which is a requirement of this installation. Do you still want to proceed?', mbConfirmation, MB_YESNO); |
| 264 | + If answer = IDYES Then |
| 265 | + Begin |
| 266 | + Result:=True; |
| 267 | + End |
| 268 | + Else |
| 269 | + Begin |
| 270 | + Result := False; |
| 271 | + Exit; |
| 272 | + End; |
| 273 | + End; |
| 274 | + OfficeBitness:= GetOfficeBitness |
| 275 | + Case OfficeBitness of |
| 276 | + OFFICE_UNKNOWN_BIT : Begin |
| 277 | + answer := MsgBox('MS Office bitness could not be determined. Are you sure that you want to proceed with the installation?', mbConfirmation, MB_YESNO); |
| 278 | + if answer = IDYES then Result:=True else Result := False End; |
| 279 | + OFFICE_32_BIT : Begin |
| 280 | + Result:=True; End; |
| 281 | + OFFICE_64_BIT : Begin |
| 282 | + Result:=True; End; |
| 283 | + Else |
| 284 | + Begin |
| 285 | + answer := MsgBox('The installed version of MS Office was found but is not compatible with this installation. Are you sure that you want to proceed?', mbConfirmation, MB_YESNO); |
| 286 | + if answer = IDYES then Result:=True else Result := False; |
| 287 | + End; |
| 288 | + End; |
| 289 | + End; |
0 commit comments