Refactor registry settings interaction classes.

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

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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<string>
{
Path = expectedPath
};
Assert.That(expectedPath, Is.EqualTo(entry.Path));
}
[Test]
public void Path_SetNullValue_ThrowsArgumentNullException()
{
var entry = new WinRegistryEntry<string>();
Assert.Throws<ArgumentNullException>(() => entry.Path = null);
}
[Test]
public void Name_SetValidValue_GetReturnsSameValue()
{
string expectedName = "Version";
var entry = new WinRegistryEntry<string>
{
Name = expectedName
};
Assert.That(expectedName, Is.EqualTo(entry.Name));
}
[Test]
public void Value_SetValidValue_GetReturnsSameValue()
{
string expectedValue = "1.0";
var entry = new WinRegistryEntry<string>
{
Value = expectedValue
};
Assert.That(expectedValue, Is.EqualTo(entry.Value));
}
[Test]
public void ValueKind_SetInvalidValue_ThrowsArgumentException()
{
var entry = new WinRegistryEntry<string>();
Assert.Throws<ArgumentException>(() => entry.SetValueKind((RegistryValueKind)100));
}
[Test]
public void IsSet_ValueHasBeenSet_NotRead_ReturnsFalse()
{
var entry = new WinRegistryEntry<string>
{
Value = "Test"
};
Assert.That(entry.IsSet, Is.False);
}
[Test]
public void IsKeyReadable_AllPropertiesSet_ReturnsTrue()
{
var entry = new WinRegistryEntry<string>
{
Hive = RegistryHive.LocalMachine,
Path = @"SOFTWARE\Microsoft",
Name = "Version"
};
Assert.That(entry.IsReadable, Is.True);
}
[Test]
public void IsKeyWritable_AllPropertiesSet_ReturnsTrue()
{
var entry = new WinRegistryEntry<string>
{
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<string>();
Assert.Throws<InvalidOperationException>(() => entry.Read());
}
[Test]
public void Write_WhenKeyIsUnwritable_ThrowsInvalidOperationException()
{
var entry = new WinRegistryEntry<string>
{
Name = "Version"
};
Assert.Throws<InvalidOperationException>(() => entry.Write());
}
[Test]
public void WriteDefaultAndReadDefault_Entry_DoesNotThrowAndReadsCorrectly()
{
var entry = new WinRegistryEntry<int>(TestHive, TestPath, 0)
{
Hive = TestHive,
Path = TestPath,
Value = 0,
};
var readEntry = new WinRegistryEntry<int>
{
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<long>
{
Hive = TestHive,
Path = TestPath,
Name = "TestRead",
Value = 200
};
var readEntry = new WinRegistryEntry<long>
{
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<string>.New(TestHive, TestPath, "FluentReadAndWriteTest", "JustATest").Write());
var entry = WinRegistryEntry<string>.New(TestHive, TestPath, "FluentReadAndWriteTest").Read();
Assert.That(entry.Value, Is.EqualTo("JustATest"));
}
[Test]
public void FluentWriteReadAndChange_DoesNotThrow_WriteReadsCorrectly()
{
var entry = WinRegistryEntry<string>
.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<string>
.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<string>
.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<string>
.New(TestHive, TestPath, "ReadLockThrow", "ReadingIsLocked")
.Write()
.Read()
.Lock();
Assert.Throws<InvalidOperationException>(() => entry.Read());
}
[OneTimeTearDown]
public void Cleanup()
{
// Delete all created tests
WinRegistry winReg = new();
winReg.DeleteTree(TestHive, TestRoot);
}
}
}

View File

