Refactor registry settings interaction classes.

This commit refactors the registry settings interaction classes by consolidating the multiple entry classes for int, string, bool, etc., into a single class that supports multiple data types. This change simplifies the code and enhances readability.

- Consolidate multiple entry classes (int, string, bool, etc.) into a single class
- Introduce WindowsRegistryEntry<T> to support multiple data types
This commit is contained in:
xRushG
2024-06-11 09:03:34 +02:00
parent b9ab5c76f7
commit 08eb35a360
10 changed files with 2840 additions and 132 deletions

View File

@@ -6,39 +6,98 @@ using System.Runtime.Versioning;
namespace mRemoteNG.Tools.WindowsRegistry
{
[SupportedOSPlatform("windows")]
/// <summary>
/// Interface for the Registry class providing methods for interacting with the Windows Registry.
/// </summary>
public interface IRegistry
{
#region registry reader
#region Registry Reader
/// <summary>
/// Gets the names of subkeys under the specified registry hive and path.
/// </summary>
string[] GetSubKeyNames(RegistryHive hive, string path);
string GetPropertyValue(WindowsRegistryKey key);
string GetPropertyValue(RegistryHive hive, string path, string name);
/// <summary>
/// Gets the value of a registry entry specified by its name.
/// </summary>
string GetValue(RegistryHive hive, string path, string name);
/// <summary>
/// Gets the string value of a registry entry specified by its name.
/// </summary>
string GetStringValue(RegistryHive hive, string path, string name, string defaultValue = null);
/// <summary>
/// Gets the boolean value of a registry entry specified by its name.
/// </summary>
bool GetBoolValue(RegistryHive hive, string path, string propertyName, bool defaultValue = false);
int GetDwordValue(RegistryHive hive, string path, string propertyName, int defaultValue = 0);
WindowsRegistryKey GetWindowsRegistryKey(RegistryHive hive, string path, string name);
WindowsRegistryKey GetWindowsRegistryKey(WindowsRegistryKey key);
/// <summary>
/// Gets the DWORD value of a registry entry specified by its name.
/// </summary>
int GetIntegerValue(RegistryHive hive, string path, string propertyName, int defaultValue = -1);
List<WindowsRegistryKey> GetRegistryEntries(RegistryHive hive, string path);
List<WindowsRegistryKey> GetRegistryEntryiesRecursive(RegistryHive hive, string path);
/// <summary>
/// Retrieves a specific registry entry of type string from the Windows Registry.
/// </summary>
WinRegistryEntry<string> GetEntry(RegistryHive hive, string path, string name);
/// <summary>
/// Retrieves a list of string-type registry entries from the Windows Registry under the specified path.
/// </summary>
List<WinRegistryEntry<string>> GetEntries(RegistryHive hive, string path);
/// <summary>
/// Retrieves a list of string-type registry entries from the Windows Registry under the specified path and its subkeys recursively.
/// </summary>
List<WinRegistryEntry<string>> GetEntriesRecursive(RegistryHive hive, string path);
#endregion
#region registry writer
void SetRegistryValue(WindowsRegistryKey key);
void SetRegistryValue(RegistryHive hive, string path, string name, object value, RegistryValueKind valueKind);
void DeleteRegistryKey(RegistryHive hive, string path, bool ignoreNotFound = false);
#region Registry Writer
/// <summary>
/// Sets the value of a registry entry.
/// </summary>
void SetValue(RegistryHive hive, string path, string name, object value, RegistryValueKind valueKind);
/// <summary>
/// Creates a new registry key.
/// </summary>
void CreateKey(RegistryHive hive, string path);
/// <summary>
/// Deletes a registry value.
/// </summary>
void DeleteRegistryValue(RegistryHive hive, string path, string name);
/// <summary>
/// Deletes a registry key and all its subkeys and values.
/// </summary>
void DeleteTree(RegistryHive hive, string path);
#endregion
#region registry tools
#region Registry Tools
/// <summary>
/// Converts a string representation of a registry hive to the corresponding RegistryHive enum value.
/// </summary>
RegistryHive ConvertStringToRegistryHive(string hiveString);
/// <summary>
/// Converts a string representation of a registry value kind to the corresponding RegistryValueKind enum value.
/// </summary>
RegistryValueKind ConvertStringToRegistryValueKind(string valueType);
/// <summary>
/// Converts a .NET type to the corresponding RegistryValueKind enum value.
/// </summary>
RegistryValueKind ConvertTypeToRegistryValueKind(Type valueType);
/// <summary>
/// Converts a RegistryValueKind enum value to the corresponding .NET type.
/// </summary>
Type ConvertRegistryValueKindToType(RegistryValueKind valueKind);
#endregion
}
}
}

View File

@@ -6,31 +6,74 @@ using System.Runtime.Versioning;
namespace mRemoteNG.Tools.WindowsRegistry
{
[SupportedOSPlatform("windows")]
/// <summary>
/// Interface for the Registry class providing methods for interacting with read actions in the Windows Registry.
/// </summary>
public interface IRegistryRead
{
#region registry reader
#region Registry Reader
/// <summary>
/// Gets the names of subkeys under the specified registry hive and path.
/// </summary>
string[] GetSubKeyNames(RegistryHive hive, string path);
string GetPropertyValue(WindowsRegistryKey key);
string GetPropertyValue(RegistryHive hive, string path, string name);
/// <summary>
/// Gets the value of a registry entry specified by its name.
/// </summary>
string GetValue(RegistryHive hive, string path, string name);
/// <summary>
/// Gets the string value of a registry entry specified by its name.
/// </summary>
string GetStringValue(RegistryHive hive, string path, string name, string defaultValue = null);
/// <summary>
/// Gets the boolean value of a registry entry specified by its name.
/// </summary>
bool GetBoolValue(RegistryHive hive, string path, string propertyName, bool defaultValue = false);
int GetDwordValue(RegistryHive hive, string path, string propertyName, int defaultValue = 0);
WindowsRegistryKey GetWindowsRegistryKey(RegistryHive hive, string path, string name);
WindowsRegistryKey GetWindowsRegistryKey(WindowsRegistryKey key);
/// <summary>
/// Gets the DWORD value of a registry entry specified by its name.
/// </summary>
int GetIntegerValue(RegistryHive hive, string path, string propertyName, int defaultValue = -1);
/// <summary>
/// Retrieves a specific registry entry of type string from the Windows Registry.
/// </summary>
WinRegistryEntry<string> GetEntry(RegistryHive hive, string path, string name);
/// <summary>
/// Retrieves a list of string-type registry entries from the Windows Registry under the specified path.
/// </summary>
List<WinRegistryEntry<string>> GetEntries(RegistryHive hive, string path);
/// <summary>
/// Retrieves a list of string-type registry entries from the Windows Registry under the specified path and its subkeys recursively.
/// </summary>
List<WinRegistryEntry<string>> GetEntriesRecursive(RegistryHive hive, string path);
List<WindowsRegistryKey> GetRegistryEntries(RegistryHive hive, string path);
List<WindowsRegistryKey> GetRegistryEntryiesRecursive(RegistryHive hive, string path);
#endregion
#region registry tools
#region Registry Tools
/// <summary>
/// Converts a string representation of a registry hive to the corresponding RegistryHive enum value.
/// </summary>
RegistryHive ConvertStringToRegistryHive(string hiveString);
/// <summary>
/// Converts a string representation of a registry value kind to the corresponding RegistryValueKind enum value.
/// </summary>
RegistryValueKind ConvertStringToRegistryValueKind(string valueType);
/// <summary>
/// Converts a .NET type to the corresponding RegistryValueKind enum value.
/// </summary>
RegistryValueKind ConvertTypeToRegistryValueKind(Type valueType);
/// <summary>
/// Converts a RegistryValueKind enum value to the corresponding .NET type.
/// </summary>
Type ConvertRegistryValueKindToType(RegistryValueKind valueKind);
#endregion
}
}

