{ --- TYPES AND VARIABLES --- } type TProduct = record File: String; Title: String; Parameters: String; ForceSuccess : boolean; InstallClean : boolean; MustRebootAfter : boolean; end; InstallResult = (InstallSuccessful, InstallRebootRequired, InstallError); var installMemo, downloadMessage: string; products: array of TProduct; delayedReboot, isForcedX86: boolean; DependencyPage: TOutputProgressWizardPage; procedure AddProduct(filename, parameters, title, size, url: string; forceSuccess, installClean, mustRebootAfter : boolean); { Adds a product to the list of products to download. Parameters: filename: the file name under which to save the file parameters: the parameters with which to run the file title: the product title size: the file size url: the URL to download from forceSuccess: whether to continue in case of setup failure installClean: whether the product needs a reboot before installing mustRebootAfter: whether the product needs a reboot after installing } var path: string; i: Integer; begin installMemo := installMemo + '%1' + title + #13; path := ExpandConstant('{src}{\}') + CustomMessage('DependenciesDir') + '\' + filename; if not FileExists(path) then begin path := ExpandConstant('{tmp}{\}') + filename; if not FileExists(path) then begin isxdl_AddFile(url, path); downloadMessage := downloadMessage + '%1' + title + ' (' + size + ')' + #13; end; end; i := GetArrayLength(products); SetArrayLength(products, i + 1); products[i].File := path; products[i].Title := title; products[i].Parameters := parameters; products[i].ForceSuccess := forceSuccess; products[i].InstallClean := installClean; products[i].MustRebootAfter := mustRebootAfter; end; function SmartExec(product : TProduct; var resultcode : Integer): boolean; { Executes a product and returns the exit code. Parameters: product: the product to install resultcode: the exit code } begin if (LowerCase(Copy(product.File, Length(product.File) - 2, 3)) = 'exe') then begin Result := Exec(product.File, product.Parameters, '', SW_SHOWNORMAL, ewWaitUntilTerminated, resultcode); end else begin Result := ShellExec('', product.File, product.Parameters, '', SW_SHOWNORMAL, ewWaitUntilTerminated, resultcode); end; end; function PendingReboot: boolean; { Checks whether the machine has a pending reboot. } var names: String; begin if (RegQueryMultiStringValue(HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Control\Session Manager', 'PendingFileRenameOperations', names)) then begin Result := true; end else if ((RegQueryMultiStringValue(HKEY_LOCAL_MACHINE, 'SYSTEM\CurrentControlSet\Control\Session Manager', 'SetupExecute', names)) and (names <> '')) then begin Result := true; end else begin Result := false; end; end; function InstallProducts: InstallResult; { Installs the downloaded products } var resultCode, i, productCount, finishCount: Integer; begin Result := InstallSuccessful; productCount := GetArrayLength(products); if productCount > 0 then begin DependencyPage := CreateOutputProgressPage(CustomMessage('depinstall_title'), CustomMessage('depinstall_description')); DependencyPage.Show; for i := 0 to productCount - 1 do begin if (products[i].InstallClean and (delayedReboot or PendingReboot())) then begin Result := InstallRebootRequired; break; end; DependencyPage.SetText(FmtMessage(CustomMessage('depinstall_status'), [products[i].Title]), ''); DependencyPage.SetProgress(i, productCount); while true do begin // set 0 as used code for shown error if SmartExec fails resultCode := 0; if SmartExec(products[i], resultCode) then begin // setup executed; resultCode contains the exit code if (products[i].MustRebootAfter) then begin // delay reboot after install if we installed the last dependency anyways if (i = productCount - 1) then begin delayedReboot := true; end else begin Result := InstallRebootRequired; end; break; end else if (resultCode = 0) or (products[i].ForceSuccess) then begin finishCount := finishCount + 1; break; end else if (resultCode = 3010) then begin // Windows Installer resultCode 3010: ERROR_SUCCESS_REBOOT_REQUIRED delayedReboot := true; finishCount := finishCount + 1; break; end; end; case MsgBox(FmtMessage(SetupMessage(msgErrorFunctionFailed), [products[i].Title, IntToStr(resultCode)]), mbError, MB_ABORTRETRYIGNORE) of IDABORT: begin Result := InstallError; break; end; IDIGNORE: begin break; end; end; end; if Result <> InstallSuccessful then begin break; end; end; // only leave not installed products for error message for i := 0 to productCount - finishCount - 1 do begin products[i] := products[i+finishCount]; end; SetArrayLength(products, productCount - finishCount); DependencyPage.Hide; end; end; { -------------------- INNO EVENT FUNCTIONS -------------------- } function PrepareToInstall(var NeedsRestart: boolean): String; { Before the "preparing to install" page. See: http://www.jrsoftware.org/ishelp/index.php?topic=scriptevents } var i: Integer; s: string; begin delayedReboot := false; case InstallProducts() of InstallError: begin s := CustomMessage('depinstall_error'); for i := 0 to GetArrayLength(products) - 1 do begin s := s + #13 + ' ' + products[i].Title; end; Result := s; end; InstallRebootRequired: begin Result := products[0].Title; NeedsRestart := true; // write into the registry that the installer needs to be executed again after restart RegWriteStringValue(HKEY_CURRENT_USER, 'SOFTWARE\Microsoft\Windows\CurrentVersion\RunOnce', 'InstallBootstrap', ExpandConstant('{srcexe}')); end; end; end; function NeedRestart : boolean; { Checks whether a restart is needed at the end of install See: http://www.jrsoftware.org/ishelp/index.php?topic=scriptevents } begin Result := delayedReboot; end; function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo, MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String; { Just before the "ready" page. See: http://www.jrsoftware.org/ishelp/index.php?topic=scriptevents } var s: string; begin if downloadMessage <> '' then s := s + CustomMessage('depdownload_memo_title') + ':' + NewLine + FmtMessage(downloadMessage, [Space]) + NewLine; if installMemo <> '' then s := s + CustomMessage('depinstall_memo_title') + ':' + NewLine + FmtMessage(installMemo, [Space]) + NewLine; if MemoDirInfo <> '' then s := s + MemoDirInfo + NewLine + NewLine; if MemoGroupInfo <> '' then s := s + MemoGroupInfo + NewLine + NewLine; if MemoTasksInfo <> '' then s := s + MemoTasksInfo; Result := s end; function NextButtonClick(CurPageID: Integer): boolean; { At each "next" button click See: http://www.jrsoftware.org/ishelp/index.php?topic=scriptevents } begin Result := true; if CurPageID = wpReady then begin if downloadMessage <> '' then begin // change isxdl language only if it is not english because isxdl default language is already english if (ActiveLanguage() <> 'en') then begin ExtractTemporaryFile(CustomMessage('isxdl_langfile')); isxdl_SetOption('language', ExpandConstant('{tmp}{\}') + CustomMessage('isxdl_langfile')); end; //isxdl_SetOption('title', FmtMessage(SetupMessage(msgSetupWindowTitle), [CustomMessage('appname')])); //if SuppressibleMsgBox(FmtMessage(CustomMessage('depdownload_msg'), [FmtMessage(downloadMessage, [''])]), mbConfirmation, MB_YESNO, IDYES) = IDNO then // Result := false //else if if isxdl_DownloadFiles(StrToInt(ExpandConstant('{wizardhwnd}'))) = 0 then Result := false; end; end; end; { ----------------------------- ARCHITECTURE HELPER FUNCTIONS ----------------------------- } function IsX86: boolean; { Gets whether the computer is x86 (32 bits). } begin Result := isForcedX86 or (ProcessorArchitecture = paX86) or (ProcessorArchitecture = paUnknown); end; function IsX64: boolean; { Gets whether the computer is x64 (64 bits). } begin Result := (not isForcedX86) and Is64BitInstallMode and (ProcessorArchitecture = paX64); end; function IsIA64: boolean; { Gets whether the computer is IA64 (Itanium 64 bits). } begin Result := (not isForcedX86) and Is64BitInstallMode and (ProcessorArchitecture = paIA64); end; function GetString(x86, x64, ia64: String): String; { Gets a string depending on the computer architecture. Parameters: x86: the string if the computer is x86 x64: the string if the computer is x64 ia64: the string if the computer is IA64 } begin if IsX64() and (x64 <> '') then begin Result := x64; end else if IsIA64() and (ia64 <> '') then begin Result := ia64; end else begin Result := x86; end; end; function GetArchitectureString(): String; { Gets the "standard" architecture suffix string. Returns either _x64, _ia64 or nothing. } begin if IsX64() then begin Result := '_x64'; end else if IsIA64() then begin Result := '_ia64'; end else begin Result := ''; end; end; procedure SetForceX86(value: boolean); { Forces the setup to use X86 products } begin isForcedX86 := value; end;