{"id":146,"date":"2021-02-28T18:26:04","date_gmt":"2021-02-28T18:26:04","guid":{"rendered":"https:\/\/engy.us\/blog\/?p=146"},"modified":"2021-12-31T18:19:25","modified_gmt":"2021-12-31T18:19:25","slug":"installing-net-5-runtime-automatically-with-inno-setup","status":"publish","type":"post","link":"https:\/\/engy.us\/blog\/2021\/02\/28\/installing-net-5-runtime-automatically-with-inno-setup\/","title":{"rendered":"Installing .NET 5 Runtime Automatically with Inno Setup"},"content":{"rendered":"\n<p>In this guide I will walk through how to get the .NET 5 runtime to download and install on-the-fly in an Inno Setup installer.<\/p>\n\n\n\n<p>It works in 3 steps:<\/p>\n\n\n\n<ol><li>Detect if the desired .NET runtime is installed<\/li><li>Download the .NET Runtime bootstrap installer with Inno Download Plugin<\/li><li>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.<\/li><\/ol>\n\n\n\n<p>You can download the regular .NET Runtime or the .NET Desktop Runtime, which supports Windows desktop applications. If you install the Desktop Runtime you do not need to also install the normal .NET Runtime.<\/p>\n\n\n\n<p>Here&#8217;s the full code I&#8217;m using, with the x64 .NET 5 Desktop Runtime:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#include &lt;idp.iss&gt;\n\n\/\/ Other parts of installer file go here\n\n&#91;CustomMessages]\nIDP_DownloadFailed=Download of .NET 5 failed. .NET 5 Desktop runtime is required to run VidCoder.\nIDP_RetryCancel=Click 'Retry' to try downloading the files again, or click 'Cancel' to terminate setup.\nInstallingDotNetRuntime=Installing .NET 5 Desktop Runtime. This might take a few minutes...\nDotNetRuntimeFailedToLaunch=Failed to launch .NET Runtime Installer with error \"%1\". Please fix the error then run this installer again.\nDotNetRuntimeFailed1602=.NET Runtime installation was cancelled. This installation can continue, but be aware that this application may not run unless the .NET Runtime installation is completed successfully.\nDotNetRuntimeFailed1603=A fatal error occurred while installing the .NET Runtime. Please fix the error, then run the installer again.\nDotNetRuntimeFailed5100=Your computer does not meet the requirements of the .NET Runtime. Please consult the documentation.\nDotNetRuntimeFailedOther=The .NET Runtime 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.\n\n&#91;Code]\n\nvar\n  requiresRestart: boolean;\n\nfunction CompareVersion(V1, V2: string): Integer;\nvar\n  P, N1, N2: Integer;\nbegin\n  Result := 0;\n  while (Result = 0) and ((V1 &lt;&gt; '') or (V2 &lt;&gt; '')) do\n  begin\n    P := Pos('.', V1);\n    if P &gt; 0 then\n    begin\n      N1 := StrToInt(Copy(V1, 1, P - 1));\n      Delete(V1, 1, P);\n    end\n      else\n    if V1 &lt;&gt; '' then\n    begin\n      N1 := StrToInt(V1);\n      V1 := '';\n    end\n      else\n    begin\n      N1 := 0;\n    end;\n    P := Pos('.', V2);\n    if P &gt; 0 then\n    begin\n      N2 := StrToInt(Copy(V2, 1, P - 1));\n      Delete(V2, 1, P);\n    end\n      else\n    if V2 &lt;&gt; '' then\n    begin\n      N2 := StrToInt(V2);\n      V2 := '';\n    end\n      else\n    begin\n      N2 := 0;\n    end;\n    if N1 &lt; N2 then Result := -1\n      else\n    if N1 &gt; N2 then Result := 1;\n  end;\nend;\n\nfunction NetRuntimeIsMissing(): Boolean;\nvar\n  runtimes: TArrayOfString;\n  registryKey: string;\n  I: Integer;\n  meetsMinimumVersion: Boolean;\n  meetsMaximumVersion: Boolean;\n  minimumVersion: string;\n  maximumExclusiveVersion: string;\nbegin\n  Result := True;\n\n  minimumVersion := '5.0.0';\n  maximumExclusiveVersion := '5.1.0';\n  registryKey := 'SOFTWARE\\WOW6432Node\\dotnet\\Setup\\InstalledVersions\\x64\\sharedfx\\Microsoft.WindowsDesktop.App';\n  if RegGetValueNames(HKLM, registryKey, runtimes) then\n  begin\n    for I := 0 to GetArrayLength(runtimes)-1 do\n    begin\n      meetsMinimumVersion := not (CompareVersion(runtimes&#91;I], minimumVersion) = -1);\n      meetsMaximumVersion := CompareVersion(runtimes&#91;I], maximumExclusiveVersion) = -1;\n      if meetsMinimumVersion and meetsMaximumVersion then\n      begin\n        Log(Format('&#91;.NET] Selecting %s', &#91;runtimes&#91;I]]));\n        Result := False;\n          Exit;\n      end;\n    end;\n  end;\nend;\n\nprocedure InitializeWizard;\nbegin\n  if NetRuntimeIsMissing() then\n  begin\n    idpAddFile('http:\/\/go.microsoft.com\/fwlink\/?linkid=2155258', ExpandConstant('{tmp}\\NetRuntimeInstaller.exe'));\n    idpDownloadAfter(wpReady);\n  end;\nend;\n\nfunction InstallDotNetRuntime(): String;\nvar\n  StatusText: string;\n  ResultCode: Integer;\nbegin\n  StatusText := WizardForm.StatusLabel.Caption;\n  WizardForm.StatusLabel.Caption := CustomMessage('InstallingDotNetRuntime');\n  WizardForm.ProgressGauge.Style := npbstMarquee;\n  try\n    if not Exec(ExpandConstant('{tmp}\\NetRuntimeInstaller.exe'), '\/passive \/norestart \/showrmui \/showfinalerror', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) then\n    begin\n      Result := FmtMessage(CustomMessage('DotNetRuntimeFailedToLaunch'), &#91;SysErrorMessage(resultCode)]);\n    end\n    else\n    begin\n      \/\/ See https:\/\/msdn.microsoft.com\/en-us\/library\/ee942965(v=vs.110).aspx#return_codes\n      case resultCode of\n        0: begin\n          \/\/ Successful\n        end;\n        1602 : begin\n          MsgBox(CustomMessage('DotNetRuntimeFailed1602'), mbInformation, MB_OK);\n        end;\n        1603: begin\n          Result := CustomMessage('DotNetRuntimeFailed1603');\n        end;\n        1641: begin\n          requiresRestart := True;\n        end;\n        3010: begin\n          requiresRestart := True;\n        end;\n        5100: begin\n          Result := CustomMessage('DotNetRuntimeFailed5100');\n        end;\n        else begin\n          MsgBox(FmtMessage(CustomMessage('DotNetRuntimeFailedOther'), &#91;IntToStr(resultCode)]), mbError, MB_OK);\n        end;\n      end;\n    end;\n  finally\n    WizardForm.StatusLabel.Caption := StatusText;\n    WizardForm.ProgressGauge.Style := npbstNormal;\n    \n    DeleteFile(ExpandConstant('{tmp}\\NetRuntimeInstaller.exe'));\n  end;\nend;\n\nfunction PrepareToInstall(var NeedsRestart: Boolean): String;\nbegin\n  \/\/ 'NeedsRestart' only has an effect if we return a non-empty string, thus aborting the installation.\n  \/\/ If the installers indicate that they want a restart, this should be done at the end of installation.\n  \/\/ Therefore we set the global 'restartRequired' if a restart is needed, and return this from NeedRestart()\n\n  if NetRuntimeIsMissing() then\n  begin\n    Result := InstallDotNetRuntime();\n  end;\nend;\n\nfunction NeedRestart(): Boolean;\nbegin\n  Result := requiresRestart;\nend;<\/code><\/pre>\n\n\n\n<h2>Detecting if the desired .NET Runtime is installed<\/h2>\n\n\n\n<p><a href=\"https:\/\/www.aaflalo.me\/2020\/06\/net-core-detection-in-inno-setup\/\">Antoine Aflalo has found the registry key location<\/a> to check to see what versions of the .NET runtime are installed.<\/p>\n\n\n\n<p>Check <code>SOFTWARE\\dotnet\\Setup\\InstalledVersions\\x86\\sharedfx<\/code> on x86 or <code>SOFTWARE\\WOW6432Node\\dotnet\\Setup\\InstalledVersions\\x64\\sharedfx<\/code> on x64. Use <code>Microsoft.WindowsDesktop.App<\/code> for the Desktop Runtime and <code>Microsoft.NETCore.App<\/code> for the regular runtime.<\/p>\n\n\n\n<p>I am checking <code>SOFTWARE\\WOW6432Node\\dotnet\\Setup\\InstalledVersions\\x64\\sharedfx\\Microsoft.WindowsDesktop.App<\/code> .<\/p>\n\n\n\n<p>We are checking for a version installed &gt;= 5.0.0 and &lt; 5.1.0. Having .NET 6 installed would not run apps built for the .NET 5 Runtime.<\/p>\n\n\n\n<h2>Downloading the bootstrapper<\/h2>\n\n\n\n<p>First you need to locate the correct download link for the installer. This will depend on if you want the x86 or x64 version, what version you&#8217;re installing, and whether you want the normal or Desktop runtime. The <a href=\"https:\/\/github.com\/DomGries\/InnoDependencyInstaller\/blob\/master\/CodeDependencies.iss\">Inno Dependency Installer source code has a good listing of stable links<\/a>; I haven&#8217;t been able to find an official list yet.<\/p>\n\n\n\n<p>You could also use the Inno Dependency Installer instead, though I like having slimmer installer code for just the framework I want to install, and I think checking the registry is more efficient than including another executable to run and check the version.<\/p>\n\n\n\n<p>Now install <a href=\"https:\/\/mitrichsoftware.wordpress.com\/inno-setup-tools\/inno-download-plugin\/\">Inno Download Plugin<\/a>. This will make the idpAddFile and idpDownloadAfter calls inside InitializeWizard work. Remember that you need <code>#include &lt;idp.iss&gt;<\/code> at the top of your file to bring this in.<\/p>\n\n\n\n<p>You will also need to add a line to the end of <code>C:\\Program Files (x86)\\Inno Setup 6\\ISPPBuiltins.iss<\/code> . The Inno Download Plugin installer tries to do it but hasn&#8217;t been updated in a while and is still trying to update Inno Setup 5.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#pragma include __INCLUDE__ + \";\" + \"C:\\Program Files (x86)\\Inno Download Plugin\"<\/code><\/pre>\n\n\n\n<p>This &#8220;InitializeWizard&#8221; 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 &#8220;Ready to install&#8221; page. We include our direct download link determined earlier, and save it in a temp folder, with a file name of &#8220;NetRuntimeInstaller.exe&#8221;. This name I picked arbitrarily; we just need to refer to it later when we&#8217;re installing and cleaning up.<\/p>\n\n\n\n<h2>Installing the bootstrapper<\/h2>\n\n\n\n<p>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 InstallDotNetRuntime and return its result.<\/p>\n\n\n\n<p>Inside InstallDotNetRuntime we&#8217;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. <\/p>\n\n\n\n<p>The .NET installer UI will show on top of the first window:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img data-attachment-id=\"148\" data-permalink=\"https:\/\/engy.us\/blog\/2021\/02\/28\/installing-net-5-runtime-automatically-with-inno-setup\/image\/\" data-orig-file=\"https:\/\/i0.wp.com\/engy.us\/blog\/wp-content\/uploads\/2021\/02\/image.png?fit=646%2C461&amp;ssl=1\" data-orig-size=\"646,461\" data-comments-opened=\"1\" data-image-meta=\"{&quot;aperture&quot;:&quot;0&quot;,&quot;credit&quot;:&quot;&quot;,&quot;camera&quot;:&quot;&quot;,&quot;caption&quot;:&quot;&quot;,&quot;created_timestamp&quot;:&quot;0&quot;,&quot;copyright&quot;:&quot;&quot;,&quot;focal_length&quot;:&quot;0&quot;,&quot;iso&quot;:&quot;0&quot;,&quot;shutter_speed&quot;:&quot;0&quot;,&quot;title&quot;:&quot;&quot;,&quot;orientation&quot;:&quot;0&quot;}\" data-image-title=\"image\" data-image-description=\"\" data-medium-file=\"https:\/\/i0.wp.com\/engy.us\/blog\/wp-content\/uploads\/2021\/02\/image.png?fit=300%2C214&amp;ssl=1\" data-large-file=\"https:\/\/i0.wp.com\/engy.us\/blog\/wp-content\/uploads\/2021\/02\/image.png?fit=525%2C375&amp;ssl=1\" loading=\"lazy\" width=\"525\" height=\"375\" src=\"https:\/\/i0.wp.com\/engy.us\/blog\/wp-content\/uploads\/2021\/02\/image.png?resize=525%2C375&#038;ssl=1\" alt=\"\" class=\"wp-image-148\" srcset=\"https:\/\/i0.wp.com\/engy.us\/blog\/wp-content\/uploads\/2021\/02\/image.png?w=646&amp;ssl=1 646w, https:\/\/i0.wp.com\/engy.us\/blog\/wp-content\/uploads\/2021\/02\/image.png?resize=300%2C214&amp;ssl=1 300w\" sizes=\"(max-width: 525px) 100vw, 525px\" data-recalc-dims=\"1\" \/><\/figure>\n\n\n\n<p>After running the installer, the bootstrapper is deleted (whether or not the install succeeded).<\/p>\n\n\n\n<p>And now your installer is done!<\/p>\n\n\n\n<h2>Testing the installer<\/h2>\n\n\n\n<p>I would recommend setting up a Windows 10 VM in Hyper-V to test it out. You can go to &#8220;Apps and Features&#8221; and sort by install date to uninstall your app and the .NET Runtime to repeat the testing.<\/p>\n\n\n\n<p>Thanks to <a href=\"http:\/\/antonymale.co.uk\/\">Antony Male<\/a> for suggesting updates to the error handling code.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In this guide I will walk through how to get the .NET 5 runtime to download and install on-the-fly in an Inno Setup installer. It works in 3 steps: Detect if the desired .NET runtime is installed Download the .NET Runtime bootstrap installer with Inno Download Plugin Run the bootstrap installer in quiet mode, which &hellip; <\/p>\n<p class=\"link-more\"><a href=\"https:\/\/engy.us\/blog\/2021\/02\/28\/installing-net-5-runtime-automatically-with-inno-setup\/\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Installing .NET 5 Runtime Automatically with Inno Setup&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"spay_email":"","jetpack_publicize_message":"","jetpack_is_tweetstorm":false},"categories":[1],"tags":[],"jetpack_featured_media_url":"","jetpack_publicize_connections":[],"jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/pahBcK-2m","jetpack-related-posts":[{"id":51,"url":"https:\/\/engy.us\/blog\/2015\/07\/17\/installing-net-framework-4-5-automatically-with-inno-setup\/","url_meta":{"origin":146,"position":0},"title":"Installing .NET Framework 4.7 automatically with Inno Setup","date":"July 17, 2015","format":false,"excerpt":"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\u2026","rel":"","context":"In \".net framework 4.5 inno setup\"","img":{"alt_text":"","src":"https:\/\/i0.wp.com\/engy.us\/blog\/wp-content\/uploads\/2015\/07\/7028.InstallingFramework2-1.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":25,"url":"https:\/\/engy.us\/blog\/2012\/04\/06\/datetime-and-datetimeoffset-in-net-good-practices-and-common-pitfalls\/","url_meta":{"origin":146,"position":1},"title":"DateTime and DateTimeOffset in .NET: Good practices and common pitfalls","date":"April 6, 2012","format":false,"excerpt":"It becomes necessary to deal with dates and times in most .NET programs. A lot of programs use DateTime but that structure is frought with potential issues when you start serializing, parsing, comparing\u00a0and displaying dates from\u00a0different time zones and cultures. In this post I will go over these issues and\u00a0the\u2026","rel":"","context":"In \".net\"","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":174,"url":"https:\/\/engy.us\/blog\/2021\/12\/11\/aws-cognito-authentication-in-electron\/","url_meta":{"origin":146,"position":2},"title":"AWS Cognito Authentication in Electron","date":"December 11, 2021","format":false,"excerpt":"The AWS Cognito authentication service as of this writing does not officially support the Electron platform. But there is a Javascript SDK for Cognito, as part of AWS Amplify. Others have tried using it on Electron but have run into issues. I ran into several more than what are described\u2026","rel":"","context":"Similar post","img":{"alt_text":"","src":"https:\/\/i2.wp.com\/engy.us\/blog\/wp-content\/uploads\/2021\/12\/image.png?resize=350%2C200&ssl=1","width":350,"height":200},"classes":[]},{"id":165,"url":"https:\/\/engy.us\/blog\/2021\/10\/03\/efficient-svg-icons-in-web-components-with-webpack-and-svgo\/","url_meta":{"origin":146,"position":3},"title":"Efficient SVG icons in Web Components with Webpack and SVGO","date":"October 3, 2021","format":false,"excerpt":"So many ways to load them There are a lot of different ways to show an SVG on a webpage: <img>, <embed>, <object>, <iframe>, <canvas> and <svg> among them. I think for any halfway modern browser there are really only two serious contenders here. Referencing an SVG file: <img src=\"image.svg\"\u2026","rel":"","context":"Similar post","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":120,"url":"https:\/\/engy.us\/blog\/2020\/01\/01\/implementing-a-custom-window-title-bar-in-wpf\/","url_meta":{"origin":146,"position":4},"title":"Implementing a Custom Window Title Bar in WPF","date":"January 1, 2020","format":false,"excerpt":"There are several good reasons for wanting custom window chrome in WPF, such as fitting in additional UI or implementing a Dark theme. However the actual implementation is kind of tricky, since it is now your job to provide a bunch of features that you used to get for free.\u2026","rel":"","context":"With 12 comments","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]},{"id":82,"url":"https:\/\/engy.us\/blog\/2018\/10\/04\/item-change-tracking-in-dynamicdata\/","url_meta":{"origin":146,"position":5},"title":"Item change tracking in DynamicData","date":"October 4, 2018","format":false,"excerpt":"Since ReactiveUI's ReactiveList<T> has recently been deprecated, I've been moving to DynamicData. One feature I used from ReactiveList was item change tracking. It might look something like this: var myList = new ReactiveList<MyClass>(); myList.ChangeTrackingEnabled = true; myList.ItemChanged .Where(x => x.PropertyName == nameof(MyClass.SomeProperty)) .Select(x => x.Sender) .Subscribe(myObject => { \/\/ Do\u2026","rel":"","context":"With 5 comments","img":{"alt_text":"","src":"","width":0,"height":0},"classes":[]}],"_links":{"self":[{"href":"https:\/\/engy.us\/blog\/wp-json\/wp\/v2\/posts\/146"}],"collection":[{"href":"https:\/\/engy.us\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/engy.us\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/engy.us\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/engy.us\/blog\/wp-json\/wp\/v2\/comments?post=146"}],"version-history":[{"count":6,"href":"https:\/\/engy.us\/blog\/wp-json\/wp\/v2\/posts\/146\/revisions"}],"predecessor-version":[{"id":184,"href":"https:\/\/engy.us\/blog\/wp-json\/wp\/v2\/posts\/146\/revisions\/184"}],"wp:attachment":[{"href":"https:\/\/engy.us\/blog\/wp-json\/wp\/v2\/media?parent=146"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/engy.us\/blog\/wp-json\/wp\/v2\/categories?post=146"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/engy.us\/blog\/wp-json\/wp\/v2\/tags?post=146"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}