@@ -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<bool>.New(TestHive, TestPath, "IsTrueString", true).Write());
var entry = WinRegistryEntry<bool>.New(TestHive, TestPath, "IsTrueString").Read();
Assert.That(entry.Value, Is.True);
}
[Test]
public void StringFalse_SuccessfulToBool_ReturnsFalse()
{
Assert.DoesNotThrow(() => WinRegistryEntry<bool>.New(TestHive, TestPath, "IsFalseString", false).Write());
var entry = WinRegistryEntry<bool>.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<bool>.New(TestHive, TestPath, "IsTrueDword", true).SetValueKind(RegistryValueKind.DWord).Write());
var entry = WinRegistryEntry<bool>.New(TestHive, TestPath, "IsTrueDword").Read();
Assert.That(entry.Value, Is.True);
}
[Test]
public void DWordFalse_SuccessfulToBool_ReturnsFalse()
{
Assert.DoesNotThrow(() => WinRegistryEntry<bool>.New(TestHive, TestPath, "IsFalseDword", false).SetValueKind(RegistryValueKind.DWord).Write());
var entry = WinRegistryEntry<bool>.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<bool>.New(TestHive, TestPath, "FluentReadAndWriteTest", false).SetValueKind(RegistryValueKind.DWord).Write());
var entry = WinRegistryEntry<bool>.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);
}
}
}

View File

@@ -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<int>.New(TestHive, TestPath, "IsValid", 1).Write());
var entry = WinRegistryEntry<int>.New(TestHive, TestPath, "IsValid").Read();
Assert.That(entry.IsValid, Is.True);
}
[Test]
public void IsValid_AllowedValuesSet_ValueInAllowedValues_ReturnsTrue()
{
Assert.DoesNotThrow(() => WinRegistryEntry<int>.New(TestHive, TestPath, "IsValid", 2).Write());
var entry = WinRegistryEntry<int>.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<int>.New(TestHive, TestPath, "IsValid", 4).Write());
var entry = WinRegistryEntry<int>.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<int>.New(TestHive, TestPath, "IsValid", 5).Write());
var entry = WinRegistryEntry<int>.New(TestHive, TestPath, "IsValid").SetValidation(1,10).Read();
Assert.That(entry.IsValid, Is.True);
}
[Test]
public void IsValid_RangeSet_ValueOutOfRange_ReturnsFalse()
{
Assert.DoesNotThrow(() => WinRegistryEntry<int>.New(TestHive, TestPath, "IsValid", 50).Write());
var entry = WinRegistryEntry<int>.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<int>.New(TestHive, TestPath, "IsValidZero", 0).Write());
var entry = WinRegistryEntry<int>.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<int>.New(TestHive, TestPath, "IsValidDefMin", 10).Write());
var entry = WinRegistryEntry<int>.New(TestHive, TestPath, "IsValidDefMin").SetValidation("*", "10").Read();
Assert.That(entry.IsValid, Is.True);
}
[Test]
public void IsValid_RangeSet_DefaultMax_ValueInRange_ReturnsTrue()
{
Assert.DoesNotThrow(() => WinRegistryEntry<int>.New(TestHive, TestPath, "IsValidDefMax", 1000).Write());
var entry = WinRegistryEntry<int>.New(TestHive, TestPath, "IsValidDefMax").SetValidation("50", "*").Read();
Assert.That(entry.IsValid, Is.True);
}
[Test]
public void IsValid_EnumSet_ValueInEnum_ReturnsTrue()
{
Assert.DoesNotThrow(() => WinRegistryEntry<int>.New(TestHive, TestPath, "IsValid", 2).Write());
var entry = WinRegistryEntry<int>.New(TestHive, TestPath, "IsValid").SetValidation<TestEnum>().Read();
Assert.That(entry.IsValid, Is.True);
}
[Test]
public void IsValid_EnumSet_ValueNotInEnum_ReturnsFalse()
{
Assert.DoesNotThrow(() => WinRegistryEntry<int>.New(TestHive, TestPath, "IsValid", 5).Write());
var entry = WinRegistryEntry<int>.New(TestHive, TestPath, "IsValid").SetValidation<TestEnum>().Read();
Assert.That(entry.IsValid, Is.False);
}
[Test]
public void IsValid_InvalidValueKind_ThrowArgumentException()
{
Assert.Throws<ArgumentException>(() => WinRegistryEntry<int>.New(TestHive, TestPath, "IsValid", 5).SetValueKind(RegistryValueKind.Unknown));
}
[Test]
public void Value_SetToNegativeNumber_ThrowArgumentException()
{
Assert.Throws<ArgumentException>(() => WinRegistryEntry<int>.New(TestHive, TestPath, "IsValid", -100));
}
[Test]
public void FluentWrite_And_Read_DoesNotThrow_ReturnsInt12()
{
Assert.DoesNotThrow(() => WinRegistryEntry<int>.New(TestHive, TestPath, "FluentReadAndWriteTest", 12).Write());
var entry = WinRegistryEntry<int>.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);
}
}
}

