Search code examples
inno-setuppascalscript

Inno Setup: Install .NET framework, reboot the system and continue installation


I need to install some system prerequisites and after finalizing them restart the system, then it must continue the installation where I left off. The prerequisites are .NET framework and an update of Windows.

My [Code] section:

const
  (*** Customize the following to your own name. ***)
  RunOnceName = 'My Program Setup restart';

  QuitMessageReboot = 'Os requisitos para a instalação do sistema não estão completos. Precisamos reiniciar seu computador para continuar a instalação do sistema.'#13#13'Depois de reiniciar o seu computador, o setup irá continuar a instalação após o primeiro login com uma conta administradora.';
  QuitMessageError = 'Error. Cannot continue.';

var
  Restarted: Boolean;
  ResultCode: Integer;

function InitializeSetup(): Boolean;
begin
  Restarted := ExpandConstant('{param:restart|0}') = '1';

  if not Restarted then begin
    Result := not RegValueExists(HKLM, 'Software\Microsoft\Windows\CurrentVersion\RunOnce', RunOnceName);
    if not Result then
      MsgBox(QuitMessageReboot, mbError, mb_Ok);
  end else
    Result := True;
end;

function DetectAndInstallPrerequisites: Boolean;
begin
  (*** Place your prerequisite detection and installation code below. ***)
  (*** Return False if missing prerequisites were detected but their installation failed, else return True. ***)

  if not Exec(ExpandConstant('{tmp}\dotNetFx40_Full_x86_x64.exe'), '/q /norestart', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
  begin
    MsgBox(QuitMessageReboot + IntToStr(ResultCode) + '....',
      mbError, MB_OK);    
  end;

  Result := True;
end;

function Quote(const S: String): String;
begin
  Result := '"' + S + '"';
end;

function AddParam(const S, P, V: String): String;
begin
  if V <> '""' then
    Result := S + ' /' + P + '=' + V;
end;

function AddSimpleParam(const S, P: String): String;
begin
 Result := S + ' /' + P;
end;

procedure CreateRunOnceEntry;
var
  RunOnceData: String;
begin
  RunOnceData := Quote(ExpandConstant('{srcexe}')) + ' /restart=1';
  RunOnceData := AddParam(RunOnceData, 'LANG', ExpandConstant('{language}'));
  RunOnceData := AddParam(RunOnceData, 'DIR', Quote(WizardDirValue));
  RunOnceData := AddParam(RunOnceData, 'GROUP', Quote(WizardGroupValue));
  if WizardNoIcons then
    RunOnceData := AddSimpleParam(RunOnceData, 'NOICONS');
  RunOnceData := AddParam(RunOnceData, 'TYPE', Quote(WizardSetupType(False)));
  RunOnceData := AddParam(RunOnceData, 'COMPONENTS', Quote(WizardSelectedComponents(False)));
  RunOnceData := AddParam(RunOnceData, 'TASKS', Quote(WizardSelectedTasks(False)));

  (*** Place any custom user selection you want to remember below. ***)

  //<your code here>

  RegWriteStringValue(HKLM, 'Software\Microsoft\Windows\CurrentVersion\RunOnce', RunOnceName, RunOnceData);
end;

function PrepareToInstall(var NeedsRestart: Boolean): String;
var
  ChecksumBefore, ChecksumAfter: String;
begin
  ChecksumBefore := MakePendingFileRenameOperationsChecksum;
  if DetectAndInstallPrerequisites then begin
    ChecksumAfter := MakePendingFileRenameOperationsChecksum;
    if ChecksumBefore <> ChecksumAfter then begin
      CreateRunOnceEntry;
      NeedsRestart := True;
      Result := QuitMessageReboot;
    end;
  end else
    Result := QuitMessageError;
end;

function ShouldSkipPage(PageID: Integer): Boolean;
begin
  Result := Restarted;
end;

The return is the variable QuitMessageReboot concatenated with the ResultCode that has the value 2.

The dotNetFx40_Full_x86_x64.exe is installed by:

[Files]
;.Net Framework
Source: "Dependencias\.Net Framework\dotNetFx40_Full_x86_x64.exe"; DestDir: "{tmp}"; \
    Flags: deleteafterinstall

Solution

  • As the name says, the PrepareToInstall event happens before the installation. The files are not installed yet. So the Exec obviously fails, as there's nothing to execute.

    You can extract the dotNetFx40_Full_x86_x64.exe from the code using ExtractTemporaryFile function:

    [Files]
    Source: "...\dotNetFx40_Full_x86_x64.exe"; Flags: dontcopy
    
    [Code]
    function DetectAndInstallPrerequisites: Boolean;
    var
      ResultCode: Integer;
      Success: Boolean;
    begin
      ExtractTemporaryFile('dotNetFx40_Full_x86_x64.exe');
    
      WizardForm.PreparingLabel.Caption := 'Installing .NET framework...';
      WizardForm.PreparingLabel.Visible := True;
      try
        Success :=
          Exec(
            ExpandConstant('{tmp}\dotNetFx40_Full_x86_x64.exe'), '/q /norestart', '',
            SW_SHOW, ewWaitUntilTerminated, ResultCode);
      finally
        WizardForm.PreparingLabel.Caption := '';
        WizardForm.PreparingLabel.Visible := False;
      end;
    
      if not Success then
      ...
    end;
    

    Make sure you add the dontcopy flag to the [Files] section entry.