In this guide I will walk through how to get the .NET framework to download and install on-the-fly in an Inno Setup installer.
It works in 3 steps:
- Detect if the desired .NET framework is installed
- Download the .NET Framework bootstrap installer with Inno Download Plugin
- Run the bootstrap installer in quiet mode, which will download and install the .NET Framework. This is better than downloading the full installer since it only downloads the files it needs for your platform.
Here’s the full code:
#include <idp.iss> // Other parts of installer file go here [CustomMessages] IDP_DownloadFailed=Download of .NET Framework 4.7.2 failed. .NET Framework 4.7 is required to run VidCoder. IDP_RetryCancel=Click 'Retry' to try downloading the files again, or click 'Cancel' to terminate setup. InstallingDotNetFramework=Installing .NET Framework 4.7.2. This might take a few minutes... DotNetFrameworkFailedToLaunch=Failed to launch .NET Framework Installer with error "%1". Please fix the error then run this installer again. DotNetFrameworkFailed1602=.NET Framework installation was cancelled. This installation can continue, but be aware that this application may not run unless the .NET Framework installation is completed successfully. DotNetFrameworkFailed1603=A fatal error occurred while installing the .NET Framework. Please fix the error, then run the installer again. DotNetFrameworkFailed5100=Your computer does not meet the requirements of the .NET Framework. Please consult the documentation. DotNetFrameworkFailedOther=The .NET Framework installer exited with an unexpected status code "%1". Please review any other messages shown by the installer to determine whether the installation completed successfully, and abort this installation and fix the problem if it did not. [Code] var requiresRestart: boolean; function NetFrameworkIsMissing(): Boolean; var bSuccess: Boolean; regVersion: Cardinal; begin Result := True; bSuccess := RegQueryDWordValue(HKLM, 'Software\Microsoft\NET Framework Setup\NDP\v4\Full', 'Release', regVersion); if (True = bSuccess) and (regVersion >= 461308) then begin Result := False; end; end; procedure InitializeWizard; begin if NetFrameworkIsMissing() then begin idpAddFile('http://go.microsoft.com/fwlink/?LinkId=863262', ExpandConstant('{tmp}\NetFrameworkInstaller.exe')); idpDownloadAfter(wpReady); end; end; function InstallFramework(): String; var StatusText: string; ResultCode: Integer; begin StatusText := WizardForm.StatusLabel.Caption; WizardForm.StatusLabel.Caption := CustomMessage('InstallingDotNetFramework'); WizardForm.ProgressGauge.Style := npbstMarquee; try if not Exec(ExpandConstant('{tmp}\NetFrameworkInstaller.exe'), '/passive /norestart /showrmui /showfinalerror', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then begin Result := FmtMessage(CustomMessage('DotNetFrameworkFailedToLaunch'), [SysErrorMessage(resultCode)]); end else begin // See https://msdn.microsoft.com/en-us/library/ee942965(v=vs.110).aspx#return_codes case resultCode of 0: begin // Successful end; 1602 : begin MsgBox(CustomMessage('DotNetFrameworkFailed1602'), mbInformation, MB_OK); end; 1603: begin Result := CustomMessage('DotNetFrameworkFailed1603'); end; 1641: begin requiresRestart := True; end; 3010: begin requiresRestart := True; end; 5100: begin Result := CustomMessage('DotNetFrameworkFailed5100'); end; else begin MsgBox(FmtMessage(CustomMessage('DotNetFrameworkFailedOther'), [IntToStr(resultCode)]), mbError, MB_OK); end; end; end; finally WizardForm.StatusLabel.Caption := StatusText; WizardForm.ProgressGauge.Style := npbstNormal; DeleteFile(ExpandConstant('{tmp}\NetFrameworkInstaller.exe')); end; end; function PrepareToInstall(var NeedsRestart: Boolean): String; begin // 'NeedsRestart' only has an effect if we return a non-empty string, thus aborting the installation. // If the installers indicate that they want a restart, this should be done at the end of installation. // Therefore we set the global 'restartRequired' if a restart is needed, and return this from NeedRestart() if NetFrameworkIsMissing() then begin Result := InstallFramework(); end; end; function NeedRestart(): Boolean; begin Result := requiresRestart; end;
Detecting if the desired .NET Framework is installed
First you need to determine what registry key to check to see if your .NET version is installed. There is a good Stack Overflow answer that covers this, though the Microsoft docs page is more likely to be up to date. There is also a good article on how to apply that in Inno Setup’s Pascal scripting language.
I wrote mine to check for .NET 4.7. (See the NetFrameworkIsMissing method)
Downloading the bootstrapper
Next we need to find out where to download the installer from. The .NET Framework Deployment Guide for Developers has a great list of stable download links for the bootstrapper (web) installers. I picked 4.7.2, as it still supports code targeting .NET 4.7, and we might as well give the users the latest we can. This link should prompt you to download an .exe file directly; if it’s bringing you to a download webpage, it won’t work.
Now install Inno Download Plugin. This will make the idpAddFile and idpDownloadAfter calls inside InitializeWizard work.
This “InitializeWizard” method is a special one that InnoSetup calls when first setting up its wizard pages. We call our helper function to detect if the framework is installed, then schedule a download step after the “Ready to install” page. We include our direct download link determined earlier, and save it in a temp folder, with a file name of “NetFrameworkInstaller.exe”. This name I picked arbitrarily; we just need to refer to it later when we’re installing and cleaning up.
Installing the bootstrapper
Our code is activated on PrepareToInstall. When this function returns a string, that string is shown as an error message that stops the install from happening. We call into InstallFramework and return its result.
Inside InstallFramework we’re running the bootstrapper we downloaded earlier, with a flag to make the install passive (non interactive). The /showrmui option prompts the user to close applications to avoid a system restart. /showfinalerror tells the installer to show an error message if the install fails. Our main installer will show this screen while the framework is getting downloaded and installed:
Then the .NET installer UI will show alongside the first window:
If you’d like to keep it “cleaner” and just show the first screen you can swap out the /passive argument for /q. However I like showing the user the .NET install progress since it can take a long time and it reassures them that work is still really happening.
After running the installer, the bootstrapper is deleted (whether or not the install succeeded).
And now your installer is done!
Testing the installer
To test a .NET 4 or 4.7 install, I’d recommend setting up a Windows 7 virtual machine in Hyper-V. Windows 7 comes with .NET 2, 3 and 3.5 out of the box, but not .NET 4 or 4.7. After it installs the framework you can go to Programs and Features to cleanly remove it and test it all over again.
To test an earlier .NET version you should just be able to use a modern OS like Windows 8.1 or 10.
Thanks to Antony Male for suggesting updates to the error handling code.
Great article.
ITD is just old today. It should be replaced by :
IDP : code.google.com/…/inno-download-plugin
just a few changes :
#include <idp.iss>
[Code]
procedure InitializeWizard();
begin
if Framework45IsNotInstalled() then
begin
idpAddFile('go.microsoft.com/fwlink, ExpandConstant('{tmp}NetFrameworkInstaller.exe'));
idpDownloadAfter(wpReady);
end;
end;
Thanks for the tip, Vincent! I've updated the guide to point to IDP instead. It's quite nice to remove that ugly step of hacking InnoTools Downloader.
Hi there,
The steps to follow is what I was exactly looking for. But, in my case, it is .Net 3.5 SP1 (VB 2008 Pro). So, I would be very happy and appreciate it very much if the author of this post could help me what I should modify so that I can use it to my application installation using Inno Setup.
Thank you in advance,
I am getting Error
Line73:Column11: Unknown Identifier 'InstallFramework'
For me too
Helped a lot
Thank you!!!
Very nice, thanks!
If you run much the same code from the `PrepareToInstall` hook, by returning a string containing the error message you can get Inno Setup to display the error and abort the setup, rather than showing a dialog box and continuing with the setup.
Note that this runs before temporary files have been extracted, so you’ll have to run `ExtractTemporaryFile(‘NetFrameworkInstaller.exe’)` yourself, and add the flags `dontcopy noencryption` to the installer under `[Files]`.
I ended up with the following full code (for .NET 4.6.1), which appears to work fine.
“`
[Files]
Source: “{#RuntimeDependencies}\{#VCRedistExe}”; DestDir: “{tmp}”; Flags: dontcopy nocompression noencryption
[Code]
var
requiresRestart: boolean;
// Detect .NET framework 4.6.1 is missing
// See https://msdn.microsoft.com/en-us/library/hh925568(v=vs.110).aspx
function DotNetIsMissing(): Boolean;
var
readVal: cardinal;
success: Boolean;
begin
success := RegQueryDWordValue(HKLM, ‘SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full’, ‘Release’, readVal);
success := success and ((readVal = 394254) or (readVal = 394271));
Result := not success;
end;
// Adapted from https://blogs.msdn.microsoft.com/davidrickard/2015/07/17/installing-net-framework-4-5-automatically-with-inno-setup/
function InstallDotNet(): String;
var
statusText: string;
resultCode: Integer;
begin
statusText := WizardForm.StatusLabel.Caption;
WizardForm.StatusLabel.Caption := CustomMessage(‘InstallingDotNetFramework’);
WizardForm.ProgressGauge.Style := npbstMarquee;
try
ExtractTemporaryFile(‘{#DotNetRuntimeExe}’);
if not Exec(ExpandConstant(‘{tmp}\{#DotNetRuntimeExe}’), ‘/passive /norestart /showrmui /showfinalerror’, ”, SW_SHOW, ewWaitUntilTerminated, resultCode) then
begin
Result := FmtMessage(CustomMessage(‘DotNetFrameworkFailedToLaunch’), [SysErrorMessage(resultCode)]);
end
else
begin
// See https://msdn.microsoft.com/en-us/library/ee942965(v=vs.110).aspx#return_codes
case resultCode of
0: begin
// Successful
end;
1602 : begin
MsgBox(CustomMessage(‘DotNetFrameworkFailed1602’), mbInformation, MB_OK);
end;
1603: begin
Result := CustomMessage(‘DotNetFrameworkFailed1603’);
end;
1641: begin
requiresRestart := True;
end;
3010: begin
requiresRestart := True;
end;
5100: begin
Result := CustomMessage(‘DotNetFrameworkFailed5100’);
end;
else begin
MsgBox(FmtMessage(CustomMessage(‘DotNetFrameworkFailedOther’), [IntToStr(resultCode)]), mbError, MB_OK);
end;
end;
end;
finally
WizardForm.StatusLabel.Caption := statusText;
WizardForm.ProgressGauge.Style := npbstNormal;
end;
end;
function PrepareToInstall(var NeedsRestart: Boolean): String;
begin
// ‘NeedsRestart’ only has an effect if we return a non-empty string, thus aborting the installation.
// If the installers indicate that they want a restart, this should be done at the end of installation.
// Therefore we set the global ‘restartRequired’ if a restart is needed, and return this from NeedRestart()
if DotNetIsMissing() then
begin
Result := InstallDotNet();
end;
end;
function NeedRestart(): Boolean;
begin
Result := requiresRestart;
end
“`
And the following strings:
“`
InstallingDotNetFramework=Installing .NET Framework. This might take a few minutes…
DotNetFrameworkFailedToLaunch=Failed to launch .NET Framework Installer with error “%1”. Please fix the error then run this installer again.
DotNetFrameworkFailed1602=.NET Framework installation was cancelled. This installation can continue, but be aware that this application may not run unless the .NET Framework installation is completed successfully.
DotNetFrameworkFailed1603=A fatal error occurred while installing the .NET Framework. Please fix the error, then run the installer again.
DotNetFrameworkFailed5100=Your computer does not meet the requirements of the .NET Framework. Please consult the documentation.
DotNetFrameworkFailedOther=The Visual .NET Framework installer exited with an unexpected status code “%1”. Please review any other messages shown by the installer to determine whether the installation completed successfully, and abort this installation and fix the problem if it did not.
“`
… since the comments don’t support whitespace by the looks of it, here’s a gist: https://gist.github.com/canton7/72104b1fb154442d5a9ba937fd3ee781
Hey Antony! Sorry it took so long to approve your comment. The old blog platform doesn’t give me email notifications and I kind of left things for a while. Anyway the changes you’ve made make a lot of sense to me. I’ve updated the guide to include them.
My install hangs on the dot install stating Waiting for another install to complete…
Never mind it just took a while. It’s working.
I have another question. What is you have two dependencies. So want to include Dot Net and MySQL Connector Net. When I duplicate your code for the MySQL install I get a compile error “Duplicate identifier ‘CURSTEPCHANGED’. If I compile seperatly they both work. How can I do this…
Sorry I figured it out. Had to combine the two instances.
got an error ‘2’ when .NET tried to install – have you seen this? thanks.
Hi David
Thanks for the post, it’s been really helpful.
Unfortunately my .Net install fails as soon as it starts, with a 216 error code. I’m unable to find any reference to this code anywhere.
Any help appreciated
Code file .iss script:
Use #define to specify the pre-requisite .NET Framework
To create an installer that auto-detects if the specified framework version is present
If not, notify the user, attempt to download it, and automatically install it.
https://mitrichsoftware.wordpress.com/inno-setup-tools/inno-download-plugin/comment-page-1/#comment-227
Supports .NET Framework 4.0 up to 4.7.2.
I get an error in “#include ”
Error message:
[ISPP] File not found: “idp.iss”
someone can help me?
Did you install the Inno Download Plugin? https://code.google.com/p/inno-download-plugin/
Yes, sure!
Are there other operations to do?
Use the full Path – #include “C:\Program Files (x86)\Inno Download Plugin\idp.iss”
Thank you Cezar.
Thanks for this.
I need to install .net 4.8. I assume I need to use a different linkid for the web installer. How do I find that?