330 lines
9.4 KiB
ObjectPascal
330 lines
9.4 KiB
ObjectPascal
|
{
|
||
|
--- 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;
|