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:
- Zip file containing all WiX 3.0 setup authoring files, a build batch file that can be used to compile and link the MSI, and a copy of the built MSI - https://cid-27e6a35d1a492af7.skydrive.live.com/self.aspx/Blog%7C_Tools/example%7C_per%7C_user%7C_msi.zip
- Source WXS file for the example MSI - https://cid-27e6a35d1a492af7.skydrive.live.com/self.aspx/Blog%7C_Tools/example%7C_per%7C_user%7C_msi%7C_setup.wxs
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:
- Download and install wix3.msi from the latest build of WiX 3.0 at https://wix.sourceforge.net/releases
- 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
- Make any desired changes to the sample setup authoring files
- 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>
Comments
Anonymous
November 18, 2007
PingBack from http://www.universityupdate.com/Technology/Windows-Vista/5577619.aspxAnonymous
November 18, 2007
PingBack from http://msdnrss.thecoderblogs.com/2007/11/19/using-wix-30-to-create-a-per-user-msi-that-does-not-prompt-for-elevation-on-windows-vista/Anonymous
November 17, 2008
I previously wrote an introductory post describing Windows game deployment features that are a part ofAnonymous
November 21, 2010
Hi, I have an application that is designed for XP, VISTA and Windows 7. For the earlier version, the installer was used with 'run as an Administrator'. For the new version, we want it to be installed on per user basis. I tried to modify the installer using the package attributes above. I retained just <Property Id="ALLUSERS" Secure="yes"/> after building the installer. After building and viewing msi file in ORCA I find that ALLUSERS which I did not define to have any value is set to 1 (implying installs will be per machine). I have some questions about this. I am not writing to any common areas (program files, HKLM etc.) I am however, installing a third party driver through custom actions. These custom actions are deferred custom actions so they have an attribute noImpersonate. Is this the reason installer setting the property ALLUSERS=1 in the property table? If the custom actions are deferred I have to set the attribute Impersonate ="no". Is installing a driver in iteself against the spirit and the rules of perUser install and so cannot be done in perUser installs? Another important detail is that there is a mergemodule which installs MSVC rutime dlls to WindowsSystem32 and has an explicitly stated property ALLUSERS=1 (I checked the merge module in ORCA) but since this clashes with the rest of the WIX file the compiler and linker ignore this property and give a warning (ICE 105) to that effect and state that this property will not be merged in the installer. Can a per machine install be upgraded to peruser install (I think it is just not possible) if the upgrade codes are same for both the versions? Thanks.Anonymous
November 21, 2010
Hi Umesh - Simply setting ALLUSERS to blank is not enough to make a .msi file a per-user .msi. Driver installations are per-machine, so you won't be able to create a per-user .msi that installs a driver. Also, the VC runtime files are per-machine, so you won't be able to install those in a per-user .msi. To create a per-user .msi, you have to follow all 3 of the rules listed in this blog post - do not install anything to a per-machine location, set ALLUSERS to blank, and set bit 3 of the word count summary property to 8 (as documented at msdn.microsoft.com/.../Aa372870.aspx). Upgrading a per-machine .msi to a per-user .msi will require elevation when it does the RemoveExistingProducts and uninstalls the previous version of the .msi.