HOWTO include Visual C++ RTL in a Inno Setup kit

Introduction

Compiling our programs using external RTLs is generally always a good option to have smaller & efficient programs.

In the case of Visual Studio ones, the best option is to use its own setup kit, because:

  • it frees us from caring about RTL requirements (needed files, components registrations, …)
  • from Visual Studio 2015 to 2022, it is a single standard package.
  • once installed, it is usually maintained by Windows Update.

On the other side, one of the few drawbacks is that it is that the whole package is about 24 Mb for the 64-bits and 14 Mb for 32-bits versions.  It can be a bit excessive for small programs, and a problem to keep updated in our setup package.

The optimal solution might be to download (from MS site) and install it, if not already present.

The Solution

After some research, I downloaded a sample, and after some work, I wrote this Object Pascal code to be added in the [Code] section in our Inno Setup script.

{ ---- VCRedist processing code ----- }
// minimal RTL version required (default: 14.23.27820.0)
// minimal VC2019 RTL version: 14.29.30133.0
// minimal VC2022 RTL version: 14.32.31326.0
const VCRTL_MIN_V1 = 14;
const VCRTL_MIN_V2 = 23;
const VCRTL_MIN_V3 = 27820;
const VCRTL_MIN_V4 = 0;

 // check if the needed RTL 32/64 bits is installed (by looking the registry)
function RTL_IsNeeded (bUse64BitsRTL: Boolean): Boolean;
var
  sRegKey: string;
  v1: Cardinal;
  v2: Cardinal;
  v3: Cardinal;
  v4: Cardinal;
begin
  // is it running on 64 bits OS?
  if IsWin64 then
  begin
    sRegKey := 'SOFTWARE\WOW6432Node\Microsoft\VisualStudio\14.0\VC\Runtimes\';
    // need 64 bits RTL?
    if bUse64BitsRTL then
      sRegKey := sRegKey + 'x64'
    else
      sRegKey := sRegKey + 'x86'
  end
  // else we have only 32 bits RTL on 32 bits OS...
  else if not bUse64BitsRTL then
    sRegKey := 'SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\X86';
  // if we have a key, let's check it
  if ((sRegKey <> '') and
      RegQueryDWordValue (HKEY_LOCAL_MACHINE, sRegKey, 'Major', v1)  and
      RegQueryDWordValue (HKEY_LOCAL_MACHINE, sRegKey, 'Minor', v2) and
      RegQueryDWordValue (HKEY_LOCAL_MACHINE, sRegKey, 'Bld', v3) and
      RegQueryDWordValue (HKEY_LOCAL_MACHINE, sRegKey, 'RBld', v4)) then
  begin
    Log ('VC 2015-2022 Redist version: ' + IntToStr (v1) +
        '.' + IntToStr (v2) + '.' + IntToStr (v3) +
        '.' + IntToStr (v4));
    { Version info was found. Return true if later or equal to our
       minimal required version RTL_MIN_Vx }
    Result := not (
        (v1 > VCRTL_MIN_V1) or ((v1 = VCRTL_MIN_V1) and
         ((v2 > VCRTL_MIN_V2) or ((v2 = VCRTL_MIN_V2) and
          ((v3 > VCRTL_MIN_V3) or ((v3 = VCRTL_MIN_V3) and
           (v4 >= VCRTL_MIN_V4)))))));
  end
  else
    Result := TRUE;
end;

// onDownload event handler
function OnDownloadProgress (const Url, FileName: String;
    const Progress, ProgressMax: Int64): Boolean;
begin
  if Progress = ProgressMax then
    Log(Format('Successfully downloaded file to {tmp}: %s', [FileName]));
  Result := TRUE;
end;

// download and install the requested RTL 32/64 bits
function RTL_DownloadAndInstall (bUse64BitsRTL: Boolean): Boolean;
var
  ret_code: Integer;
  sFile: String;
  sSetupFile: String;
  DownloadPage: TDownloadWizardPage;
begin
  DownloadPage := CreateDownloadPage (SetupMessage (msgWizardPreparing),
        'Needed Visual Studio C++ Runtime files...', @OnDownloadProgress);
  DownloadPage.Clear;
  if bUse64BitsRTL then
    sSetupFile := 'VC_redist.x64.exe'
  else
    sSetupfile := 'VC_redist.x86.exe';
  DownloadPage.Add ('https://aka.ms/vs/17/release/' + sSetupFile,
      sSetupFile, '');
  DownloadPage.Show;
  try
    try
      DownloadPage.Download;
      { run the downloaded setup files }
      sFile := ExpandConstant ('{tmp}\' + sSetupFile);
      Log (Format ('- starting: %s', [sFile]));
      DownloadPage.SetText ('installing package...', sSetupFile);
      Exec (sFile, '/install /passive /norestart', '',
          SW_SHOW, ewWaitUntilTerminated, ret_code);
      Log (Format ('- done : %u', [ret_code]));
      { let's go on!! }
      Result := TRUE;
    except
      SuppressibleMsgBox (AddPeriod (GetExceptionMessage), mbCriticalError,
          MB_OK, IDOK);
      Result := FALSE;
    end;
  finally
    DownloadPage.Hide;
  end;
end;

To link it to the setup flow, you can add something like the following Object Pascal function to the [Code] section:

function NextButtonClick (CurPageID: Integer): Boolean;
var
  bUse64BitsRTL: Boolean;
begin
  Result := TRUE;
  if (CurPageID = wpReady) then
  begin
    { do we need to download RTM? }
    bUse64BitsRTL := TRUE;
    if RTL_IsNeeded (bUse64BitsRTL) then
      Result := RTL_DownloadAndInstall (bUse64BitsRTL);
  end
end;

At this point, you have only the set bUse64BitsRTL to properly select the needed RTL type (32/64-bits) and the scripts should take care of all the dirty work.

If you need, you can also set a specific minimal RTL version by updating VCRTL_MIN_Vx constants.  You can get your current one by looking in the folder below in your Visual Studio root folder:

<Visual Studio root folder>\VC\Redist\MSVC

One of the remarkable things is that this script is using only Inno Setup included features (ie no external plugins, …).

 

Finally, the only serious drawback of this solution is when the MS site is down (yes, it happened).  If this file is needed but it cannot be downloaded, this means that your program cannot even start.

My current solution is to offer an emergency page to download a local and signed copy of the needed RTL setup file.