can someone suggest the best way how to copy content of one subcategory in registry to another?
For example we have:
HKEY_CURRENT_USER.Software.MyProgram.ver_1
and running the function check, if there no ver_2, so create ...MyProgram.ver_2 and copy there all content of ...MyProgram.ver_1.
An extension method:
public static void CopyTo(this RegistryKey src, RegistryKey dst)
{
// copy the values
foreach (var name in src.GetValueNames())
{
dst.SetValue(name, src.GetValue(name), src.GetValueKind(name));
}
// copy the subkeys
foreach (var name in src.GetSubKeyNames())
{
using (var srcSubKey = src.OpenSubKey(name, false))
{
var dstSubKey = dst.CreateSubKey(name);
srcSubKey.CopyTo(dstSubKey);
}
}
}
Called like this:
var ver1 = Registry.CurrentUser.OpenSubKey(#"Software\MyProgram\ver_1");
var ver2 = Registry.CurrentUser.OpenSubKey(#"Software\MyProgram\ver_2");
ver1.CopyTo(ver2);
Based on Wallys extension method I implemented an MoveTo extension method.
This code also contains an key.GetParent(...) extension method which will open the parent key (needed because I use DeleteSubKeyTree method to delete the Key after copy succeded.
public static void MoveTo(this RegistryKey src, RegistryKey dst)
{
src.CopyTo(dst);
src.Delete();
}
public static void Delete(this RegistryKey key)
{
using (var parentKey = key.GetParent(true))
{
var keyName = key.Name.Split('\\').Last();
parentKey.DeleteSubKeyTree(keyName);
}
}
public static RegistryKey GetParent(this RegistryKey key)
{
return key.GetParent(false);
}
public static RegistryKey GetParent(this RegistryKey key, bool writable)
{
var items = key.Name.Split('\\');
var hiveName = items.First();
var parentKeyName = String.Join("\\", items.Skip(1).Reverse().Skip(1).Reverse());
var hive = GetHive(hiveName);
using (var baseKey = RegistryKey.OpenBaseKey(hive, key.View))
{
return baseKey.OpenSubKey(parentKeyName, writable);
}
}
private static RegistryHive GetHive(string hiveName)
{
if (hiveName.Equals("HKEY_CLASSES_ROOT", StringComparison.OrdinalIgnoreCase))
return RegistryHive.ClassesRoot;
else if (hiveName.Equals("HKEY_CURRENT_USER", StringComparison.OrdinalIgnoreCase))
return RegistryHive.CurrentUser;
else if (hiveName.Equals("HKEY_LOCAL_MACHINE", StringComparison.OrdinalIgnoreCase))
return RegistryHive.LocalMachine;
else if (hiveName.Equals("HKEY_USERS", StringComparison.OrdinalIgnoreCase))
return RegistryHive.Users;
else if (hiveName.Equals("HKEY_PERFORMANCE_DATA", StringComparison.OrdinalIgnoreCase))
return RegistryHive.PerformanceData;
else if (hiveName.Equals("HKEY_CURRENT_CONFIG", StringComparison.OrdinalIgnoreCase))
return RegistryHive.CurrentConfig;
else if (hiveName.Equals("HKEY_DYN_DATA", StringComparison.OrdinalIgnoreCase))
return RegistryHive.DynData;
else
throw new NotImplementedException(hiveName);
}
This question answer the "how to read/write registry" question, and based on that you can do this:
read "ver_2" key
if "ver_2" doesn't exist, create it
if "ver_2" didn't exist above, read each value in "ver_1" key and write an identical value under "ver_2"
Basically, you read a value, write it, then read another, write it, and so on.
Related
I am new to object-oriented programming and I am working on a small personal project with some SQL scripts.
I have a scenario where a SQL script calls a static method with a file path as input.
queries = Select Query from Table where Utils.ContainsKeyword(Query, #Path1) AND NOT Utils.ContainsKeyword(Query, #Path2);
I had initially created a static class that does the following:
public static class Utils
{
public static bool ContainsKeyword(string query, string path)
{
var isQueryInFile = false;
var stringFromFile = GetStringFromFile(path);
List<Regex>regexList = GetRegexList(stringFromFile);
if(regexList!= null)
{
isQueryInFile = regexList.Any(pattern => pattern.IsMatch(query));
}
return isQueryInFile;
}
private static string GetStringFromFile(string path)
{
var words = String.Empty;
if(!string.IsNullOrEmpty(path))
{
try
{
using (StreamReader sr = File.OpenText(path))
{
words = sr.ReadToEnd().Replace(Environment.Newline, "");
}
}
catch { return words; }
}
return words;
}
private static List<Regex> GetRegexList(string words)
{
if(string.IsNullOrEmpty(words)) { return null; }
return words.Split(',').Select(w=> new Regex(#"\b" + Regex.Escape(w) + #'\b', RegexOptions.Compiled | RegexOptions.IgnoreCase)).ToList();
}
}
My problem is that I neither want to read from the file every time the ContainsKeyword static method is called nor do I want to create a new RegexList every time. Also, I cannot change the SQL script and I have to send the path to the file as an input parameter for the method call in the SQL script since the path might change in the future.
Is there a way to make sure I only read the contents from the input path only once, store them in a string, and use the string for the match with different input queries?
To read the content only once, saving in memory will probaby be needed. Memory capacity could be an issue.
public Dictionary<string, string> FileContentCache { get; set; } // make sure that gets initialized
public string GetFileContentCache(string path)
{
if (FileContentCache == null) FileContentCache = new Dictionary<string, string>();
if (FileContentCache.ContainsKey(path))
return FileContentCache[path];
var fileData = GetStringFromFile(path);
FileContentCache.Add(path, fileData);
return fileData;
}
Right now I use this to list all the applications listed in the registry for 32bit & 64.
I have seen the other examples of how to check if an application is installed without any luck.
string registryKey = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
RegistryKey key = Registry.LocalMachine.OpenSubKey(registryKey);
if (key != null)
{
foreach (String a in key.GetSubKeyNames())
{
RegistryKey subkey = key.OpenSubKey(a);
Console.WriteLine(subkey.GetValue("DisplayName"));
}
}
registryKey = #"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall";
key = Registry.LocalMachine.OpenSubKey(registryKey);
if (key != null)
{
foreach (String a in key.GetSubKeyNames())
{
RegistryKey subkey = key.OpenSubKey(a);
Console.WriteLine(subkey.GetValue("DisplayName"));
}
}
So this snippet lists it all in the console window and what I am trying to do is
just find one program title out of the list of display names to see if it's installed.
The last thing I tried was
if (subkey.Name.Contains("OpenSSL"))
Console.Writeline("OpenSSL Found");
else
Console.Writeline("OpenSSL Not Found");
Anything I tried came back either false or a false positive. Is there anyone that can show me how to just grab a title out of the list?
Please don't post up the well-known private static void IsApplicationInstalled(p_name) function. It does not work for me at all.
After searching and troubleshooting, I got it to work this way:
public static bool checkInstalled (string c_name)
{
string displayName;
string registryKey = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
RegistryKey key = Registry.LocalMachine.OpenSubKey(registryKey);
if (key != null)
{
foreach (RegistryKey subkey in key.GetSubKeyNames().Select(keyName => key.OpenSubKey(keyName)))
{
displayName = subkey.GetValue("DisplayName") as string;
if (displayName != null && displayName.Contains(c_name))
{
return true;
}
}
key.Close();
}
registryKey = #"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall";
key = Registry.LocalMachine.OpenSubKey(registryKey);
if (key != null)
{
foreach (RegistryKey subkey in key.GetSubKeyNames().Select(keyName => key.OpenSubKey(keyName)))
{
displayName = subkey.GetValue("DisplayName") as string;
if (displayName != null && displayName.Contains(c_name))
{
return true;
}
}
key.Close();
}
return false;
}
And I simply just call it using
if(checkInstalled("Application Name"))
This is a clean way to do this without that much code.
private static bool IsSoftwareInstalled(string softwareName)
{
var key = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall") ??
Registry.LocalMachine.OpenSubKey(
#"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall");
if (key == null)
return false;
return key.GetSubKeyNames()
.Select(keyName => key.OpenSubKey(keyName))
.Select(subkey => subkey.GetValue("DisplayName") as string)
.Any(displayName => displayName != null && displayName.Contains(softwareName));
}
Call it with a if-statement:
if (IsSoftwareInstalled("OpenSSL"))
I have checked #Stellan Lindell's code and it doesn't work in all cases.
My version should work in all scenarios and checks the specific version of installed programs(x86, x64).
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Win32;
namespace Test
{
internal class Program
{
public enum ProgramVersion
{
x86,
x64
}
private static IEnumerable<string> GetRegisterSubkeys(RegistryKey registryKey)
{
return registryKey.GetSubKeyNames()
.Select(registryKey.OpenSubKey)
.Select(subkey => subkey.GetValue("DisplayName") as string);
}
private static bool CheckNode(RegistryKey registryKey, string applicationName, ProgramVersion? programVersion)
{
return GetRegisterSubkeys(registryKey).Any(displayName => displayName != null
&& displayName.Contains(applicationName)
&& displayName.Contains(programVersion.ToString()));
}
private static bool CheckApplication(string registryKey, string applicationName, ProgramVersion? programVersion)
{
RegistryKey key = Registry.LocalMachine.OpenSubKey(registryKey);
if (key != null)
{
if (CheckNode(key, applicationName, programVersion))
return true;
key.Close();
}
return false;
}
public static bool IsSoftwareInstalled(string applicationName, ProgramVersion? programVersion)
{
string[] registryKey = new [] {
#"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
#"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
};
return registryKey.Any(key => CheckApplication(key, applicationName, programVersion));
}
private static void Main()
{
// Examples
Console.WriteLine("Notepad++: " + IsSoftwareInstalled("Notepad++", null));
Console.WriteLine("Notepad++(x86): " + IsSoftwareInstalled("Notepad++", ProgramVersion.x86));
Console.WriteLine("Notepad++(x64): " + IsSoftwareInstalled("Notepad++", ProgramVersion.x64));
Console.WriteLine("Microsoft Visual C++ 2009: " + IsSoftwareInstalled("Microsoft Visual C++ 2009", null));
Console.WriteLine("Microsoft Visual C-- 2009: " + IsSoftwareInstalled("Microsoft Visual C-- 2009", null));
Console.WriteLine("Microsoft Visual C++ 2013: " + IsSoftwareInstalled("Microsoft Visual C++ 2013", null));
Console.WriteLine("Microsoft Visual C++ 2012 Redistributable (x86): " + IsSoftwareInstalled("Microsoft Visual C++ 2013", ProgramVersion.x86));
Console.WriteLine("Microsoft Visual C++ 2012 Redistributable (x64): " + IsSoftwareInstalled("Microsoft Visual C++ 2013", ProgramVersion.x64));
Console.ReadKey();
}
}
}
The solution #Hyperion is ok but it has an error because for 32 bit configurations. No 64 bit registers are returned. To receive 64 bit registers, do the following:
string registryKey = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
RegistryKey key64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
RegistryKey key = key64.OpenSubKey(registryKey);
Solutions above are really good, but sometimes you have to check if product is installed also on another machine. So there is a version based on solutions above from #Stellan Lindell and #Mroczny Arturek
This method works OK for local machine and also remote machine...
public static bool IsSoftwareInstalled(string softwareName, string remoteMachine = null, StringComparison strComparison = StringComparison.Ordinal)
{
string uninstallRegKey = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
RegistryView[] enumValues = (RegistryView[])Enum.GetValues(typeof(RegistryView));
//Starts from 1, because first one is Default, so we dont need it...
for (int i = 1; i < enumValues.Length; i++)
{
//This one key is all what we need, because RegView will do the rest for us
using (RegistryKey key = (string.IsNullOrWhiteSpace(remoteMachine))
? RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, enumValues[i]).OpenSubKey(uninstallRegKey)
: RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, remoteMachine, enumValues[i]).OpenSubKey(uninstallRegKey))
{
if (key != null)
{
if (key.GetSubKeyNames()
.Select(keyName => key.OpenSubKey(keyName))
.Select(subKey => subKey.GetValue("DisplayName") as string)
//SomeTimes we really need the case sensitive/insensitive option...
.Any(displayName => displayName != null && displayName.IndexOf(softwareName, strComparison) >= 0))
{ return true; }
}
}
}
return false;
}
The registry version is only one from two standard options.. Another option is using WMI, but the registry one is much better due to performance, so take WMI only as a alternative.
//This one does't have a case sensitive/insesitive option, but if you need it, just don't use LIKE %softwareName%
//and get all products (SELECT Name FROM Win32_Product). After that just go trough the result and compare...
public static bool IsSoftwareInstalledWMI(string softwareName, string remoteMachine = null)
{
string wmiPath = (!string.IsNullOrEmpty(remoteMachine))
? #"\\" + remoteMachine + #"\root\cimv2"
: #"\\" + Environment.MachineName + #"\root\cimv2";
SelectQuery select = new SelectQuery(string.Format("SELECT * FROM Win32_Product WHERE Name LIKE \"%{0}%\"", softwareName));
if (SelectStringsFromWMI(select, new ManagementScope(wmiPath)).Count > 0) { return true; }
return false;
}
There is my SelectStringsFromWMI method, but you can do this on your own, this is not important part of this solution. But if you are intersted, there it is...
public static List<Dictionary<string, string>> SelectStringsFromWMI(SelectQuery select, ManagementScope wmiScope)
{
List<Dictionary<string, string>> result = new List<Dictionary<string, string>>();
using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(wmiScope, select))
{
using (ManagementObjectCollection objectCollection = searcher.Get())
{
foreach (ManagementObject managementObject in objectCollection)
{
//With every new object we add new Dictionary
result.Add(new Dictionary<string, string>());
foreach (PropertyData property in managementObject.Properties)
{
//Always add data to newest Dictionary
result.Last().Add(property.Name, property.Value?.ToString());
}
}
return result;
}
}
}
!!UPDATE!!
Due to really bad performance, there is another improvement. Just get values async..
public static bool IsSoftwareInstalled(string softwareName, string remoteMachine = null, StringComparison strComparison = StringComparison.Ordinal)
{
string uninstallRegKey = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
RegistryView[] enumValues = (RegistryView[])Enum.GetValues(typeof(RegistryView));
//Starts from 1, because first one is Default, so we dont need it...
for (int i = 1; i < enumValues.Length; i++)
{
//This one key is all what we need, because RegView will do the rest for us
using (RegistryKey regKey = (string.IsNullOrWhiteSpace(remoteMachine))
? RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, enumValues[i]).OpenSubKey(uninstallRegKey)
: RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, remoteMachine, enumValues[i]).OpenSubKey(uninstallRegKey))
{
if (SearchSubKeysForValue(regKey, "DisplayName", softwareName, strComparison).Result)
{ return true; }
}
}
return false;
}
And the SearchSubKeysForValue method (can be build as extension method):
public static async Task<bool> SearchSubKeysForValue(RegistryKey regKey, string valueName, string searchedValue, StringComparison strComparison = StringComparison.Ordinal)
{
bool result = false;
string[] subKeysNames = regKey.GetSubKeyNames();
List<Task<bool>> tasks = new List<Task<bool>>();
for (int i = 0; i < subKeysNames.Length - 1; i++)
{
//We have to save current value for i, because we cannot use it in async task due to changed values for it during foor loop
string subKeyName = subKeysNames[i];
tasks.Add(Task.Run(() =>
{
string value = regKey.OpenSubKey(subKeyName)?.GetValue(valueName)?.ToString() ?? null;
return (value != null && value.IndexOf(searchedValue, strComparison) >= 0);
}));
}
bool[] results = await Task.WhenAll(tasks).ConfigureAwait(false);
result = results.Contains(true);
return result;
}
I tried the solutions here, but they didnt work in some cases. The reason was, that my programm is 32 bit and runs on 64 bit Windows. With the solutions posted here a 32bit process can not check whether a 64 bit application is installed.
How to access 64 bit registry with a 32 bit process
RegistryKey.OpenBaseKey
I modifed the solutions here to get a working one for this issue:
Usage example
Console.WriteLine(IsSoftwareInstalled("Notepad++"));
Code
public static bool IsSoftwareInstalled(string softwareName)
{
var registryUninstallPath = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
var registryUninstallPathFor32BitOn64Bit = #"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall";
if (Is32BitWindows())
return IsSoftwareInstalled(softwareName, RegistryView.Registry32, registryUninstallPath);
var is64BitSoftwareInstalled = IsSoftwareInstalled(softwareName, RegistryView.Registry64, registryUninstallPath);
var is32BitSoftwareInstalled = IsSoftwareInstalled(softwareName, RegistryView.Registry64, registryUninstallPathFor32BitOn64Bit);
return is64BitSoftwareInstalled || is32BitSoftwareInstalled;
}
private static bool Is32BitWindows() => Environment.Is64BitOperatingSystem == false;
private static bool IsSoftwareInstalled(string softwareName, RegistryView registryView, string installedProgrammsPath)
{
var uninstallKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, registryView)
.OpenSubKey(installedProgrammsPath);
if (uninstallKey == null)
return false;
return uninstallKey.GetSubKeyNames()
.Select(installedSoftwareString => uninstallKey.OpenSubKey(installedSoftwareString))
.Select(installedSoftwareKey => installedSoftwareKey.GetValue("DisplayName") as string)
.Any(installedSoftwareName => installedSoftwareName != null && installedSoftwareName.Contains(softwareName));
}
Here is my version for 64 bits
public static string[] checkInstalled(string findByName)
{
string[] info = new string[3];
string registryKey = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
//64 bits computer
RegistryKey key64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
RegistryKey key = key64.OpenSubKey(registryKey);
if (key != null)
{
foreach (RegistryKey subkey in key.GetSubKeyNames().Select(keyName => key.OpenSubKey(keyName)))
{
string displayName = subkey.GetValue("DisplayName") as string;
if (displayName != null && displayName.Contains(findByName))
{
info[0] = displayName;
info[1] = subkey.GetValue("InstallLocation").ToString();
info[2] = subkey.GetValue("Version").ToString();
}
}
key.Close();
}
return info;
}
you can call this method like this
string[] JavaVersion = Software.checkInstalled("Java(TM) SE Development Kit");
if the array is empty means no installation found. if it is not empty it will give you the original name, relative path, and location which in most cases that is all we are looking to get.
first of all - don't look at the code and say it's too long
it only looks that way.
I'm writing a program that will search my computer and delete files based on their MD5 value (and to speed things up i don't want to search all the files, just those that have specific file names).
I am sending a FileInfo to a method named ConditionallyDeleteNotWantedFile, it then takes that file's name and trys to find it in the dictionary - retrieve that file's MD5 and computes the current FileInfo MD5 to see if they are the same.
If it does - delete the file.
the problem? exception is thrown when i try to delete... even tho no other process uses it. when i try to delete the file using windows explorer it says vshost (meaning:VS...)
what am i missing ?
public static bool ConditionallyDeleteNotWantedFile(FileInfo fi)
{
string dictMD5;
if (NotWanted.TryGetValue(fi.Name, out dictMD5))
{
string temp = ComputeMD5Hash(fi.FullName);
// temp will only be null if we couldn't open the file for
// read in the md5 calc operation. probably file is in use.
if (temp == null)
return false;
if (temp == dictMD5)
{
try
{
fi.Delete();
}
catch { fi.Delete(); // this exception is raised with
// "being used by another process"
}
return true;
}
}
return false;
}
public static string ComputeMD5Hash(string fileName)
{
return ComputeHash(fileName, new MD5CryptoServiceProvider());
}
public static string ComputeHash(string fileName, HashAlgorithm
hashAlgorithm)
{
try
{
FileStream stmcheck = File.OpenRead(fileName);
try
{
stmcheck = File.OpenRead(fileName);
byte[] hash = hashAlgorithm.ComputeHash(stmcheck);
string computed = BitConverter.ToString(hash).Replace("-", "");
stmcheck.Close();
return computed;
}
finally
{
stmcheck.Close();
}
}
catch
{
return null;
}
}
I don't know if that's the key, but you're opening the stream twice in ComputeHash, and there's a path that does not close it. May I suggest this:
public static string ComputeHash(string fileName, HashAlgorithm hashAlgorithm)
{
string hashFixed = null;
try
{
using (FileStream stmcheck = File.OpenRead(fileName))
{
try
{
byte[] hash = hashAlgorithm.ComputeHash(stmcheck);
hashFixed = BitConverter.ToString(hash).Replace("-", "");
}
catch
{
//logging as needed
}
finally
{
stmcheck.Close();
}
}
}
catch
{
//logging as needed
}
return hashFixed;
}
I have the following method:
public static Boolean CheckContents(string ExportDirectory, string FileName, string DspFleName, String RteFleName, string FulRteName, string EqpFleName, int CompanyId, string CompanyName)
{
if (DspFleName != "None")
{
IList<string> DspFle= DspFleName.Split(',');
IList<string> ActualFiles = Directory.GetFiles(ExportDirectory);
for (int i = 0; i < DspFle.Count; i++)
{
if (DspFle[i] != ActualFiles[i])
{
return false;
}
}
}
return true;
}
}
Basically what this code is meant to do is get all file names from the DspFle field which is seperated by a ,. So this would look like so:
test.txt,test2.csv
Then it is getting the acutal files in the directory that is specified from 'ExportDirectory' and returns those into an IList
I am having 2 problems here:
1.The Directory.GetFiles returns the whole file path so that will always return false. I also tried Path.GetFileNames and this only returns the file name but it does not return the extension.
2.I need to compare my entire DspFle to my ActualFile IList as the file names could be in different parts of the list.
Any ideas?
Your code expects not only for the file to exist, but to be in the same position...
Try this one instead :
public static Boolean CheckContents(string ExportDirectory, string DspFleName)
{
if (DspFleName == "None")
return true;
var DspFle = DspFleName.Split(',');
var ActualFiles = Directory.GetFiles(ExportDirectory);
foreach(var file in DspFle)
if (!ActualFiles.Any(x=>Path.GetFileName(x).Equals(file)))
return false;
return true;
}
List<String> fileNames = new List<String>();
String[] files = Directory.GetFiles(".");
foreach (String file in files)
{
fileNames.Add(System.IO.Path.GetFileName(file));
}
That will return the filename with extensions. You can then compare to your IList at that point.
Why bother going through all the trouble of building two lists when you could just check if each file exists in the directory? Effectively that is what your code is doing anyway.
foreach(string DspFle in DspFleName.Split(',')) {
string CheckPath = Path.Combine(ExportDirectory,DspFle[i]);
if (!File.Exists(CheckPath)) return false;
}
return true;
Maybe this is will do what you want?
if (DspFle == "None")
return true;
List<string> DspFle = DspFleName.Split(',');
List<string> ActualFiles = new List<string>();
foreach (string file in Directory.GetFiles(ExportDirectory)
{
DirectoryInfo di = new DirectoryInfo(file);
ActualFiles.Add(di.Name);
}
foreach (string file in DspFle)
{
if (!ActualFiles.Contains(dspFile))
return false;
}
return true;
DirectoryInfo will allow you to return the name of a file including the extension.
I'm trying to write a simple console app that dumps the contents of HKLM to the console. The output should look something like:
HKEY_LOCAL_MACHINE
HKEY_LOCAL_MACHINE\BCD00000000
HKEY_LOCAL_MACHINE\BCD00000000\Description
KeyName: BCD00000000
System: 1
TreatAsSystem: 1
GuidCache: System.Byte[]
HKEY_LOCAL_MACHINE\BCD00000000\Objects
HKEY_LOCAL_MACHINE\BCD00000000\Objects\{0ce4991b-e6b3-4b16-b23c-5e0d9250e5d9}
HKEY_LOCAL_MACHINE\BCD00000000\Objects\{0ce4991b-e6b3-4b16-b23c-5e0d9250e5d9}\Description
Type: 537919488
HKEY_LOCAL_MACHINE\BCD00000000\Objects\{0ce4991b-e6b3-4b16-b23c-5e0d9250e5d9}\Elements
HKEY_LOCAL_MACHINE\BCD00000000\Objects\{0ce4991b-e6b3-4b16-b23c-5e0d9250e5d9}\Elements\16000020
Element: System.Byte[]
I haven't had much luck researching how to do this. Any help would be greatly appreciated.
You know there's already an app that dumps registry contents, right?
REG EXPORT HKLM hklm.reg
Fun part is, it exports the keys in a text format, but that text file can be imported using either REG or the registry editor.
cHao way is the safiest approach to your question. In the meanwhile, I was bored on this sunday night and wrote something. Just change the Console.WriteLine or add a few other Console.WriteLine to suit your need, whatever need there is.
class Program
{
static void Main(string[] args)
{
Registry.CurrentUser.GetSubKeyNames()
.Select(x => Registry.CurrentUser.OpenSubKey(x))
.Traverse(key =>
{
if (key != null)
{
// You will most likely hit some security exception
return key.GetSubKeyNames().Select(subKey => key.OpenSubKey(subKey));
}
return null;
})
.ForEach(key =>
{
key.GetValueNames()
.ForEach(valueName => Console.WriteLine("{0}\\{1}:{2} ({3})", key, valueName, key.GetValue(valueName), key.GetValueKind(valueName)));
});
Console.ReadLine();
}
}
public static class Extensions
{
public static IEnumerable<T> Traverse<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> fnRecurse)
{
foreach (T item in source)
{
yield return item;
IEnumerable<T> seqRecurse = fnRecurse(item);
if (seqRecurse != null)
{
foreach (T itemRecurse in Traverse(seqRecurse, fnRecurse))
{
yield return itemRecurse;
}
}
}
}
public static void ForEach<T>(this IEnumerable<T> source, Action<T> action)
{
foreach (var item in source)
{
action(item);
}
}
}
thanks for the answer Pierre-Alain Vigeant, i like ur solution. for the most part it worked with a couple of minor alterations for the text formatting, but i still couldnt deal with the security exception that was being thrown. turns out linq is not so great for this because it does alot of behind the scenes stuff. the following solution is a basic idea of how to do it
class Program
{
static void Main(string[] args)
{
RegistryKey key = Registry.LocalMachine;
Traverse(key, 0);
key.Close();
Console.Read();
}
private static void Traverse(RegistryKey key, int indent)
{
Console.WriteLine(key.Name);
string[] names = key.GetSubKeyNames();
foreach (var subkeyname in names)
{
try
{
string[] valnames = key.GetValueNames();
foreach (string valname in valnames)
{
Console.WriteLine(returnIndentions(indent)+valname + ":" + key.GetValue(valname));
}
Traverse(key.OpenSubKey(subkeyname),indent++);
}
catch {
//do nothing
}
}
}
private static string returnIndentions(int indent)
{
string indentions = "";
for (int i = 0; i < indent; i++) {
indentions += " ";
}
return indentions;
}
}
using System;
using System.Text;
using Microsoft.Win32;
class Program
{
static void Main(string[] args)
{
using RegistryKey key = Registry.LocalMachine;
string keyName = args[0]; // eg #"SOFTWARE\Microsoft\Speech\Voices"
var sb = new StringBuilder();
var subKey = key.OpenSubKey(keyName);
Traverse(subKey);
void Traverse(RegistryKey key, int indent = 0)
{
sb.AppendLine(new string(' ', Math.Max(0, indent - 2)) + key.Name);
indent++;
string[] valnames = key.GetValueNames();
foreach (string valname in valnames)
{
sb.AppendLine(new string(' ', indent) + valname + " : " + key.GetValue(valname));
}
string[] names = key.GetSubKeyNames();
foreach (var subkeyname in names)
{
Traverse(key.OpenSubKey(subkeyname), indent + 2);
}
}
Console.WriteLine(sb.ToString());
}
}