View File

@@ -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<long>.New(TestHive, TestPath, "IsValid", 3047483647).Write());
var entry = WinRegistryEntry<long>.New(TestHive, TestPath, "IsValid").Read();
Assert.That(entry.IsValid, Is.True);
}
[Test]
public void IsValid_AllowedValuesSet_ValueInAllowedValues_ReturnsTrue()
{
Assert.DoesNotThrow(() => WinRegistryEntry<long>.New(TestHive, TestPath, "IsValid", 2147483649).Write());
var entry = WinRegistryEntry<long>.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<long>.New(TestHive, TestPath, "IsValid", 4).Write());
var entry = WinRegistryEntry<long>.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<long>.New(TestHive, TestPath, "IsValid", 2147483652).Write());
var entry = WinRegistryEntry<long>.New(TestHive, TestPath, "IsValid").SetValidation(2147483647, 2200000000).Read();
Assert.That(entry.IsValid, Is.True);
}
[Test]
public void IsValid_RangeSet_ValueOutOfRange_ReturnsFalse()
{
Assert.DoesNotThrow(() => WinRegistryEntry<long>.New(TestHive, TestPath, "IsValid", 20).Write());
var entry = WinRegistryEntry<long>.New(TestHive, TestPath, "IsValid").SetValidation(2147483647, 2200000000).Read();
Assert.That(entry.IsValid, Is.False);
}
[Test]
public void IsValid_InvalidValueKind_ThrowArgumentException()
{
Assert.Throws<ArgumentException>(() => WinRegistryEntry<long>.New(TestHive, TestPath, "IsValid", 5).SetValueKind(RegistryValueKind.Unknown));
}
[Test]
public void Value_SetToNegativeNumber_ThrowArgumentException()
{
Assert.Throws<ArgumentException>(() => WinRegistryEntry<long>.New(TestHive, TestPath, "IsValid", -100));
}
[Test]
public void FluentWrite_And_Read_DoesNotThrow_ReturnsLong15()
{
Assert.DoesNotThrow(() => WinRegistryEntry<long>.New(TestHive, TestPath, "FluentReadAndWriteTest", 15).Write());
var entry = WinRegistryEntry<long>.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);
}
}
}

View File