View File

@@ -0,0 +1,491 @@
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Versioning;
using System.Security;
namespace mRemoteNG.Tools.WindowsRegistry
{
[SupportedOSPlatform("windows")]
/// <summary>
/// Provides functionality to interact with the Windows Registry, including reading, writing, and managing registry entries.
/// </summary>
/// <remarks>
/// This class encapsulates common functionality for working with the Windows Registry. It offers methods to open registry keys, create subkeys, and read or write values.
/// The class serves as a base for specialized registry entry classes, centralizing registry operations and validation checks.
/// Users can create instances of this class to perform various registry operations, such as retrieving subkey names, reading values of different types, creating keys, and deleting registry entries or keys.
/// Additionally, the class includes methods for converting between different registry value types and handling custom validation rules.
///
/// License:
/// This class is licensed under MIT License.
/// </remarks>
public class WinRegistry : IRegistry, IRegistryRead
{
#region Public Read Method: GetSubKeyNames
/// <summary>
/// Retrieves the names of subkeys under a specified registry key path.
/// </summary>
/// <param name="Hive">The RegistryHive where the subkeys are located.</param>
/// <param name="Path">The path to the registry key containing the subkeys.</param>
/// <returns>An array of strings containing the names of subkeys, or an empty array if no subkeys are found.</returns>
public string[] GetSubKeyNames(RegistryHive Hive, string Path)
{
ThrowIfHiveInvalid(Hive);
ThrowIfPathInvalid(Path);
using var key = RegistryKey.OpenBaseKey(Hive, RegistryView.Default).OpenSubKey(Path);
if (key != null)
return key.GetSubKeyNames();
else
return Array.Empty<string>();
}
#endregion
#region Public Read Value Method
/// <summary>
/// Retrieves the data value associated with the specified registry key and value name.
/// </summary>
/// <param name="Hive">The registry hive.</param>
/// <param name="Path">The registry key path.</param>
/// <param name="Name">The name of the value. Null or Empty to get default.</param>
/// <returns>The value data as a string, or null if the value is not found.</returns>
/// <exception cref = "ArgumentException" > Thrown when the specified registry hive, path or name is invalid</exception>
public string GetValue(RegistryHive Hive, string Path, string Name)
{
ThrowIfHiveInvalid(Hive);
ThrowIfPathInvalid(Path);
using var key = RegistryKey.OpenBaseKey(Hive, RegistryView.Default).OpenSubKey(Path);
if (key == null)
return null;
// Ensure name is null when null or empty to get defaults value
if (string.IsNullOrEmpty(Name))
Name = null;
return key.GetValue(Name)?.ToString();
}
/// <summary>
/// Retrieves the data value associated with the specified registry key and uses the default value if the value name is not specified.
/// </summary>
/// <param name="Hive">The registry hive.</param>
/// <param name="Path">The registry key path.</param>
/// <returns>The value data as a string, or null if the value is not found.</returns>
public string GetDefaultValue(RegistryHive Hive, string Path)
{
return GetValue(Hive, Path, null);
}
/// <summary>
/// Retrieves the string value from the specified REG_SZ registry key.
/// </summary>
/// <param name="Hive">The registry hive.</param>
/// <param name="Path">The registry key path.</param>
/// <param name="Name">The name of the value. Null or Empty to get default.</param>
/// <param name="DefaultValue">The default value to return if the property is not found.</param>
/// <returns>The value data as string, or the specified default value if the value is not found.</returns>
public string GetStringValue(RegistryHive Hive, string Path, string Name, string DefaultValue = null)
{
string value = GetValue(Hive, Path, Name);
return value ?? DefaultValue;
}
/// <summary>
/// Retrieves the bool value from from the specified REG_SZ or REG_DWORD registry key.
/// </summary>
/// <param name="Hive">The registry hive.</param>
/// <param name="Path">The registry key path.</param>
/// <param name="Name">The name of the value.</param>
/// <param name="DefaultValue">The default value to return if the property is not found or cannot be parsed. (Default = false)</param>
/// <returns>The value data as bool, parsed from its REG_SZ or REG_DWORD representation if possible, or the specified default value if the value is not found or cannot be parsed.</returns>
public bool GetBoolValue(RegistryHive Hive, string Path, string Name, bool DefaultValue = false)
{
string value = GetValue(Hive, Path, Name);
if (!string.IsNullOrEmpty(value))
{
if (int.TryParse(value, out int intValue))
return intValue == 1;
if (bool.TryParse(value, out bool boolValue))
return boolValue;
}
return DefaultValue;
}
/// <summary>
/// Retrieves the integer value from from the specified REG_DWORD registry key.
/// </summary>
/// <param name="Hive">The registry hive.</param>
/// <param name="Path">The registry key path.</param>
/// <param name="Name">The name of the value.</param>
/// <param name="DefaultValue">The default value to return if the property is not found or cannot be parsed. (Default = 0)</param>
/// <returns>The value data as integer, parsed from its REG_DWORD representation if possible, or the specified default value if the value is not found or cannot be parsed.</returns>
public int GetIntegerValue(RegistryHive Hive, string Path, string Name, int DefaultValue = 0)
{
string value = GetValue(Hive, Path, Name);
if (int.TryParse(value, out int intValue))
return intValue;
return DefaultValue;
}
#endregion
#region Public Read Tree Methods
/// <summary>
/// Retrieves a windows registry entry for a specific registry hive, path, and value name.
/// </summary>
/// <param name="hive">The RegistryHive of the key.</param>
/// <param name="path">The path of the key.</param>
/// <param name="name">The name of the value to retrieve.</param>
/// <returns>A WinRegistryEntry<string> object representing the specified registry key and value.</returns>
public WinRegistryEntry<string> GetEntry(RegistryHive hive, string path, string name)
{
return WinRegistryEntry<string>
.New(hive, path, name)
.Read();
}
/// <summary>
/// Retrieves a list of registry entries and their values under a given key path.
/// </summary>
/// <param name="hive">The registry hive.</param>
/// <param name="path">The registry key path.</param>
/// <returns>A list of WinRegistryEntry<string> objects, each representing a value within the specified registry key path.</returns>
public List<WinRegistryEntry<string>> GetEntries(RegistryHive hive, string path)
{
using var key = RegistryKey.OpenBaseKey(hive, RegistryView.Default).OpenSubKey(path);
if (key == null)
return new List<WinRegistryEntry<string>>(); // Return an empty list when no key is found
return key.GetValueNames()
.Select(name => WinRegistryEntry<string>.New(hive, path, name)
.Read())
.ToList();
}
/// <summary>
/// Recursively retrieves registry entries under a given key path and its subkeys.
/// </summary>
/// <param name="hive">The registry hive.</param>
/// <param name="path">The registry key path.</param>
/// <returns>A list of WinRegistryEntry<string> objects, each representing a value within the specified registry key path.</returns>
public List<WinRegistryEntry<string>> GetEntriesRecursive(RegistryHive hive, string path)
{
List<WinRegistryEntry<string>> list = new();
using (var baseKey = RegistryKey.OpenBaseKey(hive, RegistryView.Default))
using (var key = baseKey.OpenSubKey(path))
{
if (key != null)
{
foreach (string subPathName in key.GetSubKeyNames())
{
string subKey = $"{path}\\{subPathName}";
list.AddRange(GetEntriesRecursive(hive, subKey));
}
}
}
list.AddRange(GetEntries(hive, path));
return list;
}
#endregion
#region Public Write Methods
/// <summary>
/// Sets the value of a specific property within a registry key using individual parameters.
/// </summary>
/// <param name="Hive">The registry hive.</param>
/// <param name="Path">The registry key path.</param>
/// <param name="Name">The name of the value.</param>
/// <param name="Value">The value to set for the property.</param>
/// <param name="ValueKind">The data type of the value to set.</param>
/// <exception cref="InvalidOperationException">Thrown when an error occurs while writing to the Windows Registry key.</exception>
public void SetValue(RegistryHive Hive, string Path, string Name, object Value, RegistryValueKind ValueKind)
{
ThrowIfHiveInvalid(Hive);
ThrowIfPathInvalid(Path);
string name = string.IsNullOrEmpty(Name) ? null : Name;
RegistryValueKind valueKind = string.IsNullOrEmpty(Name) ? RegistryValueKind.String : ValueKind;
ThrowIfValueKindInvalid(valueKind);
try
{
using RegistryKey baseKey = RegistryKey.OpenBaseKey(Hive, RegistryView.Default);
using RegistryKey registryKey = baseKey.CreateSubKey(Path, true);
registryKey.SetValue(name, Value, valueKind);
}
catch (Exception ex)
{
throw new InvalidOperationException("Error writing to the Windows Registry.", ex);
}
}
/// <summary>
/// Sets the default value of a registry key to the specified string value.
/// </summary>
/// <param name="Hive">The registry hive.</param>
/// <param name="Path">The registry key path.</param>
/// <param name="Value">The value to set for the default property.</param>
/// <exception cref="ArgumentException">Thrown when the specified registry hive or path is invalid.</exception>
/// <exception cref="InvalidOperationException">Thrown when an error occurs while writing to the Windows Registry key.</exception>
public void SetDefaultValue(RegistryHive Hive, string Path, string Value)
{
SetValue(Hive, Path, null, Value, RegistryValueKind.String);
}
/// <summary>
/// Creates a registry key at the specified location.
/// </summary>
/// <param name="Hive">The registry hive to create the key under.</param>
/// <param name="Path">The path of the registry key to create.</param>
/// <exception cref="ArgumentException">Thrown when the specified registry hive or path is invalid</exception>
/// <exception cref="InvalidOperationException">Thrown when an error occurs while creating the Windows Registry key.</exception>
public void CreateKey(RegistryHive Hive, string Path)
{
ThrowIfHiveInvalid(Hive);
ThrowIfPathInvalid(Path);
try
{
using RegistryKey baseKey = RegistryKey.OpenBaseKey(Hive, RegistryView.Default);
baseKey.CreateSubKey(Path);
}
catch (Exception ex)
{
throw new InvalidOperationException("Error creating the Windows Registry key.", ex);
}
}
#endregion
#region Public Delete Methods
/// <summary>
/// Deletes a registry value under the specified registry key.
/// </summary>
/// <param name="Hive">The registry hive to open.</param>
/// <param name="Path">The path of the registry key where the value exists.</param>
/// <param name="Name">The name of the value to delete.</param>
/// <exception cref="ArgumentException">Thrown when the specified registry hive, path or name is invalid</exception>
/// <exception cref="UnauthorizedAccessException">Thrown when the user does not have the necessary permissions to delete the registry value.</exception>
/// <exception cref="SecurityException">Thrown when the user does not have the necessary permissions to delete the registry value due to security restrictions.</exception>
public void DeleteRegistryValue(RegistryHive Hive, string Path, string Name)
{
ThrowIfHiveInvalid(Hive);
ThrowIfPathInvalid(Path);
ThrowIfNameInvalid(Name);
try
{
using RegistryKey baseKey = RegistryKey.OpenBaseKey(Hive, RegistryView.Default);
using RegistryKey key = baseKey.OpenSubKey(Path, true);
key?.DeleteValue(Name, true);
}
catch (SecurityException ex)
{
throw new SecurityException("Insufficient permissions to delete the registry key or value.", ex);
}
catch (UnauthorizedAccessException ex)
{
throw new UnauthorizedAccessException("Insufficient permissions to delete the registry key or value due to security restrictions.", ex);
}
catch (Exception ex)
{
throw new Exception("An error occurred while deleting the registry key or value.", ex);
}
}
/// <summary>
/// Deletes a registry key and its subkeys.
/// </summary>
/// <param name="Hive">The registry hive to open.</param>
/// <param name="Path">The path of the registry key to delete.</param>
/// <exception cref="ArgumentException">Thrown when the specified registry hive or path is invalid</exception>
/// <exception cref="UnauthorizedAccessException">Thrown when the user does not have the necessary permissions to delete the registry key.</exception>
/// <exception cref="SecurityException">Thrown when the user does not have the necessary permissions to delete the registry key due to security restrictions.</exception>
public void DeleteTree(RegistryHive Hive, string Path)
{
ThrowIfHiveInvalid(Hive);
ThrowIfPathInvalid(Path);
try
{
using RegistryKey baseKey = RegistryKey.OpenBaseKey(Hive, RegistryView.Default);
baseKey?.DeleteSubKeyTree(Path, true);
}
catch (SecurityException ex)
{
throw new SecurityException("Insufficient permissions to delete the registry key or value.", ex);
}
catch (UnauthorizedAccessException ex)
{
throw new UnauthorizedAccessException("Insufficient permissions to delete the registry key or value due to security restrictions.", ex);
}
catch (Exception ex)
{
throw new Exception("An error occurred while deleting the registry key or value.", ex);
}
}
#endregion
#region Public Converter Methods
/*******************************************************
*
* Converter methods
*
******************************************************/
/// <summary>
/// Converts a string representation of a Registry Hive to the corresponding RegistryHive enum value.
/// </summary>
/// <param name="HiveString">A string representation of a Registry Hive, not case-sensitive.</param>
/// <returns>The RegistryHive enum value corresponding to the provided string representation.</returns>
/// <exception cref="ArgumentException">Thrown if the provided string does not match a valid Registry Hive.</exception>
public RegistryHive ConvertStringToRegistryHive(string HiveString)
{
if (string.IsNullOrEmpty(HiveString))
throw new ArgumentNullException(nameof(HiveString), "The registry hive string cannot be null or empty. Please provide a valid registry hive string.");
return HiveString.ToLower() switch
{
"hkcr" or "hkey_classes_root" or "classesroot" => RegistryHive.ClassesRoot,
"hkcu" or "hkey_current_user" or "currentuser" => RegistryHive.CurrentUser,
"hklm" or "hkey_local_machine" or "localmachine" => RegistryHive.LocalMachine,
"hku" or "hkey_users" or "users" => RegistryHive.Users,
"hkcc" or "hkey_current_config" or "currentconfig" => RegistryHive.CurrentConfig,
_ => throw new ArgumentException("Invalid registry hive string.", nameof(HiveString)),
};
}
/// <summary>
/// Converts a string representation of a RegistryValueKind to the corresponding RegistryValueKind enum value.
/// </summary>
/// <param name="ValueType">A string representation of a RegistryValueKind, not case-sensitive.</param>
/// <returns>The RegistryValueKind enum value corresponding to the provided string representation.</returns>
/// <exception cref="ArgumentException">Thrown if the provided string does not match a valid RegistryValueKind.</exception>
public RegistryValueKind ConvertStringToRegistryValueKind(string ValueType)
{
if (string.IsNullOrEmpty(ValueType))
throw new ArgumentNullException(nameof(ValueType), "The registry value type string cannot be null or empty. Please provide a valid registry value type string.");
return ValueType.ToLower() switch
{
"string" or "reg_sz" => RegistryValueKind.String,
"dword" or "reg_dword" => RegistryValueKind.DWord,
"binary" or "reg_binary" => RegistryValueKind.Binary,
"qword" or "reg_qword" => RegistryValueKind.QWord,
"multistring" or "reg_multi_sz" => RegistryValueKind.MultiString,
"expandstring" or "reg_expand_sz" => RegistryValueKind.ExpandString,
_ => throw new ArgumentException("Invalid RegistryValueKind string representation.", nameof(ValueType)),
};
}
/// <summary>
/// Converts a .NET data type to the corresponding RegistryValueKind.
/// </summary>
/// <param name="ValueType">The .NET data type to convert.</param>
/// <returns>The corresponding RegistryValueKind.</returns>
public RegistryValueKind ConvertTypeToRegistryValueKind(Type ValueType)
{
if (ValueType == null)
throw new ArgumentNullException(nameof(ValueType), "The value type argument cannot be null.");
return Type.GetTypeCode(ValueType) switch
{
TypeCode.String => RegistryValueKind.String,
TypeCode.Int32 => RegistryValueKind.DWord,
TypeCode.Int64 => RegistryValueKind.QWord,
TypeCode.Boolean => RegistryValueKind.DWord,
TypeCode.Byte => RegistryValueKind.Binary,
/*
TypeCode.Single => RegistryValueKind.String,
TypeCode.Double => RegistryValueKind.String;
TypeCode.DateTime => RegistryValueKind.String;
TypeCode.Char => RegistryValueKind.String;
TypeCode.Decimal => RegistryValueKind.String;
*/
_ => RegistryValueKind.String,// Default to String for unsupported types
};
}
/// <summary>
/// Converts a RegistryValueKind enumeration value to its corresponding .NET Type.
/// </summary>
/// <param name="ValueKind">The RegistryValueKind value to be converted.</param>
/// <returns>The .NET Type that corresponds to the given RegistryValueKind.</returns>
public Type ConvertRegistryValueKindToType(RegistryValueKind ValueKind)
{
return ValueKind switch
{
RegistryValueKind.String or RegistryValueKind.ExpandString => typeof(string),
RegistryValueKind.DWord => typeof(int),
RegistryValueKind.QWord => typeof(long),
RegistryValueKind.Binary => typeof(byte[]),
RegistryValueKind.MultiString => typeof(string[]),
_ => typeof(object),
};
}
#endregion
#region throw if invalid methods
/// <summary>
/// Validates the specified RegistryHive value.
/// </summary>
/// <param name="hive">The RegistryHive value to validate.</param>
/// <exception cref="ArgumentException">Thrown when an unknown or unsupported RegistryHive value is provided.</exception>
private static void ThrowIfHiveInvalid(RegistryHive Hive)
{
if (!Enum.IsDefined(typeof(RegistryHive), Hive) || Hive == RegistryHive.CurrentConfig || Hive == 0)
throw new ArgumentException("Invalid parameter: Unknown or unsupported RegistryHive value.", nameof(Hive));
}
/// <summary>
/// Throws an exception if the specified path is null or empty.
/// </summary>
/// <param name="path">The path to validate.</param>
/// <returns>The validated parameter path.</returns>
private static void ThrowIfPathInvalid(string Path)
{
if (string.IsNullOrWhiteSpace(Path))
throw new ArgumentException("Invalid parameter: Path cannot be null, empty, or consist only of whitespace characters.", nameof(Path));
}
/// <summary>
/// Throws an exception if the specified name is null or empty.
/// </summary>
/// <param name="name">The name to validate.</param>
private static void ThrowIfNameInvalid(string Name)
{
if (Name == null)
throw new ArgumentNullException(nameof(Name), "Invalid parameter: Name cannot be null.");
}
/// <summary>
/// Throws an exception if the specified RegistryValueKind is unknown.
/// </summary>
/// <param name="valueKind">The RegistryValueKind to validate.</param>
/// <exception cref="InvalidOperationException">Thrown when the RegistryValueKind is Unknown.</exception>
private static void ThrowIfValueKindInvalid(RegistryValueKind ValueKind)
{
if (!Enum.IsDefined(typeof(RegistryValueKind), ValueKind) || ValueKind == RegistryValueKind.Unknown || ValueKind == RegistryValueKind.None)
throw new ArgumentException("Invalid parameter: Unknown or unsupported RegistryValueKind value.", nameof(ValueKind));
}
#endregion
}
}

