diff --git a/mRemoteNG/Tools/WindowsRegistry/IRegistry.cs b/mRemoteNG/Tools/WindowsRegistry/IRegistry.cs
index 0bf2da86c..3458a37fb 100644
--- a/mRemoteNG/Tools/WindowsRegistry/IRegistry.cs
+++ b/mRemoteNG/Tools/WindowsRegistry/IRegistry.cs
@@ -6,39 +6,98 @@ using System.Runtime.Versioning;
namespace mRemoteNG.Tools.WindowsRegistry
{
[SupportedOSPlatform("windows")]
- ///
- /// Interface for the Registry class providing methods for interacting with the Windows Registry.
- ///
public interface IRegistry
{
- #region registry reader
+ #region Registry Reader
+
+ ///
+ /// Gets the names of subkeys under the specified registry hive and path.
+ ///
string[] GetSubKeyNames(RegistryHive hive, string path);
- string GetPropertyValue(WindowsRegistryKey key);
- string GetPropertyValue(RegistryHive hive, string path, string name);
+ ///
+ /// Gets the value of a registry entry specified by its name.
+ ///
+ string GetValue(RegistryHive hive, string path, string name);
+
+ ///
+ /// Gets the string value of a registry entry specified by its name.
+ ///
+ string GetStringValue(RegistryHive hive, string path, string name, string defaultValue = null);
+
+ ///
+ /// Gets the boolean value of a registry entry specified by its name.
+ ///
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);
+ ///
+ /// Gets the DWORD value of a registry entry specified by its name.
+ ///
+ int GetIntegerValue(RegistryHive hive, string path, string propertyName, int defaultValue = -1);
- List GetRegistryEntries(RegistryHive hive, string path);
- List GetRegistryEntryiesRecursive(RegistryHive hive, string path);
+ ///
+ /// Retrieves a specific registry entry of type string from the Windows Registry.
+ ///
+ WinRegistryEntry GetEntry(RegistryHive hive, string path, string name);
+
+ ///
+ /// Retrieves a list of string-type registry entries from the Windows Registry under the specified path.
+ ///
+ List> GetEntries(RegistryHive hive, string path);
+
+ ///
+ /// Retrieves a list of string-type registry entries from the Windows Registry under the specified path and its subkeys recursively.
+ ///
+ List> 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
+
+ ///
+ /// Sets the value of a registry entry.
+ ///
+ void SetValue(RegistryHive hive, string path, string name, object value, RegistryValueKind valueKind);
+
+ ///
+ /// Creates a new registry key.
+ ///
+ void CreateKey(RegistryHive hive, string path);
+
+ ///
+ /// Deletes a registry value.
+ ///
+ void DeleteRegistryValue(RegistryHive hive, string path, string name);
+
+ ///
+ /// Deletes a registry key and all its subkeys and values.
+ ///
+ void DeleteTree(RegistryHive hive, string path);
#endregion
- #region registry tools
+ #region Registry Tools
+
+ ///
+ /// Converts a string representation of a registry hive to the corresponding RegistryHive enum value.
+ ///
RegistryHive ConvertStringToRegistryHive(string hiveString);
+
+ ///
+ /// Converts a string representation of a registry value kind to the corresponding RegistryValueKind enum value.
+ ///
RegistryValueKind ConvertStringToRegistryValueKind(string valueType);
+
+ ///
+ /// Converts a .NET type to the corresponding RegistryValueKind enum value.
+ ///
RegistryValueKind ConvertTypeToRegistryValueKind(Type valueType);
+
+ ///
+ /// Converts a RegistryValueKind enum value to the corresponding .NET type.
+ ///
Type ConvertRegistryValueKindToType(RegistryValueKind valueKind);
+
#endregion
}
-}
\ No newline at end of file
+}
diff --git a/mRemoteNG/Tools/WindowsRegistry/IRegistryRead.cs b/mRemoteNG/Tools/WindowsRegistry/IRegistryRead.cs
index 28b5d1423..7228266b5 100644
--- a/mRemoteNG/Tools/WindowsRegistry/IRegistryRead.cs
+++ b/mRemoteNG/Tools/WindowsRegistry/IRegistryRead.cs
@@ -6,31 +6,74 @@ using System.Runtime.Versioning;
namespace mRemoteNG.Tools.WindowsRegistry
{
[SupportedOSPlatform("windows")]
- ///
- /// Interface for the Registry class providing methods for interacting with read actions in the Windows Registry.
- ///
public interface IRegistryRead
{
- #region registry reader
+ #region Registry Reader
+
+ ///
+ /// Gets the names of subkeys under the specified registry hive and path.
+ ///
string[] GetSubKeyNames(RegistryHive hive, string path);
- string GetPropertyValue(WindowsRegistryKey key);
- string GetPropertyValue(RegistryHive hive, string path, string name);
+ ///
+ /// Gets the value of a registry entry specified by its name.
+ ///
+ string GetValue(RegistryHive hive, string path, string name);
+
+ ///
+ /// Gets the string value of a registry entry specified by its name.
+ ///
+ string GetStringValue(RegistryHive hive, string path, string name, string defaultValue = null);
+
+ ///
+ /// Gets the boolean value of a registry entry specified by its name.
+ ///
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);
+ ///
+ /// Gets the DWORD value of a registry entry specified by its name.
+ ///
+ int GetIntegerValue(RegistryHive hive, string path, string propertyName, int defaultValue = -1);
+
+ ///
+ /// Retrieves a specific registry entry of type string from the Windows Registry.
+ ///
+ WinRegistryEntry GetEntry(RegistryHive hive, string path, string name);
+
+ ///
+ /// Retrieves a list of string-type registry entries from the Windows Registry under the specified path.
+ ///
+ List> GetEntries(RegistryHive hive, string path);
+
+ ///
+ /// Retrieves a list of string-type registry entries from the Windows Registry under the specified path and its subkeys recursively.
+ ///
+ List> GetEntriesRecursive(RegistryHive hive, string path);
- List GetRegistryEntries(RegistryHive hive, string path);
- List GetRegistryEntryiesRecursive(RegistryHive hive, string path);
#endregion
- #region registry tools
+ #region Registry Tools
+
+ ///
+ /// Converts a string representation of a registry hive to the corresponding RegistryHive enum value.
+ ///
RegistryHive ConvertStringToRegistryHive(string hiveString);
+
+ ///
+ /// Converts a string representation of a registry value kind to the corresponding RegistryValueKind enum value.
+ ///
RegistryValueKind ConvertStringToRegistryValueKind(string valueType);
+
+ ///
+ /// Converts a .NET type to the corresponding RegistryValueKind enum value.
+ ///
RegistryValueKind ConvertTypeToRegistryValueKind(Type valueType);
+
+ ///
+ /// Converts a RegistryValueKind enum value to the corresponding .NET type.
+ ///
Type ConvertRegistryValueKindToType(RegistryValueKind valueKind);
+
#endregion
}
}
\ No newline at end of file
diff --git a/mRemoteNG/Tools/WindowsRegistry/WinRegistry.cs b/mRemoteNG/Tools/WindowsRegistry/WinRegistry.cs
new file mode 100644
index 000000000..f4d55ce7b
--- /dev/null
+++ b/mRemoteNG/Tools/WindowsRegistry/WinRegistry.cs
@@ -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")]
+ ///
+ /// Provides functionality to interact with the Windows Registry, including reading, writing, and managing registry entries.
+ ///
+ ///
+ /// 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.
+ ///
+ public class WinRegistry : IRegistry, IRegistryRead
+ {
+ #region Public Read Method: GetSubKeyNames
+
+ ///
+ /// Retrieves the names of subkeys under a specified registry key path.
+ ///
+ /// The RegistryHive where the subkeys are located.
+ /// The path to the registry key containing the subkeys.
+ /// An array of strings containing the names of subkeys, or an empty array if no subkeys are found.
+ 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();
+ }
+
+ #endregion
+
+ #region Public Read Value Method
+
+ ///
+ /// Retrieves the data value associated with the specified registry key and value name.
+ ///
+ /// The registry hive.
+ /// The registry key path.
+ /// The name of the value. Null or Empty to get default.
+ /// The value data as a string, or null if the value is not found.
+ /// Thrown when the specified registry hive, path or name is invalid
+ 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();
+ }
+
+ ///
+ /// Retrieves the data value associated with the specified registry key and uses the default value if the value name is not specified.
+ ///
+ /// The registry hive.
+ /// The registry key path.
+ /// The value data as a string, or null if the value is not found.
+ public string GetDefaultValue(RegistryHive Hive, string Path)
+ {
+ return GetValue(Hive, Path, null);
+ }
+
+ ///
+ /// Retrieves the string value from the specified REG_SZ registry key.
+ ///
+ /// The registry hive.
+ /// The registry key path.
+ /// The name of the value. Null or Empty to get default.
+ /// The default value to return if the property is not found.
+ /// The value data as string, or the specified default value if the value is not found.
+ public string GetStringValue(RegistryHive Hive, string Path, string Name, string DefaultValue = null)
+ {
+ string value = GetValue(Hive, Path, Name);
+ return value ?? DefaultValue;
+ }
+
+ ///
+ /// Retrieves the bool value from from the specified REG_SZ or REG_DWORD registry key.
+ ///
+ /// The registry hive.
+ /// The registry key path.
+ /// The name of the value.
+ /// The default value to return if the property is not found or cannot be parsed. (Default = false)
+ /// 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.
+ 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;
+ }
+
+ ///
+ /// Retrieves the integer value from from the specified REG_DWORD registry key.
+ ///
+ /// The registry hive.
+ /// The registry key path.
+ /// The name of the value.
+ /// The default value to return if the property is not found or cannot be parsed. (Default = 0)
+ /// 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.
+ 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
+
+ ///
+ /// Retrieves a windows registry entry for a specific registry hive, path, and value name.
+ ///
+ /// The RegistryHive of the key.
+ /// The path of the key.
+ /// The name of the value to retrieve.
+ /// A WinRegistryEntry object representing the specified registry key and value.
+ public WinRegistryEntry GetEntry(RegistryHive hive, string path, string name)
+ {
+ return WinRegistryEntry
+ .New(hive, path, name)
+ .Read();
+ }
+
+ ///
+ /// Retrieves a list of registry entries and their values under a given key path.
+ ///
+ /// The registry hive.
+ /// The registry key path.
+ /// A list of WinRegistryEntry objects, each representing a value within the specified registry key path.
+ public List> GetEntries(RegistryHive hive, string path)
+ {
+ using var key = RegistryKey.OpenBaseKey(hive, RegistryView.Default).OpenSubKey(path);
+ if (key == null)
+ return new List>(); // Return an empty list when no key is found
+
+ return key.GetValueNames()
+ .Select(name => WinRegistryEntry.New(hive, path, name)
+ .Read())
+ .ToList();
+ }
+
+ ///
+ /// Recursively retrieves registry entries under a given key path and its subkeys.
+ ///
+ /// The registry hive.
+ /// The registry key path.
+ /// A list of WinRegistryEntry objects, each representing a value within the specified registry key path.
+ public List> GetEntriesRecursive(RegistryHive hive, string path)
+ {
+ List> 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
+
+ ///
+ /// Sets the value of a specific property within a registry key using individual parameters.
+ ///
+ /// The registry hive.
+ /// The registry key path.
+ /// The name of the value.
+ /// The value to set for the property.
+ /// The data type of the value to set.
+ /// Thrown when an error occurs while writing to the Windows Registry key.
+ 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);
+ }
+ }
+
+ ///
+ /// Sets the default value of a registry key to the specified string value.
+ ///
+ /// The registry hive.
+ /// The registry key path.
+ /// The value to set for the default property.
+ /// Thrown when the specified registry hive or path is invalid.
+ /// Thrown when an error occurs while writing to the Windows Registry key.
+ public void SetDefaultValue(RegistryHive Hive, string Path, string Value)
+ {
+ SetValue(Hive, Path, null, Value, RegistryValueKind.String);
+ }
+
+ ///
+ /// Creates a registry key at the specified location.
+ ///
+ /// The registry hive to create the key under.
+ /// The path of the registry key to create.
+ /// Thrown when the specified registry hive or path is invalid
+ /// Thrown when an error occurs while creating the Windows Registry key.
+ 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
+
+ ///
+ /// Deletes a registry value under the specified registry key.
+ ///
+ /// The registry hive to open.
+ /// The path of the registry key where the value exists.
+ /// The name of the value to delete.
+ /// Thrown when the specified registry hive, path or name is invalid
+ /// Thrown when the user does not have the necessary permissions to delete the registry value.
+ /// Thrown when the user does not have the necessary permissions to delete the registry value due to security restrictions.
+ 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);
+ }
+ }
+
+ ///
+ /// Deletes a registry key and its subkeys.
+ ///
+ /// The registry hive to open.
+ /// The path of the registry key to delete.
+ /// Thrown when the specified registry hive or path is invalid
+ /// Thrown when the user does not have the necessary permissions to delete the registry key.
+ /// Thrown when the user does not have the necessary permissions to delete the registry key due to security restrictions.
+ 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
+ *
+ ******************************************************/
+ ///
+ /// Converts a string representation of a Registry Hive to the corresponding RegistryHive enum value.
+ ///
+ /// A string representation of a Registry Hive, not case-sensitive.
+ /// The RegistryHive enum value corresponding to the provided string representation.
+ /// Thrown if the provided string does not match a valid Registry Hive.
+ 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)),
+ };
+ }
+
+ ///
+ /// Converts a string representation of a RegistryValueKind to the corresponding RegistryValueKind enum value.
+ ///
+ /// A string representation of a RegistryValueKind, not case-sensitive.
+ /// The RegistryValueKind enum value corresponding to the provided string representation.
+ /// Thrown if the provided string does not match a valid RegistryValueKind.
+ 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)),
+ };
+ }
+
+
+ ///
+ /// Converts a .NET data type to the corresponding RegistryValueKind.
+ ///
+ /// The .NET data type to convert.
+ /// The corresponding RegistryValueKind.
+ 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
+ };
+ }
+
+ ///
+ /// Converts a RegistryValueKind enumeration value to its corresponding .NET Type.
+ ///
+ /// The RegistryValueKind value to be converted.
+ /// The .NET Type that corresponds to the given RegistryValueKind.
+ 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
+
+ ///
+ /// Validates the specified RegistryHive value.
+ ///
+ /// The RegistryHive value to validate.
+ /// Thrown when an unknown or unsupported RegistryHive value is provided.
+ 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));
+ }
+
+ ///
+ /// Throws an exception if the specified path is null or empty.
+ ///
+ /// The path to validate.
+ /// The validated parameter path.
+ 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));
+ }
+
+ ///
+ /// Throws an exception if the specified name is null or empty.
+ ///
+ /// The name to validate.
+ private static void ThrowIfNameInvalid(string Name)
+ {
+ if (Name == null)
+ throw new ArgumentNullException(nameof(Name), "Invalid parameter: Name cannot be null.");
+ }
+
+ ///
+ /// Throws an exception if the specified RegistryValueKind is unknown.
+ ///
+ /// The RegistryValueKind to validate.
+ /// Thrown when the RegistryValueKind is Unknown.
+ 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
+ }
+}
\ No newline at end of file
diff --git a/mRemoteNG/Tools/WindowsRegistry/WinRegistryEntry.cs b/mRemoteNG/Tools/WindowsRegistry/WinRegistryEntry.cs
new file mode 100644
index 000000000..6e7649432
--- /dev/null
+++ b/mRemoteNG/Tools/WindowsRegistry/WinRegistryEntry.cs
@@ -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")]
+ ///
+ /// Represents an entry in the Windows Registry.
+ ///
+ ///
+ /// 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:
+ ///
+ /// var registryEntry = new RegistryEntry(RegistryHive.LocalMachine, @"Software\MyApp", "Settings");
+ /// var value = registryEntry.Read();
+ /// if (value != **)
+ /// registryEntry.Write("newVal");
+ ///
+ ///
+ ///
+ /// var registryEntry = new RegistryEntry(RegistryHive.LocalMachine, @"Software\MyApp", "Settings").Read();
+ ///
+ ///
+ ///
+ /// var registryEntry = new RegistryEntry(RegistryHive.LocalMachine, @"Software\MyApp", "Settings").SetValidation(min, max).Read();
+ /// if (registryEntry.IsValid())
+ /// Do Something
+ ///
+ ///
+ ///
+ public class WinRegistryEntry
+ {
+ #region Registry Fileds & Properties
+
+ ///
+ /// Represents the registry hive associated with the registry key.
+ ///
+ ///
+ /// The default value is .
+ ///
+ 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;
+
+ ///
+ /// Represents the path of the registry entry.
+ ///
+ 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;
+
+ ///
+ /// Represents the name of the registry entry.
+ ///
+ public string Name { get; set; }
+
+ ///
+ /// Represents the kind of data stored in the registry value.
+ ///
+ public RegistryValueKind ValueKind { get; private set; } = InitialRegistryValueKind();
+
+ ///
+ /// Represents the value of the registry entry.
+ ///
+ 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;
+
+ ///
+ /// Represents the raw value retrieved directly from the registry.
+ ///
+ private string RawValue;
+
+ ///
+ /// Represents the type of the generic parameter T.
+ ///
+ private readonly Type ElementType = typeof(T);
+
+ ///
+ /// Indicates whether the reading operation for the registry value was successful.
+ ///
+ private bool ReadOperationSucceeded;
+
+ ///
+ /// Indicates whether a lock operation should be performed after a successful read operation.
+ ///
+ private bool DoLock;
+
+ ///
+ /// Indicates whether the WinRegistryEntry is currently locked, preventing further read operations.
+ ///
+ public bool IsLocked { get; private set; }
+
+ ///
+ /// 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).
+ ///
+ public bool IsSet => IsEntrySet();
+
+ ///
+ /// 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.
+ ///
+ public bool IsValid => CheckIsValid();
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Initializes a new instance of the class with default values.
+ ///
+ public WinRegistryEntry() { }
+
+ ///
+ /// Initializes a new instance of the class for reading a default value from the specified registry hive and path.
+ ///
+ /// The registry hive of the entry.
+ /// The path of the registry entry.
+ public WinRegistryEntry(RegistryHive hive, string path)
+ {
+ Hive = hive;
+ Path = path;
+ }
+
+ ///
+ /// Initializes a new instance of the class for writing a default value to the specified registry hive and path.
+ ///
+ /// The registry hive of the entry.
+ /// The path of the registry entry.
+ /// The value of the registry entry.
+ public WinRegistryEntry(RegistryHive hive, string path, T value)
+ {
+ Hive = hive;
+ Path = path;
+ Value = value;
+ }
+
+ ///
+ /// Initializes a new instance of the class for reading a specific value from the specified registry hive, path, and name.
+ ///
+ /// The registry hive of the entry.
+ /// The path of the registry entry.
+ /// The name of the registry entry.
+ public WinRegistryEntry(RegistryHive hive, string path, string name)
+ {
+ Hive = hive;
+ Path = path;
+ Name = name;
+ }
+
+ ///
+ /// Initializes a new instance of the class for writing a specific value to the specified registry hive, path, and name.
+ ///
+ /// The registry hive of the entry.
+ /// The path of the registry entry.
+ /// The name of the registry entry.
+ /// The value of the registry entry.
+ public WinRegistryEntry(RegistryHive hive, string path, string name, T value)
+ {
+ Hive = hive;
+ Path = path;
+ Name = name;
+ Value = value;
+ }
+
+ #endregion
+
+ #region Factory Methods
+
+ ///
+ /// Creates a new instance of the class for reading a default value from the specified registry hive and path.
+ ///
+ /// The registry hive of the entry.
+ /// The path of the registry entry.
+ /// A new instance of the class.
+ public static WinRegistryEntry New(RegistryHive hive, string path)
+ {
+ return new WinRegistryEntry(hive, path);
+ }
+
+ ///
+ /// Creates a new instance of the class for writing a value to the specified registry hive and path.
+ ///
+ /// The registry hive of the entry.
+ /// The path of the registry entry.
+ /// The value of the registry entry.
+ /// A new instance of the class.
+ public static WinRegistryEntry New(RegistryHive hive, string path, T value)
+ {
+ return new WinRegistryEntry(hive, path, value);
+ }
+
+ ///
+ /// Creates a new instance of the class for reading a specific value from the specified registry hive, path, and name.
+ ///
+ /// The registry hive of the entry.
+ /// The path of the registry entry.
+ /// The name of the registry entry.
+ /// A new instance of the class.
+ public static WinRegistryEntry New(RegistryHive hive, string path, string name)
+ {
+ return new WinRegistryEntry(hive, path, name);
+ }
+
+ ///
+ /// Creates a new instance of the class for writing a specific value to the specified registry hive, path, and name.
+ ///
+ /// The registry hive of the entry.
+ /// The path of the registry entry.
+ /// The name of the registry entry.
+ /// The value of the registry entry.
+ /// A new instance of the class.
+ public static WinRegistryEntry New(RegistryHive hive, string path, string name, T value)
+ {
+ return new WinRegistryEntry(hive, path, name, value);
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ ///
+ /// Sets the kind of the registry value, ensuring it is a valid and defined .
+ ///
+ /// The registry value kind to set.
+ /// The current instance of to allow for method chaining.
+ public WinRegistryEntry 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;
+ }
+
+ ///
+ /// Sets the allowed values for validation, with an option for case sensitivity.
+ ///
+ /// The array of allowed values.
+ public WinRegistryEntry SetValidation(T[] allowedValues)
+ {
+ ResetValidation();
+
+ if (allowedValues != null && allowedValues.Length > 0)
+ {
+ AllowedValues = allowedValues;
+ }
+
+ return this;
+ }
+
+ ///
+ /// Sets up validation using an array of allowed integer values.
+ ///
+ /// The array of allowed integer values.
+ public WinRegistryEntry SetValidation(int[] allowedValues)
+ {
+ T[] mappedValues = allowedValues?.Select(value => (T)(object)value).ToArray();
+ return SetValidation(mappedValues);
+ }
+
+ ///
+ /// Sets up validation using an array of allowed integer values.
+ ///
+ /// The array of allowed integer values.
+ public WinRegistryEntry SetValidation(long[] allowedValues)
+ {
+ T[] mappedValues = allowedValues?.Select(value => (T)(object)value).ToArray();
+ return SetValidation(mappedValues);
+ }
+
+ ///
+ /// Sets up validation for a range of integer values.
+ ///
+ /// The minimum value of the range.
+ /// The maximum value of the range.
+ public WinRegistryEntry SetValidation(int minValue, int maxValue)
+ {
+ ValidateRange(minValue, maxValue);
+ ResetValidation();
+
+ MinInt32Value = minValue;
+ MaxInt32Value = maxValue;
+
+ return this;
+ }
+
+ ///
+ /// Sets up validation for a range of integer values.
+ ///
+ /// The minimum value of the range.
+ /// The maximum value of the range.
+ public WinRegistryEntry SetValidation(long minValue, long maxValue)
+ {
+ ValidateRange(minValue, maxValue);
+ ResetValidation();
+
+ MinInt64Value = minValue;
+ MaxInt64Value = maxValue;
+
+ return this;
+ }
+
+ ///
+ /// Sets up validation rules for a range of Int32, Int64 values.
+ ///
+ /// The minimum value of the range. "*" can be provided to indicate no minimum value.
+ /// The maximum value of the range. "*" can be provided to indicate no maximum value.
+ /// The current instance of the WinRegistryEntry class.
+ /// Thrown when the registry entry type is not a valid Int32 or Int64.
+ /// Thrown when an invalid minimum value is provided for Int32 or Int64.
+ /// Thrown when an invalid maximum value is provided for Int32 or Int64.
+
+ public WinRegistryEntry 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.");
+ }
+ }
+
+ ///
+ /// Sets the validation to use an enumeration type
+ ///
+ /// The enumeration type.
+ public WinRegistryEntry SetValidation() where TEnum : Enum
+ {
+ ResetValidation();
+
+ Type enumType = typeof(TEnum);
+ if (enumType != null)
+ EnumType = enumType;
+
+ return this;
+ }
+
+ ///
+ /// Checks if the Windows Registry key is ready for reading by ensuring that the hive,
+ /// path, and name properties are set.
+ ///
+ /// True if the key is ready for reading, otherwise false.
+ public bool IsReadable()
+ {
+ return IsHiveSet() && IsPathSet();
+ }
+
+ ///
+ /// 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
+ ///
+ /// Returns true if the key is write-ready, otherwise false.
+ public bool IsWritable()
+ {
+ return IsHiveSet() && IsValueKindSet() && IsPathSet();
+ }
+
+ ///
+ /// Reads the value of the registry entry from the specified registry path and assigns it to the Value property.
+ ///
+ /// The current instance of to allow for method chaining.
+ public WinRegistryEntry 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;
+ }
+
+ ///
+ /// Writes the value of the registry entry to the specified registry path.
+ ///
+ /// The current instance of to allow for method chaining.
+ public WinRegistryEntry 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;
+ }
+
+ ///
+ /// Writes a new value to the registry entry.
+ ///
+ /// The new value to be written to the registry entry.
+ /// The current instance of to allow for method chaining.
+ public WinRegistryEntry Write(T newValue)
+ {
+ Value = newValue;
+ return Write();
+ }
+
+ ///
+ /// Clears the current values of the instance.
+ ///
+ ///
+ /// This method resets the values to their default states.
+ /// After invoking this method, the "Validations" properties IsSet and IsValid will return false.
+ /// This is useful in scenarios where the value needs to be validated through alternative mechanisms.
+ ///
+ public void Clear()
+ {
+ RawValue = null;
+ Value = default;
+ ReadOperationSucceeded = false;
+ }
+
+ ///
+ /// 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.
+ ///
+ /// The current instance of to allow for method chaining.
+ public WinRegistryEntry Lock()
+ {
+ if (ReadOperationSucceeded)
+ IsLocked = true;
+ else
+ DoLock = true;
+
+ return this;
+ }
+
+ #endregion
+
+ #region Private Methods
+
+ ///
+ /// Converts a string value to the specified .NET data type .
+ ///
+ /// The target .NET data type to which the value is converted.
+ /// The string value to be converted.
+ /// The converted value of type .
+ /// Thrown when Conversion for failed..
+ /// Thrown when Conversion not supported for type .
+ 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}'.");
+ }
+
+ ///
+ /// Logs an error message to the standard error stream.
+ ///
+ /// The error message to log.
+ private static void LogError(string message)
+ {
+ Console.Error.WriteLine($"Error: {message}");
+ }
+
+ ///
+ /// Logs an info message to the standard error stream.
+ ///
+ /// The error message to log.
+ private static void LogInfo(string message)
+ {
+ Console.WriteLine($"Info: {message}");
+ }
+
+ ///
+ /// Validates the provided value based on its type-specific rules.
+ ///
+ /// The value to be validated.
+ /// Thrown when is bool and value is not True, False, 0 or 1.
+ /// Thrown when is int/long and value is negative.
+ /// Thrown when Value type is not supported.
+ private T ValueValidationRules(T value)
+ {
+ // Boolean values are either string or DWORD. Mapping is needed to update ValueKind.
+ var booleanRegistryValueKindMap = new Dictionary
+ {
+ { "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));
+ }
+
+ ///
+ /// Validates and corrects a string value based on a set of allowed values or enumeration values.
+ ///
+ /// The input value to be validated and potentially corrected.
+ /// The validated and potentially corrected value.
+ 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()
+ .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;
+ }
+
+ ///
+ /// Private method to validate the range values.
+ ///
+ /// The type of the values being validated.
+ /// The minimum value of the range.
+ /// The maximum value of the range.
+ /// The type of registry entry (used for error messages).
+ private static void ValidateRange(U minValue, U maxValue) where U : IComparable
+ {
+ 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.");
+ }
+
+ ///
+ /// Validates the specified registry value kind.
+ ///
+ /// The registry value kind to validate.
+ /// The validated .
+ /// Thrown when Invalid parameter: Unknown or unsupported
+ /// Thrown when Value type is not supported.
+ 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.")
+ };
+ }
+
+ ///
+ /// Determines the initial RegistryValueKind based on the type .
+ ///
+ /// The initial RegistryValueKind determined by the type .
+ 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
+ };
+ }
+
+ ///
+ /// Determines whether the registry entry has been set.
+ ///
+ private bool IsEntrySet() => RawValue != null && ReadOperationSucceeded;
+
+ ///
+ /// Determines whether the registry hive has been explicitly set.
+ ///
+ /// true if the hive is set; otherwise, false.
+ private bool IsHiveSet() => Hive != 0;
+
+ ///
+ /// Determines whether the value kind of the registry entry has been explicitly set.
+ ///
+ /// true if the value kind is set; otherwise, false.
+ private bool IsValueKindSet() => ValueKind != 0;
+
+ ///
+ /// Determines whether the path of the registry entry has been explicitly set.
+ ///
+ /// true if the path is set; otherwise, false.
+ private bool IsPathSet() => Path != null;
+
+ ///
+ /// Checks if the current value is valid according to its type-specific rules and constraints.
+ ///
+ /// True if the value is valid; otherwise, false.
+ 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."),
+ };
+ }
+
+ ///
+ /// 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.
+ ///
+ private void ResetValidation()
+ {
+ AllowedValues = null;
+
+ MinInt32Value = null;
+ MaxInt32Value = null;
+ MinInt64Value = null;
+ MaxInt64Value = null;
+
+ EnumType = null;
+ }
+
+ ///
+ /// Validates a string value based on allowed values or enumeration values.
+ ///
+ /// True if the string value is valid; otherwise, false.
+ 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()
+ .Any(e => e.ToString().Equals(Value.ToString(), StringComparison.OrdinalIgnoreCase));
+ }
+
+ return true;
+ }
+
+ ///
+ /// Validates an integer value based on allowed values, minimum and maximum values, or enumeration values.
+ ///
+ /// True if the integer value is valid; otherwise, false.
+ 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;
+ }
+
+ ///
+ /// Validates a long integer value based on allowed values, minimum and maximum values, or enumeration values.
+ ///
+ /// True if the long integer value is valid; otherwise, false.
+ 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
+ }
+}
diff --git a/mRemoteNGTests/Tools/Registry/RegistryEntryTest/BaseRegistryEntryTest.cs b/mRemoteNGTests/Tools/Registry/RegistryEntryTest/BaseRegistryEntryTest.cs
new file mode 100644
index 000000000..3f95d613e
--- /dev/null
+++ b/mRemoteNGTests/Tools/Registry/RegistryEntryTest/BaseRegistryEntryTest.cs
@@ -0,0 +1,248 @@
+
+using Microsoft.Win32;
+using NUnit.Framework.Internal;
+using System.Runtime.Versioning;
+using System;
+using NUnit.Framework;
+using mRemoteNG.Tools.WindowsRegistry;
+
+namespace mRemoteNGTests.Tools.Registry.RegistryEntryTest
+{
+ [SupportedOSPlatform("windows")]
+ internal class BaseRegistryEntryTest
+ {
+ private const string TestRoot = @"Software\mRemoteNGTest";
+ private const RegistryHive TestHive = RegistryHive.CurrentUser;
+ private const string TestPath = $"{TestRoot}\\EntryTests";
+
+ [Test]
+ public void Path_SetValidValue_GetReturnsSameValue()
+ {
+ string expectedPath = @"SOFTWARE\Microsoft";
+ var entry = new WinRegistryEntry
+ {
+ Path = expectedPath
+ };
+ Assert.That(expectedPath, Is.EqualTo(entry.Path));
+ }
+
+ [Test]
+ public void Path_SetNullValue_ThrowsArgumentNullException()
+ {
+ var entry = new WinRegistryEntry();
+ Assert.Throws(() => entry.Path = null);
+ }
+
+ [Test]
+ public void Name_SetValidValue_GetReturnsSameValue()
+ {
+ string expectedName = "Version";
+ var entry = new WinRegistryEntry
+ {
+ Name = expectedName
+ };
+ Assert.That(expectedName, Is.EqualTo(entry.Name));
+ }
+
+ [Test]
+ public void Value_SetValidValue_GetReturnsSameValue()
+ {
+ string expectedValue = "1.0";
+ var entry = new WinRegistryEntry
+ {
+ Value = expectedValue
+ };
+ Assert.That(expectedValue, Is.EqualTo(entry.Value));
+ }
+
+ [Test]
+ public void ValueKind_SetInvalidValue_ThrowsArgumentException()
+ {
+ var entry = new WinRegistryEntry();
+ Assert.Throws(() => entry.SetValueKind((RegistryValueKind)100));
+ }
+
+ [Test]
+ public void IsSet_ValueHasBeenSet_NotRead_ReturnsFalse()
+ {
+ var entry = new WinRegistryEntry
+ {
+ Value = "Test"
+ };
+ Assert.That(entry.IsSet, Is.False);
+ }
+
+ [Test]
+ public void IsKeyReadable_AllPropertiesSet_ReturnsTrue()
+ {
+ var entry = new WinRegistryEntry
+ {
+ Hive = RegistryHive.LocalMachine,
+ Path = @"SOFTWARE\Microsoft",
+ Name = "Version"
+ };
+ Assert.That(entry.IsReadable, Is.True);
+ }
+
+ [Test]
+ public void IsKeyWritable_AllPropertiesSet_ReturnsTrue()
+ {
+ var entry = new WinRegistryEntry
+ {
+ Hive = RegistryHive.LocalMachine,
+ Path = @"SOFTWARE\Microsoft",
+ Name = "Version",
+ Value = "1.0"
+ };
+ Assert.That(entry.IsWritable, Is.True);
+ }
+
+ [Test]
+ public void Read_WhenRegistryKeyIsNull_ThrowsInvalidOperationException()
+ {
+ var entry = new WinRegistryEntry();
+ Assert.Throws(() => entry.Read());
+ }
+
+ [Test]
+ public void Write_WhenKeyIsUnwritable_ThrowsInvalidOperationException()
+ {
+ var entry = new WinRegistryEntry
+ {
+ Name = "Version"
+ };
+ Assert.Throws(() => entry.Write());
+ }
+
+ [Test]
+ public void WriteDefaultAndReadDefault_Entry_DoesNotThrowAndReadsCorrectly()
+ {
+ var entry = new WinRegistryEntry(TestHive, TestPath, 0)
+ {
+ Hive = TestHive,
+ Path = TestPath,
+ Value = 0,
+ };
+
+ var readEntry = new WinRegistryEntry
+ {
+ Hive = TestHive,
+ Path = TestPath,
+ };
+
+ Assert.That(() => entry.Write(), Throws.Nothing);
+ Assert.That(() => readEntry.Read(), Throws.Nothing);
+ Assert.Multiple(() =>
+ {
+ Assert.That(readEntry.ValueKind, Is.EqualTo(RegistryValueKind.String));
+ Assert.That(readEntry.Value, Is.EqualTo(entry.Value));
+ });
+ }
+
+ [Test]
+ public void WriteAndRead_Entry_DoesNotThrowAndReadsCorrectly()
+ {
+ var entry = new WinRegistryEntry
+ {
+ Hive = TestHive,
+ Path = TestPath,
+ Name = "TestRead",
+ Value = 200
+ };
+
+ var readEntry = new WinRegistryEntry
+ {
+ Hive = TestHive,
+ Path = TestPath,
+ Name = "TestRead"
+ };
+
+ Assert.That(() => entry.Write(), Throws.Nothing);
+ Assert.That(() => readEntry.Read(), Throws.Nothing);
+ Assert.Multiple(() =>
+ {
+ Assert.That(readEntry.ValueKind, Is.EqualTo(entry.ValueKind));
+ Assert.That(readEntry.Value, Is.EqualTo(entry.Value));
+ });
+ }
+
+ [Test]
+ public void FluentWriteAndRead_DoesNotThrow_ReturnsStrJustATest()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "FluentReadAndWriteTest", "JustATest").Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "FluentReadAndWriteTest").Read();
+ Assert.That(entry.Value, Is.EqualTo("JustATest"));
+ }
+
+ [Test]
+ public void FluentWriteReadAndChange_DoesNotThrow_WriteReadsCorrectly()
+ {
+ var entry = WinRegistryEntry
+ .New(TestHive, TestPath, "FluentReadWriteAndChangeTest", "JustASecondTest")
+ .Write()
+ .Read();
+ string result1 = entry.Value;
+
+ string result2 = entry
+ .Write("JustAChangeTest")
+ .Read()
+ .Value;
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(result1, Is.EqualTo("JustASecondTest"));
+ Assert.That(result2, Is.EqualTo("JustAChangeTest"));
+ });
+ }
+
+ [Test]
+ public void Read_LockAfter_IsLocked()
+ {
+ var entry = WinRegistryEntry
+ .New(TestHive, TestPath, "IsLockedAfter", "After")
+ .Write()
+ .Read()
+ .Lock();
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(entry.IsLocked, Is.EqualTo(true));
+ });
+ }
+
+ [Test]
+ public void Read_LockBevore_IsLocked()
+ {
+ var entry = WinRegistryEntry
+ .New(TestHive, TestPath, "IsLockedBevore", "Bevore")
+ .Write()
+ .Read()
+ .Lock();
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(entry.IsLocked, Is.EqualTo(true));
+ });
+ }
+
+ [Test]
+ public void Read_Lock_ThrowWhenRead()
+ {
+ var entry = WinRegistryEntry
+ .New(TestHive, TestPath, "ReadLockThrow", "ReadingIsLocked")
+ .Write()
+ .Read()
+ .Lock();
+
+ Assert.Throws(() => entry.Read());
+ }
+
+ [OneTimeTearDown]
+ public void Cleanup()
+ {
+ // Delete all created tests
+ WinRegistry winReg = new();
+ winReg.DeleteTree(TestHive, TestRoot);
+ }
+ }
+}
diff --git a/mRemoteNGTests/Tools/Registry/RegistryEntryTest/BoolEntryTest.cs b/mRemoteNGTests/Tools/Registry/RegistryEntryTest/BoolEntryTest.cs
new file mode 100644
index 000000000..9ba1a35fd
--- /dev/null
+++ b/mRemoteNGTests/Tools/Registry/RegistryEntryTest/BoolEntryTest.cs
@@ -0,0 +1,71 @@
+
+using Microsoft.Win32;
+using NUnit.Framework.Internal;
+using System.Runtime.Versioning;
+using NUnit.Framework;
+using mRemoteNG.Tools.WindowsRegistry;
+
+namespace mRemoteNGTests.Tools.Registry.RegistryEntryTest
+{
+ [SupportedOSPlatform("windows")]
+ internal class BoolEntryTest
+ {
+ private const string TestRoot = @"Software\mRemoteNGTest";
+ private const RegistryHive TestHive = RegistryHive.CurrentUser;
+ private const string TestPath = $"{TestRoot}\\BoolEntryTest";
+
+ [Test]
+ public void StringTrue_SuccessfulToBool_ReturnsTrue()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "IsTrueString", true).Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "IsTrueString").Read();
+ Assert.That(entry.Value, Is.True);
+ }
+
+ [Test]
+ public void StringFalse_SuccessfulToBool_ReturnsFalse()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "IsFalseString", false).Write());
+
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "IsFalseString");
+ entry.Value = true; // change Value to true to ensure a value was readed
+ entry.Read();
+
+ Assert.That(entry.Value, Is.False);
+ }
+
+ [Test]
+ public void DWordTrue_SuccessfulToBool_ReturnsTrue()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "IsTrueDword", true).SetValueKind(RegistryValueKind.DWord).Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "IsTrueDword").Read();
+ Assert.That(entry.Value, Is.True);
+ }
+
+ [Test]
+ public void DWordFalse_SuccessfulToBool_ReturnsFalse()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "IsFalseDword", false).SetValueKind(RegistryValueKind.DWord).Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "IsFalseDword");
+ entry.Value = true; // change Value to true to ensure a value was readed
+ entry.Read();
+ Assert.That(entry.Value, Is.False);
+ }
+
+ [Test]
+ public void FluentWrite_And_Read_DoesNotThrow_ReturnsBoolFalse()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "FluentReadAndWriteTest", false).SetValueKind(RegistryValueKind.DWord).Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "FluentReadAndWriteTest", true).Read();
+ Assert.That(entry.Value, Is.False);
+ }
+
+ [OneTimeTearDown]
+ public void Cleanup()
+ {
+ // Delete all created tests
+ WinRegistry winReg = new();
+ winReg.DeleteTree(TestHive, TestRoot);
+ }
+ }
+}
diff --git a/mRemoteNGTests/Tools/Registry/RegistryEntryTest/IntegerEntryTest.cs b/mRemoteNGTests/Tools/Registry/RegistryEntryTest/IntegerEntryTest.cs
new file mode 100644
index 000000000..e3fb2072c
--- /dev/null
+++ b/mRemoteNGTests/Tools/Registry/RegistryEntryTest/IntegerEntryTest.cs
@@ -0,0 +1,133 @@
+
+using Microsoft.Win32;
+using NUnit.Framework.Internal;
+using System;
+using System.Runtime.Versioning;
+using NUnit.Framework;
+using mRemoteNG.Tools.WindowsRegistry;
+
+namespace mRemoteNGTests.Tools.Registry.RegistryEntryTest
+{
+ [SupportedOSPlatform("windows")]
+ internal class IntegerEntryTest
+ {
+ private const string TestRoot = @"Software\mRemoteNGTest";
+ private const RegistryHive TestHive = RegistryHive.CurrentUser;
+ private const string TestPath = $"{TestRoot}\\IntegerEntryTest";
+
+ public enum TestEnum
+ {
+ First = 1,
+ Second = 2,
+ Third = 3
+ }
+
+ [Test]
+ public void IsValid_NoValidationSet_EntryComplete_ReturnsTrue()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "IsValid", 1).Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "IsValid").Read();
+ Assert.That(entry.IsValid, Is.True);
+ }
+
+ [Test]
+ public void IsValid_AllowedValuesSet_ValueInAllowedValues_ReturnsTrue()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "IsValid", 2).Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "IsValid").SetValidation(new int[] { 1, 2, 3 }).Read();
+ Assert.That(entry.IsValid, Is.True);
+ }
+
+ [Test]
+ public void IsValid_AllowedValuesSet_ValueNotInAllowedValues_ReturnsFalse()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "IsValid", 4).Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "IsValid").SetValidation(new int[] { 1, 2, 3 }).Read();
+ Assert.That(entry.IsValid, Is.False);
+ }
+
+ [Test]
+ public void IsValid_RangeSet_ValueInRange_ReturnsTrue()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "IsValid", 5).Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "IsValid").SetValidation(1,10).Read();
+ Assert.That(entry.IsValid, Is.True);
+ }
+
+ [Test]
+ public void IsValid_RangeSet_ValueOutOfRange_ReturnsFalse()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "IsValid", 50).Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "IsValid").SetValidation(1, 10).Read();
+ Assert.That(entry.IsValid, Is.False);
+ }
+
+ [Test]
+ public void IsValid_RangeSet_Value0_ValueOutOfRange_ReturnsTrue()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "IsValidZero", 0).Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "IsValidZero").SetValidation(0, 10).Read();
+ Assert.That(entry.IsValid, Is.True);
+ }
+
+ [Test]
+ public void IsValid_RangeSet_DefaultMin_ValueInRange_ReturnsTrue()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "IsValidDefMin", 10).Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "IsValidDefMin").SetValidation("*", "10").Read();
+ Assert.That(entry.IsValid, Is.True);
+ }
+
+ [Test]
+ public void IsValid_RangeSet_DefaultMax_ValueInRange_ReturnsTrue()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "IsValidDefMax", 1000).Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "IsValidDefMax").SetValidation("50", "*").Read();
+ Assert.That(entry.IsValid, Is.True);
+ }
+
+ [Test]
+ public void IsValid_EnumSet_ValueInEnum_ReturnsTrue()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "IsValid", 2).Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "IsValid").SetValidation().Read();
+ Assert.That(entry.IsValid, Is.True);
+ }
+
+ [Test]
+ public void IsValid_EnumSet_ValueNotInEnum_ReturnsFalse()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "IsValid", 5).Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "IsValid").SetValidation().Read();
+ Assert.That(entry.IsValid, Is.False);
+ }
+
+ [Test]
+ public void IsValid_InvalidValueKind_ThrowArgumentException()
+ {
+ Assert.Throws(() => WinRegistryEntry.New(TestHive, TestPath, "IsValid", 5).SetValueKind(RegistryValueKind.Unknown));
+ }
+
+ [Test]
+ public void Value_SetToNegativeNumber_ThrowArgumentException()
+ {
+ Assert.Throws(() => WinRegistryEntry.New(TestHive, TestPath, "IsValid", -100));
+ }
+
+ [Test]
+ public void FluentWrite_And_Read_DoesNotThrow_ReturnsInt12()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "FluentReadAndWriteTest", 12).Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "FluentReadAndWriteTest", 42).Read();
+ Assert.That(entry.Value, Is.EqualTo(12));
+ }
+
+ [OneTimeTearDown]
+ public void Cleanup()
+ {
+ // Delete all created tests
+ WinRegistry winReg = new();
+ winReg.DeleteTree(TestHive, TestPath);
+ }
+ }
+}
diff --git a/mRemoteNGTests/Tools/Registry/RegistryEntryTest/LongIntegerEntryTest.cs b/mRemoteNGTests/Tools/Registry/RegistryEntryTest/LongIntegerEntryTest.cs
new file mode 100644
index 000000000..d7e5bc90b
--- /dev/null
+++ b/mRemoteNGTests/Tools/Registry/RegistryEntryTest/LongIntegerEntryTest.cs
@@ -0,0 +1,86 @@
+
+using Microsoft.Win32;
+using NUnit.Framework.Internal;
+using System;
+using System.Runtime.Versioning;
+using NUnit.Framework;
+using mRemoteNG.Tools.WindowsRegistry;
+
+namespace mRemoteNGTests.Tools.Registry.RegistryEntryTest
+{
+ [SupportedOSPlatform("windows")]
+ internal class LongIntegerEntryTest
+ {
+ private const string TestRoot = @"Software\mRemoteNGTest";
+ private const RegistryHive TestHive = RegistryHive.CurrentUser;
+ private const string TestPath = $"{TestRoot}\\LongIntegerEntryTest";
+
+ [Test]
+ public void IsValid_NoValidationSet_EntryComplete_ReturnsTrue()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "IsValid", 3047483647).Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "IsValid").Read();
+ Assert.That(entry.IsValid, Is.True);
+ }
+
+ [Test]
+ public void IsValid_AllowedValuesSet_ValueInAllowedValues_ReturnsTrue()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "IsValid", 2147483649).Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "IsValid").SetValidation(new long[] { 2147483648, 2147483649, 2147483650 }).Read();
+ Assert.That(entry.IsValid, Is.True);
+ }
+
+ [Test]
+ public void IsValid_AllowedValuesSet_ValueNotInAllowedValues_ReturnsFalse()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "IsValid", 4).Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "IsValid").SetValidation(new long[] { 2147483648, 2147483649, 2147483650 }).Read();
+ Assert.That(entry.IsValid, Is.False);
+ }
+
+ [Test]
+ public void IsValid_RangeSet_ValueInRange_ReturnsTrue()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "IsValid", 2147483652).Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "IsValid").SetValidation(2147483647, 2200000000).Read();
+ Assert.That(entry.IsValid, Is.True);
+ }
+
+ [Test]
+ public void IsValid_RangeSet_ValueOutOfRange_ReturnsFalse()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "IsValid", 20).Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "IsValid").SetValidation(2147483647, 2200000000).Read();
+ Assert.That(entry.IsValid, Is.False);
+ }
+
+ [Test]
+ public void IsValid_InvalidValueKind_ThrowArgumentException()
+ {
+ Assert.Throws(() => WinRegistryEntry.New(TestHive, TestPath, "IsValid", 5).SetValueKind(RegistryValueKind.Unknown));
+ }
+
+ [Test]
+ public void Value_SetToNegativeNumber_ThrowArgumentException()
+ {
+ Assert.Throws(() => WinRegistryEntry.New(TestHive, TestPath, "IsValid", -100));
+ }
+
+ [Test]
+ public void FluentWrite_And_Read_DoesNotThrow_ReturnsLong15()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "FluentReadAndWriteTest", 15).Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "FluentReadAndWriteTest", 42).Read();
+ Assert.That(entry.Value, Is.EqualTo(15));
+ }
+
+ [OneTimeTearDown]
+ public void Cleanup()
+ {
+ // Delete all created tests
+ WinRegistry winReg = new();
+ winReg.DeleteTree(TestHive, TestRoot);
+ }
+ }
+}
diff --git a/mRemoteNGTests/Tools/Registry/RegistryEntryTest/StringEntryTest.cs b/mRemoteNGTests/Tools/Registry/RegistryEntryTest/StringEntryTest.cs
new file mode 100644
index 000000000..80fd4d9ff
--- /dev/null
+++ b/mRemoteNGTests/Tools/Registry/RegistryEntryTest/StringEntryTest.cs
@@ -0,0 +1,112 @@
+
+using Microsoft.Win32;
+using NUnit.Framework.Internal;
+using System;
+using System.Runtime.Versioning;
+using NUnit.Framework;
+using mRemoteNG.Tools.WindowsRegistry;
+
+namespace mRemoteNGTests.Tools.Registry.RegistryEntryTest
+{
+ [SupportedOSPlatform("windows")]
+ internal class StringEntryTest
+ {
+ private const string TestRoot = @"Software\mRemoteNGTest";
+ private const RegistryHive TestHive = RegistryHive.CurrentUser;
+ private const string TestPath = $"{TestRoot}\\StringEntryTest";
+
+ public enum TestEnum
+ {
+ First = 1,
+ Second = 2,
+ Third = 3
+ }
+
+ [Test]
+ public void IsValid_NoValidationSet_EntryComplete_ReturnsTrue()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "IsValid", "IsValid").Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "IsValid").Read();
+ Assert.That(entry.IsValid, Is.True);
+ }
+
+ [Test]
+ public void IsValid_AllowedValuesSet_ValueInAllowedValues_ReturnsTrue()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "ArrayIsValid", "Banana").Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "ArrayIsValid").SetValidation(new string[] { "Banana", "Strawberry", "Apple" }).Read();
+ Assert.That(entry.IsValid, Is.True);
+ }
+
+ [Test]
+ public void IsValid_AllowedValuesSet_ValueNotInAllowedValues_ReturnsFalse()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "ArrayIsInValid", "Cheese").Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "ArrayIsInValid").SetValidation(new string[] { "Banana", "Strawberry", "Apple" }).Read();
+ Assert.That(entry.IsValid, Is.False);
+ }
+
+ [Test]
+ public void IsValid_AllowedValuesSet_CorrectsValueSpellingAndValidatesSuccessfully()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "ArrayCorrectsSpellingIsValid", "StrawBerry").Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "ArrayCorrectsSpellingIsValid").SetValidation(new string[] { "Banana", "Strawberry", "Apple" }).Read();
+
+ Assert.Multiple(() =>
+ {
+ Assert.That(entry.IsValid, Is.True);
+ Assert.That(entry.Value, Is.EqualTo("Strawberry"));
+ });
+ }
+
+ [Test]
+ public void IsValid_EnumSet_ValueInEnum_ReturnsTrue()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "IsValid", "Second").Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "IsValid").SetValidation().Read();
+ Assert.That(entry.IsValid, Is.True);
+ }
+
+ [Test]
+ public void IsValid_EnumSet_ValueNotInEnum_ReturnsFalse()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "IsInValid", "Fourth").Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "IsInValid").SetValidation().Read();
+ Assert.That(entry.IsValid, Is.False);
+ }
+
+ [Test]
+ public void IsValid_EnumSet_CorrectsValueSpellingAndValidatesSuccessfully()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "IsValidCorrectsSpelling", "SecOND").Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "IsValidCorrectsSpelling").SetValidation().Read();
+ Assert.Multiple(() =>
+ {
+ Assert.That(entry.IsValid, Is.True);
+ Assert.That(entry.Value, Is.EqualTo("Second"));
+ });
+ }
+
+ [Test]
+ public void IsValid_InvalidValueKind_ThrowArgumentException()
+ {
+ Assert.Throws(() => WinRegistryEntry.New(TestHive, TestPath, "IsValid", "Windows").SetValueKind(RegistryValueKind.Unknown));
+ }
+
+ [Test]
+ public void FluentWrite_And_Read_DoesNotThrow_ReturnsStrJustATest()
+ {
+ Assert.DoesNotThrow(() => WinRegistryEntry.New(TestHive, TestPath, "FluentReadAndWriteTest", "JustATest").Write());
+ var entry = WinRegistryEntry.New(TestHive, TestPath, "FluentReadAndWriteTest", "TestFailed").Read();
+ Assert.That(entry.Value, Is.EqualTo("JustATest"));
+ }
+
+ [OneTimeTearDown]
+ public void Cleanup()
+ {
+ // Delete all created tests
+ WinRegistry winReg = new();
+ winReg.DeleteTree(TestHive, TestRoot);
+ }
+ }
+}
diff --git a/mRemoteNGTests/Tools/Registry/WindowsRegistryTests.cs b/mRemoteNGTests/Tools/Registry/WindowsRegistryTests.cs
index 2ee7ff0dd..40a4de20f 100644
--- a/mRemoteNGTests/Tools/Registry/WindowsRegistryTests.cs
+++ b/mRemoteNGTests/Tools/Registry/WindowsRegistryTests.cs
@@ -1,195 +1,756 @@
using System;
+using Microsoft.Win32;
+using System.Runtime.Versioning;
using System.Collections.Generic;
using System.Linq;
-using System.Windows.Forms;
-using Microsoft.Win32;
+using System.Reflection;
using mRemoteNG.Tools.WindowsRegistry;
using NUnit.Framework;
namespace mRemoteNGTests.Tools.Registry
{
- public class WindowsRegistryTests
+ [SupportedOSPlatform("windows")]
+ internal class WindowsRegistryTests : WinRegistry
{
- private IRegistryRead _registryReader;
- private IRegistryWrite _registryWriter;
+ private const string TestRoot = @"Software\mRemoteNGTest";
+ private const RegistryHive TestHive = RegistryHive.CurrentUser;
+ private const string TestPath = $"{TestRoot}\\WinRegistryTests";
[SetUp]
public void Setup()
{
- _registryReader = new WindowsRegistry();
- _registryWriter = new WindowsRegistry();
}
- #region GetSubKeyNames() tests
- [Test]
- public void CanGetSubkeyNames()
+ #region WindowsRegistry.ThrowIfHiveInvalid()
+
+ private static MethodInfo? GetPrivateStaticMethod(string methodName)
{
- var subKeyNames = _registryReader.GetSubKeyNames(RegistryHive.CurrentUser, "Software");
- Assert.That(subKeyNames, Does.Contain("Microsoft"));
+ return typeof(WinRegistry).GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Static);
}
[Test]
- public void GetSubkeyNamesThrowsIfGivenNullKeyPath()
+ public void ThrowIfHiveInvalid_ValidHive_DoesNotThrow()
{
- Assert.Throws(() => _registryReader.GetSubKeyNames(RegistryHive.CurrentUser, null));
+ var method = GetPrivateStaticMethod("ThrowIfHiveInvalid");
+ if (method != null)
+ Assert.DoesNotThrow(() => method.Invoke(null, new object[] { RegistryHive.LocalMachine }));
+ else
+ Assert.Fail("The method ThrowIfHiveInvalid could not be found.");
}
[Test]
- public void GetSubkeyNamesThrowsIfGivenUnknownHive()
+ public void ThrowIfHiveInvalid_CurrentConfig_ThrowsArgumentException()
{
- Assert.Throws(() => _registryReader.GetSubKeyNames(new RegistryHive(), "Software"));
+ var method = GetPrivateStaticMethod("ThrowIfHiveInvalid");
+ if (method != null)
+ Assert.Throws(() => method.Invoke(null, new object[] { null }));
+ else
+ Assert.Fail("The method ThrowIfHiveInvalid could not be found.");
}
+
+ [Test]
+ public void ThrowIfHiveInvalid_InvalidHive_ThrowsArgumentException()
+ {
+ var method = GetPrivateStaticMethod("ThrowIfHiveInvalid");
+ if (method != null)
+ Assert.Throws(() => method ? .Invoke(null, new object[] { (RegistryHive)100 }));
+ else
+ Assert.Fail("The method ThrowIfHiveInvalid could not be found.");
+ }
+
#endregion
- #region GetPropertyValue() tests
+ #region WindowsRegistry.ThrowIfPathInvalid()
+
[Test]
- public void CanGetPropertyValue()
+ public void ThrowIfPathInvalid_ValidPath_DoesNotThrow()
{
- var keyValue = _registryReader.GetPropertyValue(RegistryHive.ClassesRoot, @".dll\PersistentHandler", "");
- Assert.That(keyValue, Is.EqualTo("{098f2470-bae0-11cd-b579-08002b30bfeb}"));
+ var method = GetPrivateStaticMethod("ThrowIfPathInvalid");
+ if (method != null)
+ Assert.DoesNotThrow(() => method.Invoke(null, new object[] { @"SOFTWARE\Microsoft" }));
+ else
+ Assert.Fail("The method ThrowIfPathInvalid could not be found.");
}
[Test]
- public void CanGetPropertyValueByRegistryKeyObject()
+ public void ThrowIfPathInvalid_NullPath_ThrowsArgumentException()
{
- WindowsRegistryKey key = new()
- {
- Hive = RegistryHive.ClassesRoot,
- Path = @".dll\PersistentHandler",
- Name = ""
- };
-
- var keyValue = _registryReader.GetPropertyValue(key);
- Assert.That(keyValue, Is.EqualTo("{098f2470-bae0-11cd-b579-08002b30bfeb}"));
- }
- [Test]
- public void GetPropertyValueThrowsIfGivenNullKeyPath()
- {
- Assert.Throws(() => _registryReader.GetPropertyValue(RegistryHive.CurrentUser, null, ""));
+ var method = GetPrivateStaticMethod("ThrowIfPathInvalid");
+ if (method != null)
+ Assert.Throws(() => method.Invoke(null, new object[] { null }));
+ else
+ Assert.Fail("The method ThrowIfPathInvalid could not be found.");
}
[Test]
- public void GetPropertyValueThrowsIfGivenNullPropertyName()
+ public void ThrowIfPathInvalid_EmptyPath_ThrowsArgumentException()
{
- Assert.Throws(() => _registryReader.GetPropertyValue(RegistryHive.CurrentUser, "", null));
+ var method = GetPrivateStaticMethod("ThrowIfPathInvalid");
+ if (method != null)
+ Assert.Throws(() => method.Invoke(null, new object[] { string.Empty }));
+ else
+ Assert.Fail("The method ThrowIfPathInvalid could not be found.");
}
+
+ [Test]
+ public void ThrowIfPathInvalid_WhitespacePath_ThrowsArgumentException()
+ {
+ var method = GetPrivateStaticMethod("ThrowIfPathInvalid");
+ if (method != null)
+ Assert.Throws(() => method.Invoke(null, new object[] { " " }));
+ else
+ Assert.Fail("The method ThrowIfPathInvalid could not be found.");
+ }
+
#endregion
- #region GetWindowsRegistryKey() tests
+ #region WindowsRegistry.ThrowIfNameInvalid()
+
[Test]
- public void CanGetWindowsRegistryKey()
+ public void ThrowIfNameInvalid_ValidName_DoesNotThrow()
{
- WindowsRegistryKey keyValue = _registryReader.GetWindowsRegistryKey(RegistryHive.ClassesRoot, @".dll\PersistentHandler", "");
- Assert.That(keyValue.Value, Is.EqualTo("{098f2470-bae0-11cd-b579-08002b30bfeb}"));
+ var method = GetPrivateStaticMethod("ThrowIfNameInvalid");
+ if (method != null)
+ Assert.DoesNotThrow(() => method.Invoke(null, new object[] { "TestName" }));
+ else
+ Assert.Fail("The method ThrowIfNameInvalid could not be found.");
}
[Test]
- public void CanGetWindowsRegistryKeyByObject()
+ public void ThrowIfNameInvalid_NullName_ThrowsArgumentNullException()
{
- WindowsRegistryKey key = new()
- {
- Hive = RegistryHive.ClassesRoot,
- Path = @".dll\PersistentHandler",
- Name = ""
- };
+ var method = GetPrivateStaticMethod("ThrowIfNameInvalid");
+ if (method != null)
+ Assert.Throws(() => method.Invoke(null, new object[] { null }));
+ else
+ Assert.Fail("The method ThrowIfNameInvalid could not be found.");
+ }
- WindowsRegistryKey keyValue = _registryReader.GetWindowsRegistryKey(key);
- Assert.That(keyValue.Value, Is.EqualTo("{098f2470-bae0-11cd-b579-08002b30bfeb}"));
+ #endregion
+
+ #region WindowsRegistry.ThrowIfValueKindInvalid()
+
+ [Test]
+ public void ThrowIfValueKindInvalid_ValidValueKind_DoesNotThrow()
+ {
+ var method = GetPrivateStaticMethod("ThrowIfValueKindInvalid");
+ if (method != null)
+ Assert.DoesNotThrow(() => method.Invoke(null, new object[] { RegistryValueKind.String }));
+ else
+ Assert.Fail("The method ThrowIfValueKindInvalid could not be found.");
}
[Test]
- public void CanGetWindowsRegistryKeyForKeyNotExists()
+ public void ThrowIfValueKindInvalid_InvalidValueKind_ThrowsArgumentException()
{
- // No exception. Only null value
- WindowsRegistryKey keyValue = _registryReader.GetWindowsRegistryKey(RegistryHive.LocalMachine, @"Software\Testabcdefg", "abcdefg");
- Assert.That(keyValue.Value, Is.EqualTo(null));
+ var method = GetPrivateStaticMethod("ThrowIfValueKindInvalid");
+ if (method != null)
+ Assert.Throws(() => method.Invoke(null, new object[] { (RegistryValueKind)100 }));
+ else
+ Assert.Fail("The method ThrowIfValueKindInvalid could not be found.");
}
[Test]
- public void GetWindowsRegistryThrowNotReadable()
+ public void ThrowIfValueKindInvalid_ValueKindUnknown_ThrowsArgumentException()
{
- WindowsRegistryKey key = new()
- {
- Hive = RegistryHive.ClassesRoot,
- };
+ var method = GetPrivateStaticMethod("ThrowIfValueKindInvalid");
+ if (method != null)
+ Assert.Throws(() => method.Invoke(null, new object[] { RegistryValueKind.Unknown }));
+ else
+ Assert.Fail("The method ThrowIfValueKindInvalid could not be found.");
+ }
+
+ [Test]
+ public void ThrowIfValueKindInvalid_ValueKindNone_ThrowsArgumentException()
+ {
+ var method = GetPrivateStaticMethod("ThrowIfValueKindInvalid");
+ if (method != null)
+ Assert.Throws(() => method.Invoke(null, new object[] { RegistryValueKind.None }));
+ else
+ Assert.Fail("The method ThrowIfValueKindInvalid could not be found.");
+ }
+
+ #endregion
+
+ #region WindowsRegistry.SetValue()
+
+ [Test]
+ public void SetValue_NewKey_SetsValueSuccessfully()
+ {
+ const string testName = "TestValue";
+ const string testValue = "Test1";
+ const string testPath = TestPath + @"\SetValue";
+ Assert.DoesNotThrow(() => SetValue(TestHive, testPath, testName, testValue, RegistryValueKind.String));
+
+ string key = GetStringValue(TestHive, testPath, testName, testValue);
+ Assert.That(key, Is.EqualTo(testValue));
+ }
+
+ [Test]
+ public void SetValue_OverwriteValue_SetsValueSuccessfully()
+ {
+ const string testName = "TestValue";
+ const string testValue = "Test2";
+ const string testPath = TestPath + @"\SetValue";
+ Assert.DoesNotThrow(() => SetValue(TestHive, testPath, testName, testValue, RegistryValueKind.String));
+
+ string key = GetStringValue(TestHive, testPath, testName, testValue);
+ Assert.That(key, Is.EqualTo(testValue));
+ }
+
+ [Test]
+ public void SetValue_DefaultValueWithWrongValueKind_SetsValueSuccessfully()
+ {
+ const string? testName = null;
+ const string testValue = "SetDefault";
+ const string testPath = TestPath + @"\SetValue";
+
+ Assert.DoesNotThrow(() => SetValue(TestHive, testPath, testName, testValue, RegistryValueKind.Unknown));
+
+ string key = GetStringValue(TestHive, testPath, testName, testValue);
+ Assert.That(key, Is.EqualTo(testValue));
+ }
+
+ #endregion
+
+ #region WindowsRegistry.CreateKey()
+
+ [Test]
+ public void CreateKey_NewKey_CreatesKeySuccessfully()
+ {
+ const string testPath = TestPath + @"\TestSubKey";
+ Assert.DoesNotThrow(() => CreateKey(TestHive, testPath));
+ }
+
+ [Test]
+ public void CreateKey_ExistingKey_DoesNotThrow()
+ {
+ const string testPath = TestPath + @"\TestSubKey";
+ Assert.DoesNotThrow(() => CreateKey(TestHive, testPath));
+ }
+
+ [Test]
+ public void CreateKey_InvalidHive_ThrowsArgumentException()
+ {
+ Assert.Throws(() => CreateKey((RegistryHive)(-1), @"Software\Test"));
+ }
+
+ #endregion
+
+ #region WindowsRegistry.GetSubKeyNames()
+
+ [Test]
+ public void GetSubKeyNames_ExistingKey_ReturnsSubKeyNames()
+ {
+ const string testPath = TestPath + @"\GetSubKeyNames";
+ const string testSubKey1 = testPath + @"\subkey1";
+ const string testSubKey2 = testPath + @"\subkey2";
+ const string testSubKey3 = testPath + @"\subkey3";
+
+ CreateKey(TestHive, testSubKey1);
+ CreateKey(TestHive, testSubKey2);
+ CreateKey(TestHive, testSubKey3);
+
+ string[] subKeyNames = GetSubKeyNames(TestHive, testPath);
+
+ Assert.That(subKeyNames.Length, Is.EqualTo(3));
+ Assert.That(subKeyNames, Contains.Item("subkey1"));
+ Assert.That(subKeyNames, Contains.Item("subkey2"));
+ Assert.That(subKeyNames, Contains.Item("subkey3"));
+ }
- Assert.Throws(() => _registryReader.GetWindowsRegistryKey(key));
+ [Test]
+ public void GetSubKeyNames_NonExistingKey_ReturnsEmptyArray()
+ {
+ string[] subKeyNames = GetSubKeyNames(TestHive, @"Software\NonExistingKey");
+ Assert.That(subKeyNames, Is.Empty);
}
+
#endregion
- #region GetRegistryEntries() + Recurse tests
+ #region WindowsRegistry.GetValue()
+
[Test]
- public void CanGetRegistryEntries()
+ public void GetValue_RetrieveValue_ReturnsValue()
{
- List keys = _registryReader.GetRegistryEntries(RegistryHive.LocalMachine, @"HARDWARE\DESCRIPTION\System\BIOS");
- Assert.That(keys.Count, Is.Not.EqualTo(0));
+ const string testPath = TestPath + @"\GetValue";
+ const string testName = "TestValue";
+ const string testValue = "Test";
+
+ Assert.DoesNotThrow(() => SetValue(TestHive, testPath, testName, testValue, RegistryValueKind.String));
+
+ string key = GetValue(TestHive, testPath, testName);
+ Assert.That(key, Is.EqualTo(testValue));
}
[Test]
- public void CanGetRegistryEntriesRecurse()
+ public void GetValue_RetrieveDefault_RetrunsValue()
{
- List keys = _registryReader.GetRegistryEntryiesRecursive(RegistryHive.LocalMachine, @"SOFTWARE\Microsoft\Windows\Windows Search");
- Assert.That(keys.Count, Is.Not.EqualTo(0));
+ const string testPath = TestPath + @"\GetValue";
+ const string testName = "";
+ const string testValue = "TestDefault{098f2470-bae0-11cd-b579-08002b30bfeb}";
+
+ Assert.DoesNotThrow(() => SetValue(TestHive, testPath, testName, testValue, RegistryValueKind.String));
+
+ var key = GetValue(TestHive, testPath, testName);
+ Assert.That(key, Is.EqualTo(testValue));
}
+
+ [Test]
+ public void GetValue_NonExistingValue_ReturnsNull()
+ {
+ const string testPath = TestPath + @"\GetValue";
+ const string nonExistingName = "NonExistingValue";
+
+ string key = GetValue(TestHive, testPath, nonExistingName);
+ Assert.That(key, Is.Null);
+ }
+
+ [Test]
+ public void SetAndGetDefaultValue_RetrieveValue_ReturnsValue()
+ {
+ const string testPath = TestPath + @"\DefautValue";
+ const string testValue = "DefaultTestTestValue";
+
+ Assert.DoesNotThrow(() => SetDefaultValue(TestHive, testPath, testValue));
+
+ string key = GetDefaultValue(TestHive, testPath);
+ Assert.That(key, Is.EqualTo(testValue));
+ }
+
#endregion
- #region new WindowsRegistryKey() tests
+ #region WindowsRegistry.GetStringValue()
+
[Test]
- public void IsWindowsRegistryKeyValid()
+ public void GetStringValue_RetrieveValue_ReturnsValue()
{
- // Tests property rules of WindowsRegistryKey
- WindowsRegistryKey key = new();
+ const string testPath = TestPath + @"\GetStringValue";
+ const string testName = "TestValue";
+ const string testValue = "Test";
+
+ Assert.DoesNotThrow(() => SetValue(TestHive, testPath, testName, testValue, RegistryValueKind.String));
+
+ var key = GetStringValue(TestHive, testPath, testName);
+ Assert.That(key, Is.TypeOf().And.EqualTo(testValue));
+ }
+
+ [Test]
+ public void GetStringValue_RetrieveDefault_ReturnsValue()
+ {
+ const string testPath = TestPath + @"\GetStringValue";
+ const string testName = "";
+ const string testValue = "TestDefault{098f2470-bae0-11cd-b579-08002b30bfeb}";
+
+ Assert.DoesNotThrow(() => SetValue(TestHive, testPath, testName, testValue, RegistryValueKind.String));
+
+ var key = GetStringValue(TestHive, testPath, testName);
+ Assert.That(key, Is.TypeOf().And.EqualTo(testValue));
+ }
+
+ [Test]
+ public void GetStringValue_NonExistingValue_ReturnsNull()
+ {
+ const string testPath = TestPath + @"\GetStringValue";
+ const string nonExistingName = "NonExistingValue";
+
+ var key = GetStringValue(TestHive, testPath, nonExistingName);
+ Assert.That(key, Is.Null);
+ }
+
+ [Test]
+ public void GetStringValue_NonExistingValue_ReturnsDefault()
+ {
+ const string testPath = TestPath + @"\GetStringValue";
+ const string nonExistingName = "NonExistingValue";
+ const string defaultValue = "DefaultValue";
+
+ string key = GetStringValue(TestHive, testPath, nonExistingName, defaultValue);
+ Assert.That(key, Is.TypeOf().And.EqualTo(defaultValue));
+ }
+
+ #endregion
+
+ #region WindowsRegistry.GetBoolValue()
+
+ [Test]
+ public void GetBoolValue_FromString_ReturnsTrue()
+ {
+ const string testPath = TestPath + @"\GetBoolValue";
+ const string testName = "strBool_true";
+ const string testValue = "true";
+ Assert.DoesNotThrow(() => SetValue(TestHive, testPath, testName, testValue, RegistryValueKind.String));
+
+ var key = GetBoolValue(TestHive, testPath, testName, false); // set default to false
+ Assert.That(key, Is.TypeOf().And.EqualTo(true));
+ }
+
+ [Test]
+ public void GetBoolValue_FromString_ReturnsFalse()
+ {
+ const string testPath = TestPath + @"\GetBoolValue";
+ const string testName = "strBool_false";
+ const string testValue = "false";
+ Assert.DoesNotThrow(() => SetValue(TestHive, testPath, testName, testValue, RegistryValueKind.String));
+
+ var key = GetBoolValue(TestHive, testPath, testName, true); // set default to true
+ Assert.That(key, Is.TypeOf().And.EqualTo(false));
+ }
+
+ [Test]
+ public void GetBoolValue_FromDword_ReturnsFalse()
+ {
+ const string testPath = TestPath + @"\GetBoolValue";
+ const string testName = "intBool_false";
+ const int testValue = 0;
+ Assert.DoesNotThrow(() => SetValue(TestHive, testPath, testName, testValue, RegistryValueKind.DWord));
+
+ var key = GetBoolValue(TestHive, testPath, testName, true); // set default to true
+ Assert.That(key, Is.TypeOf().And.EqualTo(false));
+ }
+
+ [Test]
+ public void GetBoolValue_FromDword_ReturnsTrue()
+ {
+ const string testPath = TestPath + @"\GetBoolValue";
+ const string testName = "intBool_true";
+ const int testValue = 1;
+ Assert.DoesNotThrow(() => SetValue(TestHive, testPath, testName, testValue, RegistryValueKind.DWord));
+
+ var key = GetBoolValue(TestHive, testPath, testName, false); // set default to false
+ Assert.That(key, Is.TypeOf().And.EqualTo(true));
+ }
+
+ [Test]
+ public void GetBoolValue_NonExistingValue_ReturnsFalse()
+ {
+ const string testPath = TestPath + @"\GetStringValue";
+ const string nonExistingName = "NonExistingValue";
+
+ var key = GetBoolValue(TestHive, testPath, nonExistingName);
+ Assert.That(key, Is.TypeOf().And.EqualTo(false));
+ }
+
+ #endregion
+
+ #region WindowsRegistry.GetDwordValue()
+
+ [Test]
+ public void GetIntegerValue_RetrieveValue_ReturnsValue()
+ {
+ const string testPath = TestPath + @"\GetIntegerValue";
+ const string testName = "ExistingDword";
+ const int testValue = 2;
+ Assert.DoesNotThrow(() => SetValue(TestHive, testPath, testName, testValue, RegistryValueKind.DWord));
+
+ var key = GetIntegerValue(TestHive, testPath, testName);
+ Assert.That(key, Is.TypeOf().And.EqualTo(testValue));
+ }
+
+ [Test]
+ public void GetIntegerValue_NonExistingValue_ReturnsZero()
+ {
+ const string testPath = TestPath + @"\GetStringValue";
+ const string nonExistingName = "NonExistingValue";
+
+ var key = GetIntegerValue(TestHive, testPath, nonExistingName);
+ Assert.That(key, Is.TypeOf().And.EqualTo(0));
+ }
+
+ [Test]
+ public void GetIntegerValue_NotExistingKey_ReturnsDefault()
+ {
+ const string testPath = TestPath + @"\GetStringValue";
+ const string testName = "NotExistingDword";
+
+ var key = GetIntegerValue(TestHive, testPath, testName, 12); // set default to true
+ Assert.That(key, Is.TypeOf().And.EqualTo(12));
+ }
+
+ #endregion
+
+ #region WindowsRegistry.GetEntry()
+
+ [Test]
+ public void GetRegistryEntry_ReturnsCorrectEntry()
+ {
+ const string testName = "TestValue";
+ const int testValue = 2011;
+ const string testPath = TestPath + @"\GetRegistryEntry";
+ Assert.DoesNotThrow(() => SetValue(TestHive, testPath, testName, testValue, RegistryValueKind.DWord));
+
+ var entry = GetEntry(TestHive, testPath, testName);
Assert.Multiple(() =>
{
- Assert.DoesNotThrow(() => key.Hive = RegistryHive.CurrentUser);
- Assert.DoesNotThrow(() => key.ValueKind = RegistryValueKind.String);
- Assert.DoesNotThrow(() => key.Path = "Software");
- Assert.DoesNotThrow(() => key.Name = "NotThereButOK");
- //Assert.DoesNotThrow(() => key.Value = "Equal", "");
+ Assert.That(entry.Hive, Is.EqualTo(TestHive));
+ Assert.That(entry.Path, Is.EqualTo(testPath));
+ Assert.That(entry.Name, Is.EqualTo(testName));
+ Assert.That(entry.ValueKind, Is.EqualTo(RegistryValueKind.DWord));
+ Assert.That(entry.IsSet, Is.EqualTo(true));
});
-
- }
- [Test]
- public void WindowsRegistryKeyThrowHiveNullException()
- {
- WindowsRegistryKey key = new();
- Assert.Throws(() => key.Hive = 0, "Expected IsHiveValid to throw ArgumentNullException");
}
- [Test]
- public void WindowsRegistryKeyValueKindUnknown()
- {
- WindowsRegistryKey key = new();
- Assert.That(key.ValueKind, Is.EqualTo(RegistryValueKind.Unknown));
+ #endregion
+
+ #region WinRegistryEC.GetRegistryEntries()
- }
[Test]
- public void WindowsRegistryKeyThrowPathNullException()
+ public void GetRegistryEntries_ReturnsCorrectEntries()
{
- WindowsRegistryKey key = new();
- Assert.Throws(() => key.Path = null, "Expected IsPathValid to throw ArgumentNullException");
+ const string testPath = TestPath + @"\GetRegistryEntries";
+ const string testNamePrefix = "TestEntry";
+ const string testValue = "winRegEntriesTest";
+
+ for (int i = 1; i <= 10; i++)
+ {
+ string testName = $"{testNamePrefix}{i}";
+ Assert.DoesNotThrow(() => SetValue(TestHive, testPath, testName, testValue, RegistryValueKind.String));
+ }
+
+ const string testPathSubkeys = testPath + @"\Subkey";
+ const string testNameSubKey = "TestSubEntry";
+ Assert.DoesNotThrow(() => SetValue(TestHive, testPathSubkeys, testNameSubKey, testValue, RegistryValueKind.String));
+
+ var entries = GetEntries(TestHive, testPath);
+
+
+ Assert.That(entries, Is.Not.Null);
+ Assert.That(entries, Is.Not.Empty);
+ Assert.That(entries, Is.InstanceOf>>());
+
+ foreach (var entry in entries)
+ {
+ Assert.Multiple(() =>
+ {
+ Assert.That(entry.Name, Is.Not.Null & Is.Not.EqualTo(testNameSubKey)); //Subkeys should not be read (non-recursive).
+
+ Assert.That(entry.Hive, Is.EqualTo(TestHive));
+ Assert.That(entry.Path, Is.EqualTo(testPath));
+ Assert.That(entry.Value, Is.EqualTo(testValue));
+ Assert.That(entry.ValueKind, Is.EqualTo(RegistryValueKind.String));
+ });
+ }
}
+
+ #endregion
+
+ #region WinRegistryEC.GetRegistryEntriesRecursive()
+
[Test]
- public void WindowsRegistryKeyThrowNameNullException()
+ public void GetRegistryEntriesRecursive_ReturnsCorrectEntries()
{
- WindowsRegistryKey key = new();
- Assert.Throws(() => key.Name = null, "Expected IsNameValid to throw ArgumentNullException");
+ const string testPath = TestPath + @"\GetRegistryEntriesRecursive";
+ const string testNamePrefix = "TestEntry";
+ const string testValue = "winRegEntriesTest";
+
+ for (int i = 1; i <= 10; i++)
+ {
+ string testName = $"{testNamePrefix}{i}";
+ Assert.DoesNotThrow(() => SetValue(TestHive, testPath, testName, testValue, RegistryValueKind.String));
+ }
+
+ const string testSubkeyPath = testPath + @"\Subkey";
+ const string testNameSubKey = "TestSubEntry";
+ Assert.DoesNotThrow(() => SetValue(TestHive, testSubkeyPath, testNameSubKey, testValue, RegistryValueKind.String));
+
+ var entries = GetEntriesRecursive(TestHive, testPath);
+
+ Assert.That(entries, Is.Not.Null);
+ Assert.That(entries, Is.Not.Empty);
+ Assert.That(entries, Is.InstanceOf>> ());
+
+ // Assert that the subkey is included in the entries list
+ Assert.That(entries.Any(e => e.Name == testNameSubKey), Is.True, "Subkey entry should be included in the entries list.");
+
+ foreach (var entry in entries)
+ {
+ Assert.Multiple(() =>
+ {
+ Assert.That(entry.Name, Is.Not.Null);
+ Assert.That(entry.Hive, Is.EqualTo(TestHive));
+ Assert.That(entry.Value, Is.EqualTo(testValue));
+ Assert.That(entry.ValueKind, Is.EqualTo(RegistryValueKind.String));
+ });
+ }
+ }
+
+ #endregion
+
+ #region WindowsRegistry.DeleteRegistryValue()
+ [Test]
+ public void DeleteRegistryValue_DeletesValue()
+ {
+ const string testPath = TestPath + @"\DeleteRegistryValue";
+ const string testName01 = "TestValue01";
+ const string testName02 = "TestValue02";
+
+ Assert.DoesNotThrow(() => SetValue(TestHive, testPath, testName01, "Test", RegistryValueKind.String));
+ Assert.DoesNotThrow(() => SetValue(TestHive, testPath, testName02, "Test", RegistryValueKind.String));
+
+ Assert.DoesNotThrow(() => DeleteRegistryValue(TestHive, testPath, testName01));
+ Assert.Multiple(() =>
+ {
+ Assert.That(GetStringValue(TestHive, testPath, testName01), Is.Null);
+ Assert.That(GetStringValue(TestHive, testPath, testName02), Is.Not.Null);
+ });
}
#endregion
- #region SetRegistryValue() tests
- [Test]
- public void CanSetRegistryValue()
- {
- Assert.DoesNotThrow(() => _registryWriter.SetRegistryValue(RegistryHive.CurrentUser, @"SOFTWARE\mRemoteNGTest", "TestKey", "A value string", RegistryValueKind.String));
- }
+ #region WindowsRegistry.DeleteTree()
[Test]
- public void SetRegistryValueThrowAccessDenied()
+ public void DeleteTree_RemovesKeyAndSubkeys()
{
- Assert.Throws(() => _registryWriter.SetRegistryValue(RegistryHive.LocalMachine, @"SOFTWARE\mRemoteNGTest", "TestKey", "A value string", RegistryValueKind.String));
+ const string testPath = TestPath + @"\DeleteTree";
+ Assert.DoesNotThrow(() => SetValue(TestHive, $"{testPath}\\Subkey0", "Test", "Test", RegistryValueKind.String));
+ Assert.DoesNotThrow(() => SetValue(TestHive, $"{testPath}\\Subkey1", "Test", "Test", RegistryValueKind.String));
+ Assert.DoesNotThrow(() => SetValue(TestHive, $"{testPath}\\Subkey2", "Test", "Test", RegistryValueKind.String));
+ Assert.DoesNotThrow(() => SetValue(TestHive, $"{testPath}\\Subkey3", "Test", "Test", RegistryValueKind.String));
+
+ Assert.DoesNotThrow(() => DeleteTree(TestHive, testPath));
+ Assert.That(GetSubKeyNames(TestHive, testPath), Is.Empty);
}
+
#endregion
+
+ #region WindowsRegistry.ConvertStringToRegistryHive()
+
+ [Test]
+ public void ConvertStringToRegistryHive_ReturnsCorrectValue()
+ {
+ Assert.Multiple(() =>
+ {
+ Assert.That(ConvertStringToRegistryHive("HKCR"), Is.EqualTo(RegistryHive.ClassesRoot));
+ Assert.That(ConvertStringToRegistryHive("HKey_Classes_Root"), Is.EqualTo(RegistryHive.ClassesRoot));
+ Assert.That(ConvertStringToRegistryHive("ClassesRoot"), Is.EqualTo(RegistryHive.ClassesRoot));
+
+ Assert.That(ConvertStringToRegistryHive("HKCU"), Is.EqualTo(RegistryHive.CurrentUser));
+ Assert.That(ConvertStringToRegistryHive("HKey_Current_User"), Is.EqualTo(RegistryHive.CurrentUser));
+ Assert.That(ConvertStringToRegistryHive("currentuser"), Is.EqualTo(RegistryHive.CurrentUser));
+
+ Assert.That(ConvertStringToRegistryHive("HKLM"), Is.EqualTo(RegistryHive.LocalMachine));
+ Assert.That(ConvertStringToRegistryHive("HKey_Local_Machine"), Is.EqualTo(RegistryHive.LocalMachine));
+ Assert.That(ConvertStringToRegistryHive("LocalMachine"), Is.EqualTo(RegistryHive.LocalMachine));
+
+ Assert.That(ConvertStringToRegistryHive("HKU"), Is.EqualTo(RegistryHive.Users));
+ Assert.That(ConvertStringToRegistryHive("HKey_users"), Is.EqualTo(RegistryHive.Users));
+ Assert.That(ConvertStringToRegistryHive("Users"), Is.EqualTo(RegistryHive.Users));
+
+ Assert.That(ConvertStringToRegistryHive("HKCC"), Is.EqualTo(RegistryHive.CurrentConfig));
+ Assert.That(ConvertStringToRegistryHive("HKey_Current_Config"), Is.EqualTo(RegistryHive.CurrentConfig));
+ Assert.That(ConvertStringToRegistryHive("CurrentConfig"), Is.EqualTo(RegistryHive.CurrentConfig));
+ });
+ }
+
+ [Test]
+ public void ConvertStringToRegistryHive_InvalidHiveNull_ThrowArgumentNullException()
+ {
+ Assert.That(() => ConvertStringToRegistryHive(null), Throws.ArgumentNullException);
+ }
+
+ [Test]
+ public void ConvertStringToRegistryHive_InvalidHive_ThrowArgumentException()
+ {
+ Assert.That(() => ConvertStringToRegistryHive("InvalidHive"), Throws.ArgumentException);
+ }
+
+ #endregion
+
+ #region WindowsRegistry.ConvertStringToRegistryValueKind()
+
+ [Test]
+ public void ConvertStringToRegistryValueKind_ReturnsCorrectValue()
+ {
+ Assert.Multiple(() =>
+ {
+ Assert.That(ConvertStringToRegistryValueKind("string"), Is.EqualTo(RegistryValueKind.String));
+ Assert.That(ConvertStringToRegistryValueKind("reg_sz"), Is.EqualTo(RegistryValueKind.String));
+
+ Assert.That(ConvertStringToRegistryValueKind("dword"), Is.EqualTo(RegistryValueKind.DWord));
+ Assert.That(ConvertStringToRegistryValueKind("reg_dword"), Is.EqualTo(RegistryValueKind.DWord));
+
+ Assert.That(ConvertStringToRegistryValueKind("binary"), Is.EqualTo(RegistryValueKind.Binary));
+ Assert.That(ConvertStringToRegistryValueKind("reg_binary"), Is.EqualTo(RegistryValueKind.Binary));
+
+ Assert.That(ConvertStringToRegistryValueKind("qword"), Is.EqualTo(RegistryValueKind.QWord));
+ Assert.That(ConvertStringToRegistryValueKind("reg_qword"), Is.EqualTo(RegistryValueKind.QWord));
+
+ Assert.That(ConvertStringToRegistryValueKind("multistring"), Is.EqualTo(RegistryValueKind.MultiString));
+ Assert.That(ConvertStringToRegistryValueKind("reg_multi_sz"), Is.EqualTo(RegistryValueKind.MultiString));
+
+ Assert.That(ConvertStringToRegistryValueKind("expandstring"), Is.EqualTo(RegistryValueKind.ExpandString));
+ Assert.That(ConvertStringToRegistryValueKind("reg_expand_sz"), Is.EqualTo(RegistryValueKind.ExpandString));
+ });
+ }
+
+ [Test]
+ public void ConvertStringToRegistryValueKind_InvalidHiveNull_ThrowArgumentNullException()
+ {
+ Assert.That(() => ConvertStringToRegistryValueKind(null), Throws.ArgumentNullException);
+ }
+
+ [Test]
+ public void ConvertStringToRegistryValueKind_InvalidHive_ThrowArgumentException()
+ {
+ Assert.That(() => ConvertStringToRegistryValueKind("InvalidKind"), Throws.ArgumentException);
+ }
+
+ #endregion
+
+ #region WindowsRegistry.ConvertTypeToRegistryValueKind()
+
+ [Test]
+ public void ConvertTypeToRegistryValueKind_ReturnsCorrectValue()
+ {
+ Assert.Multiple(() =>
+ {
+ Assert.That(ConvertTypeToRegistryValueKind(typeof(string)), Is.EqualTo(RegistryValueKind.String));
+ Assert.That(ConvertTypeToRegistryValueKind(typeof(int)), Is.EqualTo(RegistryValueKind.DWord));
+ Assert.That(ConvertTypeToRegistryValueKind(typeof(long)), Is.EqualTo(RegistryValueKind.QWord));
+ Assert.That(ConvertTypeToRegistryValueKind(typeof(bool)), Is.EqualTo(RegistryValueKind.DWord));
+ Assert.That(ConvertTypeToRegistryValueKind(typeof(byte)), Is.EqualTo(RegistryValueKind.Binary));
+ });
+ }
+
+ [Test]
+ public void ConvertTypeToRegistryValueKind_UnknownType_ReturnsString()
+ {
+ Assert.That(ConvertTypeToRegistryValueKind(typeof(Single)), Is.EqualTo(RegistryValueKind.String));
+ }
+
+ [Test]
+ public void ConvertTypeToRegistryValueKind_WithNullValueType_ThrowsArgumentNullException()
+ {
+ Assert.Throws(() => ConvertTypeToRegistryValueKind(null));
+ }
+
+ #endregion
+
+ #region WindowsRegistry.ConvertRegistryValueKindToType()
+
+ [Test]
+ public void ConvertRegistryValueKindToType_ReturnsCorrectType()
+ {
+ Assert.Multiple(() =>
+ {
+ Assert.That(ConvertRegistryValueKindToType(RegistryValueKind.String), Is.EqualTo(typeof(string)));
+ Assert.That(ConvertRegistryValueKindToType(RegistryValueKind.ExpandString), Is.EqualTo(typeof(string)));
+ Assert.That(ConvertRegistryValueKindToType(RegistryValueKind.DWord), Is.EqualTo(typeof(int)));
+ Assert.That(ConvertRegistryValueKindToType(RegistryValueKind.QWord), Is.EqualTo(typeof(long)));
+ Assert.That(ConvertRegistryValueKindToType(RegistryValueKind.Binary), Is.EqualTo(typeof(byte[])));
+ Assert.That(ConvertRegistryValueKindToType(RegistryValueKind.MultiString), Is.EqualTo(typeof(string[])));
+ Assert.That(ConvertRegistryValueKindToType((RegistryValueKind)100), Is.EqualTo(typeof(object)));
+ });
+ }
+
+ #endregion
+
+ [OneTimeTearDown]
+ public void Cleanup()
+ {
+ // Delete all created tests
+ DeleteTree(TestHive, TestPath);
+ }
}
}