@@ -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<string>.New(TestHive, TestPath, "IsValid", "IsValid").Write());
var entry = WinRegistryEntry<string>.New(TestHive, TestPath, "IsValid").Read();
Assert.That(entry.IsValid, Is.True);
}
[Test]
public void IsValid_AllowedValuesSet_ValueInAllowedValues_ReturnsTrue()
{
Assert.DoesNotThrow(() => WinRegistryEntry<string>.New(TestHive, TestPath, "ArrayIsValid", "Banana").Write());
var entry = WinRegistryEntry<string>.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<string>.New(TestHive, TestPath, "ArrayIsInValid", "Cheese").Write());
var entry = WinRegistryEntry<string>.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<string>.New(TestHive, TestPath, "ArrayCorrectsSpellingIsValid", "StrawBerry").Write());
var entry = WinRegistryEntry<string>.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<string>.New(TestHive, TestPath, "IsValid", "Second").Write());
var entry = WinRegistryEntry<string>.New(TestHive, TestPath, "IsValid").SetValidation<TestEnum>().Read();
Assert.That(entry.IsValid, Is.True);
}
[Test]
public void IsValid_EnumSet_ValueNotInEnum_ReturnsFalse()
{
Assert.DoesNotThrow(() => WinRegistryEntry<string>.New(TestHive, TestPath, "IsInValid", "Fourth").Write());
var entry = WinRegistryEntry<string>.New(TestHive, TestPath, "IsInValid").SetValidation<TestEnum>().Read();
Assert.That(entry.IsValid, Is.False);
}
[Test]
public void IsValid_EnumSet_CorrectsValueSpellingAndValidatesSuccessfully()
{
Assert.DoesNotThrow(() => WinRegistryEntry<string>.New(TestHive, TestPath, "IsValidCorrectsSpelling", "SecOND").Write());
var entry = WinRegistryEntry<string>.New(TestHive, TestPath, "IsValidCorrectsSpelling").SetValidation<TestEnum>().Read();
Assert.Multiple(() =>
{
Assert.That(entry.IsValid, Is.True);
Assert.That(entry.Value, Is.EqualTo("Second"));
});
}
[Test]
public void IsValid_InvalidValueKind_ThrowArgumentException()
{
Assert.Throws<ArgumentException>(() => WinRegistryEntry<string>.New(TestHive, TestPath, "IsValid", "Windows").SetValueKind(RegistryValueKind.Unknown));
}
[Test]
public void FluentWrite_And_Read_DoesNotThrow_ReturnsStrJustATest()
{
Assert.DoesNotThrow(() => WinRegistryEntry<string>.New(TestHive, TestPath, "FluentReadAndWriteTest", "JustATest").Write());
var entry = WinRegistryEntry<string>.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);
}
}
}

View File

