Inno Setup で .NET Framework をインストールさせる方法を調べました

Windows のインストーラーで、 .NET Framework をインストールさせたかったので調べました。

InstallShield はウィザードから選択するだけで、 .NET Framework がなければインストールさせることができたのですが、 Inno Setup は実装する必要があるようです。

環境

  • Windows 10 Pro
  • Inno Setup 5.5.9

.NET Framework がインストールされていることをチェックする

簡単にコマンドで結果が得られるかな?と思っていましたが、レジストリーをチェックする必要があるみたいでした。

.NET Framework 1~4.0 の場合

.NET Framework 1~4.0 までは、次のレジストリーを調べれば良いみたいです。

To find .NET Framework versions by viewing the registry (.NET Framework 1-4)

…略…

In the Registry Editor, open the following subkey:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP 

The installed versions are listed under the NDP subkey. The version number is stored in the Version entry. For the .NET Framework 4 the Version entry is under the Client or Full subkey (under NDP), or under both subkeys.

Note

The “NET Framework Setup” folder in the registry does not begin with a period.

How to: Determine which .NET Framework versions are installed | Microsoft Docs

NDP の下の Version エントリーを確認すれば良いみたいです。

.NET Framework 4.0 については、 NDP の下に次のサブキーが登録されているようです。

  • Clinet Profile: NDP\Client
  • 完全な .NET Framework: NDP\Full

Client Profile?

Client Profile は次のようなもののようです。

.NET Framework 4 Client Profile は、クライアント アプリケーション用に最適化された .NET Framework 4 のサブセットです。 WPF (Windows Presentation Foundation)、Windows フォーム、WCF (Windows Communication Foundation)、および ClickOnce の機能を含むほとんどのクライアント アプリケーション向けの機能が用意されています。 これにより、.NET Framework 4 Client Profile を対象とするアプリケーションの配置が速くなり、インストール パッケージが小さくなります。

…略…

.NET Framework 4 Client Profile に含まれていない機能

.NET Framework 4 Client Profile には、次の機能は含まれていません。 アプリケーションでこれらの機能を使用するには、.NET Framework 4 をインストールする必要があります。

  • ASP.NET
  • Windows Communication Foundation (WCF) の高度な機能
  • .NET Framework Oracle 用データ プロバイダー
  • コンパイルに使用する MSBuild

.NET Framework Client Profile

これを .NET Framework がインストールされていると判断するか、しないか、あると思いまして。 インストーラーを作成するような、クライアント向けに作成したアプリケーションであれば、 .NET Framework がインストールされていると判断しても良さそうです。

そのアプリケーションが、なぜか、クライアント向けであるにもかかわらず、 ASP.NET で実装されたウェブアプリケーションだったりすると、話はまた別だと思いますけど。 このあたりは、 “含まれていない機能” を参考に判断すれば良さそうです。

.NET Framework 4.5 and later の場合

.NET Framework 4.5 以降は、次のレジストリーを調べれば良いみたいです。

To find .NET Framework versions by viewing the registry (.NET Framework 4.5 and later)

In the Registry Editor, open the following subkey:

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4\Full

Note

that the path to the Full subkey includes the subkey Net Framework rather than .NET Framework.

Check for a DWORD value named Release. The existence of the Release DWORD indicates that the .NET Framework 4.5 or newer has been installed on that computer.

How to: Determine which .NET Framework versions are installed | Microsoft Docs

NDP\v4\FullRelease をチェックすれば良いみたいです。 リンク先に Release の値とバージョンの対応の表があるので、それに従って制御する必要がありそうです。

Inno Setup での実装

このチェックを Inno Setup のスクリプトで実装してくれている方がいるようでした。

InitializeSetup 関数の中に記述されているバージョンとサービスパックの指定を要件に合うように変えてあげれば良さそうです。 必要であればメッセージボックスの制御も変えます。

チェックのタイミングは、 [Files] セクションの Source の行に Check: function とつけて、ファイルをコピーする前のタイミングでチェックしているようです。 チェックが False であれば、ファイルをコピーしないし、 .NET Framework もインストールしないイメージかな。

.NET Framework をインストールさせる

まず、 .NET Framework のインストーラーを探しました。

.NET Framework 4.7.1 (Offline Installer)

次のリンクからオフラインインストーラーをダウンロードしました。

Download Microsoft .NET Framework 4.7.1 (Offline Installer) for Windows 7 SP1, Windows 8.1, Windows 10 Anniversary Update, Wind

Web Installer でも良いと思いますが、 setup.exe のインストーラーで完結する形にしたかったので、オフラインインストーラーをダウンロードしました。

.NET Framework の配置ガイドです。

Chaining by using the default .NET Framework UI

To silently chain the .NET Framework installation process and let the .NET Framework installer provide the UI, add the following command to your setup program:

<.NET Framework redistributable> /q /norestart /ChainingPackage <PackageName>

…略…

/norestart

Prevents the setup program from rebooting automatically. If you use this option, the chaining app has to capture the return code and handle rebooting (see Getting Progress Information from an Installation Package in the MSDN Library).

/q

Sets quiet mode.

.NET Framework deployment guide for developers | Microsoft Docs

インストーラーに含めて、その中で実行する場合は、 /norestart と /q オプションをつけるようです。

/norestart は、自動的に再起動させないためのオプションのようです。 これをつける場合は、呼び出す側でリターンコードを受け取って、後で再起動をさせる(自動でも手動でも)ようにする必要があるようです。

/q は、 .NET Framework 自体のインストーラーのプログレスウィンドウを表示しないためのオプションのようです。

Inno Setup での実装

次のリンクに同様の QA がありました。

.NET Framework のインストーラーを起動するには、 Exec を使えば良さそうです。 リターンコードは Exec の 6 つ目の引数で受け取れるようです。

それから、 [File] セクションの Source の行に、 AfterInstall: procedure をつけることで、コピーした後にスクリプトを実行することができるようです。

Inno Setup のコード

調べたことをまとめて、次のようなコードになりました。

[Files]
Source: "testsetup\NDP471-KB4033342-x86-x64-AllOS-ENU.exe"; DestDir: {tmp}; Flags: deleteafterinstall; AfterInstall: InstallFramework; Check: not InitializeSetup

[Code]
// [Inno Setup: Verify that .NET 4.0 is installed - Stack Overflow](https://stackoverflow.com/questions/4104011/inno-setup-verify-that-net-4-0-is-installed)
// [www.kynosarges.de/DotNetVersion.html](www.kynosarges.de/DotNetVersion.html)
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
//
// 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
    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.7.1', 0) then begin
    // comment out
    //MsgBox('MyApp requires Microsoft .NET Framework 4.7.1.'#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;

// [inno setup - How can I install .NET framework as a prerequisite using InnoSetup? - Stack Overflow](https://stackoverflow.com/questions/20752882/how-can-i-install-net-framework-as-a-prerequisite-using-innosetup#20753218)
procedure InstallFramework;
var
  ResultCode: Integer;
begin
  if not Exec(ExpandConstant('{tmp}\NDP471-KB4033342-x86-x64-AllOS-ENU.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;

終わり

インストーラーはテストでインストール、アンインストールを繰り返すのが面倒でした。