Wayne Werner
Wayne Werner

Reputation: 51917

How can I install .NET framework as a prerequisite using Inno Setup?

I have a question similar to Inno Setup: Verify that .NET 4.0 is installed, but it seems to be slightly different.

[Files]
Source: "dependencies\dotNetFx40_Full_x86_x64.exe"; DestDir: {tmp}; Flags: deleteafterinstall; Check: FrameworkIsNotInstalled
Source: "C:\Windows\Microsoft.NET\assembly\GAC_MSIL\MySql.Data\v4.0_6.5.4.0__c5687fc88969c44d\MySql.Data.dll"; DestDir: "{app}\lib"; StrongAssemblyName: "MySql.Data, Version=6.5.4.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d, ProcessorArchitecture=MSIL"; Flags: "gacinstall sharedfile uninsnosharedfileprompt"

[Run]
Filename: {tmp}\dotNetFx40_Full_x86_x64.exe; Description: Install Microsoft .NET Framework 4.0; Parameters: /q /norestart; Check: FrameworkIsNotInstalled

[code]
function FrameworkIsNotInstalled: Boolean;
begin
  Result := not RegKeyExists(HKEY_LOCAL_MACHINE, 'Software\Microsoft\.NETFramework\policy\v4.0');
end;

As you can see, I'm trying to register a file with the GAC. Unfortunately on some machines it's possible that the .NET framework is not installed. So I need to install it first. Is there anyway that I can force an installation of the .NET runtime before I try to register my files?

Upvotes: 43

Views: 44399

Answers (5)

Liran
Liran

Reputation: 11