@@ -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<ArgumentNullException>(() => _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<ArgumentException>(() => _registryReader.GetSubKeyNames(new RegistryHive(), "Software"));
var method = GetPrivateStaticMethod("ThrowIfHiveInvalid");
if (method != null)
Assert.Throws<TargetInvocationException>(() => 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<TargetInvocationException>(() => 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<ArgumentNullException>(() => _registryReader.GetPropertyValue(RegistryHive.CurrentUser, null, ""));
var method = GetPrivateStaticMethod("ThrowIfPathInvalid");
if (method != null)
Assert.Throws<TargetInvocationException>(() => 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<ArgumentNullException>(() => _registryReader.GetPropertyValue(RegistryHive.CurrentUser, "", null));
var method = GetPrivateStaticMethod("ThrowIfPathInvalid");
if (method != null)
Assert.Throws<TargetInvocationException>(() => 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<TargetInvocationException>(() => 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 = ""
};
WindowsRegistryKey keyValue = _registryReader.GetWindowsRegistryKey(key);
Assert.That(keyValue.Value, Is.EqualTo("{098f2470-bae0-11cd-b579-08002b30bfeb}"));
var method = GetPrivateStaticMethod("ThrowIfNameInvalid");
if (method != null)
Assert.Throws<TargetInvocationException>(() => method.Invoke(null, new object[] { null }));
else
Assert.Fail("The method ThrowIfNameInvalid could not be found.");
}
[Test]
public void CanGetWindowsRegistryKeyForKeyNotExists()
{
// No exception. Only null value
WindowsRegistryKey keyValue = _registryReader.GetWindowsRegistryKey(RegistryHive.LocalMachine, @"Software\Testabcdefg", "abcdefg");
Assert.That(keyValue.Value, Is.EqualTo(null));
}
[Test]
public void GetWindowsRegistryThrowNotReadable()
{
WindowsRegistryKey key = new()
{
Hive = RegistryHive.ClassesRoot,
};
Assert.Throws<InvalidOperationException>(() => _registryReader.GetWindowsRegistryKey(key));
}
#endregion
#region GetRegistryEntries() + Recurse tests
#region WindowsRegistry.ThrowIfValueKindInvalid()
[Test]
public void CanGetRegistryEntries()
public void ThrowIfValueKindInvalid_ValidValueKind_DoesNotThrow()
{
List<WindowsRegistryKey> keys = _registryReader.GetRegistryEntries(RegistryHive.LocalMachine, @"HARDWARE\DESCRIPTION\System\BIOS");
Assert.That(keys.Count, Is.Not.EqualTo(0));
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 CanGetRegistryEntriesRecurse()
public void ThrowIfValueKindInvalid_InvalidValueKind_ThrowsArgumentException()
{
List<WindowsRegistryKey> keys = _registryReader.GetRegistryEntryiesRecursive(RegistryHive.LocalMachine, @"SOFTWARE\Microsoft\Windows\Windows Search");
Assert.That(keys.Count, Is.Not.EqualTo(0));
var method = GetPrivateStaticMethod("ThrowIfValueKindInvalid");
if (method != null)
Assert.Throws<TargetInvocationException>(() => method.Invoke(null, new object[] { (RegistryValueKind)100 }));
else
Assert.Fail("The method ThrowIfValueKindInvalid could not be found.");
}
[Test]
public void ThrowIfValueKindInvalid_ValueKindUnknown_ThrowsArgumentException()
{
var method = GetPrivateStaticMethod("ThrowIfValueKindInvalid");
if (method != null)
Assert.Throws<TargetInvocationException>(() => 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<TargetInvocationException>(() => method.Invoke(null, new object[] { RegistryValueKind.None }));
else
Assert.Fail("The method ThrowIfValueKindInvalid could not be found.");
}
#endregion
#region new WindowsRegistryKey() tests
#region WindowsRegistry.SetValue()
[Test]
public void IsWindowsRegistryKeyValid()
public void SetValue_NewKey_SetsValueSuccessfully()
{
// Tests property rules of WindowsRegistryKey
WindowsRegistryKey key = new();
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<ArgumentException>(() => 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"));
}
[Test]
public void GetSubKeyNames_NonExistingKey_ReturnsEmptyArray()
{
string[] subKeyNames = GetSubKeyNames(TestHive, @"Software\NonExistingKey");
Assert.That(subKeyNames, Is.Empty);
}
#endregion
#region WindowsRegistry.GetValue()
[Test]
public void GetValue_RetrieveValue_ReturnsValue()
{
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 GetValue_RetrieveDefault_RetrunsValue()
{
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 WindowsRegistry.GetStringValue()
[Test]
public void GetStringValue_RetrieveValue_ReturnsValue()
{
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<string>().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<string>().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<string>().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<bool>().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<bool>().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<bool>().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<bool>().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<bool>().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<int>().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<int>().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<int>().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<ArgumentNullException>(() => 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<ArgumentNullException>(() => 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));
}
[Test]
public void WindowsRegistryKeyThrowNameNullException()
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<List<WinRegistryEntry<string>>>());
foreach (var entry in entries)
{
WindowsRegistryKey key = new();
Assert.Throws<ArgumentNullException>(() => key.Name = null, "Expected IsNameValid to throw ArgumentNullException");
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 GetRegistryEntriesRecursive_ReturnsCorrectEntries()
{
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<List< WinRegistryEntry<string>>> ());
// 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
#region WindowsRegistry.DeleteTree()
[Test]
public void CanSetRegistryValue()
public void DeleteTree_RemovesKeyAndSubkeys()
{
Assert.DoesNotThrow(() => _registryWriter.SetRegistryValue(RegistryHive.CurrentUser, @"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 SetRegistryValueThrowAccessDenied()
public void ConvertStringToRegistryHive_InvalidHiveNull_ThrowArgumentNullException()
{
Assert.Throws<InvalidOperationException>(() => _registryWriter.SetRegistryValue(RegistryHive.LocalMachine, @"SOFTWARE\mRemoteNGTest", "TestKey", "A value string", RegistryValueKind.String));
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<ArgumentNullException>(() => 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);
}
}
}