56

I have a component that requires .NET 4.0 to run, how can my Inno Setup installer verify that it is installed, and if not, prompt the user to install it?

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
Mediator
  • 14,951
  • 35
  • 113
  • 191

5 Answers5

90

The InitializeSetup function is called when the Inno Setup executable is run. Inserting this code for a custom script should do what you want:

function IsDotNetDetected(version: string; service: cardinal): boolean;
// Indicates whether the specified version and service pack of the .NET Framework is installed.
//
// version -- Specify one of these strings for the required .NET Framework version:
//    'v1.1'          .NET Framework 1.1
//    'v2.0'          .NET Framework 2.0
//    'v3.0'          .NET Framework 3.0
//    'v3.5'          .NET Framework 3.5
//    'v4\Client'     .NET Framework 4.0 Client Profile
//    'v4\Full'       .NET Framework 4.0 Full Installation
//    'v4.5'          .NET Framework 4.5
//    'v4.5.1'        .NET Framework 4.5.1
//    'v4.5.2'        .NET Framework 4.5.2
//    'v4.6'          .NET Framework 4.6
//    'v4.6.1'        .NET Framework 4.6.1
//    'v4.6.2'        .NET Framework 4.6.2
//    'v4.7'          .NET Framework 4.7
//    'v4.7.1'        .NET Framework 4.7.1
//    'v4.7.2'        .NET Framework 4.7.2
//    'v4.8'          .NET Framework 4.8
//
// service -- Specify any non-negative integer for the required service pack level:
//    0               No service packs required
//    1, 2, etc.      Service pack 1, 2, etc. required
var
    key, versionKey: string;
    install, release, serviceCount, versionRelease: cardinal;
    success: boolean;
begin
    versionKey := version;
    versionRelease := 0;

    // .NET 1.1 and 2.0 embed release number in version key
    if version = 'v1.1' then begin
        versionKey := 'v1.1.4322';
    end else if version = 'v2.0' then begin
        versionKey := 'v2.0.50727';
    end

    // .NET 4.5 and newer install as update to .NET 4.0 Full
    else if Pos('v4.', version) = 1 then begin
        versionKey := 'v4\Full';
        case version of
          'v4.5':   versionRelease := 378389;
          'v4.5.1': versionRelease := 378675; // 378758 on Windows 8 and older
          'v4.5.2': versionRelease := 379893;
          'v4.6':   versionRelease := 393295; // 393297 on Windows 8.1 and older
          'v4.6.1': versionRelease := 394254; // 394271 before Win10 November Update
          'v4.6.2': versionRelease := 394802; // 394806 before Win10 Anniversary Update
          'v4.7':   versionRelease := 460798; // 460805 before Win10 Creators Update
          'v4.7.1': versionRelease := 461308; // 461310 before Win10 Fall Creators Update
          'v4.7.2': versionRelease := 461808; // 461814 before Win10 April 2018 Update
          'v4.8':   versionRelease := 528040; // 528049 before Win10 May 2019 Update
        end;
    end;

    // installation key group for all .NET versions
    key := 'SOFTWARE\Microsoft\NET Framework Setup\NDP\' + versionKey;

    // .NET 3.0 uses value InstallSuccess in subkey Setup
    if Pos('v3.0', version) = 1 then begin
        success := RegQueryDWordValue(HKLM, key + '\Setup', 'InstallSuccess', install);
    end else begin
        success := RegQueryDWordValue(HKLM, key, 'Install', install);
    end;

    // .NET 4.0 and newer use value Servicing instead of SP
    if Pos('v4', version) = 1 then begin
        success := success and RegQueryDWordValue(HKLM, key, 'Servicing', serviceCount);
    end else begin
        success := success and RegQueryDWordValue(HKLM, key, 'SP', serviceCount);
    end;

    // .NET 4.5 and newer use additional value Release
    if versionRelease > 0 then begin
        success := success and RegQueryDWordValue(HKLM, key, 'Release', release);
        success := success and (release >= versionRelease);
    end;

    result := success and (install = 1) and (serviceCount >= service);
end;

function InitializeSetup(): Boolean;
begin
    if not IsDotNetDetected('v4.6', 0) then begin
        MsgBox('MyApp requires Microsoft .NET Framework 4.6.'#13#13
            'Please use Windows Update to install this version,'#13
            'and then re-run the MyApp setup program.', mbInformation, MB_OK);
        result := false;
    end else
        result := true;
end;

