Using WiX 3.0 to create a per-user MSI that does not prompt for elevation on Windows Vista

A while back, I posted a summary of some information I learned about how Windows Installer interacts with UAC on Windows Vista.  Included in that blog post is a list of steps that must be taken to create a per-user MSI that will not cause a Windows Vista UAC elevation prompt when installing it.

Since then, I've gotten a few questions about exactly how to create an MSI that implements those guidelines.  As a result, I decided to create an example MSI to demonstrate these concepts, and also post the setup authoring for this example (created using a recent build of WiX 3.0).  Hopefully, this example will help translate the per-user MSI creation guidelines into real-world MSI concepts so that anyone who wants to create their own per-user MSIs will be able to do so relatively easily.

For this example, I chose to create a per-user installer for a Windows Media Center Presentation Layer Web application because this type of application does not require installing any per-machine resources (as long as you do not user the /allusers switch when calling RegisterMceApp as a custom action during setup to register the application with Media Center).  However, the general concepts demonstrated in this example will work for other types of per-user applications and not just per-user Media Center applications.

Where to download the example files

You can download the example MSI and setup authoring from the following locations:

How to build the example MSI

I included a copy of the built MSI in the sample zip file, but you may also want to modify the source and rebuild the MSI to experiment with some of the settings.  To build the MSI using the source files included in this example, you can use the following steps:

  1. Download and install wix3.msi from the latest build of WiX 3.0 at https://wix.sourceforge.net/releases
  2. Download and extract the contents of the example zip file from https://cid-27e6a35d1a492af7.skydrive.live.com/self.aspx/Blog%7C_Tools/example%7C_per%7C_user%7C_msi.zip
  3. Make any desired changes to the sample setup authoring files
  4. Run the file build.bat to compile and link the MSI using the Candle and Light tools included in WiX 3.0

Details about how the example was implemented

Rather than walk through the entire WXS file, I have included comments in the text of the WXS file to explain the purpose for each of the blocks of code in the file.  In addition, I want to specifically highlight a few key parts of the setup authoring that are needed in order to build an MSI that will not cause a UAC prompt to appear during installation on Windows Vista.  These highlighted items match the requirements for creating an MSI that will not prompt for elevation that are enumerated in this MSDN topic and that I summarized in my previous blog post.

1. Ensure that no actions performed in your MSI require elevation

This means that all files and registry must be created in a per-user location and no custom actions can require administrator privileges.  In this example, the directory that the application files are installed to is located under the per-user application data folder.  This is defined in the following authoring:

<Directory Id="LocalAppDataFolder" Name="AppData">
    <Directory Id="AppRootDirectory" Name="MyApplication"/>
</Directory>

In addition, the component that includes the payload for this example MSI includes a CreateFolder entry, a RemoveFolder entry and a HKEY_CURRENT_USER registry entry that is marked as the key path.  This is defined in the following authoring:

<DirectoryRef Id="AppRootDirectory">
    <Component Id="Registration.xml" Guid="f671ee4d-dd0a-4f7f-a4d1-1d181d2f3002" DiskId="1">
        <CreateFolder/>
        <RemoveFolder Id="RemoveAppRootDirectory" On="uninstall" />
        <File Id="Registration.xml" Name="Registration.xml" Source="Registration.xml" Checksum="no" />
        <File Id="Application.png" Name="Application.png" Source="Application.png" Checksum="no" />
        <RegistryKey Root="HKCU" Key="Software\MyCompany\MyApplication" Action="createAndRemoveOnUninstall">
            <RegistryValue Name="Version" Value="[ProductVersion]" Type="string" KeyPath="yes"/>
        </RegistryKey>
    </Component>
</DirectoryRef>

These settings are included in this setup authoring in order to eliminate ICE38 and ICE64 errors when building the MSI.

The example package includes some custom actions to register the application with Media Center, but these custom actions do not require administrative privileges so they will work correctly in a per-user MSI that does not run with elevated privileges.

One other note - the build script included with this example includes a command line argument to suppress errors and warnings related to ICE91 when running Light.  This ICE validation test reports warnings if any resources are installed to a per-user location in an MSI.  However, in this example, the warning can be safely ignored because the MSI is specifically designed to be per-user, and as we will see in step 2 below, we will also enforce that the MSI can only be installed per-user.

2. Set the ALLUSERS property to an empty string

The following syntax defines the ALLUSERS property but does not assign a value to it so it will be set to blank during installation:

<Property Id="ALLUSERS" Secure="yes"/>

Since this property is a Windows Installer public property, it could be changed by the user when they install the MSI via command line.  As an extra precaution, I added a launch condition to this WXS file so that the MSI will not allow you to install if you try to set the ALLUSERS property to something other than blank or 0.  This WiX authoring is used to implement this block:

<Condition Message="!(loc.LaunchCondition_AllUsers)">
    NOT ALLUSERS
</Condition>

The error message that appears when this block is invoked is defined in this entry in the WiX localization (WXL) file named Setup-en-us.wxl that is included in the zip file:

<String Id="LaunchCondition_AllUsers">Setting the ALLUSERS property is not allowed because [ProductName] is a per-user application. Setup will now exit.</String>

3. Set bit 3 of the Word Count summary property to 8 to indicate that elevated privileges are not required

This is accomplished in WiX by setting the InstallerPrivileges attribute to "limited" in the Package element.  In this example, the Package element looks like the following:

<Package Description="!(loc.Package_Description)"
         Comments="!(loc.Package_Comments)"
         InstallerVersion="200"
         Compressed="yes"
         InstallPrivileges="limited"
/>

The build script included with this example includes a command line argument to suppress errors and warnings related to ICE39 when running Light.  This ICE validation test checks the contents of the Summary Information Stream in the MSI.  Bit 3 of the Word Count summary property is a part of the Summary Information Stream, and it is a new value introduced in Windows Installer 4.0.  Unfortunately, the ICE validation test file (darice.cub) does not currently recognize this new bit, and an MSI that attempts to set it will fail ICE39 tests even though it is authored correctly.  Ignoring ICE39 errors is safe in this case because if an MSI that includes it is installed using an older version of Windows Installer that does not recognize it, Windows Installer will ignore it.

<update date="1/10/2008"> Removed a reference to the syntax ALLUSERS=0 because Windows Installer only specifically recognized ALLUSERS values of blank, 1 and 2 according to the documentation </update>

<update date="8/13/2009"> Fixed broken link to the sample files used in this blog post. </update>