Search code examples
c#registryms-office

When reading Office path from registry, result is unreliable


I am reading the path for the latest version of Word installed from the registry, however even though the key exists, it does not return the path.

    private string GetWordInstallPath()
    {
        int[] versions = new int[]
        {
            16, // 2016
            15, // 2013
            14, // 2010
            12, // 2007
            11, // 2003
            10, // XP
            9,  // 2000
            8,  // 98
            7  // 97
        };
        string path = "";
        string hklm = @"SOFTWARE\Microsoft\Office\{0}.0\Word\InstallRoot";
        foreach (int ver in versions)
        {
            RegistryKey key = Registry.LocalMachine.OpenSubKey(string.Format(hklm, ver), false);
            if (key != null)
            {
                path = key.GetValue("Path", "").ToString();
                key.Close();
                break;
            }
        }
        return path;
    }
}

In my case, I have Office 2016 installed, and I do not have Office 2013 installed, however the above method returns :

C:\Program Files\Microsoft Office 15\root\Office 15\

As you can see here, there is no such entry for SOFTWARE\Microsoft\Office\15.0\Word\InstallRoot, however there is a path for Office 2016.

Office 2016 path

Why is 'key' null when reading the path for 16, but returns a path for a non-existent installation in a folder that doesn't even exist on my system. The expected result from the above code is to loop through the values in versions checking for the subkey InstallPath for word, and on the first match, return the path for that version as it would be the most current version of word installed.

That's kind of impossible to do when it is pulling values for non-existent installs and NOT returning the existing install. This also needs to be done without using Interop.

The method above is from mangling together bits and pieces from here, here, here, and to detect that any version is installed I used code from here (as follows fyi)

    private bool IsWordInstalled()
    {
        Type officeType = Type.GetTypeFromProgID("Word.Application");
        return (officeType == null) ? false : true;
    }

Solution

  • If using the default compile mode or 32 bit compile mode, the application will automatically try and grab any registry data from SOFTWARE\WoW6432Node\* instead of the specified path SOFTWARE\*. In this case, it worked as follows:

    32 bit compile/debug

    SOFTWARE\Microsoft\Office\16.0\Word\InstallRoot

    was automatically translated in the background to

    SOFTWARE\WoW6432Node\Microsoft\Office\16.0\Word\InstallRoot

    since there was no 64 bit application entries in the subkey inside WoW6432Node, key returned null as it did not exist.

    64 bit compile/debug

    SOFTWARE\Microsoft\Office\16.0\Word\InstallRoot

    reads the correct key from the exact path as specified.


    It is important to note, that the translation of the registry path happens silently, without any error, and does not prompt the user or mention the adjustment in the debug logs.

    Also, any application compiled with a 'preference' to 32 bit, even if it is a hybrid compile, will still silently translate the key by inserting WoW6432Node into it.

    A 64 bit application will read the registry keys as is without modification and as such can read both the WoW6432Node entries as well as the normal ones.