From MSI to WiX, Part 13 - Installable Items - Environment variable

The main page for the series is here.

 

Introduction

I am beginning a mini-series on installable items.  Today's topic - environment variables.

MSI story

In order to install an environment variable we need to add a record into the Environment table.  Here are the fields we need to fill:

  • Environment - public key to be referenced from other tables.
  • Name - name of the environment variable we want to create.
  • Value - value of new environment variable, which is formatted field.
  • Component_ - external key to the Component table which installs this environment variable.

Also, WriteEnvironmentStrings and RemoveEnvironmentStrings standard actions must be scheduled in the InstallExecuteSequence table.  If we are setting the value of the environment variable to the value of some property, make sure that this property gets its value before WriteEnvironmentStrings action.

What we can do with environment variable?

These are the options we have when we install/delete an environment variable:

  • On install: 
    • Create new variable if the variable with the same name does not exist:
      • If variable with the same name existed before:
        • Overwrite its value with the new value.
        • Don't change the value and ignore the new value.
        • Append new value at the beginning of the list of values stored in the environment variable.
        • Append new value at the end of the list of values stored in the environment variable.
    • Delete an environment variable:
      • Regardless of its value.
      • Only if its value matches provided value.
    • Variable can be created in:
      • User's environment (used in per-user installation).
      • System's environment (used in per-machine installation).
  • On uninstall:
    • Do not delete environment variable.
    • Delete environment variable.

In MSI we are using prefixes to the name of the variabble and prefixes and suffixes to the variable's value to indicate what we want to do with the variable.  In WiX, we are using attributes of <Environment> element for that.

How it translates to WiX?

Next table shows MSI's prefixes and suffixes for Environment table and their attribute equivalents in <Environment> element:

Environment table Environment element'sattribute Description
Name column Value column
=   Action="set" Create the environment variable if it does not exist.  Set its value to a new value, possibly overwriting existing value.
+   Action="create" Create the environment variable if it does not exist.  Do not overwrite the value if variable already exists.
-missing   Permanent="no"Permanent="yes" Remove environment variable on uninstall.Do not remove environment variable on uninstall.
!

empty column

Action="remove" Value="" Remove environment variable on install regardless of its value.
XYZ Action="remove" Value="XYZ" Remove environment variable on install if its value match the value of Value attribute.
*missing   System="yes"System="no" System environment variable.User environment variable.
  [~];Value Part="last" Append a value to the end of an existing value.
  Value;[~] Part="first" Prefix a value to the front of an existing value.
  Value Part="all" Set variable to a new value.
  [~]^Value Part="last" Separator="^" Append a value to the end of an existing value.  Use symbol '^' as a separator.
  Value^[~] Part="first" Separator="^" Prefix a value to the front of an existing value.  Use symbol '^' as a separator.

Also, WiX will schedule WriteEnvironmentStrings and RemoveEnvironmentStrings standard actions for you.  If you want to put conditions on these actions, their WiX counterparts are <WriteEnvironmentStrings> and <RemoveEnvironmentStrings> respectively.  They must be scheduled in the Execute sequence tables.

Examples

Let's start with something simple:

<?xml version="1.0" encoding="UTF-8"?>

<Wix xmlns="https://schemas.microsoft.com/wix/2003/01/wi">

 

  <Product Id="{A25A5612-112C-4052-BD79-AA2767F3443C}"

           Name="Minimal Windows Installer Sample"

           Language="1033"

           Version="1.0.0.0"

           Manufacturer="Acme Corporation"

           UpgradeCode="{A9B4BC63-1AE8-4E9D-9101-0A37F4293BB7}" >

    <Package Id="????????-????-????-????-????????????"

             Description="Minimal Windows Installer Sample"

             Comments="This installer database contains the logic and data required to install Minimal Windows Installer Sample."

             InstallerVersion="200"

             Compressed="yes"

             Languages="1033" />

    <Media Id="1" Cabinet="Product.cab" EmbedCab="yes" />

    <Directory Id="TARGETDIR" Name="SourceDir">

      <Directory Id="ProgramFilesFolder">

        <Directory Id="INSTALLLOCATION"

                   Name="Minimal"

                   LongName="MinimalInstallation">

          <Component Id="Component1"

                     Guid="{1781A625-8ACB-45E7-A8BA-219D81760B2E}">

            <CreateFolder />

            <Environment Id="TestMinVar"

                         Action="set"

                         Part="all"

                         Name="MinEnvVar"

                         Permanent="no"

                         System="no"

                         Value="123" />

          </Component>

        </Directory>

      </Directory>

    </Directory>

    <Feature Id="Feature1"

             Title="Feature1 Title"

             Level="1">

      <ComponentRef Id="Component1" />

    </Feature>

  </Product>