I know this is an old post but as I'm working with Inno Setup at my current position , I just wanted to share my 2 cents. According to Inno Setup documentation the Exec(ExpandConstant) "Returns True if the specified file was executed successfully, False otherwise" The question is, what is "file was executed successfully"? I'm currently working on VS++ 2019 Redist. prerequisite and when executing @TLama example everything is working, except that VS++ 2019 isn't actually being installed at all. Running the VS++ 2019 redist exe file specifies that "Setup Failed" since "Another version of this product is already installed". So going back to my previous question, what is "file was executed successfully"? it seems that the file execution doesn't mean the installer returned exit code 0, when I've logged the ResultCode I didn't get 0, but some other exit code. So I think that besides running the try if not Exec(ExpandConstant... part, it's also worth adding another if ResultCode <> 0 statement:

try
    if not Exec(ExpandConstant('{tmp}\vc_redist_x64_2019.exe'), '/q', '', 
    SW_SHOWNORMAL,
    ewWaitUntilTerminated, ResultCode)
    then
    begin
        MsgBox('Microsoft Visual C++ 2019 Redistributable (x64) installer failed to 
        run!' + #13#10 +
       SysErrorMessage(ResultCode), mbError, MB_OK);
   end
    else begin
        if  ResultCode <> 0 then
        begin
            MsgBox('Microsoft Visual C++ 2019 Redistributable (x64) installer failed 
            to run!' + #13#10 + SysErrorMessage(ResultCode), mbError, MB_OK);
        WizardForm.Close;
        end
  //log('the reuslt code is' + IntToStr(ResultCode))
  end;
  finally
    WizardForm.StatusLabel.Caption := StatusText;
    WizardForm.ProgressGauge.Style := npbstNormal;
  end;

I'll just add that we're using an old Inno Setup version (5.6.1) so not sure if newer version are taking care of the exit code as well.

Upvotes: 1

RandomEngy
RandomEngy

Reputation: 15413

You can also set it up to download the web bootstrapper and run it if you don't want to package in the very heavy full .NET installer. I have written a blog post on how to do that with Inno Download Plugin.

Upvotes: 1

TLama
TLama

Reputation: 76753

Since the [Run] section is processed after the [Files] section, it is naturally impossible to do it with the script you've shown (hence your question). There are few ways where the one I would recommend is to execute the .NET setup from the AfterInstall parameter function of the setup entry itself. So you would remove your current [Run] section and write a script like this:

[Files]
Source: "dependencies\dotNetFx40_Full_x86_x64.exe"; DestDir: {tmp}; Flags: deleteafterinstall; AfterInstall: InstallFramework; Check: FrameworkIsNotInstalled
Source: "C:\Windows\Microsoft.NET\assembly\GAC_MSIL\MySql.Data\v4.0_6.5.4.0__c5687fc88969c44d\MySql.Data.dll"; DestDir: "{app}\lib"; StrongAssemblyName: "MySql.Data, Version=6.5.4.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d, ProcessorArchitecture=MSIL"; Flags: gacinstall sharedfile uninsnosharedfileprompt

[Code]
procedure InstallFramework;
var
  ResultCode: Integer;
begin
  if not Exec(ExpandConstant('{tmp}\dotNetFx40_Full_x86_x64.exe'), '/q /norestart', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
  begin
    { you can interact with the user that the installation failed }
    MsgBox('.NET installation failed with code: ' + IntToStr(ResultCode) + '.',
      mbError, MB_OK);
  end;
end;

The process is easy, if the Check function of the .NET setup entry of the [Files] section evaluates to True (FrameworkIsNotInstalled), the entry is processed, which copies the setup binary into the Inno Setup's temporary folder and if that succeeds, the AfterInstall function InstallFramework is called immediately after. Inside of this function, the .NET setup is manually executed by calling Exec function.

And finally, if all of that succeeds, the installation continues to process the next [Files] section entry, which is your assembly that is going to be registered. Now, with the installed .NET framework. So as you can see, the order of the [Files] section entries is crucial here.


You've additionally asked in your comment, how to show to the user some progress, since executing the .NET setup in the way I've posted here blocks the [Files] entry, which leads to showing the stopped progress bar and text about extracting files. Since it wouldn't be easy to get the .NET setup's installation progress, I would simply show to the user endless marquee progress bar during that setup execution.

To do this wrap that setup execution into a code like this:

procedure InstallFramework;
var
  StatusText: string;
begin
  StatusText := WizardForm.StatusLabel.Caption;
  WizardForm.StatusLabel.Caption := 'Installing .NET framework...';
  WizardForm.ProgressGauge.Style := npbstMarquee;
  try
    { here put the .NET setup execution code }
  finally
    WizardForm.StatusLabel.Caption := StatusText;
    WizardForm.ProgressGauge.Style := npbstNormal;
  end;
end;

This is how the wizard form looks like during that .NET setup execution (the progress bar is animated):

enter image description here

Upvotes: 70

BananaAcid
BananaAcid

Reputation: 3501

just my 2 cents on checking for .NET Framework 4.7, fits right in with @Snicker's answer:

function FrameworkIsNotInstalled: Boolean;
var
  ver: Cardinal;
begin
  Result :=
    not
    (
    (RegKeyExists(
      HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Client')
    and
        RegQueryDWordValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Client', 'Release', ver)
    )
    or
    (RegKeyExists(
      HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full')
    and
        RegQueryDWordValue(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release', ver)
    )
    )
    and (ver < 460798)
end;

Upvotes: 8

Snicker
Snicker

Reputation: 997

I just want to add something to @TLama: The close when the setup fails. It's not so easy because WizardForm.Close; just invokes the cancel-button which can be aborted by the user. Finally, the code can look like that:

[Code]
var CancelWithoutPrompt: boolean;

function InitializeSetup(): Boolean;
begin
  CancelWithoutPrompt := false;
  result := true;
end;

procedure CancelButtonClick(CurPageID: Integer; var Cancel, Confirm: Boolean);
begin
  if CurPageID=wpInstalling then
    Confirm := not CancelWithoutPrompt;
end;

function FrameworkIsNotInstalled: Boolean;
begin
  Result := not RegKeyExists(HKEY_LOCAL_MACHINE, 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full');
end;

procedure InstallFramework;
var
  StatusText: string;
  ResultCode: Integer;
begin
  StatusText := WizardForm.StatusLabel.Caption;
  WizardForm.StatusLabel.Caption := 'Installing .NET framework...';
  WizardForm.ProgressGauge.Style := npbstMarquee;
  try
      if not Exec(ExpandConstant('{tmp}\dotNetFx45_Full_asetup.exe'), '/q /norestart', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then
  begin
    // you can interact with the user that the installation failed
    MsgBox('.NET installation failed with code: ' + IntToStr(ResultCode) + '.',
      mbError, MB_OK);
    CancelWithoutPrompt := true;
    WizardForm.Close;       
  end;
  finally
    WizardForm.StatusLabel.Caption := StatusText;
    WizardForm.ProgressGauge.Style := npbstNormal;
  end;
end;

Upvotes: 12

Related Questions