In the previous tutorial we learned how to use Inno Setup to create a basic installer for Windows programs. In this tutorial we will learn how to install prerequisite software, if necessary.

As an example we will add a check to install the Visual C++ 2017 Redistributable to the previous "Stécker vum Himmel" installer.

The Runtime Library Installer

The latest Visual C++ 2017 Redistributable installer can be downloaded from the official Microsoft website.


Scripting

Adding the Installer

The first thing to do is to add the installer file to the Inno Setup script. By now you should be familiar with the scripting language, thus this step is very easy. I copied the installer into the "tmp" subdirectory:

[Files]
Source: "O:\Downloads\SvH\tmp\vc_redist.x64.exe"; DestDir: {tmp}; Flags: deleteafterinstall

Source: "O:\Downloads\SvH\x64\Stécker vum Himmel.exe"; DestDir: "{app}\x64"; Flags: ignoreversion
Source: "O:\Downloads\SvH\Data\*"; DestDir: "{app}\Data"; Flags: ignoreversion recursesubdirs createallsubdirs
Source: "O:\Downloads\SvH\x64\lua53.dll"; DestDir: "{app}\x64"; Flags: ignoreversion
DestDir

The "{tmp}" destination directory means that the file will be extracted into the temporary folder on the user's computer.

Flags

The "deleteafterinstall" flag states that the "Visual C++ 2017 Redistributable"-installer will be deleted after the runtimes are installed.


Running the Installer

To run the installer, we have to add another simple line of code to "Inno Setup"-script:

[RUN]
Filename: "{tmp}\vc_redist.x64.exe"; Check: VCRedistNeedsInstall; StatusMsg: Installing Visual Studio Runtime Libraries...
Filename: "{app}\x64\{#MyAppExeName}"; Description: "{cm:LaunchProgram,{#StringChange(MyAppName, '&', '&&')}}"; Flags: nowait postinstall skipifsilent

Once again notice that the "exe" is installed in the users temporary folder.

Check

The "Check" parameter is the name of a function that is to be called and evaluated before the "exe" is run. We will see the code of that function in a moment, for now it just matters to know that the "vc_redist.x64.exe" will only be run if the "check"-function returns true.

StatusMsg

The "StatusMsg" parameter contains the text that "Inno Setup" will display as the Visual Studio runtimes are being installed.


Pascal

Welcome back to the good old times! An Inno Setup-script can contain Pascal code. We will write a simple script to check whether the Visual C++ 2017 Redistributables are already installed or not.

Unique IDs

As we learned way back in a previous tutorial, each Windows program has a unique ID, thus to check whether the Visual C++ 2017 Redistributables are installed or not, we simply query whether software with a given ID is registered in the registry or not. The ID for the latest Visual C++ 2017 Redistributables are as follows:

const
  { Visual C++ 2017 Redistributable 14.16.27024 }
  VC_2017_REDIST_X84_ADD = '{7258184A-EC44-4B1A-A7D3-68D85A35BFD0}';
  VC_2017_REDIST_X84_MIN = '{5EEFCEFB-E5F7-4C82-99A5-813F04AA4FBD}';

  VC_2017_REDIST_X64_ADD = '{9D29FC96-9EEE-4253-943F-96B3BBFDD0B6}';
  VC_2017_REDIST_X64_MIN = '{F1B0FB3A-E0EA-47A6-9383-3650655403B0}';

You can find product codes for other Visual Studio versions in the registry under "HKEY_CLASSES_ROOT\Installer\Dependencies"; for example "HKEY_CLASSES_ROOT\Installer\Dependencies\Microsoft.VS.VC_RuntimeMinimumVSU_x86,v14".

Installation State

To query the installation state of a certain program, Microsoft offers the MsiQueryProductState function:

INSTALLSTATE MsiQueryProductStateA(
  LPCSTR szProduct
);

The parameter for the function, a long pointer to a constant string, specifies the product code that identifies the product to be queried.

The return value is one of the following:

Value Meaning
INSTALLSTATE_ABSENT The product is installed for a different user.
INSTALLSTATE_ADVERTISED The product is advertised but not installed.
INSTALLSTATE_DEFAULT The product is installed for the current user.
INSTALLSTATE_INVALIDARG An invalid parameter was passed to the function.
INSTALLSTATE_UNKNOWN The product is neither advertised or installed.

