Yesterday, a friend asked for some assistance debugging an issue with Windows Installer. It was one of those situations where you’ve looked at the problem so closely, so many times, that you just needed another set of eyes. (They’re close to shipping, so they’ve been doing nothing but look at bugs, probably for a while.)
What they’re doing is a rather interesting use of Windows Installer. They’re using it to drop a setup.exe and then run it. So, they have a custom action of type 0xC02, which is a deferred custom action, promoted to not use impersonation, which runs as an exe. This custom action spawns another version of itself (with different arguments), and then the process exits so the installer can complete. The second version of this custom action then wants to uninstall the msi, as well as launch the setup.exe. So, the msi is just a way to package up the setup.exe.
And, with UAC, it was breaking.
Now, with UAC issues, you typically think of access denied. But we didn’t expect that at all here. We had promoted the custom action, so it should be running as local system. How could local system be receiving an access denied error? Perhaps we weren’t being promoted for some reason?
So, I took a peek with Process Explorer. The processes had the privileges I expected, Local System. Here’s the chain of process creation and token inheritance:
So, I could pretty much rule out a permission denied issue – the custom action was being promoted as I expected it to be.
My next step was to turn on MSI logging and see what was going on. And, as it turned out, it wasn’t getting access denied (which I already expected), but instead was getting a 1605 error:
MSI (s) (5C:F4) [20:42:05:761]: MainEngineThread is returning 1605
Now, 1605 is ERROR_UNKNOWN_PRODUCT. Basically, it means you’re trying to uninstall something that isn’t there. This seemed odd, since the time stamp indicated that the previous installation had completed, and the rules of Windows Installer tell us that we couldn’t possibly have two concurrent instances of the installer running anyway. So, what happened?
Take a look at the colors above. I originally invoked msiexec.exe from a protected admin account. I then attempted to uninstall that product from the Local System account. Aha!
You see, this msi hadn’t set the ALLUSERS property. That meant that we were running a per-user install. When Local System tried to uninstall, it couldn’t, because it had never been installed for *that* user, it had been installed for the Protected Admin user!
When I added the ALLUSERS property to the property table, and then set the value to 1, then everything worked as expected. And this little repackaged installer was working as expected.
Close one more bug. And UAC gets a free pass on this one.