When reading Office path from registry, result is unreliable - c#

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.
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;
}

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.

Related

How Is This Returning NULL

My C# application license manager is returning NULL when checking for a Key's existence even though the key exists and my application is installed. I have tried running as an Administrator and add or removing backslashes in the Key path.
RegistryKey LitenUpKey = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\LitenUp\NIT", false);
if (LitenUpKey == null) {
// Registry Key NOT Found
return false;
}
NOTE: I am building as x64!
As #RbMm pointed out, the issues was in registry reflection between 32 bit and 64 bit. The following question showed me how to choose which view I saw. Here it is.

Check if SQL Server is installed C#

I'm finishing a application in C# which contains a SQL Server database.
How can I check if the user has SQL Server 2012 Express Local DB installed?
Is it possible to check via registry both on x86, x64?
Basically the idea is if the user does not have SQL Server installed, the application advise to install it.
As the installer I'm working for setup does not have dependencies for SQL Server 2012 Express Local DB.
Thanks.
You'll have to loop through the Uninstall GUIDs and find one that starts with keyword "Microsoft SQL Server 2012". You can find it by going to Control Panel > Programs and Features > and look at the "Display Name" column.
//HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall{guidVariable}\DisplayName
.. should match the "Microsoft SQL Server 2012*" wild card.
I don't have the exact code, but this should get you started. Just loop through all children of the "Uninstall" key, and then find the "DisplayName" key by getting the value. The "GUID" variable below should be your iterator, since you do not know that value. I'm sure you can get a list of all of the GUID values that are sub keys of "Uninstall" key.
string UninstallRegKeyPath = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
Guid UninstallGuid = new Guid(GUID);
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(UninstallRegKeyPath, true))
{
if (key == null)
{
return;
}
try
{
string guidText = UninstallGuid.ToString("B");
RegistryKey child = key.OpenSubKey(guidText);
if (child != null)
{
string displayName = child.GetValue("DisplayName").ToString();
if (displayName.Contains("Microsoft SQL Server 2012"))
{
// implement logic when MSSQL 2012 is found
}
child.Close();
}
}
}
Just be cautious. There are both 32 bit installations and 64 bit installations. Wow6432Node contains the 32 bit programs I believe, so everything installed in C:\Program Files (x86)\ by default (but may be anywhere). And there is the other location, which I'll let you find, for all of the 64 bit programs, which are installed in C:\Program Files\ by default (and again may be installed anywhere).
EDIT:
Try this. You may also want to replace "LocalMachine" with "CurrentUser" since many installers let you configure them for your user, or all users.
using (RegistryKey root = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall"))
{
string searchKey = #"Microsoft SQL Server 2012";
string subKeyName = "DisplayName";
foreach (string keyname in root.GetSubKeyNames())
{
//Console.WriteLine(keyname);
using (RegistryKey key = root.OpenSubKey(keyname))
{
try // in case "DisplayName doesn't exist
{
string displayName = key.GetValue(subKeyName).ToString();
if (displayName.StartsWith(searchKey))
Console.WriteLine("GUID: " + keyname + Environment.NewLine + displayName + Environment.NewLine);
}
catch
{
}
}
}
}
Console.ReadLine();

Unable to read registry: System.Security.SecurityException, Requested registry access is not allowed

I'm getting reported errors from users who are receiving the error "System.Security.SecurityException, Requested registry access is not allowed." when trying to read the registry. I can't think why someone would not have permission to read the registry and I'm unable to reproduce the problem on my Windows 7 PC. Affected users are running .NET 4.0
Here's the C# code I'm using:
var baseReg = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32);
var key = baseReg.OpenSubKey(RegKey, RegistryKeyPermissionCheck.ReadSubTree);
if (key != null)
{
var value = key.GetValue("DisableAutoUpdate", 0);
if (value != null)
{
updatesDisabled = Convert.ToBoolean(value);
}
}
EDIT:
I've checked permissions on the registry key concerned for the affected user and standard Users have read permission for that key.
EDIT 2 and SOLUTION:
According to the affected user, installing .NET 4.5.2 resolves the problem! I'm not sure why.
Thanks to #nozzleman's answer below this is fixed by forcing it to open the key as read-only. However it's odd that .NET 4.0 as 4.5.2 appear to behave differently.
There is an overload of the OpenSubKey(..)-Method that allows to add a third parameter. You could try passing RegistryRights.ReadKey with that one and see if that solves the issue.
baseReg.OpenSubKey(
RegKey,
RegistryKeyPermissionCheck.ReadSubTree
RegistryRights.ReadKey);
Alternatively, try the other overload accepting 2 parameters like so
baseReg.OpenSubKey(RegKey, false);
This leads to opening the subkey readonly, and you dont neet to read the whole sub tree in the given szenario..

Failed to read key HKLM\SOFTWARE\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx

I am trying to read registry keys from folder "HKLM\SOFTWARE\Microsoft.NETFramework\v4.0.30319"
Folder contains two keys SKUs and AssemblyFoldersEx. First key is successfully readed, but second is failed (return null).
How to fix this?
C# code:
class Program
{
static void Main(string[] args)
{
Microsoft.Win32.RegistryKey rkey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Microsoft\.NETFramework\v4.0.30319\SKUs", false); // success
Microsoft.Win32.RegistryKey rkey2 = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Microsoft\.NETFramework\v4.0.30319\AssemblyFoldersEx", false); // failed (returns null)
}
}
Open your project in VS and go to Properties > Build then change Platform target to Any CPU then recompile. I suspect your Platform target is x86 and you are running on a 64 bit version of Windows.
Wow6432Node branch of the registry will get you every time :)

Delete Subkey error (C#)

I have created the following registry key (copied through regedit):
HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\test
I would like to now delete this registry key, and so... I have been using the following code and am running into a small error.
RegistryKey regKey;
string regPath_Key = #"Software\Microsoft\Windows\CurrentVersion\test";
regKey = Registry.CurrentUser.OpenSubKey(regPath_Key, true);
if(regKey != null) // Always returns null, even though the key does exist.
{
Registry.CurrentUser.DeleteSubKey(regPath_Key, true);
}
The issue I am having is that the line if(regKey != null) always returns null! I have gone back and checked that the key does in fact exist multiple times - but still the same result. I am going to assume my code has issues somewhere?
Could it be that you are on a 64 bit machine and your project is set to x86 architecture? in that case, verify that the key you state exists under HKCU\Software\Wow6432Node... as every path is redirected to this 32 bit process registry...
You should not include HKEY_CURRENT_USER in the string you pass to Registry.CurrentUser.OpenSubKey(). Instead use
string regPath_Key = #"Software\Microsoft\Windows\CurrentVersion\test";

Categories