To use this function inside our "Inno Setup"-script, we first define the return values as constant variables:

[Code]
  type
  INSTALLSTATE = Longint;

  const
  INSTALLSTATE_INVALIDARG = -2;  { An invalid parameter was passed to the function. }
  INSTALLSTATE_UNKNOWN = -1;     { The product is neither advertised or installed. }
  INSTALLSTATE_ADVERTISED = 1;   { The product is advertised but not installed. }
  INSTALLSTATE_ABSENT = 2;       { The product is installed for a different user. }
  INSTALLSTATE_DEFAULT = 5;      { The product is installed for the current user. }

  { Visual C++ 2017 Redistributable 14.16.27024 }
  VC_2017_REDIST_X84_ADD = '{7258184A-EC44-4B1A-A7D3-68D85A35BFD0}';
  VC_2017_REDIST_X84_MIN = '{5EEFCEFB-E5F7-4C82-99A5-813F04AA4FBD}';

  VC_2017_REDIST_X64_ADD = '{9D29FC96-9EEE-4253-943F-96B3BBFDD0B6}';
  VC_2017_REDIST_X64_MIN = '{F1B0FB3A-E0EA-47A6-9383-3650655403B0}';

Once done, we can embed the Windows function into a pascal function as follows:

function MsiQueryProductState(szProduct: string): INSTALLSTATE; 
    external 'MsiQueryProductState{#AW}@msi.dll stdcall';

The {#AW} directive makes sure that the code works with unicode and ANSI version of Inno Setup:

#IFDEF UNICODE
  #DEFINE AW "W"
#ELSE
  #DEFINE AW "A"
#ENDIF

Now all that is left to do is to call the Windows function, embedded in a Pascal function, to check whether the Visual C++ 2017 Redistributables are installed or not:

function VCVersionInstalled(const ProductID: string): Boolean;
begin
  Result := MsiQueryProductState(ProductID) = INSTALLSTATE_DEFAULT;
end;

    function VCRedistNeedsInstall: Boolean;
begin
  Result := not VCVersionInstalled(VC_2017_REDIST_X64_MIN);
end;

Remember, when we added the installation file for the Visual C++ Redistributables, we added a "Check" parameter: VCRedistNeedsInstall. Now before the installation starts, the function "VCRedistNeedsInstall" is called, which in turn calls the "VCVersionInstalled" function, which uses the function defined by Windows to check whether the software we are querying for is installed or not.

If, and only if, the function VCRedistNeedsInstall returns true, that is, if the prerequisite software is not installed, Inno Setup runs the installer for the requested software.


That wasn't that bad now, was it? Here is the entire code at once:

...
type
  INSTALLSTATE = Longint;

  const
  INSTALLSTATE_INVALIDARG = -2;  { An invalid parameter was passed to the function. }
  INSTALLSTATE_UNKNOWN = -1;     { The product is neither advertised or installed. }
  INSTALLSTATE_ADVERTISED = 1;   { The product is advertised but not installed. }
  INSTALLSTATE_ABSENT = 2;       { The product is installed for a different user. }
  INSTALLSTATE_DEFAULT = 5;      { The product is installed for the current user. }

  { Visual C++ 2017 Redistributable 14.16.27024 }
  VC_2017_REDIST_X84_ADD = '{7258184A-EC44-4B1A-A7D3-68D85A35BFD0}';
  VC_2017_REDIST_X84_MIN = '{5EEFCEFB-E5F7-4C82-99A5-813F04AA4FBD}';

  VC_2017_REDIST_X64_ADD = '{9D29FC96-9EEE-4253-943F-96B3BBFDD0B6}';
  VC_2017_REDIST_X64_MIN = '{F1B0FB3A-E0EA-47A6-9383-3650655403B0}';

function MsiQueryProductState(szProduct: string): INSTALLSTATE; 
  external 'MsiQueryProductState{#AW}@msi.dll stdcall';

function VCVersionInstalled(const ProductID: string): Boolean;
begin
  Result := MsiQueryProductState(ProductID) = INSTALLSTATE_DEFAULT;
end;

function VCRedistNeedsInstall: Boolean;
begin
  Result := not VCVersionInstalled(VC_2017_REDIST_X64_MIN);
end;


Happy installing!


References