So you have a product that has an MSI installer and installation works perfectly. But you want to install the product using the /quiet flag, so that you automate the installation or for whatever reason and now it fails all the time. First of all, do the normal thing when you have an MSI installation error:
  • Install the package by manually running msiexec and add verbose logging: msiexec /i My.Setup.msi /L*VX theinstall.log ... (your extra parameters, including /quiet here)
  • Open theinstall.log file and look for the string "Value 3" which is the line where the install actually failed
  • See if you can see what caused the fail

My setup would take properties from the registry and only need the user password to be supplied, so I used it like this:
msiexec /i My.Setup.msi /L*VX theinstall.log /quiet USERPASSWORD="thepassword"
For me, the reason for the failure was very unclear: "CreateUser returned actual error code 1603". I had the user, I had the password, what was going on?

The solution was to add ALL the properties needed. With a silent installation, it seems some of the actions in the UI are ignored. It's not just quiet, it's skipping things. In order to get a list of all possible properties, open the same install log and look for "SecureCustomProperties", which should list all the properties that you can set from the command line, separated by semicolon. I am sure I didn't need to set them all in order to work. In fact I didn't. I only used the ones used in the UI.

Hope it helps.

This is a simple gotcha related to changing the color of a control. Let's say you have a label that you want to present in a different color. Normally you would do something like this:
<!-- I put it somewhere in Product.wxs -->
<TextStyle Id="WixUI_Font_Normal_Red" FaceName="Tahoma" Size="8" Red="255" Green="55" Blue="55" />
 
<!-- somewhere in your UI -->
<Control Id="LabelRed" Type="Text" X="62" Y="200" Width="270" Height="17" Property="MYPROPERTY">
<Text>{\WixUI_Font_Normal_Red}!(loc.MYPROPERTY)</Text>
</Control>

Yet for some reason, it doesn't work when the control is a checkbox, for example. The simple explanation is that this is by design: only text controls can change color. The solution is to split your control into the edit control without a text, then add a text control next to it with the color you need.

Here is an example of a checkbox that changes the label color based on the check value:
        <Control Id="DoNotRunScriptsCheckbox" Type="CheckBox" X="45" Y="197" Height="17" Width="17" Property="DONOTRUNSCRIPTS" CheckBoxValue="1"/>
 
<Control Id="DoNotRunScriptsLabel" Type="Text" X="62" Y="200" Width="270" Height="17" CheckBoxPropertyRef="DONOTRUNSCRIPTS">
<Text>!(loc.DoNotRunScriptsDescription)</Text>
<Condition Action="hide"><![CDATA[DONOTRUNSCRIPTS]]></Condition>
<Condition Action="show"><![CDATA[NOT DONOTRUNSCRIPTS]]></Condition>
</Control>
 
<Control Id="DoNotRunScriptsLabelRed" Type="Text" X="62" Y="200" Width="270" Height="17" CheckBoxPropertyRef="DONOTRUNSCRIPTS">
<Text>{\WixUI_Font_Normal_Red}!(loc.DoNotRunScriptsDescription)</Text>
<Condition Action="hide"><![CDATA[NOT DONOTRUNSCRIPTS]]></Condition>
<Condition Action="show"><![CDATA[DONOTRUNSCRIPTS]]></Condition>
</Control>

So you have one of those annoyingly XMLish setups from Windows Installer and you want to preserve the values you input so they are prefilled at future upgrades. There are a lot of articles on the Internet on how to do this, but all of them seem to be missing something. I am sure this one will too, but it worked for me.

Let's start with a basic setup.
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<UI Id="DatabaseAuthenticationDialogUI">
<Property Id="DATABASEDOMAIN" Secure="yes"/>
<Dialog
Id="DatabaseAuthenticationDialog"
Width="370"
Height="270"
Title="[ProductName] database authentication"
NoMinimize="yes">
<Control Id="DatabaseDomainLabel" Type="Text" X="45" Y="110" Width="100" Height="15" TabSkip="no" Text="!(loc.Domain):" />
<Control Id="DatabaseDomainEdit" Type="Edit" X="45" Y="122" Width="220" Height="18" Property="DATABASEDOMAIN" Text="{80}"/>

So this is a database authentication dialog, with only the relevant lines in it. We have a property defined as DATABASEDOMAIN and then an edit control that edits this property. Ideally, we would want to make sure this property is being saved somewhere at the end of the install and it is retrieved before the install to be populated. To do this we will first define a DATABASEDOMAIN_SAVED property and load/save it in the registry, then link it with DATABASEDOMAIN.

First, there is the issue of where to put this code. Personally, I put them all under Product, as a separate mechanism for preserving and loading values. I am sure there are other places in your XML files where you can do it. Here is how my Product.wxs code looks like (just relevant lines):
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"
xmlns:util="http://schemas.microsoft.com/wix/UtilExtension">
<Product ... >
<Property Id="SAVED_DATABASEDOMAIN" Secure="yes">
<RegistrySearch Id="FindSavedDATABASEDOMAIN"
Root="HKLM"
Key="SOFTWARE\MyCompany\MyProduct"
Name="DatabaseDomain"
Type="raw" />
</Property>
<SetProperty Id="DATABASEDOMAIN" After="AppSearch" Value="[SAVED_DATABASEDOMAIN]" Sequence="ui">
<![CDATA[SAVED_DATABASEDOMAIN]]>
</SetProperty>
</Product>
 
