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

Comments

  • Anonymous
    January 05, 2009
    Hi Alex, <Environment Id="PATH" Name="PATH" Value="[path_configmgmt]" Permanent="yes" Part="last" Action="set" System="yes" /> As you see, I am appending a value to my path. I am putting an exe in the dir that refers to [path_configmgmt]. The path is being set. But when I say start-->Run it says the exe is not found. However, when I open the env window mannually and open the path variable and click OK, with out making any changes, the start-->Cmd works. It works after I just visit the env window mannually. What might be happening ?

  • Anonymous
    February 02, 2009
    @edward jones - I have seen the same behavior when adding an environment variable (on Windows Server 2003). New console windows don't seem to inherit it unless you go in and touch it in the control panel env window. I wonder if the shell somehow is not getting the WM_SETTINGCHANGE message... We resorted to scheduling a reboot, which otherwise would have been unnecessary.

  • Anonymous
    August 11, 2010
    Hi Alex, Is there a way to get an environment variable? I'm trying to get the USERPROFILE with: <Property Id="UserFolder"  Value="$(env.USERPROFILE)EdwardsAppMyFolder"/> But this only picks up the USERPROFILE of the build machine, where the installer is built. I want it to use the USERPROFILE of the machine where the app is being installed. Any suggestions would be appreciated. Thanks.

  • Anonymous
    August 13, 2010
    Hi Edward, You need to use [%USERPROFILE].   That may not work if your installer is creating USERPROFILE environment variable.

  • Anonymous
    April 19, 2011
    Hi Alex, <Environment Id="InstallationDirectory" Action="set" Part="all" Name="InstallationPathFolder" Permanent="no" System="no" Value="[INSTALLDIR]"/> After that, I am executing the batch file, in which I am taking the path using %InstallationPathFolder% But it is not taking the path I am setting, and taking the path of system32 folder. So, please tell what to do so that Environment Variable is immediately set and can be used by the batch files Thanks