Andriy K
Andriy K

Reputation: 3427

How and why installation "per-user" manages to write to HKLM?

"Per-user" installation mode seems to have some magic under the hood.

Our application has autoupdater that doesn't use insaller, and I wanted to update application version shown in "Add/Remove programs" window. I was quite surprised that setup information (and version too) is actualy stored in

 HKLM\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\{ProductId}

and

HKLM\SOFTWARE\Microsoft\Windows\CurrentVersion\Installer\UserData\{SId}\Products\{ProductId}

So my questions are: how installer manages to write there without requesting elevation? Why per-user installation registers in HKLM at all, especially first entrance, that doesn't look to be related to any specific user at all?

Other consequent but more practical question is how can I update it from code (without elevation, of course)?

Upvotes: 2

Views: 1123

Answers (3)

I have used the following logic to update the version number, and it is correctly reflecting in Control Panel -> Add Remove Programs

using (var uninstallSubKey = Registry.CurrentUser.OpenSubKey
        ("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall",true))
{
    if (uninstallSubKey != null)
    {
        var subKeyNames = uninstallSubKey.GetSubKeyNames();
        foreach (var subKeyName in subKeyNames)
        {
            using (var subKey = uninstallSubKey.OpenSubKey(subKeyName, writable: true))
            {
                if (subKey?.GetValue("DisplayName") as string == "YOUR_PRODUCT_NAME")
                {
                    subKey.SetValue("DisplayVersion", "YOUR_NEW_VERSION",
                        RegistryValueKind.String);
                    break;

                }
            }

        }

    }
}

Thanks & Regards Rajasekhar

Upvotes: 0

Michael Urman
Michael Urman

Reputation: 15905

Windows Installer runs in multiple contexts. There's the context under which it was invoked (typically a limited user, or at least the non-administrator variant of a user in the Administrator group), and the service context. The latter has nearly full access to the machine, but does most of its work while impersonating the original invoker. Custom and built-in actions can access the service context (if permitted either by UAC or by an earlier advertisement), and the service can always use it.

So that's how it gets access. But why would it use this access to store information about the product in HKLM? While you're probably familiar with the SharedDllRefCount, that's only used to interoperability with other solutions. Windows Installer instead tracks similar or extended information on its own, and it needs access to all the instances of all the installations it manages, even if they're per-user and that user is not logged in. When a user is not logged in, their HKCU keys cannot be accessed reliably, so it is not a viable location for Windows Installer to use. Thus it uses HKLM.

You might also want to know why it must have access to the data of a per-user installation. In short, because it can't tell whether an installation is really per-user. Especially prior to Windows 7 and MSIINSTALLPERUSER, but even now one cannot easily determine if an installation may write to a shared machine location. So I'm guessing they don't even try to determine this. After all, why implement a difficult or fragile feature when the easy approach is sufficient?

So how do you update it? By installing an updated .msi file and having the Windows Installer service update it for you; these registry keys are implementation details that Microsoft is allowed to change at any time. You should only read or write them through Msi* API calls such as MsiEnumProductsEx or MsiConfigureProductEx. (Admittedly calling the latter an API to write these values is a bit of a stretch, but it's one of the few valid approaches.) This part of the answer would not be any different even if the data was stored in HKCU.

Upvotes: 2

PhilDW
PhilDW

Reputation: 20790

Windows can write to that uninstall location because it's Windows. The security restrictions are applied to what you do, not what Windows does. It is the OS after all and can do what it requires. It would be rather strange if a non-elevated install could not create an entry in Programs and Features. Those registry entries aren't actually in the MSI file.

Per-user is not strictly the issue. A per-user install can be elevated if required, although a common convention is that per user installs aren't elevated. If your updater writes to the uninstall key it will need elevation.

Side note: If your autoupdater is updating or replacing files that were installed by Windows Installer then you're breaking the rules. Updates should only be done with an MSI-based solution. The issue is that Windows knows (for example) the file version of every versioned file you installed. If the user does a repair (or one just happens for some reason) then you may find Windows wanting to restore the files to the versions that were originally installed. Similarly, if you do a patch or major upgrade then Windows won't know whether files need to be updated if the version on disk does not match the registered version, and so may ask for the original MSI file to restore them.

Upvotes: 3

Related Questions