WiX Installer Phantom Overwriting App Settings for New Users

Recently I received a bug report of some very odd behavior in a Windows application that my team and I had written. Sometimes the server URL, which was set during the application's installation, would be overwritten to the default value of http://localhost.

The application in question is a WPF application. I have kept up with the latest libraries and framework updates. It is installed with the WiX Toolkit 3.9, so even that is current.

When you have a problem that happens "sometimes," the first thing you need to do is figure out what "sometimes" means. My first thought was to check the value in the app.config file. It had indeed changed, which was odd, because the application cannot change values in that file using the Properties.Settings API. It would actually be a compiler error to try.

I checked the source code anyway, to see if someone had brute-forced their way around that limitation using the XML APIs. Nope. It wasn't that.

The next thing I checked was the modification time of the app.config file itself. I asked the user who reported the issue, let's call him John, to uninstall and reinstall and give me me the timestamps on all of the files in the Program Files folder for the application. Then we waited for the problem to recur. Fortunately, that didn't take too long. The next day I got an email from John that went something like this...
Mike, this morning when I came in, Bob was complaining that the URL had reset.
What? Who? Bob complained? Who is Bob? I thought this was John's computer. As it turns out, it's a shared PC in a common location, and multiple log onto it and use my application. Now we're getting somewhere.

I asked John to find out approximately when Bob logged on and noticed the issue, to send me a copy of the app.config file, and to provide me the same time stamps on the rest of the files in the application's folder. John came back and said it was right after lunch. Lo and behold, the app.config file had been modified at roughly the same time Bob was said to have logged on. Opening the file John had sent me, it was clear that all of the configuration settings (not just the server URL) had been reset to default. It was only the Server URL that was dramatically noticable, because http://localhost wasn't valid for their installation. Somehow, I inferred, the WiX installer was running again for Bob when he logged in, even though the application was already installed for "all users."

So now I had something concrete to go on. Firing up my Windows Azure development VM (every developer has one of these, right?), I created a second user account that I could use to test my new theory. I logged in as myself and installed the application, specifying a non-default value for the server URL. Then I logged out and back in as the new user. To my surprise, the app.config file had indeed been modified, and all values were reset.

My next stop was to the Windows event logs. This is what I saw:
  1. Detection of product '{658A4A3C-0C54-4874-BC0D-EAD6140C6938}', feature 'ProductFeature', component '{87D14857-5683-5A5D-BB43-27C83156DC14}' failed.  The resource 'HKEY_CURRENT_USER\Software\WalkingRiver\My App\' does not exist.
  2. Detection of product '{658A4A3C-0C54-4874-BC0D-EAD6140C6938}', feature 'ProductFeature' failed during request for component '{C5F530F6-29D1-5BE4-897D-910309F2C649}'
  3. Beginning a Windows Installer transaction: {658A4A3C-0C54-4874-BC0D-EAD6140C6938}. Client Process Id: 19300.
  4. Windows Installer reconfigured the product. Product Name: My App. Product Version: 1.7.1208. Product Language: 1033. Manufacturer: WalkingRiver. Reconfiguration success or error status: 0.
  5. Product: My App -- Configuration completed successfully.
  6. Ending a Windows Installer transaction: {658A4A3C-0C54-4874-BC0D-EAD6140C6938}. Client Process Id: 19300.
Near the top of the Application Event Log were the above sequence of messages from MsiExec (the Windows Installer) that said a part of my application was not found. Looking closely, I see that the installer is looking for a registry entry under HKEY_CURRENT_USER (HKCU). When it failed to find that entry, it assumed the installation was broken and needed be repaired. Re-running the installer in quiet mode (no UI), forced the app.config file to be replaced with no input from the user. Thus, all of the default values were replaced.

That answered the question of how, and partially the question of why. But why is the installer looking for a registry setting under HKCU for an application installed for "all users?" To answer that question, I had to open my WiX source code and see if I could find out. It didn't take long. In my product.wxs file, I found this:

Notice that RegistryValue tag, with Root="HKCU". This was the culprit. Even though the installer was installing the application for all users, it was setting a user-level registry entry as proof of installation for the application's shortcuts. Looking at the WiX documentation for creating shortcuts, shows that this is the preferred way of doing this.

A quick search led me to a possible solution here. Apparently, I the correct value to use is "HKMU." This will automatically resolve to "HKCU" if the application is installed "per user," or "HKLM" if the it is installed "per machine."

I quickly changed the value, rebuilt my installer, and ran my test again. When I installed the application under my own account, entering a non-default value for the Server URL, things seemed to go OK. I cleared the Windows Application event log, logged out, and then back in as my new user account.

Checking the event log, there were no messages at all from MsiExec. Then I checked the app.config file, and found that its values were those I entered during the installation. Success!

I sent the new installer to John, who reported no further issues. Whew!

Windows is a multi-user system, although not everyone creates more than one user account. In fact, I don't often have more than one account on my development machine. It wasn't likely that I would ever stumble upon this bizarre issue, especially since the WiX installer examples all show HKCU for their registry values. However, my application was run in an enterprise environment, on a shared computer, on an Active Directory network.

The moral of this story is to try to develop and test your applications in an environment that is as close as possible to your end users'.


Popular posts from this blog

How to copy your Frozen Free Fall progress to a new phone

Ionic vs. Bootstrap - for a Web App

How I Finally Got AdMob and Ionic Framework to Play Nice Together