View File

@@ -0,0 +1,904 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Versioning;
using Microsoft.Win32;
namespace mRemoteNG.Tools.WindowsRegistry
{
[SupportedOSPlatform("windows")]
/// <summary>
/// Represents an entry in the Windows Registry.
/// </summary>
/// <remarks>
/// This class encapsulates the functionality needed to interact with Windows Registry entries,
/// including reading and writing values. It provides a comprehensive set of methods to handle
/// different value types, ensuring flexibility and ease of use.
///
/// Key features include:
/// - Reading values from a specified registry path and name.
/// - Writing values to a specified registry path and name.
/// - Support for multiple data types such as strings, integers, and binary data.
/// - Ability to specify the registry hive (e.g., HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER).
///
/// This class is designed to simplify the manipulation of registry entries by providing a
/// straightforward interface for common registry operations.
///
/// Example usage:
/// <code>
/// var registryEntry = new RegistryEntry<string>(RegistryHive.LocalMachine, @"Software\MyApp", "Settings");
/// var value = registryEntry.Read();
/// if (value != **)
/// registryEntry.Write("newVal");
/// </code>
///
/// <code>
/// var registryEntry = new RegistryEntry<int>(RegistryHive.LocalMachine, @"Software\MyApp", "Settings").Read();
/// </code>
///
/// <code>
/// var registryEntry = new RegistryEntry<long>(RegistryHive.LocalMachine, @"Software\MyApp", "Settings").SetValidation(min, max).Read();
/// if (registryEntry.IsValid())
/// Do Something
/// </code>
///
/// </remarks>
public class WinRegistryEntry<T>
{
#region Registry Fileds & Properties
/// <summary>
/// Represents the registry hive associated with the registry key.
/// </summary>
/// <remarks>
/// The default value is <see cref="RegistryHive.CurrentUser"/>.
/// </remarks>
public RegistryHive Hive
{
get { return privateHive; }
set
{
privateHive =
!Enum.IsDefined(typeof(RegistryHive), value) || value == RegistryHive.CurrentConfig || value == RegistryHive.ClassesRoot
? throw new ArgumentException("Invalid parameter: Unknown or unsupported RegistryHive value.", nameof(Hive))
: value;
}
}
private RegistryHive privateHive = RegistryHive.CurrentUser;
/// <summary>
/// Represents the path of the registry entry.
/// </summary>
public string Path
{
get { return privatePath; }
set
{
privatePath =
!string.IsNullOrWhiteSpace(value)
? value
: throw new ArgumentNullException(nameof(Path), "Invalid parameter: Path cannot be null, empty, or consist only of whitespace characters.");
}
}
private string privatePath;
/// <summary>
/// Represents the name of the registry entry.
/// </summary>
public string Name { get; set; }
/// <summary>
/// Represents the kind of data stored in the registry value.
/// </summary>
public RegistryValueKind ValueKind { get; private set; } = InitialRegistryValueKind();
/// <summary>
/// Represents the value of the registry entry.
/// </summary>
public T Value
{
get { return privateValue; }
set
{
privateValue = ValueValidationRules(value);
}
}
private T privateValue;
#endregion
#region Aditional Fileds & Properties
private T[] AllowedValues;
private int? MinInt32Value;
private int? MaxInt32Value;
private long? MinInt64Value;
private long? MaxInt64Value;
private Type EnumType;
/// <summary>
/// Represents the raw value retrieved directly from the registry.
/// </summary>
private string RawValue;
/// <summary>
/// Represents the type of the generic parameter T.
/// </summary>
private readonly Type ElementType = typeof(T);
/// <summary>
/// Indicates whether the reading operation for the registry value was successful.
/// </summary>
private bool ReadOperationSucceeded;
/// <summary>
/// Indicates whether a lock operation should be performed after a successful read operation.
/// </summary>
private bool DoLock;
/// <summary>
/// Indicates whether the WinRegistryEntry is currently locked, preventing further read operations.
/// </summary>
public bool IsLocked { get; private set; }
/// <summary>
/// Gets a value indicating whether the entry has been explicitly set in the registry.
/// This check is faster as it only verifies if a value was readed (set in registry).
/// </summary>
public bool IsSet => IsEntrySet();
/// <summary>
/// Gets a value indicating whether the entry's value is valid according to custom validation rules.
/// This check includes whether the value has been set and whether it adheres to the defined validation criteria.
/// </summary>
public bool IsValid => CheckIsValid();
#endregion
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="WinRegistryEntry{T}"/> class with default values.
/// </summary>
public WinRegistryEntry() { }
/// <summary>
/// Initializes a new instance of the <see cref="WinRegistryEntry{T}"/> class for reading a default value from the specified registry hive and path.
/// </summary>
/// <param name="hive">The registry hive of the entry.</param>
/// <param name="path">The path of the registry entry.</param>
public WinRegistryEntry(RegistryHive hive, string path)
{
Hive = hive;
Path = path;
}
/// <summary>
/// Initializes a new instance of the <see cref="WinRegistryEntry{T}"/> class for writing a default value to the specified registry hive and path.
/// </summary>
/// <param name="hive">The registry hive of the entry.</param>
/// <param name="path">The path of the registry entry.</param>
/// <param name="value">The value of the registry entry.</param>
public WinRegistryEntry(RegistryHive hive, string path, T value)
{
Hive = hive;
Path = path;
Value = value;
}
/// <summary>
/// Initializes a new instance of the <see cref="WinRegistryEntry{T}"/> class for reading a specific value from the specified registry hive, path, and name.
/// </summary>
/// <param name="hive">The registry hive of the entry.</param>
/// <param name="path">The path of the registry entry.</param>
/// <param name="name">The name of the registry entry.</param>
public WinRegistryEntry(RegistryHive hive, string path, string name)
{
Hive = hive;
Path = path;
Name = name;
}
/// <summary>
/// Initializes a new instance of the <see cref="WinRegistryEntry{T}"/> class for writing a specific value to the specified registry hive, path, and name.
/// </summary>
/// <param name="hive">The registry hive of the entry.</param>
/// <param name="path">The path of the registry entry.</param>
/// <param name="name">The name of the registry entry.</param>
/// <param name="value">The value of the registry entry.</param>
public WinRegistryEntry(RegistryHive hive, string path, string name, T value)
{
Hive = hive;
Path = path;
Name = name;
Value = value;
}
#endregion
#region Factory Methods
/// <summary>
/// Creates a new instance of the <see cref="WinRegistryEntry{T}"/> class for reading a default value from the specified registry hive and path.
/// </summary>
/// <param name="hive">The registry hive of the entry.</param>
/// <param name="path">The path of the registry entry.</param>
/// <returns>A new instance of the <see cref="WinRegistryEntry{T}"/> class.</returns>
public static WinRegistryEntry<T> New(RegistryHive hive, string path)
{
return new WinRegistryEntry<T>(hive, path);
}
/// <summary>
/// Creates a new instance of the <see cref="WinRegistryEntry{T}"/> class for writing a value to the specified registry hive and path.
/// </summary>
/// <param name="hive">The registry hive of the entry.</param>
/// <param name="path">The path of the registry entry.</param>
/// <param name="value">The value of the registry entry.</param>
/// <returns>A new instance of the <see cref="WinRegistryEntry{T}"/> class.</returns>
public static WinRegistryEntry<T> New(RegistryHive hive, string path, T value)
{
return new WinRegistryEntry<T>(hive, path, value);
}
/// <summary>
/// Creates a new instance of the <see cref="WinRegistryEntry{T}"/> class for reading a specific value from the specified registry hive, path, and name.
/// </summary>
/// <param name="hive">The registry hive of the entry.</param>
/// <param name="path">The path of the registry entry.</param>
/// <param name="name">The name of the registry entry.</param>
/// <returns>A new instance of the <see cref="WinRegistryEntry{T}"/> class.</returns>
public static WinRegistryEntry<T> New(RegistryHive hive, string path, string name)
{
return new WinRegistryEntry<T>(hive, path, name);
}
/// <summary>
/// Creates a new instance of the <see cref="WinRegistryEntry{T}"/> class for writing a specific value to the specified registry hive, path, and name.
/// </summary>
/// <param name="hive">The registry hive of the entry.</param>
/// <param name="path">The path of the registry entry.</param>
/// <param name="name">The name of the registry entry.</param>
/// <param name="value">The value of the registry entry.</param>
/// <returns>A new instance of the <see cref="WinRegistryEntry{T}"/> class.</returns>
public static WinRegistryEntry<T> New(RegistryHive hive, string path, string name, T value)
{
return new WinRegistryEntry<T>(hive, path, name, value);
}
#endregion
#region Public Methods
/// <summary>
/// Sets the kind of the registry value, ensuring it is a valid and defined <see cref="RegistryValueKind"/>.
/// </summary>
/// <param name="valueKind">The registry value kind to set.</param>
/// <returns>The current instance of <see cref="WinRegistryEntry{T}"/> to allow for method chaining.</returns>
public WinRegistryEntry<T> SetValueKind(RegistryValueKind valueKind)
{
if (ValueKindValidationRule(valueKind))
ValueKind = valueKind;
else
// Validation rule returned false, so the initial value will be used for the specified system type.
// Nothing will be changed
LogError("ValueKind is not valid and cannot be set.");
return this;
}
/// <summary>
/// Sets the allowed values for validation, with an option for case sensitivity.
/// </summary>
/// <param name="allowedValues">The array of allowed values.</param>
public WinRegistryEntry<T> SetValidation(T[] allowedValues)
{
ResetValidation();
if (allowedValues != null && allowedValues.Length > 0)
{
AllowedValues = allowedValues;
}
return this;
}
/// <summary>
/// Sets up validation using an array of allowed integer values.
/// </summary>
/// <param name="allowedValues">The array of allowed integer values.</param>
public WinRegistryEntry<T> SetValidation(int[] allowedValues)
{
T[] mappedValues = allowedValues?.Select(value => (T)(object)value).ToArray();
return SetValidation(mappedValues);
}
/// <summary>
/// Sets up validation using an array of allowed integer values.
/// </summary>
/// <param name="allowedValues">The array of allowed integer values.</param>
public WinRegistryEntry<T> SetValidation(long[] allowedValues)
{
T[] mappedValues = allowedValues?.Select(value => (T)(object)value).ToArray();
return SetValidation(mappedValues);
}
/// <summary>
/// Sets up validation for a range of integer values.
/// </summary>
/// <param name="minValue">The minimum value of the range.</param>
/// <param name="maxValue">The maximum value of the range.</param>
public WinRegistryEntry<T> SetValidation(int minValue, int maxValue)
{
ValidateRange(minValue, maxValue);
ResetValidation();
MinInt32Value = minValue;
MaxInt32Value = maxValue;
return this;
}
/// <summary>
/// Sets up validation for a range of integer values.
/// </summary>
/// <param name="minValue">The minimum value of the range.</param>
/// <param name="maxValue">The maximum value of the range.</param>
public WinRegistryEntry<T> SetValidation(long minValue, long maxValue)
{
ValidateRange(minValue, maxValue);
ResetValidation();
MinInt64Value = minValue;
MaxInt64Value = maxValue;
return this;
}
/// <summary>
/// Sets up validation rules for a range of Int32, Int64 values.
/// </summary>
/// <param name="minValue">The minimum value of the range. "*" can be provided to indicate no minimum value.</param>
/// <param name="maxValue">The maximum value of the range. "*" can be provided to indicate no maximum value.</param>
/// <returns>The current instance of the WinRegistryEntry<T> class.</returns>
/// <exception cref="ArgumentException">Thrown when the registry entry type is not a valid Int32 or Int64.</exception>
/// <exception cref="ArgumentException">Thrown when an invalid minimum value is provided for Int32 or Int64.</exception>
/// <exception cref="ArgumentException">Thrown when an invalid maximum value is provided for Int32 or Int64.</exception>
public WinRegistryEntry<T> SetValidation(string minValue, string maxValue)
{
if (string.IsNullOrEmpty(minValue) || minValue == "*")
minValue = "0";
if ((string.IsNullOrEmpty(maxValue) || maxValue == "*"))
maxValue = (ElementType == typeof(int))
? Int32.MaxValue.ToString()
: Int64.MaxValue.ToString();
if (ElementType == typeof(int))
{
if (!int.TryParse(minValue, out int minIntValue))
throw new ArgumentException("Invalid minimum value for Int32.");
if (!int.TryParse(maxValue, out int maxIntValue))
throw new ArgumentException("Invalid maximum value for Int32.");
return SetValidation(minIntValue, maxIntValue);
}
else if (ElementType == typeof(long))
{
if (!long.TryParse(minValue, out long minLongValue))
throw new ArgumentException("Invalid minimum value for Int64.");
if (!long.TryParse(maxValue, out long maxLongValue))
throw new ArgumentException("Invalid maximum value for Int64.");
return SetValidation(minLongValue, maxLongValue);
}
else
{
throw new ArgumentException("Registry entry type must be either a valid Int32 or Int64 to use this validation.");
}
}
/// <summary>
/// Sets the validation to use an enumeration type
/// </summary>
/// <typeparam name="TEnum">The enumeration type.</typeparam>
public WinRegistryEntry<T> SetValidation<TEnum>() where TEnum : Enum
{
ResetValidation();
Type enumType = typeof(TEnum);
if (enumType != null)
EnumType = enumType;
return this;
}
/// <summary>
/// Checks if the Windows Registry key is ready for reading by ensuring that the hive,
/// path, and name properties are set.
/// </summary>
/// <returns>True if the key is ready for reading, otherwise false.</returns>
public bool IsReadable()
{
return IsHiveSet() && IsPathSet();
}
/// <summary>
/// Checks if the Windows Registry key is ready for a write operation.
/// The key is considered write-ready if none of the following conditions are met:
/// - The hive is set
/// - The registry value type is set
/// - The key path is set
/// </summary>
/// <returns>Returns true if the key is write-ready, otherwise false.</returns>
public bool IsWritable()
{
return IsHiveSet() && IsValueKindSet() && IsPathSet();
}
/// <summary>
/// Reads the value of the registry entry from the specified registry path and assigns it to the Value property.
/// </summary>
/// <returns>The current instance of <see cref="WinRegistryEntry{T}"/> to allow for method chaining.</returns>
public WinRegistryEntry<T> Read()
{
if (IsLocked)
throw new InvalidOperationException("Operation denied: Cannot read registry entry again. Lock is enabled.");
if (!IsReadable())
throw new InvalidOperationException("Unable to read registry key. Hive, path, and name are required.");
string rawValue = null;
string name = string.IsNullOrEmpty(Name) ? null : Name;
try
{
using var key = RegistryKey.OpenBaseKey(Hive, RegistryView.Default).OpenSubKey(Path);
if (key != null)
RawValue = rawValue = key.GetValue(name)?.ToString();
if (rawValue != null)
ValueKind = key.GetValueKind(name);
}
catch (Exception ex)
{
throw new InvalidOperationException("Error reading the Windows Registry.", ex);
}
if (string.IsNullOrEmpty(rawValue))
{
// Issue in Windows registry: Value was null or empty.
string logName = string.IsNullOrEmpty(Name) ? "Default Value" : Name;
LogInfo($"Value for {logName} is Null or Empty");
}
else if (!ValueKindValidationRule(ValueKind))
{
// Issue in Windows registry: Value kind of the value cannot be parsed to type T.
LogError($"Cannot parse a Value of type {ValueKind} to the specified type {typeof(T).FullName}.");
}
else
{
Value = ConvertValueBasedOnType(rawValue);
ReadOperationSucceeded = true;
if (DoLock)
IsLocked = true;
}
return this;
}
/// <summary>
/// Writes the value of the registry entry to the specified registry path.
/// </summary>
/// <returns>The current instance of <see cref="WinRegistryEntry{T}"/> to allow for method chaining.</returns>
public WinRegistryEntry<T> Write()
{
if (!IsWritable())
throw new InvalidOperationException("Unable to write registry key. Hive, path, name, value kind, and value are required.");
string name = string.IsNullOrEmpty(Name) ? null : Name;
RegistryValueKind valueKind = string.IsNullOrEmpty(Name) ? RegistryValueKind.String : ValueKind;
string value;
if (typeof(T) == typeof(bool))
{
value = (bool)(object)Value
? ValueKind == RegistryValueKind.DWord ? "1" : "True"
: ValueKind == RegistryValueKind.DWord ? "0" : "False";
}
else
{
value = Value.ToString();
}
try
{
using RegistryKey baseKey = RegistryKey.OpenBaseKey(Hive, RegistryView.Default);
using RegistryKey registryKey = baseKey.CreateSubKey(Path, true);
registryKey.SetValue(name, value, valueKind);
}
catch (Exception ex)
{
throw new InvalidOperationException("Error writing to the Windows Registry.", ex);
}
return this;
}
/// <summary>
/// Writes a new value to the registry entry.
/// </summary>
/// <param name="newValue">The new value to be written to the registry entry.</param>
/// <returns>The current instance of <see cref="WinRegistryEntry{T}"/> to allow for method chaining.</returns>
public WinRegistryEntry<T> Write(T newValue)
{
Value = newValue;
return Write();
}
/// <summary>
/// Clears the current values of the instance.
/// </summary>
/// <remarks>
/// This method resets the values to their default states.
/// After invoking this method, the "Validations" properties <c>IsSet</c> and <c>IsValid</c> will return <c>false</c>.
/// This is useful in scenarios where the value needs to be validated through alternative mechanisms.
/// </remarks>
public void Clear()
{
RawValue = null;
Value = default;
ReadOperationSucceeded = false;
}
/// <summary>
/// Locks the WinRegistryEntry to prevent further read operations.
/// If a read operation has already succeeded, the entry is immediately locked.
/// If no read operation has been performed yet, a flag indicating the intention to lock after a successful read operation is set.
/// </summary>
/// <returns>The current instance of <see cref="WinRegistryEntry{T}"/> to allow for method chaining.</returns>
public WinRegistryEntry<T> Lock()
{
if (ReadOperationSucceeded)
IsLocked = true;
else
DoLock = true;
return this;
}
#endregion
#region Private Methods
/// <summary>
/// Converts a string value to the specified .NET data type <typeparamref name="T"/>.
/// </summary>
/// <typeparam name="T">The target .NET data type to which the value is converted.</typeparam>
/// <param name="originalValue">The string value to be converted.</param>
/// <returns>The converted value of type <typeparamref name="T"/>.</returns>
/// <exception cref="InvalidOperationException">Thrown when Conversion for <typeparamref name="T"/> failed..</exception>
/// <exception cref="InvalidOperationException">Thrown when Conversion not supported for type <typeparamref name="T"/>.</exception>
private T ConvertValueBasedOnType(string originalValue)
{
try
{
if (ElementType == typeof(string))
{
return (T)(object)originalValue;
}
else if (ElementType == typeof(int))
{
if (int.TryParse(originalValue, out int intValue))
return (T)(object)intValue;
}
else if (ElementType == typeof(long))
{
if (long.TryParse(originalValue, out long longValue))
return (T)(object)longValue;
}
else if (ElementType == typeof(bool))
{
if (bool.TryParse(originalValue, out bool boolValue))
return (T)(object)boolValue;
else if (int.TryParse(originalValue, out int intBool))
return (T)(object)(intBool == 1);
}
}
catch
{
throw new InvalidOperationException($"Conversion for '{ElementType}' failed.");
}
throw new InvalidOperationException($"Conversion not supported for type '{ElementType}'.");
}
/// <summary>
/// Logs an error message to the standard error stream.
/// </summary>
/// <param name="message">The error message to log.</param>
private static void LogError(string message)
{
Console.Error.WriteLine($"Error: {message}");
}
/// <summary>
/// Logs an info message to the standard error stream.
/// </summary>
/// <param name="message">The error message to log.</param>
private static void LogInfo(string message)
{
Console.WriteLine($"Info: {message}");
}
/// <summary>
/// Validates the provided value based on its type-specific rules.
/// </summary>
/// <param name="value">The value to be validated.</param>
/// <exception cref="ArgumentException">Thrown when <typeparamref name="T"/> is bool and value is not True, False, 0 or 1.</exception>
/// <exception cref="ArgumentException">Thrown when <typeparamref name="T"/> is int/long and value is negative.</exception>
/// <exception cref="ArgumentException">Thrown when Value type <typeparamref name="T"/> is not supported.</exception>
private T ValueValidationRules(T value)
{
// Boolean values are either string or DWORD. Mapping is needed to update ValueKind.
var booleanRegistryValueKindMap = new Dictionary<string, RegistryValueKind>
{
{ "true", RegistryValueKind.String },
{ "false", RegistryValueKind.String },
{ "0", RegistryValueKind.DWord },
{ "1", RegistryValueKind.DWord }
};
// For string elements, directly enforce validity and correct the input.
if (ElementType == typeof(string))
{
return EnforceStringInputValidity(value);
}
// For boolean elements, check if the value is valid and convert it to the appropriate value kind.
else if (ElementType == typeof(bool))
{
if (!booleanRegistryValueKindMap.TryGetValue(value.ToString().ToLower(), out var valueKind))
throw new ArgumentException("Invalid value. Supported values are ci strings 'True'/'False' or numbers '0'/'1'.", nameof(value));
ValueKind = valueKind;
return value;
}
// For integer or long elements, ensure the value is not negative.
else if (ElementType == typeof(int) || ElementType == typeof(long))
{
if (Convert.ToInt64(value) < 0)
throw new ArgumentException("Value cannot be negative.", nameof(value));
return value;
}
// For byte elements, ensure the value is not null or empty.
else if (ElementType == typeof(byte))
{
if (value == null || ((byte[])(object)value).Length == 0)
throw new ArgumentException("Value cannot be null or empty.", nameof(value));
return value;
}
// For unsupported element types, throw an ArgumentException.
throw new ArgumentException($"Value type '{ElementType.FullName}' is not supported.", nameof(value));
}
/// <summary>
/// Validates and corrects a string value based on a set of allowed values or enumeration values.
/// </summary>
/// <param name="value">The input value to be validated and potentially corrected.</param>
/// <returns>The validated and potentially corrected value.</returns>
private T EnforceStringInputValidity(T value)
{
if (AllowedValues != null)
{
T matchedValue = AllowedValues.FirstOrDefault(v => v.ToString().Equals(value.ToString(), StringComparison.OrdinalIgnoreCase));
if (matchedValue != null)
// Correct the Value to ensure the correct spelling and avoid user typing mistakes
return matchedValue;
}
else if (EnumType != null && EnumType.IsEnum)
{
var matchedEnumValue = Enum.GetValues(EnumType)
.Cast<Enum>()
.FirstOrDefault(e => e.ToString().Equals(value.ToString(), StringComparison.OrdinalIgnoreCase));
if (matchedEnumValue != null)
// Correct the Value to ensure the correct spelling and avoid user typing mistakes
return ConvertValueBasedOnType(matchedEnumValue.ToString());
}
return value;
}
/// <summary>
/// Private method to validate the range values.
/// </summary>
/// <typeparam name="U">The type of the values being validated.</typeparam>
/// <param name="minValue">The minimum value of the range.</param>
/// <param name="maxValue">The maximum value of the range.</param>
/// <param name="type">The type of registry entry (used for error messages).</param>
private static void ValidateRange<U>(U minValue, U maxValue) where U : IComparable<U>
{
Type typeCode = typeof(U);
string type =
typeCode == typeof(int) ? "dword" :
typeCode == typeof(long) ? "qword"
: throw new ArgumentException("Registry entry type must be either Int32 or Int64 to use this validation.");
if (minValue.CompareTo(default(U)) < 0)
throw new ArgumentException($"Negative value not allowed for {type} parameter.", nameof(minValue));
if (maxValue.CompareTo(default(U)) < 0)
throw new ArgumentException($"Negative value not allowed for {type} parameter.", nameof(maxValue));
if (minValue.CompareTo(maxValue) > 0)
throw new ArgumentException("MinValue must be less than or equal to MaxValue.");
}
/// <summary>
/// Validates the specified registry value kind.
/// </summary>
/// <param name="valueKind">The registry value kind to validate.</param>
/// <returns>The validated <see cref="RegistryValueKind"/>.</returns>
/// <exception cref="ArgumentException">Thrown when Invalid parameter: Unknown or unsupported <typeparamref name="valueKind" value.</exception>
/// <exception cref="ArgumentException">Thrown when Value type <typeparamref name="T"/> is not supported.</exception>
private bool ValueKindValidationRule(RegistryValueKind valueKind)
{
if (!Enum.IsDefined(typeof(RegistryValueKind), valueKind) || valueKind == RegistryValueKind.Unknown || valueKind == RegistryValueKind.None || valueKind == 0)
throw new ArgumentException("Invalid parameter: Unknown or unsupported RegistryValueKind value.", nameof(valueKind));
return Type.GetTypeCode(ElementType) switch
{
TypeCode.Boolean => valueKind == RegistryValueKind.String || valueKind == RegistryValueKind.DWord,
TypeCode.Int32 => valueKind == RegistryValueKind.DWord,
TypeCode.Int64 => valueKind == RegistryValueKind.QWord,
TypeCode.Byte => valueKind == RegistryValueKind.Binary,
TypeCode.String => valueKind == RegistryValueKind.String || valueKind == RegistryValueKind.DWord || valueKind == RegistryValueKind.QWord, // Strings are compatible with most data types.
_ => throw new ArgumentException($"Value type '{ElementType.FullName}' is not supported.")
};
}
/// <summary>
/// Determines the initial RegistryValueKind based on the type <typeparamref name="T"/>.
/// </summary>
/// <returns>The initial RegistryValueKind determined by the type <typeparamref name="T"/>.</returns>
private static RegistryValueKind InitialRegistryValueKind()
{
return Type.GetTypeCode(typeof(T)) switch
{
TypeCode.Int32 => RegistryValueKind.DWord,
TypeCode.Int64 => RegistryValueKind.QWord,
TypeCode.Boolean => RegistryValueKind.String,
TypeCode.Byte => RegistryValueKind.Binary,
_ => RegistryValueKind.String, // Default to String for unsupported types
};
}
/// <summary>
/// Determines whether the registry entry has been set.
/// </summary>
private bool IsEntrySet() => RawValue != null && ReadOperationSucceeded;
/// <summary>
/// Determines whether the registry hive has been explicitly set.
/// </summary>
/// <returns><c>true</c> if the hive is set; otherwise, <c>false</c>.</returns>
private bool IsHiveSet() => Hive != 0;
/// <summary>
/// Determines whether the value kind of the registry entry has been explicitly set.
/// </summary>
/// <returns><c>true</c> if the value kind is set; otherwise, <c>false</c>.</returns>
private bool IsValueKindSet() => ValueKind != 0;
/// <summary>
/// Determines whether the path of the registry entry has been explicitly set.
/// </summary>
/// <returns><c>true</c> if the path is set; otherwise, <c>false</c>.</returns>
private bool IsPathSet() => Path != null;
/// <summary>
/// Checks if the current value is valid according to its type-specific rules and constraints.
/// </summary>
/// <returns>True if the value is valid; otherwise, false.</returns>
private bool CheckIsValid()
{
if (!IsEntrySet()) return false;
return Type.GetTypeCode(ElementType) switch
{
TypeCode.String => ValidateString(),
TypeCode.Boolean => true,
TypeCode.Int32 => ValidateInt32(),
TypeCode.Int64 => ValidateInt64(),
_ => throw new ArgumentException($"Value type '{ElementType.FullName}' is not supported."),
};
}
/// <summary>
/// Resets all validation setup for the entry to their default values.
/// This includes clearing allowed values, resetting case sensitivity, setting numeric ranges and enum types to null.
/// </summary>
private void ResetValidation()
{
AllowedValues = null;
MinInt32Value = null;
MaxInt32Value = null;
MinInt64Value = null;
MaxInt64Value = null;
EnumType = null;
}
/// <summary>
/// Validates a string value based on allowed values or enumeration values.
/// </summary>
/// <returns>True if the string value is valid; otherwise, false.</returns>
private bool ValidateString()
{
if (AllowedValues != null)
{
//return AllowedValues.FirstOrDefault(v => v.ToString().Equals(Value.ToString(), StringComparison.OrdinalIgnoreCase)) != null;
return AllowedValues.Contains(Value);
}
else if (EnumType != null && EnumType.IsEnum)
{
return Enum.GetValues(EnumType)
.Cast<Enum>()
.Any(e => e.ToString().Equals(Value.ToString(), StringComparison.OrdinalIgnoreCase));
}
return true;
}
/// <summary>
/// Validates an integer value based on allowed values, minimum and maximum values, or enumeration values.
/// </summary>
/// <returns>True if the integer value is valid; otherwise, false.</returns>
private bool ValidateInt32()
{
int value = (int)(object)Value;
if (AllowedValues != null)
return AllowedValues.Contains(Value);
else if (MinInt32Value != null && MaxInt32Value != null)
return value >= MinInt32Value && value <= MaxInt32Value;
else if (EnumType != null && EnumType.IsEnum)
{
foreach (object enumValue in Enum.GetValues(EnumType))
{
if (Convert.ToInt32(enumValue) == value)
{
return true;
}
}
return false;
}
return true;
}
/// <summary>
/// Validates a long integer value based on allowed values, minimum and maximum values, or enumeration values.
/// </summary>
/// <returns>True if the long integer value is valid; otherwise, false.</returns>
private bool ValidateInt64()
{
long value = (long)(object)Value;
if (AllowedValues != null)
return AllowedValues.Contains(Value);
else if (MinInt64Value != null && MaxInt64Value != null)
return value >= MinInt64Value && value <= MaxInt64Value;
return true;
}
#endregion
}
}