I trying to get a registry value:
var value = Registry.GetValue(#"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography", "MachineGuid", 0);
In Windows XP all ok, but in Windows 7 returns 0. In HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Cryptography using regedit I see MachineGuid, but if I run
var keys = Registry.LocalMachine.OpenSubKey("SOFTWARE").OpenSubKey("Microsoft").OpenSubKey("Cryptography", RegistryKeyPermissionCheck.ReadSubTree).GetValueNames();
keys.Length is 0.
What do I do wrong? With other values all ok in both of OS.
The problem is that you probably are compiling the solution as x86, if you compile as x64 you can read the values.
Try the following code compiling as x86 and x64:
class Program
{
static void Main(string[] args)
{
Console.WriteLine("MachineGUID:" + MachineGUID);
Console.ReadKey();
}
public static string MachineGUID
{
get
{
Guid guidMachineGUID;
if (Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Cryptography") != null)
{
if (Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Cryptography").GetValue("MachineGuid") != null)
{
guidMachineGUID = new Guid(Microsoft.Win32.Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Cryptography").GetValue("MachineGuid").ToString());
return guidMachineGUID.ToString();
}
}
return null;
}
}
}
You can read more about Accessing an Alternate Registry View.
You can found in here a way of reading values in x86 and x64.
It probably has to do with UAC (User Account Control). The extra layer of protection for Windows Vista and Windows 7.
You'll need to request permissions to the registry.
EDIT:
Your code right now:
var keys = Registry.LocalMachine.OpenSubKey("SOFTWARE")
.OpenSubKey("Microsoft")
.OpenSubKey("Cryptography", RegistryKeyPermissionCheck.ReadSubTree)
.GetValueNames();
Only requests the permissions on the Cryptography subkey, maybe that causes the problem (at least I had that once), so the new code would then be:
var keys = Registry.LocalMachine.OpenSubKey("SOFTWARE", RegistryKeyPermissionCheck.ReadSubTree)
.OpenSubKey("Microsoft", RegistryKeyPermissionCheck.ReadSubTree)
.OpenSubKey("Cryptography", RegistryKeyPermissionCheck.ReadSubTree)
.GetValueNames();
EDIT2:
I attached the debugger to it, on this code:
var key1 = Registry.LocalMachine.OpenSubKey("SOFTWARE", RegistryKeyPermissionCheck.ReadSubTree);
var key2 = key1.OpenSubKey("Microsoft", RegistryKeyPermissionCheck.ReadSubTree);
var key3 = key2.OpenSubKey("Cryptography", RegistryKeyPermissionCheck.ReadSubTree);
var key4 = key3.GetValueNames();
It turns out, you can read that specific value, at least that's my guess, because all data is correct, until I open key3, there the ValueCount is zero, instead of the expected 1.
I think it's a special value that's protected.
You say you're on 64-bit Windows: is your app 32-bit? If so it's probably being affected by registry redirection and is looking at HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Cryptography. You may have to P/Invoke to work around it: http://msdn.microsoft.com/en-us/library/aa384129.aspx.
If you're not an administrator, you only have read permission on HKLM. You need to open the key read-only instead. Not sure how to do that with .NET's Registry class; with the API directly, you use RegOpenKeyEx() with the KEY_READ flag.
EDIT: After checking MSDN, I see that OpenSubKey() does open read only, and returns the contents if it succeeds and nothing if it fails. Since you're chaining multiple OpenSubKey calls, it's most likely one of them that's failing that causes the others to fail. Try breaking them out into separate calls, and checking the intermediate values returned.
Maybe a little late to the party, but, none of the solutions worked for me.
This is how I've solved this issue:
public static Guid GetMachineGuid
{
get
{
var machineGuid = Guid.Empty;
var localMachineX64View = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
var cryptographySubKey = localMachineX64View.OpenSubKey(#"SOFTWARE\Microsoft\Cryptography");
if (cryptographySubKey == null) return machineGuid;
var machineGuidValue = (string)cryptographySubKey.GetValue("MachineGuid");
Guid.TryParse(machineGuidValue, out machineGuid);
return machineGuid;
}
}
I solved the problem when i imported Microsoft.Win32 and changed the application-settings to x64 like pedrocgsousa mentioned.
Related
I am building an application in which I need to be able to gather information from the user's local Registry, and then utilize that to perform various tasks. I know where the certain registry key is located, but I can't seem to figure out how to properly extract the data. Here is the one I am trying to extract:
My ideal event would happen as follows: the utility searches for the registry value, determines it and stores it (in a var or something), then a button is displayed to the user to proceed to the next screen (I'm using WinForms). I have already set the button as "invisible" beforehand. See the attached code.
using (RegistryKey key = Registry.LocalMachine.OpenSubKey("HKEY_LOCAL_MACHINE\Software\Wow6432Node\DovetailGames\FSX\10.0"))
{
if (key != null)
{
Object o = key.GetValue("Install_Path");
if (o != null)
{
sc3op2.Visible = true; //Button is "sc3op2"
}
}
}
I guess my main problem is the formatting of the code to extract these values. Any help would be appreciated. Thanks.
Your answer might be here. Apparently, it has something to do with the virtualization of the application settings for 32 and 64-bit platforms. See the updated section If it returns null, set your build architecture to Any CPU. On my 64-bit platform, I am getting null when built using x86 or Any CPU build configuration. But it is returning the value when built using x64.
const string keyName = #"Software\Wow6432Node\DovetailGames\FSX";
var o = Registry.LocalMachine.OpenSubKey(keyName, false);
var value = o?.GetValue("Install_Path", "-");
Console.WriteLine(value);
First you need to remove HKEY_LOCAL_MACHINE.
You need to use # before the string.
take a look Screenshot
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Apple Inc.\Apple Application Support"))
{
if (key != null)
{
Object o = key.GetValue("Installdir");
if (o != null)
{
// do something
}
}
}
My question is very simple, but i dint found an answer googling long time.
How to set REG_KEY_DONT_VIRTUALIZE flag to registry key created by me (i.e. HKLM\Software\MyApp)?
I want my program to be user-independent. Every user starting my app should have access to the same configuration options located in that location).
Changing application manifest I can disable registry virtualization by running program as administrator, but I want normal user be able to run the program and read registry values.
If you don't want your app to be virtualized then you use a manifest to indicate that. If you use REG_KEY_DONT_VIRTUALIZE on your key then all that will happen is that all the writes will fail because your users won't have write access to HKLM.
If you want all your users to share configuration then you'll have to store the configuration in a file rather than the registry. There's nowhere appropriate in the registry that is shared by all users and allows standard users write access.
This is pretty unclear, virtualization is only enabled for legacy non-UAC compatible programs and reading is always permitted. I have to assume that writing is the problem. Change the permissions on the key with, say, your installer or Regedit.exe so that Everybody has write access.
Without changing or adding ACLs to the key, you can ensure that the key you are using programmatically is viewing the 64-bit part of the registry by using the RegistryKey.OpenBaseKey API with the RegistryView.Registry64 flag.
This appears to work properly for 32-bit applications regardless of whether or not registry virtualization is enabled for the app.
private const string MyRegistryKeyPath = "Software\\My Company\\My App";
private static RegistryKey OpenMyAppRegistryKey(bool requireWriteAccess = false)
{
using (var baseKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64))
{
return requireWriteAccess
? baseKey.CreateSubKey(MyRegistryKeyPath, RegistryKeyPermissionCheck.ReadWriteSubTree)
: baseKey.OpenSubKey(MyRegistryKeyPath, RegistryKeyPermissionCheck.ReadSubTree);
}
}
If requireWriteAccess is false, this method will return null if the specified key does not exist.
I should also point out that this code will require elevated permissions to open the key for write access. But I believe it ensures that unelevated reads using keys opened in this fashion will only come from the 64-bit view of the registry.
To date, there is no C# or C API to set the registry key flags.
I assume the safest way is to launch the REG.exe command line tool using CreateProcess.
But, for the record, I have pasted some 'C' code from this blog which demonstrates another way using an undocumented API:
typedef enum _CONTROL_FLAGS {
RegKeyClearFlags = 0,
RegKeyDontVirtualize = 2,
RegKeyDontSilentFail = 4,
RegKeyRecurseFlag = 8
} CONTROL_FLAGS;
typedef struct _KEY_CONTROL_FLAGS_INFORMATION {
ULONG ControlFlags;
} KEY_CONTROL_FLAGS_INFORMATION, *PKEY_CONTROL_FLAGS_INFORMATION;
typedef enum _KEY_SET_INFORMATION_CLASS {
KeyWriteTimeInformation,
KeyWow64FlagsInformation,
KeyControlFlagsInformation,
KeySetVirtualizationInformation,
KeySetDebugInformation,
MaxKeySetInfoClass // MaxKeySetInfoClass should always be the last enum
} KEY_SET_INFORMATION_CLASS;
NTSYSAPI NTSTATUS NTAPI NtSetInformationKey(
IN HANDLE KeyHandle,
IN KEY_SET_INFORMATION_CLASS InformationClass,
IN PVOID KeyInformationData,
IN ULONG DataLength );
typedef NTSYSAPI NTSTATUS (NTAPI* FuncNtSetInformationKey) (
HANDLE KeyHandle,
KEY_SET_INFORMATION_CLASS InformationClass,
PVOID KeyInformationData,
ULONG DataLength );
BOOL CRegLonMigration::SetDontVirtualizeFlag(LPCTSTR keyPath)
{
FuncNtSetInformationKey ntsik = (FuncNtSetInformationKey)GetProcAddress(GetModuleHandle( _T("ntdll.dll") ), "NtSetInformationKey" );
KEY_CONTROL_FLAGS_INFORMATION kcfi = {0};
kcfi.ControlFlags = RegKeyDontVirtualize | RegKeyRecurseFlag;
HKEY hKey = NULL;
LSTATUS status;
if (ERROR_SUCCESS == (status = ::RegOpenKeyEx(ROOT_KEY, keyPath, 0, KEY_ALL_ACCESS, &hKey)))
{
NTSTATUS status = ntsik( hKey, KeyControlFlagsInformation, &kcfi, sizeof( KEY_CONTROL_FLAGS_INFORMATION ) );
RegCloseKey( hKey );
return TRUE;
}
return FALSE;
}
I have a c# unit test project that is compiled for AnyCPU. Our build server is a 64bit machine, and has a 64bit SQL Express instance installed.
The test project uses code similar to the following to identify the path to the .MDF files:
private string GetExpressPath()
{
RegistryKey sqlServerKey = Registry.LocalMachine.OpenSubKey( #"SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL" );
string sqlExpressKeyName = (string) sqlServerKey.GetValue( "SQLEXPRESS" );
RegistryKey sqlInstanceSetupKey = sqlServerKey.OpenSubKey( sqlExpressKeyName + #"\Setup" );
return sqlInstanceSetupKey.GetValue( "SQLDataRoot" ).ToString();
}
This code works fine on our 32bit workstations, and did work ok on the build server until I recently enabled code coverage analysis with NCover. Because NCover uses a 32bit COM component, the test runner (Gallio) runs as a 32bit process.
Checking the registry, there is no "Instance Names" key under
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Microsoft SQL Server
Is there a way for an application running in 32bit mode to access the registry outside Wow6432Node?
Reading the 64 bit registry is possible because of WOW64 which is a Windows subsystem providing access to 64 bit from within 32 bit applications. (Likewise, in older NT-based Windows versions it was called WOW and was an emulation layer inside 32 bit Windows to support 16 bit applications).
There is still native support for registry access under 64 bit Windows using .NET Framework 4.x and for newer .NET versions (such as .NET Core, .NET 5 and 6) as well. The following code is tested with Windows 7, 64 bit and also with Windows 10, 64 bit. It should also work with Windows 11.
Instead of using "Wow6432Node", which emulates a node by mapping one registry tree into another making it appear there virtually, you can do the follwing:
Decide, whether you need to access the 64 bit or the 32 bit registry, and use it as described below. You may also use the code I mentioned later (Additional information section), which creates a union query to get registry keys from both nodes in one query - so you can still query them by using their real path.
64 bit registry
To access the 64 bit registry, you can use RegistryView.Registry64 as follows:
// using Microsoft.Win32
string value64 = string.Empty;
RegistryKey localKey =
RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine,
RegistryView.Registry64);
localKey = localKey.OpenSubKey(#"SOFTWARE\Microsoft\Windows NT\CurrentVersion");
if (localKey != null)
{
value64 = localKey.GetValue("RegisteredOrganization").ToString();
localKey.Close();
}
Console.WriteLine(String.Format("RegisteredOrganization [value64]: {0}",value64));
32 bit registry
If you want to access the 32bit registry, use RegistryView.Registry32 as follows:
// using Microsoft.Win32
string value32 = string.Empty;
RegistryKey localKey32 =
RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine,
RegistryView.Registry32);
localKey32 = localKey32.OpenSubKey(#"SOFTWARE\Microsoft\Windows NT\CurrentVersion");
if (localKey32 != null)
{
value32 = localKey32.GetValue("RegisteredOrganization").ToString();
localKey32.Close();
}
Console.WriteLine(String.Format("RegisteredOrganization [value32]: {0}",value32));
Don't be confused, both versions are using Microsoft.Win32.RegistryHive.LocalMachine as first parameter, you make the distinction whether to use 64 bit or 32 bit by the 2nd parameter (RegistryView.Registry64 versus RegistryView.Registry32).
Note that
On a 64bit Windows, HKEY_LOCAL_MACHINE\Software\Wow6432Node contains values used by 32 bit applications running on the 64 bit system. Only true 64 bit applications store their values in HKEY_LOCAL_MACHINE\Software directly. The subtree Wow6432Node is entirely transparent for 32 bit applications, 32 bit applications still see HKEY_LOCAL_MACHINE\Software as they expect it (it is a kind of redirection). In older versions of Windows as well as 32 bit Windows 7 (and Vista 32 bit) the subtree Wow6432Node obviously does not exist.
Due to a bug in Windows 7 (64 bit), the 32 bit source code version always returns "Microsoft" regardless which organization you have registered while the 64 bit source code version returns the right organization.
Coming back to the example you've provided, do it the following way to access the 64 bit branch:
RegistryKey localKey =
RegistryKey.OpenBaseKey(Microsoft.Win32.RegistryHive.LocalMachine,
RegistryView.Registry64);
RegistryKey sqlServerKey = localKey.OpenSubKey(
#"SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL");
string sqlExpressKeyName = (string) sqlServerKey.GetValue("SQLEXPRESS");
Additional information - for practical use:
I'd like to add an interesting approach Johny Skovdal has suggested in the comments, which I've picked up to develop some useful functions by using his approach: In some situations you want to get back all keys regardless whether it is 32 bit or 64 bit. The SQL instance names are such an example. You can use a union query in that case as follows (C#6 or higher):
// using Microsoft.Win32;
public static IEnumerable<string> GetRegValueNames(RegistryView view, string regPath,
RegistryHive hive = RegistryHive.LocalMachine)
{
return RegistryKey.OpenBaseKey(hive, view)
?.OpenSubKey(regPath)?.GetValueNames();
}
public static IEnumerable<string> GetAllRegValueNames(string RegPath,
RegistryHive hive = RegistryHive.LocalMachine)
{
var reg64 = GetRegValueNames(RegistryView.Registry64, RegPath, hive);
var reg32 = GetRegValueNames(RegistryView.Registry32, RegPath, hive);
var result = (reg64 != null && reg32 != null) ? reg64.Union(reg32) : (reg64 ?? reg32);
return (result ?? new List<string>().AsEnumerable()).OrderBy(x => x);
}
public static object GetRegValue(RegistryView view, string regPath, string ValueName="",
RegistryHive hive = RegistryHive.LocalMachine)
{
return RegistryKey.OpenBaseKey(hive, view)
?.OpenSubKey(regPath)?.GetValue(ValueName);
}
public static object GetRegValue(string RegPath, string ValueName="",
RegistryHive hive = RegistryHive.LocalMachine)
{
return GetRegValue(RegistryView.Registry64, RegPath, ValueName, hive)
?? GetRegValue(RegistryView.Registry32, RegPath, ValueName, hive);
}
public static IEnumerable<string> GetRegKeyNames(RegistryView view, string regPath,
RegistryHive hive = RegistryHive.LocalMachine)
{
return RegistryKey.OpenBaseKey(hive, view)
?.OpenSubKey(regPath)?.GetSubKeyNames();
}
public static IEnumerable<string> GetAllRegKeyNames(string RegPath,
RegistryHive hive = RegistryHive.LocalMachine)
{
var reg64 = GetRegKeyNames(RegistryView.Registry64, RegPath, hive);
var reg32 = GetRegKeyNames(RegistryView.Registry32, RegPath, hive);
var result = (reg64 != null && reg32 != null) ? reg64.Union(reg32) : (reg64 ?? reg32);
return (result ?? new List<string>().AsEnumerable()).OrderBy(x => x);
}
Now you can simply use the functions above as follows:
Example 1: Get SQL instance names
var sqlRegPath=#"SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL";
foreach (var valueName in GetAllRegValueNames(sqlRegPath))
{
var value=GetRegValue(sqlRegPath, valueName);
Console.WriteLine($"{valueName}={value}");
}
will give you a list of the value names and values in sqlRegPath.
Note: You can access the default value of a key (displayed by the commandline tool REGEDT32.EXE as (Default)) if you omit the ValueName parameter in the corresponding functions above.
To get a list of SubKeys within a registry key, use the function GetRegKeyNamesor GetAllRegKeyNames. You can use this list to traverse further keys in the registry.
Example 2: Get uninstall information of installed software
var currentVersionRegPath = #"SOFTWARE\Microsoft\Windows\CurrentVersion";
var uninstallRegPath = $#"{currentVersionRegPath}\Uninstall";
var regKeys = Registry.GetAllRegKeyNames(RegPath: uninstallRegPath);
will get all 32 bit and 64 bit uninstall keys.
Notice the null handling required in the functions because SQL server can be installed as 32 bit or as 64 bit (Example 1 above). The functions are overloaded so you can still pass the 32 bit or 64 bit parameter if required - however, if you omit it then it will try to read 64 bit, if that fails (null value), it reads the 32 bit values.
There is one speciality here: Because GetAllRegValueNames is usually used in a loop context (see Example 1 above), it returns an empty enumerable rather than null to simplify foreach loops: if it wouldn't be handled that way, the loop would have to be prefixed by an if statement checking for null which would be cumbersome having to do that - so that is dealt with once in the function.
Why bothering about null? Because if you don't care, you'll have a lot more headaches finding out why that null reference exception was thrown in your code - you'd spend a lot of time finding out where and why it happened. And if it happened in production you'll be very busy studying log files or event logs (I hope you have logging implemented) ... better avoid null issues where you can in a defensive way. The operators ?., ?[...] and ?? can help you a lot (see the code provided above). There is a nice related article discussing the new nullable reference types in C#, which I recommend to read and also this one about the Elvis operator (a nickname for the ?. operator, sometimes also called safe navigation operator).
Hint: You can use the free edition of Linqpad to test all examples under Windows. It doesn't require an installation. Don't forget to press F4 and enter Microsoft.Win32 in the Namespace import tab. In Visual Studio, you require using Microsoft.Win32; at the top of your code.
Tip: To familiarize yourself with the new null handling operators, try out (and debug) the following code in LinqPad:
Example 3: Demonstrating null handling operators
static string[] test { get { return null;} } // property used to return null
static void Main()
{
test.Dump(); // output: null
// "elvis" operator:
test?.Dump(); // output:
// "elvis" operator for arrays
test?[0].Dump(); // output:
(test?[0]).Dump(); // output: null
// combined with null coalescing operator (brackets required):
(test?[0]??"<null>").Dump(); // output: "<null>"
}
Try it with .Net fiddle
If you're interested, here are some examples I put together showing what else you can do with the tool.
you have to use the KEY_WOW64_64KEY param when creating/opening the registry key. But AFAIK that's not possible with the Registry class but only when using the API directly.
This might help to get you started.
I don't have enough rep to comment, but it's worth pointing out that it works when opening a remote registry using OpenRemoteBaseKey. Adding the RegistryView.Registry64 parameter allows a 32-bit program on Machine A to access the 64-bit registry on Machine B. Before I passed that parameter, my program was reading the 32-bit after OpenRemoteBaseKey, and did not find the key I was after.
Note: In my test, the remote machine was actually my machine, but I accessed it via OpenRemoteBaseKey, just as I would for a different machine.
try this (from a 32bit process):
> %WINDIR%\sysnative\reg.exe query ...
(found that here).
If you cannot use .NET 4 with its RegistryKey.OpenBaseKey(..., RegistryView.Registry64), you need to use Windows API directly.
The minimal interop is like:
internal enum RegistryFlags
{
...
RegSz = 0x02,
...
SubKeyWow6464Key = 0x00010000,
...
}
internal enum RegistryType
{
RegNone = 0,
...
}
[DllImport("advapi32", CharSet = CharSet.Unicode, SetLastError = true)]
public static extern int RegGetValue(
UIntPtr hkey, string lpSubKey, string lpValue, RegistryFlags dwFlags,
out RegistryType pdwType, IntPtr pvData, ref uint pcbData);
Use it like:
IntPtr data = IntPtr.Zero;
RegistryType type;
uint len = 0;
RegistryFlags flags = RegistryFlags.RegSz | RegistryFlags.SubKeyWow6464Key;
UIntPtr key = (UIntPtr)((uint)RegistryHive.LocalMachine);
const string subkey= #"SOFTWARE\Microsoft\Microsoft SQL Server\Instance Names\SQL";
const string value = "SQLEXPRESS";
if (RegGetValue(key, subkey, value, flags, out type, data, ref len) == 0)
{
data = Marshal.AllocHGlobal((int)len);
if (RegGetValue(key, subkey, value, flags, out type, data, ref len) == 0)
{
string sqlExpressKeyName = Marshal.PtrToStringUni(data);
}
}
From what I have read and from my own tests, it seems to me that registry should be checked in this path "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall". Because in other paths the registers are not deleted after uninstalling the program.
In this way I got 64 registers with 32 bit configuration.
string registryKey = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
RegistryKey key64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
RegistryKey key = key64.OpenSubKey(registryKey);
if (key != null)
{
var list = key.GetSubKeyNames().Select(keyName => key.OpenSubKey(keyName).GetValue("DisplayName")).ToList();
key.Close();
}
For 32 registers is:
registryKey = #"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall";
key = Registry.LocalMachine.OpenSubKey(registryKey);
I want to check whether certain microsoft components like wmencoder, directx or wmplayer
are installed or not. If it is installed, can I also get its version number?
How can I do that?
Thanks in advance.
I use the below to determine if other applications are installed, however you will need to know the "unique" product code (from the setup project in Visual Studio) that the application is installed with in the registry.
Include
using System.Diagnostics;
using Microsoft.Win32;
Usage:
// HKEY_LOCAL_MACHINE\SOFTWARE\Classes\CLSID\{0006F03A-0000-0000-C000-000000000046} << This is outlook 2003
String retval = "";
// Look to see if Outlook 2003 is installed and if it is...
if ((checkComServerExists("{0006F03A-0000-0000-C000-000000000046}", out retval)))
{
// Update boolean flag if we get this far so we don't have to check again
Console.WriteLine("Office CSLID exists - Version: " + retval);
}
Function:
// Checks to see if the given CLSID is registerd and exists on the system
private static Boolean checkComServerExists(String CLSID, out String retval)
{
RegistryKey myRegKey = Registry.LocalMachine;
Object val;
try
{
// get the pathname to the COM server DLL/EXE if the key exists
myRegKey = myRegKey.OpenSubKey("SOFTWARE\\Classes\\CLSID\\" + CLSID + "\\LocalServer32");
val = myRegKey.GetValue(null); // the null gets default
}
catch
{
retval = "CLSID not registered";
return false;
}
FileVersionInfo myFileVersionInfo = null;
try
{
// parse out the version number embedded in the resource
// in the DLL
myFileVersionInfo = FileVersionInfo.GetVersionInfo(val.ToString());
}
catch
{
retval = String.Format("DLL {0} not found", val.ToString());
return false;
}
retval = myFileVersionInfo.FileVersion;
return true;
}
My first thought would be WMI. Class Win32_SoftwareElement (on MSDN)
But likely to take some work to get the right classes and queries. Start with the WMI tools for WMI CIM Studio.
Using PowerShell, something like:
gwmi win32_softwareelement -filter "name like '%play%'" | ft
will allow finding the right ids. (Warning: this is extremely slow.)
Possible that the MS Installer (MSI) API has something quicker.
I use RegShot to determine registry setting that can be used to check if a softwre is installed ..
Here is also a small code snippet that uses, among others, Type.GetTypeFromProgID and registry access.
How can I search for specific value in the registry keys?
For example I want to search for XXX in
HKEY_CLASSES_ROOT\Installer\Products
any code sample in C# will be appreciated,
thanks
In case you don't want to take a dependency on LogParser (as powerful as it is): I would take a look at the Microsoft.Win32.RegistryKey class (MSDN). Use OpenSubKey to open up HKEY_CLASSES_ROOT\Installer\Products, and then call GetSubKeyNames to, well, get the names of the subkeys.
Open up each of those in turn, call GetValue for the value you're interested in (ProductName, I guess) and compare the result to what you're looking for.
Help here...
Microsoft has a great (but not well known) tool for this - called LogParser
It uses a SQL engine to query all kind of text based data like the Registry,
the Filesystem, the eventlog, AD etc...
To be usable from C#, you need to build an Interop Assembly from the
Logparser.dll COM server using following (adjust LogParser.dll path)
command.
tlbimp "C:\Program Files\Log Parser 2.2\LogParser.dll"
/out:Interop.MSUtil.dll
Following is a small sample, that illustrates how to query for the Value
'VisualStudio' in the \HKLM\SOFTWARE\Microsoft tree.
using System;
using System.Runtime.InteropServices;
using LogQuery = Interop.MSUtil.LogQueryClass;
using RegistryInputFormat = Interop.MSUtil.COMRegistryInputContextClass;
using RegRecordSet = Interop.MSUtil.ILogRecordset;
class Program
{
public static void Main()
{
RegRecordSet rs = null;
try
{
LogQuery qry = new LogQuery();
RegistryInputFormat registryFormat = new RegistryInputFormat();
string query = #"SELECT Path from \HKLM\SOFTWARE\Microsoft where
Value='VisualStudio'";
rs = qry.Execute(query, registryFormat);
for(; !rs.atEnd(); rs.moveNext())
Console.WriteLine(rs.getRecord().toNativeString(","));
}
finally
{
rs.close();
}
}
}
This method will search a specified registry key for the first subkey that contains a specified value. If the key is found then the specified value is returned. Searchign is only one level deep. If you require deeper searching then I suggest modifying this code to make use of recursion. Searching is case-sensitive but again you can modify that if required.
private string SearchKey(string keyname, string data, string valueToFind, string returnValue)
{
RegistryKey uninstallKey = Registry.LocalMachine.OpenSubKey(keyname);
var programs = uninstallKey.GetSubKeyNames();
foreach (var program in programs)
{
RegistryKey subkey = uninstallKey.OpenSubKey(program);
if (string.Equals(valueToFind, subkey.GetValue(data, string.Empty).ToString(), StringComparison.CurrentCulture))
{
return subkey.GetValue(returnValue).ToString();
}
}
return string.Empty;
}
Example usage
// This code will find the version of Chrome (32 bit) installed
string version = this.SearchKey("SOFTWARE\\WOW6432Node\\Microsoft\\Windows\\CurrentVersion\\Uninstall", "DisplayName", "Google Chrome", "DisplayVersion");
#Caltor your solution gave me the answer I was looking for. I welcome improvements or a completely different solution that does not involve the registry. I am working with enterprise applications on Windows 10 with devices joined to Azure AD. I want/need to use Windows Hello for devices and for HoloLens 2 in a UWP app. My problem has been getting the AAD userPrincipal name from Windows 10. After a couple days searching and trying lots of code I searched the Windows Registry for my AAD account in the Current User key and found it. With some research it appears that this information is in a specific key. Because you can be joined to multiple directories there may be more than one entry. I was not trying to solve that issue, that is done with the AAD tenant Id. I just needed the AAD userPrincipal name.
My solution de-dups the return list so that I have a list of unique userPrincipal names. App users may have to select an account, this is tolerable for even HoloLens.
using Microsoft.Win32;
using System.Collections.Generic;
using System.Linq;
namespace WinReg
{
public class WinRegistryUserFind
{
// Windows 10 apparently places Office/Azure AAD in the registry at this location
// each login gets a unique key in the registry that ends with the aadrm.com and the values
// are held in a key named Identities and the value we want is the Email data item.
const string regKeyPath = "SOFTWARE\\Classes\\Local Settings\\Software\\Microsoft\\MSIPC";
const string matchOnEnd = "aadrm.com";
const string matchKey = "Identities";
const string matchData = "Email";
public static List<string> GetAADuserFromRegistry()
{
var usersFound = new List<string>();
RegistryKey regKey = Registry.CurrentUser.OpenSubKey(regKeyPath);
var programs = regKey.GetSubKeyNames();
foreach (var program in programs)
{
RegistryKey subkey = regKey.OpenSubKey(program);
if(subkey.Name.EndsWith(matchOnEnd))
{
var value = (subkey.OpenSubKey(matchKey) != null)? (string)subkey.OpenSubKey(matchKey).GetValue(matchData): string.Empty;
if (string.IsNullOrEmpty(value)) continue;
if((from user in usersFound where user == value select user).FirstOrDefault() == null)
usersFound.Add(value) ;
}
}
return usersFound;
}
}
}