</Wix>

This sample will create new User's variable TestMinVar with the value "123".  Let's compile and install the sample application.  Make sure that new environment variable has been created.  Uninstall the Minimal application.

Remember that at the beginning I said that the Value column of the Environment table is a formatted field.  That means that we can set the value of the environment variable to the value of property, another environment variable, or anything that can be represented by formatted string.  Let's use our environment variable to preserve INSTALLLOCATION:

            <Environment Id="TestMinVar"

                         Action="set"

                         Part="all"

                         Name="MinEnvVar"

                         Permanent="no"

                         System="no"

                         Value="[INSTALLLOCATION]" />

We also saw, that we can add the new value to existing variable.  Let's make our original sample's variable permanent:

            <Environment Id="TestMinVar"

                         Action="set"

                         Part="all"

                         Name="MinEnvVar"

                         Permanent="yes"

                         System="no"

                         Value="123" />

Now, compile, install, and uninstall the sample.  That will leave the environment variable in the User's profile.  Let's add string "456" at the end of the TestMinVar variable (don't forget to set Permanent attribute to "no" again):

            <Environment Id="TestMinVar"

                         Action="set"

                         Part="last"

                         Name="MinEnvVar"

                   Permanent="no"

                         System="no"

                         Value="456" />

What if we want to add "456" and "789"?  Can we do that:

            <Environment Id="TestMinVar"

                         Action="set"

                         Part="last"

                         Name="MinEnvVar"

                         Permanent="no"

                         System="no"

                         Value="456;789" />

If you compile, install, and uninstall the sample you will see that it appears to work, but still don't install two values using one <Environment> element.  This is what MSDN is saying (Environment table):

"Each row can contain only one value. For example, the entry Value;Value;[~] is more than one value and should not be used because it causes unpredictable results. The entry Value;[~] is just one value."

Use two separate records to add two values to environment variable:

            <Environment Id="TestMinVar"

                         Action="set"

                         Part="last"

                         Name="MinEnvVar"

                         Permanent="no"

                         System="no"

                         Value="456" />

            <Environment Id="TestMinVar"

                         Action="set"

                         Part="last"

                         Name="MinEnvVar"

                         Permanent="no"

                         System="no"

                         Value="789" />

To test conditions on standard actions you can use this sample:

<?xml version="1.0" encoding="UTF-8"?>

<Wix xmlns="https://schemas.microsoft.com/wix/2003/01/wi">

 

  <Product Id="{A25A5612-112C-4052-BD79-AA2767F3443C}"

           Name="Minimal Windows Installer Sample"

           Language="1033"

           Version="1.0.0.0"

           Manufacturer="Acme Corporation"

           UpgradeCode="{A9B4BC63-1AE8-4E9D-9101-0A37F4293BB7}" >

    <Package Id="????????-????-????-????-????????????"

             Description="Minimal Windows Installer Sample"

             Comments="This installer database contains the logic and data required to install Minimal Windows Installer Sample."

             InstallerVersion="200"

             Compressed="yes"

             Languages="1033" />

    <Media Id="1" Cabinet="Product.cab" EmbedCab="yes" />

    <Property Id="EnableEV" Value="1" />

    <Directory Id="TARGETDIR" Name="SourceDir">

      <Directory Id="ProgramFilesFolder">

        <Directory Id="INSTALLLOCATION"

                   Name="Minimal"

                   LongName="MinimalInstallation">

          <Component Id="Component1"

                     Guid="{1781A625-8ACB-45E7-A8BA-219D81760B2E}">

            <CreateFolder />

            <Environment Id="TestMinVar"

                         Action="set"

                         Part="all"

                         Name="MinEnvVar"

                         Permanent="no"

                         System="no"

                         Value="123" />

          </Component>

        </Directory>

      </Directory>

    </Directory>

    <Feature Id="Feature1"

             Title="Feature1 Title"

             Level="1">

      <ComponentRef Id="Component1" />

    </Feature>

    <InstallExecuteSequence>

      <WriteEnvironmentStrings>EnableEV=1</WriteEnvironmentStrings>

    </InstallExecuteSequence>

  </Product>

</Wix>

Here the installation of environment variables depends on the value of EnableEV property.  If in your real life program you'll set this variable during user interface phase (in the client process), you might want to change the declaration of this variable to make it public and secure:

    <Property Id="ENABLEEV" Value="1" Secure="yes" />

    . . .

    <InstallExecuteSequence>

      <WriteEnvironmentStrings>ENABLEEV=1</WriteEnvironmentStrings>

    </InstallExecuteSequence>

That will ensure that ENABLEEV property will be passed from client to server process and will preserve its value.  See here for more details.

 

InstallEnvironmentVariable.zip