<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
<Directory Id="ProgramFiles64Folder">
<Directory Id="MyFolder" Name="MyFolder">
<Directory Id="INSTALLFOLDER" Name="MyProduct">
<Component Id="InstallFolderComponent" Guid="c5ccddcc-8442-49e8-aa17-59f84feb4deb">
<RegistryKey
Root="HKLM"
Key="SOFTWARE\MyCompany\MyProduct"
>
<RegistryValue Id="DatabaseDomain"
Action="write"
Type="string"
Name="DatabaseDomain"
Value="[DATABASEDOMAIN]" />
</RegistryKey>
<CreateFolder/>
</Component>
</Directory>
</Directory>
</Directory>
</Directory>
</Fragment>
</Wix>

This is what happens:
  1. We search in the registry and set the value for DATABASEDOMAIN_SAVED.
  2. We set the DATABASEDOMAIN value from the DATABASEDOMAIN_SAVED value, if that is set. Note that Sequence is set to "ui". This is very important, as the default value is "both". In my case I spent hours to figure out why the values were written in the registry, but then would never change again. It was because there are two sequences: "ui" and "execute". The code would read the value from the registry, the user would then change the values, then, right before installing anything, the value would be read from the registry AGAIN and would overwrite the user input.
  3. Finally, when we install the product we save in the registry the value of DATABASEDOMAIN, whatever it is.

This should be it, but there are a few gotchas. One of them is checkboxes. For Windows Installer the value of a checkbox either is or isn't. It's not set to 0 or 1, true or false or anything like that. So if you save the value attached to an unchecked checkbox control, when read, even if empty, it will be set. Your checkbox will always be set from then on. The solution I used was adding a prefix, then setting the value for the checkbox only if that value is what I expect it to be. Here it is, in a gist:

    <!-- Product.wxs -->
<!-- this doesn't change -->
<Property Id="SAVED_DONOTRUNSCRIPTS" Secure="yes">
<RegistrySearch Id="FindSavedDONOTRUNSCRIPTS"
Root="HKLM"
Key="SOFTWARE\MyCompany\MyProduct"
Name="DoNotRunScripts"
Type="raw" />
</Property>
<!-- here, however, I check for Val1 to set the value of the property to 1 -->
<SetProperty Id="DONOTRUNSCRIPTS" After="AppSearch" Value="1" Sequence="ui">
<![CDATA[SAVED_DONOTRUNSCRIPTS = "Val1"]]>
</SetProperty>
<!-- Note the Val prefix when saving the value -->
<RegistryValue Id="DoNotRunScripts"
Action="write"
Type="string"
Name="DoNotRunScripts"
Value="Val[DONOTRUNSCRIPTS]" />
 
<!-- DatabaseSetup.wxs -->
<!-- Note the checkbox value -->
<Control Id="DoNotRunScriptsCheckbox" Type="CheckBox" X="45" Y="197" Height="17" Width="17" Property="DONOTRUNSCRIPTS" CheckBoxValue="1"/>

I Hope that helps people.

You are trying to install an application and in the detailed log you get something like this:
InstallSqlData:  Error 0x8007007a: Failed to copy SqlScript.Script: SqlWithWindowsAuthUpv2.9.5UpdateGetInstallJobBuildByInstallJobBuildIdScript
or
InstallSqlData:  Error 0x8007007a: failed to read SqlScripts table

For me it was related to the length of an <sql:SqlScript> tag Id property. If you are trying to use more than 55 characters for the BinaryKey property, you get a build error that you should use a max of 55 characters. For the Id you get no such warning, but instead it fails on install.

I hope it helps people (I lost two hours figuring it out).

There is a way to execute an installation using msiexec, like this: msiexec /i MySetup.msi /l*v "mylog.log", but what if you routinely install stuff on a machine and want to be able to read the log only when there is a problem? Then you can use the group policy editor to set it up:

  1. Run "Edit group policy" (gpedit.msc)
  2. Go to Computer Configuration → Administrative Templates → Windows Components → Windows Installer
  3. Select "Specify the types of events Windows Installer records in its transaction log"
  4. Select "Enabled"
  5. Type any of the letters in 'voicewarmupx' in the Logging textbox
  6. Click OK





This will create the following registry entry:
[HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\Installer]
"Logging"="voicewarmupx"
"Debug"=dword:00000007

Warning: this setting will add time to the installation process based on the options selected. Here is a list of the possible options:
  • v - Verbose output
  • o - Out-of-disk-space messages
  • i - Status messages
  • c - Initial UI parameters
  • e - All error messages
  • w - Non-fatal warnings
  • a - Start up of actions
  • r - Action-specific records
  • m - Out-of-memory or fatal exit information
  • u - User requests
  • p - Terminal properties
  • + - Append to existing file
  • ! - Flush each line to the log
  • x - Extra debugging information. The "x" flag is available only on Windows Server 2003 and later operating systems, and on the MSI redistributable version 3.0, and on later versions of the MSI redistributable.

The log files will be found in your %TEMP% folder, usually C:\Users\[your user]\AppData\Local\Temp.