(Code taken from here: http://www.kynosarges.de/DotNetVersion.html)

First, it checks for the presence of a registry entry that indicates the version of the .NET framework that is installed. If the registry entry is not present, it prompts the user to download the .NET framework. If the user says Yes, it opens the download URL. (You may have to change the version it specifies in the script to version 4.0.)


I also came across [this article on CodeProject][1], which may be a more comprehensive and customizable way of doing what you're looking for, although it may take more work to understand and will have to be modified to work with version 4.0.
Kannan Suresh
  • 4,573
  • 3
  • 34
  • 59
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • Good. But I need install framework 4.0 from local ( from my install app) – Mediator Nov 05 '10 at 08:49
  • You should be able to easily modify the above script to do just that. You'll have to download the .NET Framework 4.0 installer (either Client Profile or Full, whichever your app requires), and include this with your application's installation media. Then, from the script, instead of going out to the Internet to fetch the installer, open the existing setup file from your installation source directory. Or simply display a message that asks the user to do so ("Please run the "NetFWSetup.exe" application first"). – Cody Gray - on strike Nov 05 '10 at 08:55
  • "Please run the "NetFWSetup.exe" application first" - and the programmining instal is not like not to do? – Mediator Nov 05 '10 at 10:04
  • I just made up that message as an example of how you could prompt the user to run whatever you named the .NET Framework setup program. That's the same thing the sample is already doing, displaying a message box to the user. I was just saying that you could display the message asking them to run the included setup for the .NET Framework. I think the best option though is just modifying the sample code to automatically run the .NET Framework installer. To do this, you would just add a ShellExecute command to the code that specified the relative path of the .NET Framework setup program. – Cody Gray - on strike Nov 05 '10 at 10:09
  • I've tried, but I an error. ShellExec('', ExpandConstant('{app}\dotNetFx40_Client_x86_x64.exe'), '', '', SW_SHOW, ewNoWait, ErrorCode) – Mediator Nov 05 '10 at 12:16
  • Try this (make sure that you have ErrorCode declared as a variable of the integer type): Exec(ExpandConstant('{app}\dotNetFx40_Client_x86_x64.exe'), '', '', SW_SHOW, ewWaitUntilTerminated, ErrorCode) – Cody Gray - on strike Nov 06 '10 at 05:44
  • 2
    For those who wants to install the framework at installation time (after you go through the whole wizard and run the installation process), don't run the framework setup in `InitializeSetup` event, it would start to install the framework immediately when you run your setup wizard, what is wrong for user's experience. Use the [`Check`](http://www.jrsoftware.org/ishelp/topic_scriptcheck.htm) conditional parameter in your `[Run]` section, like [`this way`](http://stackoverflow.com/a/10111173/960757). – TLama Jun 12 '12 at 09:43
  • 4
    As the author of the code quoted above, I wanted to let you know that the code at the linked location has been updated for .NET 4.5. –  Oct 29 '12 at 20:45
  • @user574171 - Thanks. I incorporated your changes in the version I posted below. – David Taylor Aug 26 '13 at 16:59
  • @TLama it is not possible to process the return code in the [Run] section – Максим Румянцев Sep 09 '19 at 13:55
29

@Cody Gray - Thanks for your solution. Very helpful!

In case anyone is interested, here is my take on his function using enumeration values instead of strings. This change is purely a matter of personal preference. The code includes the changes for v4.5 and seems to work properly based on my limited testing.

David

[Code]

//
// Enumeration used to specify a .NET framework version 
//
type TDotNetFramework = (
    DotNet_v11_4322,  // .NET Framework 1.1
    DotNet_v20_50727, // .NET Framework 2.0
    DotNet_v30,       // .NET Framework 3.0
    DotNet_v35,       // .NET Framework 3.5
    DotNet_v4_Client, // .NET Framework 4.0 Client Profile
    DotNet_v4_Full,   // .NET Framework 4.0 Full Installation
    DotNet_v45);      // .NET Framework 4.5

//
// Checks whether the specified .NET Framework version and service pack
// is installed (See: http://www.kynosarges.de/DotNetVersion.html)
//
// Parameters:
//   Version     - Required .NET Framework version
//   ServicePack - Required service pack level (0: None, 1: SP1, 2: SP2 etc.)
//
function IsDotNetInstalled(Version: TDotNetFramework; ServicePack: cardinal): boolean;
  var
    KeyName      : string;
    Check45      : boolean;
    Success      : boolean;
    InstallFlag  : cardinal; 
    ReleaseVer   : cardinal;
    ServiceCount : cardinal;
  begin
    // Registry path for the requested .NET Version
    KeyName := 'SOFTWARE\Microsoft\NET Framework Setup\NDP\';

    case Version of
      DotNet_v11_4322:  KeyName := KeyName + 'v1.1.4322';
      DotNet_v20_50727: KeyName := KeyName + 'v2.0.50727';
      DotNet_v30:       KeyName := KeyName + 'v3.0';
      DotNet_v35:       KeyName := KeyName + 'v3.5';
      DotNet_v4_Client: KeyName := KeyName + 'v4\Client';
      DotNet_v4_Full:   KeyName := KeyName + 'v4\Full';
      DotNet_v45:       KeyName := KeyName + 'v4\Full';
    end;

    // .NET 3.0 uses "InstallSuccess" key in subkey Setup
    if (Version = DotNet_v30) then
      Success := RegQueryDWordValue(HKLM, KeyName + '\Setup', 'InstallSuccess', InstallFlag) else
      Success := RegQueryDWordValue(HKLM, KeyName, 'Install', InstallFlag);

    // .NET 4.0/4.5 uses "Servicing" key instead of "SP"
    if (Version = DotNet_v4_Client) or
       (Version = DotNet_v4_Full) or
       (Version = DotNet_v45) then
      Success := Success and RegQueryDWordValue(HKLM, KeyName, 'Servicing', ServiceCount) else
      Success := Success and RegQueryDWordValue(HKLM, KeyName, 'SP', ServiceCount);

    // .NET 4.5 is distinguished from .NET 4.0 by the Release key
    if (Version = DotNet_v45) then
      begin
        Success := Success and RegQueryDWordValue(HKLM, KeyName, 'Release', ReleaseVer);
        Success := Success and (ReleaseVer >= 378389);
      end;

    Result := Success and (InstallFlag = 1) and (ServiceCount >= ServicePack);
  end;
Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
David Taylor
  • 2,021
  • 21
  • 25
  • this looks great, but would you also show how to use this from check parameter? – vidriduch Feb 04 '16 at 10:26
  • Just add this right after the above function: `function IsDotNetReallyInstalled(): Boolean; begin result := IsDotNetInstalled(DotNet_v45, 0); end;` Then call that in your 'check' function. – Robert Koernke Feb 19 '16 at 01:30
  • Excuse me... Check parameter: Add a 'not'... not IsDotNetInstalled – Robert Koernke Feb 19 '16 at 01:43
  • Can you tell me the registry key Name for detecting 4.6.1 version? So that I can extend your code? Thanks – vishnu Apr 04 '17 at 13:21
  • @David so i have extented your code like this for 4.6.1 version if (Version = DotNet_v4_6_1) then begin Success := Success and RegQueryDWordValue(HKLM, KeyName, 'Release', ReleaseVer); Success := Success and (ReleaseVer >= 394271); end; But I am wondering with the InstallFlag value at the last line Result := Success and (InstallFlag = 1) and (ServiceCount >= ServicePack); if pass to the function with version as DotNet_v4_6_1 ? Because it never gets a value and it is not initialized. Any comment please? Thanks – vishnu Apr 04 '17 at 14:18
5

I found out that this functionality has been part of the framework since at least innosetup version 6.0.5. Therefore, you do not necessarily have to code the function yourself.

Function: IsDotNetInstalled

Here is a short example:

[Setup]
AppName=".NET Framwork Test"
AppVersion=0.0.0.1
DefaultDirName={tmp}\MyApp

[Code]
function InitializeSetup(): Boolean;
begin
  Result := IsDotNetInstalled(net45, 0);

  if Result then begin
    MsgBox('Min .NET Framwork is installed', mbInformation, MB_OK);
  end else begin
    MsgBox('Min .NET Framwork is not installed', mbInformation, MB_OK);
  end;
end;
F. Weise
  • 301
  • 3
  • 4
  • Thanks for pointing this out, I just looked at the documentation and noted it doesn't seem to support .net 6, do you know a handy way to test for it? – Walter Verhoeven May 10 '22 at 03:26
4

If you don't care if the "Full" or just the "Client" version of .NET 4 is installed:

try
  ExpandConstant('{dotnet40}');
  // Installed
except
  // Not installed
end;
Tobias81
  • 1,820
  • 1
  • 16
  • 17
4

Thanks to everyone for the existing solutions, they work great.

Still, I didn't need to support every framework version, but only one at a time, and no "ancient" ones (.NET 4.0 or older). I don't need to check for service releases too.

So, the code gets a lot simpler, and I find the reduced noise preferrable:

[Code]
// http://www.kynosarges.de/DotNetVersion.html

function IsDotNetDetected(): boolean;
var
    key: string;
    install, release: cardinal;
    success: boolean;
begin
    key := 'SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full'
    
    // success: true if the registry has been read successfully
    success := RegQueryDWordValue(HKLM, key, 'Install', install);
    success := success and RegQueryDWordValue(HKLM, key, 'Release', release);

    // install = 1 if framework is installed
    // 461808 -> .NET 4.7.2 461814 before Win10 April 2018 Update
    // see https://learn.microsoft.com/en-us/dotnet/framework/migration-guide/how-to-determine-which-versions-are-installed
    // for details
    result := success and (install = 1) and (release >= 461808);
end;

function InitializeSetup(): Boolean;
begin
    if not IsDotNetDetected() then begin
        MsgBox('MyApp requires Microsoft .NET Framework 4.7.2.'#13#13
            'Please install it and then re-run this setup program.', mbInformation, MB_OK);
        result := false;
    end else
        result := true;
end;

You just need to replace in the code above the version constant (461808) with the value you need from the Microsoft reference page and update the error message accordingly.

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
Alberto Chiesa
  • 7,022
  • 2
  • 26
  • 53