Merge pull request #2601 from xRushG/RegSettings

Enhancement of Registry Settings capabilities
This commit is contained in:
Dimitrij
2024-06-11 11:21:39 +01:00
committed by GitHub
36 changed files with 3394 additions and 2183 deletions

View File

@@ -6,19 +6,27 @@ namespace mRemoteNG.App.Info
[SupportedOSPlatform("windows")]
public static class WindowsRegistryInfo
{
#region general parameters
#region General Parameters
public const RegistryHive Hive = RegistryHive.LocalMachine;
public const string RootKey = "SOFTWARE\\mRemoteNG";
private const string OptionsSubKey = "Options";
#endregion
#region subkey location parameters
#region Key Locations
// Credential
public const string CredentialSubkey = RootKey + "\\Credentials"; // Registry subkey for general credential settings
public const string CredentialOptionsSubkey = RootKey + "\\Credentials\\Options"; // Registry subkey for credential options within the credential settings
// Registry subkey for general application credentials settings
// Registry subkey for credentials options page settings
public const string Credential = RootKey + "\\Credentials";
public const string CredentialOptions = Credential + "\\" + OptionsSubKey;
// Updates
public const string UpdateSubkey = RootKey + "\\Updates"; // Registry subkey for general update settings
public const string UpdateOptionsSubkey = RootKey + "\\Updates\\Options"; // Registry subkey for update options within the update settings
// Registry subkey for general application update settings
// Registry subkey for updates options page settings
public const string Update = RootKey + "\\Updates";
public const string UpdateOptions = Update + "\\" + OptionsSubKey;
#endregion
}
}

View File

@@ -11,28 +11,23 @@ namespace mRemoteNG.Config.Settings.Registry
/// Benefits: Simplified code, enhances maintainability, and ensures consistency. #ReadOnly
public static class CommonRegistrySettings
{
private static readonly IRegistryAdvancedRead _WindowsRegistry = new WindowsRegistryAdvanced();
private static readonly RegistryHive _Hive = WindowsRegistryInfo.Hive;
private const string __Update = WindowsRegistryInfo.UpdateSubkey;
private const string __Credential = WindowsRegistryInfo.CredentialSubkey;
#region general update registry settings
/// <summary>
/// Indicates whether searching for updates is allowed. If false, there is no way to update directly from mRemoteNG.
/// </summary>
/// <remarks>
/// Default value is true, which allows check for updates.
/// </remarks>
public static bool AllowCheckForUpdates { get; } = _WindowsRegistry.GetBoolValue(_Hive, __Update, nameof(AllowCheckForUpdates), true);
public static bool AllowCheckForUpdates { get; }
/// <summary>
/// Indicates whether automatic search for updates is allowed.
/// </summary>
/// <remarks>
/// Default value is true, which allows check for updates automaticaly.
/// Default value is true, which allows check for updates automatically.
/// </remarks>
public static bool AllowCheckForUpdatesAutomatical { get; } = _WindowsRegistry.GetBoolValue(_Hive, __Update, nameof(AllowCheckForUpdatesAutomatical), AllowCheckForUpdates);
public static bool AllowCheckForUpdatesAutomatical { get; }
/// <summary>
/// Indicates whether a manual search for updates is allowed.
@@ -40,39 +35,65 @@ namespace mRemoteNG.Config.Settings.Registry
/// <remarks>
/// The default value is true, enabling the manual check for updates.
/// </remarks>
public static bool AllowCheckForUpdatesManual { get; } = _WindowsRegistry.GetBoolValue(_Hive, __Update, nameof(AllowCheckForUpdatesManual), AllowCheckForUpdates);
public static bool AllowCheckForUpdatesManual { get; }
/// <summary>
/// Specifies whether a question about checking for updates is displayed at startup.
/// </summary>
public static bool AllowPromptForUpdatesPreference { get; } = _WindowsRegistry.GetBoolValue(_Hive, __Update, nameof(AllowPromptForUpdatesPreference), AllowCheckForUpdates);
public static bool AllowPromptForUpdatesPreference { get; }
#endregion
#region general credential registry settings
/// <summary>
/// Setting that indicates whether exporting passwords is allowed.
/// </summary>
public static bool AllowExportPasswords { get; } = _WindowsRegistry.GetBoolValue(_Hive, __Credential, nameof(AllowExportPasswords), true);
public static bool AllowExportPasswords { get; }
/// <summary>
/// Setting that indicates whether exporting usernames is allowed.
/// </summary>
public static bool AllowExportUsernames { get; } = _WindowsRegistry.GetBoolValue(_Hive, __Credential, nameof(AllowExportUsernames), true);
public static bool AllowExportUsernames { get; }
/// <summary>
/// Setting that indicates whether saving passwords is allowed.
/// Setting that indicates whether saving passwords in connections is allowed.
/// </summary>
public static bool AllowSavePasswords { get; } = _WindowsRegistry.GetBoolValue(_Hive, __Credential, nameof(AllowSavePasswords), true);
public static bool AllowSavePasswords { get; }
/// <summary>
/// Setting that indicates whether saving usernames is allowed.
/// Setting that indicates whether saving in connections usernames is allowed.
/// </summary>
public static bool AllowSaveUsernames { get; } = _WindowsRegistry.GetBoolValue(_Hive, __Credential, nameof(AllowSaveUsernames), true);
public static bool AllowSaveUsernames { get; }
/// <summary>
/// Setting that indicates whether modifying credential settings is allowed.
/// </summary>
public static bool AllowModifyCredentialSettings { get; } = _WindowsRegistry.GetBoolValue(_Hive, __Credential, nameof(AllowModifyCredentialSettings), true);
#endregion
static CommonRegistrySettings()
{
IRegistry regValueUtility = new WinRegistry();
RegistryHive hive = WindowsRegistryInfo.Hive;
#region update registry settings setup
string updateSubkey = WindowsRegistryInfo.Update;
AllowCheckForUpdates = regValueUtility.GetBoolValue(hive, updateSubkey, nameof(AllowCheckForUpdates), true);
AllowCheckForUpdatesAutomatical = regValueUtility.GetBoolValue(hive, updateSubkey, nameof(AllowCheckForUpdatesAutomatical), AllowCheckForUpdates);
AllowCheckForUpdatesManual = regValueUtility.GetBoolValue(hive, updateSubkey, nameof(AllowCheckForUpdatesManual), AllowCheckForUpdates);
AllowPromptForUpdatesPreference = regValueUtility.GetBoolValue(hive, updateSubkey, nameof(AllowPromptForUpdatesPreference), AllowCheckForUpdates);
#endregion
#region credential registry settings setup
string credentialSubkey = WindowsRegistryInfo.Credential;
AllowExportPasswords = regValueUtility.GetBoolValue(hive, credentialSubkey, nameof(AllowExportPasswords), true);
AllowExportUsernames = regValueUtility.GetBoolValue(hive, credentialSubkey, nameof(AllowExportUsernames), true);
AllowSavePasswords = regValueUtility.GetBoolValue(hive, credentialSubkey, nameof(AllowSavePasswords), true);
AllowSaveUsernames = regValueUtility.GetBoolValue(hive, credentialSubkey, nameof(AllowSaveUsernames), true);
#endregion
}
}
}

View File

@@ -9,16 +9,22 @@ namespace mRemoteNG.Config.Settings.Registry
/// Static utility class that provides access to and management of registry settings on the local machine.
/// It abstracts complex registry operations and centralizes the handling of various registry keys.
/// Benefits: Simplified code, enhances maintainability, and ensures consistency. #ReadOnly
public sealed partial class OptRegistryCredentialsPage : WindowsRegistryAdvanced
public sealed partial class OptRegistryCredentialsPage
{
#region option page credential registry settings
/// <summary>
/// Indicates whether modifying credential page settings is enabled.
/// </summary>
public bool CredentialPageEnabled { get; }
/// <summary>
/// Specifies the radio button is set to none, windows or custom on the credentials page.
/// </summary>
/// <remarks>
/// When set to noinfo or windows, WindowsCredentials and CustomCredentials are not evaluated and disabled.
/// </remarks>
public WindowsRegistryKeyString UseCredentials { get; }
public WinRegistryEntry<string> UseCredentials { get; }
/// <summary>
/// Specifies the user set via API as the default username.
@@ -26,7 +32,7 @@ namespace mRemoteNG.Config.Settings.Registry
/// <remarks>
/// Only avaiable if UseCredentials is set to custom!
/// </remarks>
public WindowsRegistryKeyString UserViaAPIDefault { get; }
public WinRegistryEntry<string> UserViaAPIDefault { get; }
/// <summary>
/// Specifies the default username.
@@ -34,7 +40,7 @@ namespace mRemoteNG.Config.Settings.Registry
/// <remarks>
/// Only avaiable if UseCredentials is set to custom!
/// </remarks>
public WindowsRegistryKeyString DefaultUsername { get; }
public WinRegistryEntry<string> DefaultUsername { get; }
/// <summary>
/// Specifies the default password.
@@ -42,7 +48,7 @@ namespace mRemoteNG.Config.Settings.Registry
/// <remarks>
/// Only avaiable if UseCredentials is set to custom!
/// </remarks>
//public WindowsRegistryKeyString DefaultPassword { get; }
public WinRegistryEntry<string> DefaultPassword { get; }
/// <summary>
/// Specifies the default domain.
@@ -50,28 +56,27 @@ namespace mRemoteNG.Config.Settings.Registry
/// <remarks>
/// Only avaiable if UseCredentials is set to custom!
/// </remarks>
public WindowsRegistryKeyString DefaultDomain { get; }
public WinRegistryEntry<string> DefaultDomain { get; }
#endregion
public OptRegistryCredentialsPage()
{
IRegistry regValueUtility = new WinRegistry();
RegistryHive hive = WindowsRegistryInfo.Hive;
string subKey = WindowsRegistryInfo.CredentialOptionsSubkey;
string subKey = WindowsRegistryInfo.CredentialOptions;
UseCredentials = GetStringValidated(hive, subKey, nameof(UseCredentials),
CredentialPageEnabled = regValueUtility.GetBoolValue(hive, subKey, nameof(CredentialPageEnabled), true);
UseCredentials = new WinRegistryEntry<string>(hive, subKey, nameof(UseCredentials)).SetValidation(
new string[] {
"noinfo",
"windows",
"custom"
}, true
);
UserViaAPIDefault = GetString(hive, subKey, nameof(UserViaAPIDefault), null);
DefaultUsername = GetString(hive, subKey, nameof(DefaultUsername), null);
//Currently not supported, but needed for configuration...
//DefaultPassword = GetPassword(hive, subKey, nameof(DefaultPassword));
DefaultDomain = GetString(hive, subKey, nameof(DefaultDomain), null);
}).Read();
UserViaAPIDefault = new WinRegistryEntry<string>(hive, subKey, nameof(UserViaAPIDefault)).Read();
DefaultUsername = new WinRegistryEntry<string>(hive, subKey, nameof(DefaultUsername)).Read();
DefaultPassword = new WinRegistryEntry<string>(hive, subKey, nameof(DefaultPassword)).Read();
DefaultDomain = new WinRegistryEntry<string>(hive, subKey, nameof(DefaultDomain)).Read();
}
}
}

View File

@@ -9,12 +9,12 @@ namespace mRemoteNG.Config.Settings.Registry
/// Static utility class that provides access to and management of registry settings on the local machine.
/// It abstracts complex registry operations and centralizes the handling of various registry keys.
/// Benefits: Simplified code, enhances maintainability, and ensures consistency. #ReadOnly
public sealed partial class OptRegistryUpdatesPage : WindowsRegistryAdvanced
public sealed partial class OptRegistryUpdatesPage
{
/// <summary>
/// Specifies the number of days between update checks.
/// </summary>
public WindowsRegistryKeyInteger CheckForUpdatesFrequencyDays { get; }
public WinRegistryEntry<int> CheckForUpdatesFrequencyDays { get; }
/// <summary>
/// Specifies the update channel for updates.
@@ -22,62 +22,58 @@ namespace mRemoteNG.Config.Settings.Registry
/// <remarks>
/// The update channel should be one of the predefined values: Stable, Preview, Nightly.
/// </remarks>
public WindowsRegistryKeyString UpdateChannel { get; }
public WinRegistryEntry<string> UpdateChannel { get; }
/// <summary>
/// Indicates whether proxy usage for updates is enabled.
/// </summary>
public WindowsRegistryKeyBoolean UseProxyForUpdates { get; }
public WinRegistryEntry<bool> UseProxyForUpdates { get; }
/// <summary>
/// Specifies the proxy address for updates.
/// </summary>
public WindowsRegistryKeyString ProxyAddress { get; }
public WinRegistryEntry<string> ProxyAddress { get; }
/// <summary>
/// Specifies the proxy port for updates.
/// </summary>
public WindowsRegistryKeyInteger ProxyPort { get; }
public WinRegistryEntry<int> ProxyPort { get; }
/// <summary>
/// Indicates whether proxy authentication is enabled.
/// </summary>
public WindowsRegistryKeyBoolean UseProxyAuthentication { get; }
public WinRegistryEntry<bool> UseProxyAuthentication { get; }
/// <summary>
/// Specifies the authentication username for the proxy.
/// </summary>
public WindowsRegistryKeyString ProxyAuthUser { get; }
public WinRegistryEntry<string> ProxyAuthUser { get; }
/// <summary>
/// Specifies the authentication password for the proxy.
/// </summary>
//public string ProxyAuthPass { get; }
public WinRegistryEntry<string> ProxyAuthPass { get; }
public OptRegistryUpdatesPage()
{
RegistryHive hive = WindowsRegistryInfo.Hive;
string subKey = WindowsRegistryInfo.UpdateOptionsSubkey;
string subKey = WindowsRegistryInfo.UpdateOptions;
CheckForUpdatesFrequencyDays = GetInteger(hive, subKey, nameof(CheckForUpdatesFrequencyDays));
UpdateChannel = GetStringValidated(hive, subKey, nameof(UpdateChannel),
new string[] {
CheckForUpdatesFrequencyDays = new WinRegistryEntry<int>(hive, subKey, nameof(CheckForUpdatesFrequencyDays)).Read();
UpdateChannel = new WinRegistryEntry<string>(hive, subKey, nameof(UpdateChannel))
.SetValidation(new string[] {
UpdateChannelInfo.STABLE,
UpdateChannelInfo.PREVIEW,
UpdateChannelInfo.NIGHTLY
}
,true
);
}).Read();
UseProxyForUpdates = GetBoolean(hive, subKey, nameof(UseProxyForUpdates));
ProxyAddress = GetString(hive, subKey, nameof(ProxyAddress), null);
ProxyPort = GetInteger(hive, subKey, nameof(ProxyPort));
UseProxyForUpdates = new WinRegistryEntry<bool>(hive, subKey, nameof(UseProxyForUpdates)).Read();
ProxyAddress = new WinRegistryEntry<string>(hive, subKey, nameof(ProxyAddress)).Read();
ProxyPort = new WinRegistryEntry<int>(hive, subKey, nameof(ProxyPort)).Read();
UseProxyAuthentication = GetBoolean(hive, subKey, nameof(UseProxyAuthentication));
ProxyAuthUser = GetString(hive, subKey, nameof(ProxyAuthUser), null);
//Currently not supported:
//ProxyAuthPass = GetPassword(Hive, _SubKey, nameof(ProxyAuthPass));
UseProxyAuthentication = new WinRegistryEntry<bool>(hive, subKey, nameof(UseProxyAuthentication)).Read();
ProxyAuthUser = new WinRegistryEntry<string>(hive, subKey, nameof(ProxyAuthUser)).Read();
ProxyAuthPass = new WinRegistryEntry<string>(hive, subKey, nameof(ProxyAuthPass)).Read();
}
}
}

View File

@@ -3399,9 +3399,9 @@ namespace mRemoteNG.Resources.Language {
/// <summary>
/// Looks up a localized string similar to *Some settings are determined by your company. For further information, please contact your administrator.
/// </summary>
internal static string OptionsAdminInfo {
internal static string OptionsCompanyPolicyMessage {
get {
return ResourceManager.GetString("OptionsAdminInfo", resourceCulture);
return ResourceManager.GetString("OptionsCompanyPolicyMessage", resourceCulture);
}
}

View File

@@ -2047,7 +2047,7 @@ Nightly umfasst Alphas, Betas und Release Candidates.</value>
<value>Maximale Anmeldeversuche erreicht. Verbindung erneut initiieren.</value>
<comment>C# to Powershell transfer issue with encoding possible</comment>
</data>
<data name="OptionsAdminInfo" xml:space="preserve">
<data name="OptionsCompanyPolicyMessage" xml:space="preserve">
<value>*Einige Einstellungen werden von Ihrem Unternehmen festgelegt. Für weitere Informationen wenden Sie sich an Ihren Systemadministrator.</value>
</data>
</root>

View File

@@ -2292,7 +2292,7 @@ Nightly Channel includes Alphas, Betas &amp; Release Candidates.</value>
<data name="ECPClickstudiosPasswordstate" xml:space="preserve">
<value>Clickstudios Passwordstate</value>
</data>
<data name="ECPNone" xml:space="preserve">
<data name="ECPNone" xml:space="preserve">
<value>None</value>
</data>
<data name="EAPAmazonWebServices" xml:space="preserve">
@@ -2365,7 +2365,7 @@ Nightly Channel includes Alphas, Betas &amp; Release Candidates.</value>
<data name="FilterSecureCRT" xml:space="preserve">
<value>SecureCRT (*.xml)</value>
</data>
<data name="OptionsAdminInfo" xml:space="preserve">
<data name="OptionsCompanyPolicyMessage" xml:space="preserve">
<value>*Some settings are determined by your company. For further information, please contact your administrator</value>
</data>
</root>

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

@@ -1,55 +0,0 @@
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Runtime.Versioning;
namespace mRemoteNG.Tools.WindowsRegistry
{
[SupportedOSPlatform("windows")]
/// <summary>
/// Interface for the RegistryWorker class providing functionality to interact with the Windows Registry to retrieve registry settings.
/// </summary>
public interface IRegistryAdvanced
{
#region WindowsRegistry reader
string[] GetSubKeyNames(RegistryHive hive, string path);
string GetPropertyValue(WindowsRegistryKey key);
string GetPropertyValue(RegistryHive hive, string path, string name);
bool GetBoolValue(RegistryHive hive, string path, string propertyName, bool defaultValue = false);
int GetDwordValue(RegistryHive hive, string path, string propertyName, int defaultValue = 0);
WindowsRegistryKey GetWindowsRegistryKey(RegistryHive hive, string path, string name);
WindowsRegistryKey GetWindowsRegistryKey(WindowsRegistryKey key);
List<WindowsRegistryKey> GetRegistryEntries(RegistryHive hive, string path);
List<WindowsRegistryKey> GetRegistryEntryiesRecursive(RegistryHive hive, string path);
#endregion
#region WindowsRegistry 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);
#endregion
#region WindowsRegistry tools
RegistryHive ConvertStringToRegistryHive(string hiveString);
RegistryValueKind ConvertStringToRegistryValueKind(string valueType);
RegistryValueKind ConvertTypeToRegistryValueKind(Type valueType);
Type ConvertRegistryValueKindToType(RegistryValueKind valueKind);
#endregion
#region WindowsRegistryAdvanced GetInteger
WindowsRegistryKeyInteger GetInteger(RegistryHive hive, string path, string propertyName, int? defaultValue = null);
#endregion
#region WindowsRegistryAdvanced GetString
WindowsRegistryKeyString GetString(RegistryHive hive, string path, string propertyName, string defaultValue = null);
WindowsRegistryKeyString GetStringValidated(RegistryHive hive, string path, string propertyName, string[] allowedValues, bool caseSensitive = false, string defaultValue = null);
#endregion
#region WindowsRegistryAdvanced GetBoolean
WindowsRegistryKeyBoolean GetBoolean(RegistryHive hive, string path, string propertyName, bool? defaultValue = null);
#endregion
}
}

View File

@@ -1,49 +0,0 @@
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Runtime.Versioning;
namespace mRemoteNG.Tools.WindowsRegistry
{
[SupportedOSPlatform("windows")]
/// <summary>
/// Interface for the RegistryWorker class providing functionality to interact with the Windows Registry to retrieve registry settings.
/// </summary>
public interface IRegistryAdvancedRead
{
#region WindowsRegistry reader
string[] GetSubKeyNames(RegistryHive hive, string path);
string GetPropertyValue(WindowsRegistryKey key);
string GetPropertyValue(RegistryHive hive, string path, string name);
bool GetBoolValue(RegistryHive hive, string path, string propertyName, bool defaultValue = false);
int GetDwordValue(RegistryHive hive, string path, string propertyName, int defaultValue = 0);
WindowsRegistryKey GetWindowsRegistryKey(RegistryHive hive, string path, string name);
WindowsRegistryKey GetWindowsRegistryKey(WindowsRegistryKey key);
List<WindowsRegistryKey> GetRegistryEntries(RegistryHive hive, string path);
List<WindowsRegistryKey> GetRegistryEntryiesRecursive(RegistryHive hive, string path);
#endregion
#region WindowsRegistry tools
RegistryHive ConvertStringToRegistryHive(string hiveString);
RegistryValueKind ConvertStringToRegistryValueKind(string valueType);
RegistryValueKind ConvertTypeToRegistryValueKind(Type valueType);
Type ConvertRegistryValueKindToType(RegistryValueKind valueKind);
#endregion
#region WindowsRegistryAdvanced GetInteger
WindowsRegistryKeyInteger GetInteger(RegistryHive hive, string path, string propertyName, int? defaultValue = null);
#endregion
#region WindowsRegistryAdvanced GetString
WindowsRegistryKeyString GetString(RegistryHive hive, string path, string propertyName, string defaultValue = null);
WindowsRegistryKeyString GetStringValidated(RegistryHive hive, string path, string propertyName, string[] allowedValues, bool caseSensitive = false, string defaultValue = null);
#endregion
#region WindowsRegistryAdvanced GetBoolean
WindowsRegistryKeyBoolean GetBoolean(RegistryHive hive, string path, string propertyName, bool? defaultValue = null);
#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

@@ -1,26 +0,0 @@
using Microsoft.Win32;
using System;
using System.Runtime.Versioning;
namespace mRemoteNG.Tools.WindowsRegistry
{
[SupportedOSPlatform("windows")]
/// <summary>
/// Interface for the Registry class providing methods for interacting with write actions in the Windows Registry.
/// </summary>
public interface IRegistryWrite
{
#region registry writer
void SetRegistryValue(WindowsRegistryKey key);
void SetRegistryValue(RegistryHive hive, string path, string name, object value, RegistryValueKind valueKind);
#endregion
#region registry tools
RegistryHive ConvertStringToRegistryHive(string hiveString);
RegistryValueKind ConvertStringToRegistryValueKind(string valueType);
RegistryValueKind ConvertTypeToRegistryValueKind(Type valueType);
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

@@ -1,531 +0,0 @@
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.Versioning;
using System.Security;
namespace mRemoteNG.Tools.WindowsRegistry
{
/// <summary>
/// This class interacting with the Windows Registry.
/// </summary>
[SupportedOSPlatform("windows")]
public class WindowsRegistry : IRegistry, IRegistryRead, IRegistryWrite
{
#region public read
/// <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)
{
if (hive == 0)
throw new ArgumentException("Unknown or unsupported RegistryHive value.", nameof(hive));
path.ThrowIfNull(nameof(path));
using (DisposableOptional<RegistryKey> key = OpenSubKey(hive, path))
{
return key.Any()
? key.First().GetSubKeyNames()
: new string[0];
}
}
/// <summary>
/// Retrieves the value of a specific property within a Windows Registry key and returns it as an Optional<string>.
/// </summary>
/// <param name="key">The WindowsRegistryKey containing information about the registry property.</param>
/// <returns>An Optional<string> containing the property value, or Optional<string>.Empty if the value is not found.</returns>
public string GetPropertyValue(WindowsRegistryKey key)
{
if (!key.IsKeyReadable())
throw new InvalidOperationException("The Windows Registry key is not ready for reading.");
return GetPropertyValue(key.Hive, key.Path, key.Name);
}
/// <summary>
/// Retrieves the value of a specific property within the Windows Registry and returns it as an Optional<string>.
/// </summary>
/// <param name="hive">The RegistryHive where the property is located.</param>
/// <param name="path">The path to the registry key containing the property.</param>
/// <param name="name">The name of the property to retrieve.</param>
/// <returns>An Optional<string> containing the property value, or Optional<string>.Empty if the value is not found.</returns>
public string GetPropertyValue(RegistryHive hive, string path, string name)
{
if (hive == 0)
throw new ArgumentException("Unknown or unsupported RegistryHive value.", nameof(hive));
path.ThrowIfNull(nameof(path));
name.ThrowIfNull(nameof(name));
using (DisposableOptional<RegistryKey> key = OpenSubKey(hive, path))
{
if (!key.Any())
return null;
object keyValue = key.First().GetValue(name);
if (keyValue == null)
return null;
return keyValue.ToString();
}
}
/// <summary>
/// Gets a boolean value from the Windows Registry based on the specified registry path and property name.
/// If the value is not found or cannot be parsed, it returns a specified default value.
/// </summary>
/// <param name="hive">The Registry hive where the value is located.</param>
/// <param name="path">The registry path to the key containing the property.</param>
/// <param name="propertyName">The name of the property to retrieve.</param>
/// <param name="defaultValue">The default value to return if the property is not found or cannot be parsed. Default is false.</param>
/// <returns>The boolean value of the specified property or the default value if not found or cannot be parsed.</returns>
public bool GetBoolValue(RegistryHive hive, string path, string propertyName, bool defaultValue = false)
{
string value = GetPropertyValue(hive, path, propertyName);
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 a DWORD value from the Windows Registry, with an optional default value.
/// </summary>
/// <param name="hive">The Registry hive to access.</param>
/// <param name="path">The Registry path containing the key.</param>
/// <param name="propertyName">The name of the Registry property.</param>
/// <param name="defaultValue">The default value to return if the property is not found or cannot be parsed.</param>
/// <returns>The DWORD value from the Registry, or the specified default value.</returns>
public int GetDwordValue(RegistryHive hive, string path, string propertyName, int defaultValue = 0)
{
string value = GetPropertyValue(hive, path, propertyName);
if (int.TryParse(value, out int intValue))
{
return intValue;
}
return defaultValue;
}
/// <summary>
/// Retrieves a WindowsRegistryKey object 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 WindowsRegistryKey object representing the specified registry key and value.</returns>
public WindowsRegistryKey GetWindowsRegistryKey(RegistryHive hive, string path, string name)
{
WindowsRegistryKey key = new()
{
Hive = hive,
Path = path,
Name = name
};
return GetWindowsRegistryKey(key);
}
/// <summary>
/// Retrieves a WindowsRegistryKey object for a specific WindowsRegistryKey, populating its value and value kind.
/// </summary>
/// <param name="key">A WindowsRegistryKey object containing the hive, path, value name and more.</param>
/// <returns>A WindowsRegistryKey object representing the specified registry key and value, with value and value kind populated.</returns>
public WindowsRegistryKey GetWindowsRegistryKey(WindowsRegistryKey key)
{
if (!key.IsKeyReadable())
throw new InvalidOperationException("The Windows Registry key is not ready for reading.");
using (RegistryKey baseKey = RegistryKey.OpenBaseKey(key.Hive, RegistryView.Default), subKey = baseKey.OpenSubKey(key.Path))
{
if (subKey != null)
{
object value = subKey.GetValue(key.Name);
if (value != null)
key.Value = value.ToString();
if (TestValueKindExists(subKey, key.Name, out RegistryValueKind ValueKind))
key.ValueKind = ValueKind;
}
}
return key;
}
/// <summary>
/// Retrieves a list of registry entries (properties) and their values under a given key path.
/// </summary>
/// <param name="hive">The RegistryHive of the key path.</param>
/// <param name="path">The path of the key from which to retrieve values.</param>
/// <returns>A list of WindowsRegistryKey objects, each representing a value within the specified registry key path.</returns>
public List<WindowsRegistryKey> GetRegistryEntries(RegistryHive hive, string path)
{
if (hive == 0)
throw new ArgumentException("Unknown or unsupported RegistryHive value.", nameof(hive));
path.ThrowIfNull(nameof(path));
List<WindowsRegistryKey> list = [];
using (RegistryKey baseKey = RegistryKey.OpenBaseKey(hive, RegistryView.Default), key = baseKey.OpenSubKey(path))
{
if (key != null)
{
foreach (string valueName in key.GetValueNames())
{
list.Add(new WindowsRegistryKey
{
Hive = hive,
Path = path,
Name = valueName,
Value = key.GetValue(valueName).ToString(),
ValueKind = key.GetValueKind(valueName)
}
);
}
}
}
return list;
}
/// <summary>
/// Recursively retrieves registry entries under a given key path and its subkeys.
/// </summary>
/// <param name="hive">The RegistryHive of the key path.</param>
/// <param name="path">The path of the key from which to retrieve values.</param>
/// <returns>A list of WindowsRegistryKey objects, each representing a value within the specified registry key path.</returns>
public List<WindowsRegistryKey> GetRegistryEntryiesRecursive(RegistryHive hive, string path)
{
if (hive == 0)
throw new ArgumentException("Unknown or unsupported RegistryHive value.", nameof(hive));
path.ThrowIfNull(nameof(path));
List<WindowsRegistryKey> list = GetRegistryEntries(hive, path);
using (RegistryKey baseKey = RegistryKey.OpenBaseKey(hive, RegistryView.Default), key = baseKey.OpenSubKey(path))
{
if (key != null)
{
foreach (string subPathName in key.GetSubKeyNames())
{
string subKey = $"{path}\\{subPathName}";
list.AddRange(GetRegistryEntryiesRecursive(hive, subKey));
}
}
}
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 path to the registry key containing the property.</param>
/// <param name="name">The name of the property to set.</param>
/// <param name="value">The value to set for the property.</param>
/// <param name="valueKind">The data type of the value to set.</param>
public void SetRegistryValue(RegistryHive hive, string path, string name, object value, RegistryValueKind valueKind)
{
WindowsRegistryKey key = (new WindowsRegistryKey
{
Hive = hive,
Path = path,
Name = name,
Value = value.ToString(),
ValueKind = valueKind
});
CreateOrSetRegistryValue(key);
}
/// <summary>
/// Sets the value of a specific property within a registry key using the provided WindowsRegistryKey object.
/// </summary>
/// <param name="key">The WindowsRegistryKey object containing information about the registry property.</param>
public void SetRegistryValue(WindowsRegistryKey key)
{
CreateOrSetRegistryValue(key);
}
/// <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>
/// <param name="ignoreNotFound">Set to true to ignore if the key is not found.</param>
public void DeleteRegistryKey(RegistryHive hive, string path, bool ignoreNotFound = false)
{
try
{
using (RegistryKey key = RegistryKey.OpenBaseKey(hive, RegistryView.Default))
{
if (key != null)
{
key.DeleteSubKeyTree(path, ignoreNotFound);
}
}
}
catch (Exception ex)
{
// Handle any exceptions according to your requirements
Console.WriteLine($"Error deleting registry key: {ex.Message}");
throw;
}
}
#endregion
#region public 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)
{
switch (hiveString.ToLower())
{
// ClassesRoot
case "hkcr":
case "hkey_classes_root":
case "classesroot":
return RegistryHive.ClassesRoot;
// CurrentUser
case "hkcu":
case "hkey_current_user":
case "currentuser":
return RegistryHive.CurrentUser;
// LocalMachine
case "hklm":
case "hkey_local_machine":
case "localmachine":
return RegistryHive.LocalMachine;
// Users
case "hku":
case "hkey_users":
case "users":
return RegistryHive.Users;
// CurrentConfig
case "hkcc":
case "hkey_current_config":
case "currentconfig":
return RegistryHive.CurrentConfig;
default:
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)
{
switch (valueType.ToLower())
{
// REG_SZ
case "string":
case "reg_sz":
return RegistryValueKind.String;
// REG_DWORD
case "dword":
case "reg_dword":
return RegistryValueKind.DWord;
// REG_BINARY
case "binary":
case "reg_binary":
return RegistryValueKind.Binary;
// REG_QWORD
case "qword":
case "reg_qword":
return RegistryValueKind.QWord;
// REG_MULTI_SZ
case "multistring":
case "reg_multi_sz":
return RegistryValueKind.MultiString;
// REG_EXPAND_SZ
case "expandstring":
case "reg_expand_sz":
return RegistryValueKind.ExpandString;
default:
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)
{
switch (Type.GetTypeCode(valueType))
{
case TypeCode.String:
return RegistryValueKind.String;
case TypeCode.Int32:
return RegistryValueKind.DWord;
case TypeCode.Int64:
return RegistryValueKind.QWord;
case TypeCode.Boolean:
return RegistryValueKind.DWord;
case TypeCode.Byte:
return RegistryValueKind.Binary;
/*
case TypeCode.Single:
return RegistryValueKind;
case TypeCode.Double:
return RegistryValueKind.String;
case TypeCode.DateTime:
return RegistryValueKind.String; // DateTime can be stored as a string or other types
case TypeCode.Char:
return RegistryValueKind.String; // Char can be stored as a string or other types
case TypeCode.Decimal:
return RegistryValueKind.String; // Decimal can be stored as a string or other types
*/
default:
return 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)
{
switch (valueKind)
{
case RegistryValueKind.String:
case RegistryValueKind.ExpandString:
return typeof(string);
case RegistryValueKind.DWord:
return typeof(int);
case RegistryValueKind.QWord:
return typeof(long);
case RegistryValueKind.Binary:
return typeof(byte[]);
case RegistryValueKind.MultiString:
return typeof(string[]);
case RegistryValueKind.Unknown:
default:
return typeof(object);
}
}
#endregion
#region private methods
/// <summary>
/// Opens a subkey within the Windows Registry under the specified hive and key path.
/// </summary>
/// <param name="hive">The Windows Registry hive where the subkey is located.</param>
/// <param name="keyPath">The path to the subkey to be opened.</param>
/// <returns>
/// A disposable optional object containing the opened registry subkey if successful;
/// otherwise, it returns an empty optional object.
/// </returns>
private DisposableOptional<RegistryKey> OpenSubKey(RegistryHive hive, string keyPath)
{
switch (hive)
{
case RegistryHive.ClassesRoot:
return new DisposableOptional<RegistryKey>(Registry.ClassesRoot.OpenSubKey(keyPath));
case RegistryHive.CurrentConfig:
return new DisposableOptional<RegistryKey>(Registry.CurrentConfig.OpenSubKey(keyPath));
case RegistryHive.CurrentUser:
return new DisposableOptional<RegistryKey>(Registry.CurrentUser.OpenSubKey(keyPath));
case RegistryHive.Users:
return new DisposableOptional<RegistryKey>(Registry.Users.OpenSubKey(keyPath));
case RegistryHive.LocalMachine:
return new DisposableOptional<RegistryKey>(Registry.LocalMachine.OpenSubKey(keyPath));
default:
throw new ArgumentOutOfRangeException(nameof(hive), hive, null);
}
}
/// <summary>
/// Attempts to retrieve the value kind of a specific property within a registry subkey.
/// </summary>
/// <param name="subKey">The registry subkey from which to retrieve the value kind.</param>
/// <param name="valueName">The name of the property for which to retrieve the value kind.</param>
/// <param name="valueKind">An output parameter that will contain the retrieved value kind, or RegistryValueKind.Unknown if the property or value kind is not found.</param>
/// <returns>True if the operation is successful, otherwise false.</returns>
private bool TestValueKindExists(RegistryKey subKey, string valueName, out RegistryValueKind valueKind)
{
// Set a default value
valueKind = RegistryValueKind.Unknown;
try
{
valueKind = subKey.GetValueKind(valueName);
return true;
}
catch (Exception)
{
return false;
}
}
/// <summary>
/// Creates or sets the value of a specific property within a registry key.
/// </summary>
/// <param name="key">The WindowsRegistryKey object containing information about the registry property.</param>
/// <exception cref="InvalidOperationException">Thrown when error by writing to the Windows Registry key.</exception>
private void CreateOrSetRegistryValue(WindowsRegistryKey key)
{
try
{
if (!key.IsKeyWritable())
throw new ArgumentNullException("The Windows Registry key is not ready for writing.");
using RegistryKey baseKey = RegistryKey.OpenBaseKey(key.Hive, RegistryView.Default);
RegistryKey registryKey = baseKey.OpenSubKey(key.Path, true);
if (registryKey == null)
{
// The specified subkey doesn't exist, so create it.
using RegistryKey newKey = baseKey.CreateSubKey(key.Path);
newKey.SetValue(key.Name, key.Value, key.ValueKind);
}
else
{
registryKey.SetValue(key.Name, key.Value, key.ValueKind);
}
}
catch (Exception ex)
{
throw new InvalidOperationException("Error writing to the Windows Registry key.", ex);
}
}
#endregion
}
}

View File

@@ -1,118 +0,0 @@
using Microsoft.Win32;
using mRemoteNG.App.Info;
using mRemoteNG.Security.SymmetricEncryption;
using System.Runtime.Versioning;
namespace mRemoteNG.Tools.WindowsRegistry
{
[SupportedOSPlatform("windows")]
/// <summary>
/// Extends the functionality of interacting with the Windows Registry, building upon the base WindowsRegistry class.
/// </summary>
public class WindowsRegistryAdvanced : WindowsRegistry , IRegistryAdvanced, IRegistryAdvancedRead
{
#region dword methods
/// <summary>
/// Retrieves a DWORD (32-bit integer) value from the Windows Registry based on the specified registry information.
/// </summary>
/// <param name="hive">The registry hive.</param>
/// <param name="path">The path to the registry key.</param>
/// <param name="propertyName">The name of the registry property.</param>
/// <param name="defaultValue">Optional default value to be used if the registry key is not present (default is null).</param>
/// <returns>A WindowsRegistryKeyInteger instance representing the retrieved DWORD value.</returns>
public WindowsRegistryKeyInteger GetInteger(RegistryHive hive, string path, string propertyName, int? defaultValue = null)
{
// Retrieve the Windows Registry key
WindowsRegistryKey key = GetWindowsRegistryKey(hive, path, propertyName);
// Create a WindowsRegistryKeyInteger instance and initialize it from the retrieved key
WindowsRegistryKeyInteger IntKey = new();
IntKey.ConvertFromWindowsRegistryKey(key);
return IntKey;
}
#endregion
#region string methods
/// <summary>
/// Retrieves a string value from the Windows Registry based on the specified registry information.
/// </summary>
/// <param name="hive">The registry hive.</param>
/// <param name="path">The path to the registry key.</param>
/// <param name="propertyName">The name of the registry property.</param>
/// <param name="defaultValue">Optional default value to be used if the registry key is not present (default is null).</param>
/// <returns>A WindowsRegistryKeyString instance representing the retrieved string value.</returns>
public WindowsRegistryKeyString GetString(RegistryHive hive, string path, string propertyName, string defaultValue = null)
{
// Retrieve the Windows Registry key
WindowsRegistryKey key = GetWindowsRegistryKey(hive, path, propertyName);
// Create a WindowsRegistryKeyString instance and initialize it from the retrieved key
WindowsRegistryKeyString StrKey = new();
StrKey.ConvertFromWindowsRegistryKey(key, defaultValue);
return StrKey;
}
/// <summary>
/// Retrieves a string value from the Windows Registry based on the specified registry information and validates it against a set of allowed values.
/// </summary>
/// <param name="hive">The registry hive.</param>
/// <param name="path">The path to the registry key.</param>
/// <param name="propertyName">The name of the registry property.</param>
/// <param name="allowedValues">An array of valid values against which the retrieved string is validated.</param>
/// <param name="caseSensitive">Optional parameter indicating whether the validation is case-sensitive (default is false).</param>
/// <returns>A WindowsRegistryKeyString instance representing the retrieved and validated string value.</returns>
public WindowsRegistryKeyString GetStringValidated(RegistryHive hive, string path, string propertyName, string[] allowedValues, bool caseSensitive = false, string defaultValue = null)
{
// Retrieve the Windows Registry key
WindowsRegistryKey key = GetWindowsRegistryKey(hive, path, propertyName);
// Create a WindowsRegistryKeyString instance and initialize it from the retrieved key
WindowsRegistryKeyString StrKey = new()
{
AllowedValues = allowedValues,
IsCaseSensitiveValidation = caseSensitive
};
StrKey.ConvertFromWindowsRegistryKey(key, defaultValue);
return StrKey;
}
/*public WindowsRegistryKeySecureString GetSecureString(RegistryHive hive, string path, string propertyName)
{
// Retrieve the Windows Registry key, the key should be encrypted
var key = GetWindowsRegistryKey(hive, path, propertyName);
// Create a WindowsRegistryKeyBoolean instance and initialize it from the retrieved key
WindowsRegistryKeySecureString secureKey = new (); // class not exsists
secureKey.ConvertFromWindowsRegistryKey(key); // no default possible!
return secureKey
}*/
#endregion
#region bool methods
/// <summary>
/// Retrieves a boolean value from the Windows Registry based on the specified registry information.
/// </summary>
/// <param name="hive">The registry hive.</param>
/// <param name="path">The path to the registry key.</param>
/// <param name="propertyName">The name of the registry property.</param>
/// <param name="defaultValue">An optional default value to use if the registry key is not present or if the value is not a valid boolean.</param>
/// <returns>A WindowsRegistryKeyBoolean instance representing the retrieved boolean value.</returns>
public WindowsRegistryKeyBoolean GetBoolean(RegistryHive hive, string path, string propertyName, bool? defaultValue = null)
{
// Retrieve the Windows Registry key
WindowsRegistryKey key = GetWindowsRegistryKey(hive, path, propertyName);
// Create a WindowsRegistryKeyBoolean instance and initialize it from the retrieved key
WindowsRegistryKeyBoolean boolKey = new ();
boolKey.ConvertFromWindowsRegistryKey(key, defaultValue);
return boolKey;
}
#endregion
}
}

View File

@@ -1,116 +0,0 @@
using System;
using System.Runtime.Versioning;
using Microsoft.Win32;
namespace mRemoteNG.Tools.WindowsRegistry
{
/// <summary>
/// Represents a Windows Registry key with a default string value, providing a flexible abstraction for registry operations.
/// This class can be extended by inherited classes to customize behavior for specific data types.
/// </summary>
[SupportedOSPlatform("windows")]
public class WindowsRegistryKey
{
#region properties
#region Property registryHive
public RegistryHive Hive
{
get { return _Hive; }
set
{
if (value == 0)
throw new ArgumentNullException("RegistryHive unknown.");
_Hive = value;
}
}
private RegistryHive _Hive { get; set; }
#endregion
#region Property path
public string Path
{
get { return _Path; }
set
{
value.ThrowIfNull(nameof(value));
_Path = value;
}
}
private string _Path { get; set; }
#endregion
#region Property name
public string Name
{
get { return _Name; }
set
{
value.ThrowIfNull(nameof(value));
_Name = value;
}
}
private string _Name { get; set; }
#endregion
#region Property value
public virtual string Value
{
get => _value;
set
{
_value = value;
UpdateIsProvidedState();
}
}
private string _value;
#endregion
public RegistryValueKind ValueKind { get; set; } = RegistryValueKind.Unknown;
public bool IsKeyPresent { get; set; } = false;
#endregion
#region public methods
/// <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 IsKeyReadable() {
return (IsHiveSet() && IsPathSet() && IsNameSet());
}
/// <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
/// - The value name is set
/// </summary>
/// <returns>Returns true if the key is write-ready, otherwise false.</returns>
public bool IsKeyWritable() {
return (IsHiveSet() && IsValueKindSet() && IsPathSet() && IsNameSet());
}
#endregion
#region protected methods
protected void UpdateIsProvidedState()
{
// Key is present when RegistryKey value is not null
IsKeyPresent = Value != null;
}
protected bool IsHiveSet() => Hive != 0;
protected bool IsValueKindSet() => ValueKind != 0;
protected bool IsPathSet() => Path != null;
protected bool IsNameSet() => Name != null;
#endregion
}
}

View File

@@ -1,75 +0,0 @@
using System;
using System.Runtime.Versioning;
namespace mRemoteNG.Tools.WindowsRegistry
{
/// <summary>
/// Represents a boolean Windows Registry key, extending the base class WindowsRegistryKey.
/// </summary>
[SupportedOSPlatform("windows")]
public class WindowsRegistryKeyBoolean : WindowsRegistryKey
{
/// <summary>
/// Gets or sets the default boolean value for a Windows Registry key.
/// </summary>
/// <remarks>
/// The default value is initially set to `false`.
/// </remarks>
public bool DefaultValue { get; private set; } = false;
/// <summary>
/// Gets or sets the boolean value for a Windows Registry key.
/// </summary>
public new bool Value
{
get => BoolValue;
private set
{
BoolValue = value;
UpdateIsProvidedState();
}
}
private bool BoolValue;
/// <summary>
/// Converts and initializes a WindowsRegistryKeyBoolean from a base WindowsRegistryKey, with an optional default value.
/// </summary>
/// <param name="baseKey">The base WindowsRegistryKey to convert from.</param>
/// <param name="defaultValue">Optional default value to set for the WindowsRegistryKeyBoolean.</param>
public void ConvertFromWindowsRegistryKey(WindowsRegistryKey baseKey, bool? defaultValue = null)
{
SetDefaultValue(defaultValue);
FromBaseKey(baseKey);
}
private void FromBaseKey(WindowsRegistryKey baseKey)
{
if (baseKey == null)
throw new ArgumentNullException(nameof(baseKey));
Hive = baseKey.Hive;
Path = baseKey.Path;
Name = baseKey.Name;
ValueKind = baseKey.ValueKind;
IsKeyPresent = baseKey.IsKeyPresent;
ConvertToBool(baseKey.Value);
}
private void SetDefaultValue(bool? defaultValue)
{
if (defaultValue.HasValue)
DefaultValue = (bool)defaultValue;
}
private void ConvertToBool(string newValue)
{
if (IsKeyPresent && bool.TryParse(newValue, out bool boolValue))
BoolValue = boolValue;
else if (IsKeyPresent && int.TryParse(newValue, out int intValue))
BoolValue = intValue == 1;
else
BoolValue = DefaultValue;
}
}
}

View File

@@ -1,78 +0,0 @@
using Microsoft.Win32;
using System;
using System.Runtime.Versioning;
namespace mRemoteNG.Tools.WindowsRegistry
{
/// <summary>
/// Represents a integer Windows Registry key, extending the base class WindowsRegistryKey.
/// </summary>
[SupportedOSPlatform("windows")]
public class WindowsRegistryKeyInteger : WindowsRegistryKey
{
/// <summary>
/// Gets or sets the default integer value for a Windows Registry key.
/// </summary>
/// <remarks>
/// The default value is initially set to `-1`.
/// </remarks>
public int DefaultValue { get; private set; } = -1;
/// <summary>
/// Gets or sets the integer value for a Windows Registry key.
/// </summary>
public new int Value
{
get => IntegerValue;
private set
{
IntegerValue = value;
UpdateIsProvidedState();
}
}
private int IntegerValue;
/// <summary>
/// Converts and initializes a WindowsRegistryKeyInteger from a base WindowsRegistryKey, with an optional default value.
/// </summary>
/// <param name="baseKey">The base WindowsRegistryKey to convert from.</param>
/// <param name="defaultValue">Optional default value to set for the WindowsRegistryKeyBoolean.</param>
public void ConvertFromWindowsRegistryKey(WindowsRegistryKey baseKey, int? defaultValue = null)
{
SetDefaultValue(defaultValue);
FromBaseKey(baseKey);
}
private void FromBaseKey(WindowsRegistryKey baseKey)
{
if (baseKey == null)
throw new ArgumentNullException(nameof(baseKey));
Hive = baseKey.Hive;
Path = baseKey.Path;
Name = baseKey.Name;
ValueKind = baseKey.ValueKind;
IsKeyPresent = baseKey.IsKeyPresent;
ConvertToInteger(baseKey.Value);
}
private void SetDefaultValue (int? defaultValue)
{
if (defaultValue.HasValue)
DefaultValue = (int)defaultValue;
}
private void ConvertToInteger(string newValue)
{
if (ValueKind != RegistryValueKind.DWord)
IsKeyPresent = false;
if (IsKeyPresent && int.TryParse(newValue.ToString(), out int intValue))
IntegerValue = intValue;
else
IntegerValue = DefaultValue;
}
}
}

View File

@@ -1,121 +0,0 @@
using Microsoft.Win32;
using System;
using System.Linq;
using System.Runtime.Versioning;
namespace mRemoteNG.Tools.WindowsRegistry
{
/// <summary>
/// Represents a string Windows Registry key, extending the base class WindowsRegistryKey, can be evaluated with a value set.
/// </summary>
[SupportedOSPlatform("windows")]
public class WindowsRegistryKeyString : WindowsRegistryKey
{
/// <summary>
/// Gets or sets the default integer value for a Windows Registry key.
/// </summary>
public string DefaultValue { get; private set; }
/// <summary>
/// Gets or sets an array of allowed values for validation.
/// </summary>
public string[] AllowedValues { get; set; }
/// <summary>
/// Gets or sets a boolean flag indicating whether validation is case-sensitive.
/// </summary>
public bool IsCaseSensitiveValidation { get; set; } = false;
/// <summary>
/// Gets a boolean indicating whether the value is valid based on the validation rules.
/// </summary>
public bool IsValid { get; private set; } = false;
public new string Value
{
get => StringValue;
private set
{
StringValue = value;
UpdateIsProvidedState();
Validate();
}
}
private string StringValue;
/// <summary>
/// Converts and initializes a WindowsRegistryKeyString from a base WindowsRegistryKey, with an optional default value.
/// </summary>
/// <param name="baseKey">The base WindowsRegistryKey to convert from.</param>
/// <param name="defaultValue">Optional default value to set for the WindowsRegistryKeyBoolean.</param>
public void ConvertFromWindowsRegistryKey(WindowsRegistryKey baseKey, string defaultValue = null)
{
SetDefaultValue(defaultValue);
FromBaseKey(baseKey);
Validate();
}
private void FromBaseKey(WindowsRegistryKey baseKey)
{
if (baseKey == null)
throw new ArgumentNullException(nameof(baseKey));
Hive = baseKey.Hive;
Path = baseKey.Path;
Name = baseKey.Name;
ValueKind = baseKey.ValueKind;
IsKeyPresent = baseKey.IsKeyPresent;
ConvertToString(baseKey.Value);
}
private void SetDefaultValue (string defaultValue)
{
DefaultValue = defaultValue;
}
private void ConvertToString(string newValue)
{
if (IsKeyPresent && newValue != null)
StringValue = newValue;
else
StringValue = DefaultValue;
}
/// <summary>
/// Validates a Windows Registry key value against a set of allowed values, considering case sensitivity.
/// </summary>
/// <param name="allowedValues">Array of allowed values.</param>
/// <param name="caseSensitive">Optional parameter to specify case sensitivity in validation.</param>
public void Validate(string[] allowedValues = null, bool? caseSensitive = null)
{
// Key must be present to evaluate
if (!IsKeyPresent)
return;
if (caseSensitive.HasValue)
IsCaseSensitiveValidation = caseSensitive.Value;
if (allowedValues != null && allowedValues.Length >= 1)
AllowedValues = allowedValues;
// AllowedValues array cannot be null or empty.
if (AllowedValues == null || AllowedValues.Length == 0 || !IsKeyPresent)
return;
if (IsKeyPresent && AllowedValues.Any(v =>
IsCaseSensitiveValidation ? v == Value : v.Equals(Value, StringComparison.OrdinalIgnoreCase)))
{
// Set to true when the value is found in the valid values
IsValid = true;
}
else
{
// Set to false when the value is not found in the valid values
IsValid = false;
StringValue = DefaultValue;
}
}
}
}

View File

@@ -43,7 +43,7 @@
lblCredentialsDomain = new Controls.MrngLabel();
radCredentialsNoInfo = new Controls.MrngRadioButton();
radCredentialsCustom = new Controls.MrngRadioButton();
lblCredentialsAdminInfo = new System.Windows.Forms.Label();
lblRegistrySettingsUsedInfo = new System.Windows.Forms.Label();
lblCredentialsGeneratorHelp = new Controls.MrngLabel();
btnCredentialsGenerator = new System.Windows.Forms.Button();
lblCredentialsGenerator = new Controls.MrngLabel();
@@ -56,7 +56,7 @@
// pnlDefaultCredentials
//
pnlDefaultCredentials.Controls.Add(pnlCredentialsSettingsPanel);
pnlDefaultCredentials.Controls.Add(lblCredentialsAdminInfo);
pnlDefaultCredentials.Controls.Add(lblRegistrySettingsUsedInfo);
pnlDefaultCredentials.Dock = System.Windows.Forms.DockStyle.Top;
pnlDefaultCredentials.Location = new System.Drawing.Point(0, 0);
pnlDefaultCredentials.Name = "pnlDefaultCredentials";
@@ -149,6 +149,7 @@
txtCredentialsPassword.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
txtCredentialsPassword.Location = new System.Drawing.Point(163, 55);
txtCredentialsPassword.Name = "txtCredentialsPassword";
txtCredentialsPassword.PasswordChar = '*';
txtCredentialsPassword.Size = new System.Drawing.Size(166, 22);
txtCredentialsPassword.TabIndex = 7;
//
@@ -232,18 +233,18 @@
radCredentialsCustom.UseVisualStyleBackColor = false;
radCredentialsCustom.CheckedChanged += radCredentialsCustom_CheckedChanged;
//
// lblCredentialsAdminInfo
// lblRegistrySettingsUsedInfo
//
lblCredentialsAdminInfo.BackColor = System.Drawing.SystemColors.ControlLight;
lblCredentialsAdminInfo.Dock = System.Windows.Forms.DockStyle.Top;
lblCredentialsAdminInfo.ForeColor = System.Drawing.SystemColors.ControlText;
lblCredentialsAdminInfo.Location = new System.Drawing.Point(0, 0);
lblCredentialsAdminInfo.Name = "lblCredentialsAdminInfo";
lblCredentialsAdminInfo.Padding = new System.Windows.Forms.Padding(0, 2, 0, 0);
lblCredentialsAdminInfo.Size = new System.Drawing.Size(610, 30);
lblCredentialsAdminInfo.TabIndex = 0;
lblCredentialsAdminInfo.Text = "Some settings are configured by your Administrator. Please contact your administrator for more information.";
lblCredentialsAdminInfo.Visible = false;
lblRegistrySettingsUsedInfo.BackColor = System.Drawing.SystemColors.ControlLight;
lblRegistrySettingsUsedInfo.Dock = System.Windows.Forms.DockStyle.Top;
lblRegistrySettingsUsedInfo.ForeColor = System.Drawing.SystemColors.ControlText;
lblRegistrySettingsUsedInfo.Location = new System.Drawing.Point(0, 0);
lblRegistrySettingsUsedInfo.Name = "lblRegistrySettingsUsedInfo";
lblRegistrySettingsUsedInfo.Padding = new System.Windows.Forms.Padding(0, 2, 0, 0);
lblRegistrySettingsUsedInfo.Size = new System.Drawing.Size(610, 30);
lblRegistrySettingsUsedInfo.TabIndex = 0;
lblRegistrySettingsUsedInfo.Text = "Some settings are configured by your Administrator. Please contact your administrator for more information.";
lblRegistrySettingsUsedInfo.Visible = false;
//
// lblCredentialsGeneratorHelp
//
@@ -304,7 +305,7 @@
internal Controls.MrngTextBox txtCredentialsUsername;
internal Controls.MrngLabel lblCredentialsDomain;
private System.Windows.Forms.TableLayoutPanel tableLayoutPanel1;
internal System.Windows.Forms.Label lblCredentialsAdminInfo;
internal System.Windows.Forms.Label lblRegistrySettingsUsedInfo;
internal System.Windows.Forms.Panel pnlCredentialsSettingsPanel;
internal Controls.MrngLabel lblCredentialsGenerator;
internal System.Windows.Forms.TextBox txtCredentialsGeneratorPsswd;

View File

@@ -5,6 +5,7 @@ using mRemoteNG.Security.SymmetricEncryption;
using mRemoteNG.Resources.Language;
using System.Runtime.Versioning;
using mRemoteNG.Config.Settings.Registry;
using System.DirectoryServices;
namespace mRemoteNG.UI.Forms.OptionsPages
{
@@ -13,8 +14,6 @@ namespace mRemoteNG.UI.Forms.OptionsPages
{
#region Private Fields
private readonly OptRegistryCredentialsPage _RegistrySettings = new();
private bool _pageEnabled = true;
#endregion
public CredentialsPage()
@@ -40,27 +39,28 @@ namespace mRemoteNG.UI.Forms.OptionsPages
lblCredentialsUsername.Text = Language.Username;
lblCredentialsPassword.Text = Language.Password;
lblCredentialsDomain.Text = Language.Domain;
lblCredentialsAdminInfo.Text = Language.OptionsAdminInfo;
lblRegistrySettingsUsedInfo.Text = Language.OptionsCompanyPolicyMessage;
}
public override void LoadSettings()
{
if (!_pageEnabled) { return; }
if (!_RegistrySettings.CredentialPageEnabled)
return;
// ReSharper disable once SwitchStatementMissingSomeCases
switch (Properties.OptionsCredentialsPage.Default.EmptyCredentials)
{
case "noinfo":
radCredentialsNoInfo.Checked = true;
break;
case "windows":
radCredentialsWindows.Checked = true;
break;
case "custom":
radCredentialsCustom.Checked = true;
break;
}
case "noinfo":
radCredentialsNoInfo.Checked = true;
break;
case "windows":
radCredentialsWindows.Checked = true;
break;
case "custom":
radCredentialsCustom.Checked = true;
break;
}
txtCredentialsUsername.Text = Properties.OptionsCredentialsPage.Default.DefaultUsername;
LegacyRijndaelCryptographyProvider cryptographyProvider = new();
txtCredentialsPassword.Text = cryptographyProvider.Decrypt(Properties.OptionsCredentialsPage.Default.DefaultPassword, Runtime.EncryptionKey);
@@ -92,13 +92,19 @@ namespace mRemoteNG.UI.Forms.OptionsPages
public override void LoadRegistrySettings()
{
if (!CommonRegistrySettings.AllowModifyCredentialSettings)
// CredentialPageEnabled reg setting: enabled/default: true; Disabled: false.
if (!_RegistrySettings.CredentialPageEnabled)
{
DisablePage();
return;
}
if (_RegistrySettings.UseCredentials.IsKeyPresent)
// UseCredentials reg setting with validation:
// 1. Is not set or valid, stop processing.
// 2. Set the 'EmptyCredentials' option based on value
// 3. Only proceed when "custom"
if (!_RegistrySettings.UseCredentials.IsValid) { return; }
else if (_RegistrySettings.UseCredentials.IsValid)
{
Properties.OptionsCredentialsPage.Default.EmptyCredentials = _RegistrySettings.UseCredentials.Value;
@@ -107,11 +113,13 @@ namespace mRemoteNG.UI.Forms.OptionsPages
case "noinfo":
DisableControl(radCredentialsWindows);
DisableControl(radCredentialsCustom);
break;
SetVisibilitySettingsUsedInfo();
return;
case "windows":
DisableControl(radCredentialsNoInfo);
DisableControl(radCredentialsCustom);
break;
SetVisibilitySettingsUsedInfo();
return;
case "custom":
DisableControl(radCredentialsNoInfo);
DisableControl(radCredentialsWindows);
@@ -119,95 +127,87 @@ namespace mRemoteNG.UI.Forms.OptionsPages
}
}
if (_RegistrySettings.UseCredentials.Value != "custom") { return; }
// ***
// The following is only used when set to custom!
if (!CommonRegistrySettings.AllowSaveUsernames)
{
Properties.OptionsCredentialsPage.Default.DefaultUsername = "";
DisableControl(txtCredentialsUsername);
}
else if (_RegistrySettings.DefaultUsername.IsKeyPresent)
// DefaultUsername reg setting: set DefaultUsername option based on value
if (_RegistrySettings.DefaultUsername.IsSet)
{
Properties.OptionsCredentialsPage.Default.DefaultUsername = _RegistrySettings.DefaultUsername.Value;
DisableControl(txtCredentialsUsername);
}
if (!CommonRegistrySettings.AllowSavePasswords)
// DefaultPassword reg setting:
// 1. Test decription works to prevents potential issues
// 2. Set DefaultPassword option based on value
// 3. Clears reg setting if fails
if (_RegistrySettings.DefaultPassword.IsSet)
{
Properties.OptionsCredentialsPage.Default.DefaultPassword = "";
DisableControl(txtCredentialsPassword);
}
//else if (_RegistrySettings.DefaultPassword.IsKeyPresent)
//{
// Properties.OptionsCredentialsPage.Default.DefaultPassword = _RegistrySettings.DefaultPassword.Value;
// DisableControl(txtCredentialsPassword);
//}
try
{
LegacyRijndaelCryptographyProvider cryptographyProvider = new();
string decryptedPassword;
string defaultPassword = _RegistrySettings.DefaultPassword.Value;
if (_RegistrySettings.DefaultDomain.IsKeyPresent)
decryptedPassword = cryptographyProvider.Decrypt(defaultPassword, Runtime.EncryptionKey);
Properties.OptionsCredentialsPage.Default.DefaultPassword = defaultPassword;
DisableControl(txtCredentialsPassword);
}
catch
{
// Fire-and-forget: The DefaultPassword in the registry is not encrypted.
_RegistrySettings.DefaultPassword.Clear();
}
}
// DefaultDomain reg setting: set DefaultDomain option based on value
if (_RegistrySettings.DefaultDomain.IsSet)
{
Properties.OptionsCredentialsPage.Default.DefaultDomain = _RegistrySettings.DefaultDomain.Value;
DisableControl(txtCredentialsDomain);
}
if (!CommonRegistrySettings.AllowSaveUsernames)
{
Properties.OptionsCredentialsPage.Default.UserViaAPIDefault = "";
DisableControl(txtCredentialsUserViaAPI);
}
else if (_RegistrySettings.UserViaAPIDefault.IsKeyPresent)
// UserViaAPIDefault reg setting: set UserViaAPIDefault option based on value
if (_RegistrySettings.UserViaAPIDefault.IsSet)
{
Properties.OptionsCredentialsPage.Default.UserViaAPIDefault = _RegistrySettings.UserViaAPIDefault.Value;
DisableControl(txtCredentialsUserViaAPI);
}
lblCredentialsAdminInfo.Visible = ShowAdministratorInfo();
SetVisibilitySettingsUsedInfo();
}
public override bool ShowAdministratorInfo()
/// <summary>
/// Checks if any credantil registry settings are being used.
/// </summary>
/// <returns>
/// True if any relevant registry settings are used; otherwise, false.
/// </returns>
public override bool ShowRegistrySettingsUsedInfo()
{
return !CommonRegistrySettings.AllowModifyCredentialSettings
|| !CommonRegistrySettings.AllowExportPasswords
return !CommonRegistrySettings.AllowExportPasswords
|| !CommonRegistrySettings.AllowExportUsernames
|| !CommonRegistrySettings.AllowSavePasswords
|| !CommonRegistrySettings.AllowSaveUsernames
|| _RegistrySettings.DefaultUsername.IsKeyPresent
//|| _RegistrySettings.DefaultPassword.IsKeyPresent
|| _RegistrySettings.DefaultDomain.IsKeyPresent
|| _RegistrySettings.UserViaAPIDefault.IsKeyPresent;
}
private void radCredentialsCustom_CheckedChanged(object sender, EventArgs e)
{
if (!_RegistrySettings.DefaultUsername.IsKeyPresent && CommonRegistrySettings.AllowSaveUsernames)
{
lblCredentialsUsername.Enabled = radCredentialsCustom.Checked;
txtCredentialsUsername.Enabled = radCredentialsCustom.Checked;
}
if (/*!_RegistrySettings.DefaultPassword.IsKeyPresent &&*/ CommonRegistrySettings.AllowSavePasswords)
{
lblCredentialsPassword.Enabled = radCredentialsCustom.Checked;
txtCredentialsPassword.Enabled = radCredentialsCustom.Checked;
}
if (!_RegistrySettings.DefaultDomain.IsKeyPresent)
{
lblCredentialsDomain.Enabled = radCredentialsCustom.Checked;
txtCredentialsDomain.Enabled = radCredentialsCustom.Checked;
}
if (!_RegistrySettings.UserViaAPIDefault.IsKeyPresent)
{
lblCredentialsUserViaAPI.Enabled = radCredentialsCustom.Checked;
txtCredentialsUserViaAPI.Enabled = radCredentialsCustom.Checked;
}
|| !_RegistrySettings.CredentialPageEnabled
|| _RegistrySettings.UseCredentials.IsValid;
/*
* Checking these values is unnecessary because UseCredentials must be valid and set to Custom.
*
||_RegistrySettings.DefaultUsername.IsSet
|| _RegistrySettings.DefaultPassword.IsSet
|| _RegistrySettings.DefaultDomain.IsSet
|| _RegistrySettings.UserViaAPIDefault.IsSet;
*/
}
/// <summary>
/// Disables the page by setting default values and disabling controls.
/// </summary>
public override void DisablePage()
{
Properties.OptionsCredentialsPage.Default.EmptyCredentials = "noinfo";
//radCredentialsNoInfo.Enabled = false;
radCredentialsWindows.Enabled = false;
radCredentialsCustom.Enabled = false;
@@ -216,8 +216,50 @@ namespace mRemoteNG.UI.Forms.OptionsPages
txtCredentialsDomain.Enabled = false;
txtCredentialsUserViaAPI.Enabled = false;
lblCredentialsAdminInfo.Visible = true;
_pageEnabled = false;
SetVisibilitySettingsUsedInfo();
}
#region Event Handlers
private void radCredentialsCustom_CheckedChanged(object sender, EventArgs e)
{
if (!_RegistrySettings.DefaultUsername.IsSet && CommonRegistrySettings.AllowSaveUsernames)
{
lblCredentialsUsername.Enabled = radCredentialsCustom.Checked;
txtCredentialsUsername.Enabled = radCredentialsCustom.Checked;
}
if (!_RegistrySettings.DefaultPassword.IsSet && CommonRegistrySettings.AllowSavePasswords)
{
lblCredentialsPassword.Enabled = radCredentialsCustom.Checked;
txtCredentialsPassword.Enabled = radCredentialsCustom.Checked;
}
if (!_RegistrySettings.DefaultDomain.IsSet)
{
lblCredentialsDomain.Enabled = radCredentialsCustom.Checked;
txtCredentialsDomain.Enabled = radCredentialsCustom.Checked;
}
if (!_RegistrySettings.UserViaAPIDefault.IsSet && CommonRegistrySettings.AllowSaveUsernames)
{
lblCredentialsUserViaAPI.Enabled = radCredentialsCustom.Checked;
txtCredentialsUserViaAPI.Enabled = radCredentialsCustom.Checked;
}
}
#endregion
#region Private Methods
/// <summary>
/// Updates the visibility of the information label indicating whether registry settings are used.
/// </summary>
private void SetVisibilitySettingsUsedInfo()
{
lblRegistrySettingsUsedInfo.Visible = ShowRegistrySettingsUsedInfo();
}
#endregion
}
}

View File

@@ -1,4 +1,64 @@
<root>
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">

View File

@@ -1,4 +1,5 @@
using System.ComponentModel;
using System;
using System.ComponentModel;
using System.Drawing;
using System.Runtime.Versioning;
using System.Windows.Forms;
@@ -42,13 +43,27 @@ namespace mRemoteNG.UI.Forms.OptionsPages
public virtual void RevertSettings()
{
}
/// <summary>
/// Loads registry settings related to update options and proxy configurations.
/// This method retrieves values from the registry and initializes the corresponding controls
/// on the page with these values. It also updates internal flags and properties accordingly.
/// </summary>
public virtual void LoadRegistrySettings()
{
}
public virtual bool ShowAdministratorInfo()
/// <summary>
/// Determines if any registry settings are being used.
/// </summary>
/// <returns>
/// A boolean value indicating whether registry settings are used, as determined by the configuration on the options page.
/// </returns>
public virtual bool ShowRegistrySettingsUsedInfo()
{
return false;
}
public virtual void DisablePage()
{
}
@@ -94,10 +109,8 @@ namespace mRemoteNG.UI.Forms.OptionsPages
((TextBox)control).ReadOnly = control.Enabled;
}
}
}
internal class DropdownList
internal class DropdownList
{
public int Index { get; set; }
public string DisplayString { get; set; }

View File

@@ -56,7 +56,7 @@ namespace mRemoteNG.UI.Forms.OptionsPages
btnTestProxy = new MrngButton();
groupBoxReleaseChannel = new MrngGroupBox();
pnlDefaultUpdate = new System.Windows.Forms.Panel();
lblUpdateAdminInfo = new System.Windows.Forms.Label();
lblRegistrySettingsUsedInfo = new System.Windows.Forms.Label();
pnlUpdateCheck.SuspendLayout();
pnlProxy.SuspendLayout();
tblProxyBasic.SuspendLayout();
@@ -97,6 +97,7 @@ namespace mRemoteNG.UI.Forms.OptionsPages
cboUpdateCheckFrequency.Name = "cboUpdateCheckFrequency";
cboUpdateCheckFrequency.Size = new System.Drawing.Size(120, 21);
cboUpdateCheckFrequency.TabIndex = 1;
cboUpdateCheckFrequency.Enabled = false;
//
// btnUpdateCheckNow
//
@@ -137,7 +138,6 @@ namespace mRemoteNG.UI.Forms.OptionsPages
//
// cboReleaseChannel
//
cboReleaseChannel._mice = MrngComboBox.MouseState.HOVER;
cboReleaseChannel.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
cboReleaseChannel.FormattingEnabled = true;
cboReleaseChannel.Location = new System.Drawing.Point(7, 21);
@@ -273,6 +273,7 @@ namespace mRemoteNG.UI.Forms.OptionsPages
txtProxyPassword.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
txtProxyPassword.Location = new System.Drawing.Point(163, 29);
txtProxyPassword.Name = "txtProxyPassword";
txtProxyPassword.PasswordChar = '*';
txtProxyPassword.Size = new System.Drawing.Size(184, 22);
txtProxyPassword.TabIndex = 3;
txtProxyPassword.UseSystemPasswordChar = true;
@@ -332,25 +333,25 @@ namespace mRemoteNG.UI.Forms.OptionsPages
pnlDefaultUpdate.Controls.Add(pnlProxy);
pnlDefaultUpdate.Controls.Add(groupBoxReleaseChannel);
pnlDefaultUpdate.Controls.Add(pnlUpdateCheck);
pnlDefaultUpdate.Controls.Add(lblUpdateAdminInfo);
pnlDefaultUpdate.Controls.Add(lblRegistrySettingsUsedInfo);
pnlDefaultUpdate.Dock = System.Windows.Forms.DockStyle.Top;
pnlDefaultUpdate.Location = new System.Drawing.Point(0, 0);
pnlDefaultUpdate.Name = "pnlDefaultUpdate";
pnlDefaultUpdate.Size = new System.Drawing.Size(610, 483);
pnlDefaultUpdate.TabIndex = 4;
//
// lblUpdateAdminInfo
// lblRegistrySettingsUsedInfo
//
lblUpdateAdminInfo.BackColor = System.Drawing.SystemColors.ControlLight;
lblUpdateAdminInfo.Dock = System.Windows.Forms.DockStyle.Top;
lblUpdateAdminInfo.ForeColor = System.Drawing.SystemColors.ControlText;
lblUpdateAdminInfo.Location = new System.Drawing.Point(0, 0);
lblUpdateAdminInfo.Name = "lblUpdateAdminInfo";
lblUpdateAdminInfo.Padding = new System.Windows.Forms.Padding(0, 2, 0, 0);
lblUpdateAdminInfo.Size = new System.Drawing.Size(610, 30);
lblUpdateAdminInfo.TabIndex = 0;
lblUpdateAdminInfo.Text = "Some settings are configured by your Administrator. Please contact your administrator for more information.";
lblUpdateAdminInfo.Visible = false;
lblRegistrySettingsUsedInfo.BackColor = System.Drawing.SystemColors.ControlLight;
lblRegistrySettingsUsedInfo.Dock = System.Windows.Forms.DockStyle.Top;
lblRegistrySettingsUsedInfo.ForeColor = System.Drawing.SystemColors.ControlText;
lblRegistrySettingsUsedInfo.Location = new System.Drawing.Point(0, 0);
lblRegistrySettingsUsedInfo.Name = "lblRegistrySettingsUsedInfo";
lblRegistrySettingsUsedInfo.Padding = new System.Windows.Forms.Padding(0, 2, 0, 0);
lblRegistrySettingsUsedInfo.Size = new System.Drawing.Size(610, 30);
lblRegistrySettingsUsedInfo.TabIndex = 0;
lblRegistrySettingsUsedInfo.Text = "Some settings are configured by your Administrator. Please contact your administrator for more information.";
lblRegistrySettingsUsedInfo.Visible = false;
//
// UpdatesPage
//
@@ -397,7 +398,7 @@ namespace mRemoteNG.UI.Forms.OptionsPages
private System.Windows.Forms.TableLayoutPanel tblProxyBasic;
private System.Windows.Forms.TableLayoutPanel tblProxyAuthentication;
private System.Windows.Forms.Panel pnlDefaultUpdate;
internal System.Windows.Forms.Label lblUpdateAdminInfo;
internal System.Windows.Forms.Label lblRegistrySettingsUsedInfo;
internal MrngComboBox cboUpdateCheckFrequency;
}
}

View File

@@ -21,7 +21,6 @@ namespace mRemoteNG.UI.Forms.OptionsPages
private AppUpdater _appUpdate;
private readonly OptRegistryUpdatesPage _RegistrySettings = new();
private bool _pageEnabled = true;
#endregion
@@ -62,69 +61,22 @@ namespace mRemoteNG.UI.Forms.OptionsPages
btnTestProxy.Text = Language.TestProxy;
lblUpdateAdminInfo.Text = Language.OptionsAdminInfo;
lblRegistrySettingsUsedInfo.Text = Language.OptionsCompanyPolicyMessage;
}
public override void LoadSettings()
{
// Checks the combination of the following registry settings:
// 1. AllowCheckForUpdates is false.
// 2. AllowCheckForUpdatesAutomatical and AllowCheckForUpdatesManual are false.
// 3. Disable page and stop processing at this point.
if (UpdatesForbidden())
return;
chkCheckForUpdatesOnStartup.Checked = Properties.OptionsUpdatesPage.Default.CheckForUpdatesOnStartup;
if (!_RegistrySettings.CheckForUpdatesFrequencyDays.IsKeyPresent)
cboUpdateCheckFrequency.Enabled = chkCheckForUpdatesOnStartup.Checked;
if (CommonRegistrySettings.AllowCheckForUpdatesAutomatical)
{
cboUpdateCheckFrequency.Items.Clear();
int nDaily = cboUpdateCheckFrequency.Items.Add(Language.Daily);
int nWeekly = cboUpdateCheckFrequency.Items.Add(Language.Weekly);
int nMonthly = cboUpdateCheckFrequency.Items.Add(Language.Monthly);
if (Properties.OptionsUpdatesPage.Default.CheckForUpdatesFrequencyDays < 1)
{
chkCheckForUpdatesOnStartup.Checked = false;
cboUpdateCheckFrequency.SelectedIndex = nDaily;
} // Daily
else
{
switch (Properties.OptionsUpdatesPage.Default.CheckForUpdatesFrequencyDays)
{
case 1:
cboUpdateCheckFrequency.SelectedIndex = nDaily;
break;
case 7:
cboUpdateCheckFrequency.SelectedIndex = nWeekly;
break;
case 31:
cboUpdateCheckFrequency.SelectedIndex = nMonthly;
break;
default:
int nCustom =
cboUpdateCheckFrequency.Items.Add(string.Format(Language.UpdateFrequencyCustom, Properties.OptionsUpdatesPage.Default.CheckForUpdatesFrequencyDays));
cboUpdateCheckFrequency.SelectedIndex = nCustom;
break;
}
}
}
int stable = cboReleaseChannel.Items.Add(UpdateChannelInfo.STABLE);
int beta = cboReleaseChannel.Items.Add(UpdateChannelInfo.PREVIEW);
int dev = cboReleaseChannel.Items.Add(UpdateChannelInfo.NIGHTLY);
switch (Properties.OptionsUpdatesPage.Default.UpdateChannel)
{
case UpdateChannelInfo.STABLE:
cboReleaseChannel.SelectedIndex = stable;
break;
case UpdateChannelInfo.PREVIEW:
cboReleaseChannel.SelectedIndex = beta;
break;
case UpdateChannelInfo.NIGHTLY:
cboReleaseChannel.SelectedIndex = dev;
break;
default:
cboReleaseChannel.SelectedIndex = stable;
break;
}
InitialiseCheckForUpdatesOnStartupComboBox();
InitialiseReleaseChannelComboBox();
chkUseProxyForAutomaticUpdates.Checked = Properties.OptionsUpdatesPage.Default.UpdateUseProxy;
tblProxyBasic.Enabled = Properties.OptionsUpdatesPage.Default.UpdateUseProxy;
@@ -145,19 +97,29 @@ namespace mRemoteNG.UI.Forms.OptionsPages
{
base.SaveSettings();
// Checks the combination of the following registry settings:
// 1. AllowCheckForUpdates is false.
// 2. AllowCheckForUpdatesAutomatical and AllowCheckForUpdatesManual are false.
// 3. Disable page and stop processing at this point.
if (UpdatesForbidden())
return;
Properties.OptionsUpdatesPage.Default.CheckForUpdatesOnStartup = chkCheckForUpdatesOnStartup.Checked;
if (cboUpdateCheckFrequency.SelectedItem.ToString() == Language.Daily)
string UpdateCheckFrequency = cboUpdateCheckFrequency.SelectedItem?.ToString();
if (UpdateCheckFrequency == Language.Never)
{
Properties.OptionsUpdatesPage.Default.CheckForUpdatesOnStartup = false;
}
if (UpdateCheckFrequency == Language.Daily)
{
Properties.OptionsUpdatesPage.Default.CheckForUpdatesFrequencyDays = 1;
}
else if (cboUpdateCheckFrequency.SelectedItem.ToString() == Language.Weekly)
else if (UpdateCheckFrequency == Language.Weekly)
{
Properties.OptionsUpdatesPage.Default.CheckForUpdatesFrequencyDays = 7;
}
else if (cboUpdateCheckFrequency.SelectedItem.ToString() == Language.Monthly)
else if (UpdateCheckFrequency == Language.Monthly)
{
Properties.OptionsUpdatesPage.Default.CheckForUpdatesFrequencyDays = 31;
}
@@ -176,9 +138,16 @@ namespace mRemoteNG.UI.Forms.OptionsPages
public override void LoadRegistrySettings()
{
// Checks the combination of the following registry settings:
// 1. AllowCheckForUpdates is false.
// 2. AllowCheckForUpdatesAutomatical and AllowCheckForUpdatesManual are false.
// 3. Disable page and stop processing at this point.
if (UpdatesForbidden())
return;
// AllowCheckForUpdatesAutomatical reg setting:
// 1. Allowed/default: true; Disabled: false.
// 2. Disable the option "check for updates on startup".
if (!CommonRegistrySettings.AllowCheckForUpdatesAutomatical)
{
Properties.OptionsUpdatesPage.Default.CheckForUpdatesOnStartup = false;
@@ -186,87 +155,134 @@ namespace mRemoteNG.UI.Forms.OptionsPages
DisableControl(cboUpdateCheckFrequency);
}
if (_RegistrySettings.CheckForUpdatesFrequencyDays.IsKeyPresent && _RegistrySettings.CheckForUpdatesFrequencyDays.Value >= 1)
// CheckForUpdatesFrequencyDays reg setting:
// 1. Is 0 or less, than CheckForUpdatesOnStartup will be disabled.
// 2. Is Valid than set CheckForUpdatesFrequencyDays option based on value.
if (CommonRegistrySettings.AllowCheckForUpdatesAutomatical && _RegistrySettings.CheckForUpdatesFrequencyDays.IsSet)
{
Properties.OptionsUpdatesPage.Default.CheckForUpdatesFrequencyDays = _RegistrySettings.CheckForUpdatesFrequencyDays.Value;
DisableControl(cboUpdateCheckFrequency);
if (_RegistrySettings.CheckForUpdatesFrequencyDays.Value < 0)
{
Properties.OptionsUpdatesPage.Default.CheckForUpdatesOnStartup = false;
DisableControl(chkCheckForUpdatesOnStartup);
}
else if (_RegistrySettings.CheckForUpdatesFrequencyDays.IsValid)
{
Properties.OptionsUpdatesPage.Default.CheckForUpdatesOnStartup = true;
DisableControl(chkCheckForUpdatesOnStartup);
Properties.OptionsUpdatesPage.Default.CheckForUpdatesFrequencyDays = _RegistrySettings.CheckForUpdatesFrequencyDays.Value;
DisableControl(cboUpdateCheckFrequency);
}
}
// Enable or disable the "Update Check" button if allowed or not.
btnUpdateCheckNow.Enabled = CommonRegistrySettings.AllowCheckForUpdatesManual;
if (_RegistrySettings.UpdateChannel.IsKeyPresent)
// UpdateChannel reg setting: set UpdateChannel option based on value
if (_RegistrySettings.UpdateChannel.IsValid)
{
Properties.OptionsUpdatesPage.Default.UpdateChannel = _RegistrySettings.UpdateChannel.Value;
DisableControl(cboReleaseChannel);
}
if (_RegistrySettings.UseProxyForUpdates.IsKeyPresent)
// UseProxyForUpdates reg setting: set UseProxyForUpdates option based on value
// 1. Continues with the options checks for "ProxyAddress" and "ProxyPort"
// 2. Moved on to the "UseProxyAuthentication" options if true
if (_RegistrySettings.UseProxyForUpdates.IsSet)
{
Properties.OptionsUpdatesPage.Default.UpdateUseProxy = _RegistrySettings.UseProxyForUpdates.Value;
bool UseProxy = _RegistrySettings.UseProxyForUpdates.Value;
Properties.OptionsUpdatesPage.Default.UpdateUseProxy = UseProxy;
DisableControl(chkUseProxyForAutomaticUpdates);
if (_RegistrySettings.UseProxyForUpdates.Value == false)
// If the proxy is not used, reset the proxy address, port, and authentication settings to defaults.
if (!UseProxy)
{
Properties.OptionsUpdatesPage.Default.UpdateProxyAddress = "";
Properties.OptionsUpdatesPage.Default.UpdateProxyPort = 80;
Properties.OptionsUpdatesPage.Default.UpdateProxyUseAuthentication = false;
}
if (_RegistrySettings.ProxyAddress.IsKeyPresent && _RegistrySettings.UseProxyForUpdates.Value == true)
// ProxyAddress reg setting: set ProxyAddress option based on value
if (_RegistrySettings.ProxyAddress.IsSet && UseProxy)
{
Properties.OptionsUpdatesPage.Default.UpdateProxyAddress = _RegistrySettings.ProxyAddress.Value;
DisableControl(txtProxyAddress);
}
if (_RegistrySettings.ProxyPort.IsKeyPresent && _RegistrySettings.UseProxyForUpdates.Value == true)
// ProxyPort reg setting: set ProxyPort option based on value
if (_RegistrySettings.ProxyPort.IsSet && UseProxy)
{
// only set value if not is 0 to prevent errors..
if (_RegistrySettings.ProxyPort.Value >= 1)
_RegistrySettings.ProxyPort.SetValidation((int)numProxyPort.Minimum, (int)numProxyPort.Maximum);
if (_RegistrySettings.ProxyPort.IsValid)
{
Properties.OptionsUpdatesPage.Default.UpdateProxyPort = _RegistrySettings.ProxyPort.Value;
DisableControl(numProxyPort);
DisableControl(numProxyPort);
}
}
}
if (_RegistrySettings.UseProxyAuthentication.IsKeyPresent)
// UseProxyAuthentication reg setting: set UseProxyAuthentication option based on value
// 1. Only applied when UpdateUseProxy is true
// 2. Continues with the options checks for "ProxyAuthUser" and "ProxyAuthPass"
if (Properties.OptionsUpdatesPage.Default.UpdateUseProxy && _RegistrySettings.UseProxyAuthentication.IsSet)
{
Properties.OptionsUpdatesPage.Default.UpdateProxyUseAuthentication = _RegistrySettings.UseProxyAuthentication.Value;
bool UseProxyAuth = _RegistrySettings.UseProxyAuthentication.Value;
Properties.OptionsUpdatesPage.Default.UpdateProxyUseAuthentication = UseProxyAuth;
DisableControl(chkUseProxyAuthentication);
if (_RegistrySettings.UseProxyAuthentication.Value == false)
// If proxy authentication is not used, reset the proxy authentication username and password to defaults.
if (!UseProxyAuth)
{
Properties.OptionsUpdatesPage.Default.UpdateProxyAuthUser = "";
//Properties.OptionsUpdatesPage.Default.UpdateProxyAuthPass = "";
Properties.OptionsUpdatesPage.Default.UpdateProxyAuthPass = "";
}
if (_RegistrySettings.ProxyAuthUser.IsKeyPresent && _RegistrySettings.UseProxyAuthentication.Value == true)
// ProxyAuthUser reg setting: set ProxyAuthUser option based on value
if (_RegistrySettings.ProxyAuthUser.IsSet && UseProxyAuth)
{
Properties.OptionsUpdatesPage.Default.UpdateProxyAuthUser = _RegistrySettings.ProxyAuthUser.Value;
DisableControl(txtProxyUsername);
}
/*if (_RegistrySettings.ProxyAuthPass.IsProvided && _RegistrySettings.UseProxyAuthentication.Value == true)
// ProxyAuthPass reg setting:
// 1. Test decription works to prevents potential issues
// 2. Set ProxyAuthPass option based on value
if (_RegistrySettings.ProxyAuthPass.IsSet && UseProxyAuth)
{
Properties.OptionsUpdatesPage.Default.UpdateProxyAuthPass = _RegistrySettings.ProxyAuthPass;
DisableControl(txtProxyPassword);
}*/
// Prevents potential issues when using UpdateProxyAuthPass later.
try
{
LegacyRijndaelCryptographyProvider cryptographyProvider = new();
string decryptedPassword;
string ProxyAuthPass = _RegistrySettings.ProxyAuthPass.Value;
decryptedPassword = cryptographyProvider.Decrypt(ProxyAuthPass, Runtime.EncryptionKey);
Properties.OptionsUpdatesPage.Default.UpdateProxyAuthPass = ProxyAuthPass;
DisableControl(txtProxyPassword);
}
catch
{
// Fire-and-forget: The password in the registry is not encrypted.
}
}
}
lblUpdateAdminInfo.Visible = ShowAdministratorInfo();
// Updates the visibility of the information label indicating whether registry settings are used.
lblRegistrySettingsUsedInfo.Visible = ShowRegistrySettingsUsedInfo();
}
public override bool ShowAdministratorInfo()
public override bool ShowRegistrySettingsUsedInfo()
{
return !CommonRegistrySettings.AllowCheckForUpdatesAutomatical
|| !CommonRegistrySettings.AllowCheckForUpdatesManual
|| _RegistrySettings.CheckForUpdatesFrequencyDays.IsKeyPresent
|| _RegistrySettings.UpdateChannel.IsKeyPresent
|| _RegistrySettings.UseProxyForUpdates.IsKeyPresent
|| _RegistrySettings.ProxyAddress.IsKeyPresent
|| _RegistrySettings.ProxyPort.IsKeyPresent
|| _RegistrySettings.UseProxyAuthentication.IsKeyPresent
|| _RegistrySettings.ProxyAuthUser.IsKeyPresent;
|| _RegistrySettings.CheckForUpdatesFrequencyDays.IsSet
|| _RegistrySettings.UpdateChannel.IsValid
|| _RegistrySettings.UseProxyForUpdates.IsSet
|| _RegistrySettings.ProxyAddress.IsSet
|| _RegistrySettings.ProxyPort.IsValid
|| _RegistrySettings.UseProxyAuthentication.IsSet
|| _RegistrySettings.ProxyAuthUser.IsSet
|| _RegistrySettings.ProxyAuthPass.IsSet;
}
#endregion
@@ -275,8 +291,10 @@ namespace mRemoteNG.UI.Forms.OptionsPages
private void chkCheckForUpdatesOnStartup_CheckedChanged(object sender, EventArgs e)
{
if (!_RegistrySettings.CheckForUpdatesFrequencyDays.IsKeyPresent)
if (!_RegistrySettings.CheckForUpdatesFrequencyDays.IsValid)
cboUpdateCheckFrequency.Enabled = chkCheckForUpdatesOnStartup.Checked;
InitialiseCheckForUpdatesOnStartupComboBox();
}
private void btnUpdateCheckNow_Click(object sender, EventArgs e)
@@ -286,24 +304,16 @@ namespace mRemoteNG.UI.Forms.OptionsPages
private void chkUseProxyForAutomaticUpdates_CheckedChanged(object sender, EventArgs e)
{
tblProxyBasic.Enabled = chkUseProxyForAutomaticUpdates.Checked;
btnTestProxy.Enabled = chkUseProxyForAutomaticUpdates.Checked;
// Enables/disables proxy settings controls
bool useProxy = chkUseProxyForAutomaticUpdates.Checked;
tblProxyBasic.Enabled = useProxy;
btnTestProxy.Enabled = useProxy;
if (chkUseProxyForAutomaticUpdates.Checked)
{
if (!_RegistrySettings.UseProxyForUpdates.IsKeyPresent)
chkUseProxyAuthentication.Enabled = true;
if (chkUseProxyAuthentication.Checked && !_RegistrySettings.UseProxyAuthentication.IsKeyPresent)
{
tblProxyAuthentication.Enabled = true;
}
}
else
{
chkUseProxyAuthentication.Enabled = false;
tblProxyAuthentication.Enabled = false;
}
// Enables/disables proxy authentication controls
bool useProxyAuth = chkUseProxyAuthentication.Checked;
bool useProxyAuthRegIsSet = _RegistrySettings.UseProxyAuthentication.IsSet;
chkUseProxyAuthentication.Enabled = useProxy && !useProxyAuthRegIsSet;
tblProxyAuthentication.Enabled = useProxy && useProxyAuth && !useProxyAuthRegIsSet;
}
private async void btnTestProxy_Click(object sender, EventArgs e)
@@ -348,23 +358,116 @@ namespace mRemoteNG.UI.Forms.OptionsPages
private void chkUseProxyAuthentication_CheckedChanged(object sender, EventArgs e)
{
if (chkUseProxyForAutomaticUpdates.Checked)
{
tblProxyAuthentication.Enabled = chkUseProxyAuthentication.Checked;
}
#endregion
#region Private Methods
/// <summary>
/// Initializes the update check frequency ComboBox.
/// </summary>
/// <remarks>
/// Clears existing items, adds default options (Daily, Weekly, Monthly), and sets the selected option
/// based on the saved frequency. If the saved frequency is less than 1, adds and selects "Never".
/// </remarks>
private void InitialiseCheckForUpdatesOnStartupComboBox()
{
cboUpdateCheckFrequency.Items.Clear();
int nDaily = cboUpdateCheckFrequency.Items.Add(Language.Daily);
int nWeekly = cboUpdateCheckFrequency.Items.Add(Language.Weekly);
int nMonthly = cboUpdateCheckFrequency.Items.Add(Language.Monthly);
if (Properties.OptionsUpdatesPage.Default.CheckForUpdatesFrequencyDays < 1)
{
chkCheckForUpdatesOnStartup.Checked = false;
Properties.OptionsUpdatesPage.Default.CheckForUpdatesOnStartup = false;
int nNever = cboUpdateCheckFrequency.Items.Add(Language.Never);
cboUpdateCheckFrequency.SelectedIndex = nNever;
}
else if (!chkCheckForUpdatesOnStartup.Checked)
{
int nNever = cboUpdateCheckFrequency.Items.Add(Language.Never);
cboUpdateCheckFrequency.SelectedIndex = nNever;
}
else
{
switch (Properties.OptionsUpdatesPage.Default.CheckForUpdatesFrequencyDays)
{
case 1:
cboUpdateCheckFrequency.SelectedIndex = nDaily;
break;
case 7:
cboUpdateCheckFrequency.SelectedIndex = nWeekly;
break;
case 31:
cboUpdateCheckFrequency.SelectedIndex = nMonthly;
break;
default:
int nCustom =
cboUpdateCheckFrequency.Items.Add(string.Format(Language.UpdateFrequencyCustom, Properties.OptionsUpdatesPage.Default.CheckForUpdatesFrequencyDays));
cboUpdateCheckFrequency.SelectedIndex = nCustom;
break;
}
}
}
/// <summary>
/// Initializes the release channel ComboBox
/// </summary>
/// <remarks>
/// Set available options (STABLE, PREVIEW, NIGHTLY) and selects the appropriate channel based on saved settings
/// </remarks>
private void InitialiseReleaseChannelComboBox()
{
cboReleaseChannel.Items.Clear();
int stable = cboReleaseChannel.Items.Add(UpdateChannelInfo.STABLE);
int beta = cboReleaseChannel.Items.Add(UpdateChannelInfo.PREVIEW);
int dev = cboReleaseChannel.Items.Add(UpdateChannelInfo.NIGHTLY);
cboReleaseChannel.SelectedIndex = Properties.OptionsUpdatesPage.Default.UpdateChannel switch
{
UpdateChannelInfo.STABLE => stable,
UpdateChannelInfo.PREVIEW => beta,
UpdateChannelInfo.NIGHTLY => dev,
_ => stable,
};
}
/// <summary>
/// Determines if updates are forbidden based on registry settings.
/// </summary>
/// <returns>
/// True if updates are forbidden; otherwise, false.
/// </returns>
private bool UpdatesForbidden()
{
bool forbidden = !CommonRegistrySettings.AllowCheckForUpdates
bool disablePage = !CommonRegistrySettings.AllowCheckForUpdates
|| (!CommonRegistrySettings.AllowCheckForUpdatesAutomatical
&& !CommonRegistrySettings.AllowCheckForUpdatesManual);
if (forbidden && _pageEnabled)
if (disablePage)
{
DisablePage();
return forbidden;
// Ensure the UI (CheckFrequency ComboBox) appears correct when disabled
cboUpdateCheckFrequency.Items.Clear();
int nNever = cboUpdateCheckFrequency.Items.Add(Language.Never);
cboUpdateCheckFrequency.SelectedIndex = nNever;
// Ensure the UI (ReleaseChannel ComboBox) appears correct when disabled
cboReleaseChannel.Items.Clear();
int stable = cboReleaseChannel.Items.Add(UpdateChannelInfo.STABLE);
cboReleaseChannel.SelectedIndex = stable;
}
return disablePage;
}
/// <summary>
/// Disables all controls on the page related to update settings and proxy configurations.
/// </summary>
public override void DisablePage()
{
chkCheckForUpdatesOnStartup.Checked = false;
@@ -391,9 +494,7 @@ namespace mRemoteNG.UI.Forms.OptionsPages
txtProxyPassword.ReadOnly = true;
btnTestProxy.Enabled = false;
lblUpdateAdminInfo.Visible = true;
_pageEnabled = false;
lblRegistrySettingsUsedInfo.Visible = true;
}
#endregion

View File

@@ -117,7 +117,4 @@
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="lblUpdateAdminInfo.Locked" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>True</value>
</metadata>
</root>

View File

@@ -377,6 +377,7 @@ namespace mRemoteNG.UI.Forms
if (!CommonRegistrySettings.AllowCheckForUpdatesAutomatical) return;
if (!Properties.OptionsUpdatesPage.Default.CheckForUpdatesOnStartup) return;
if (Properties.OptionsUpdatesPage.Default.CheckForUpdatesFrequencyDays == 0) return;
DateTime nextUpdateCheck =
Convert.ToDateTime(Properties.OptionsUpdatesPage.Default.CheckForUpdatesLastCheck.Add(TimeSpan.FromDays(Convert.ToDouble(Properties.OptionsUpdatesPage.Default.CheckForUpdatesFrequencyDays))));

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,188 +0,0 @@
using Microsoft.Win32;
using mRemoteNG.Tools.WindowsRegistry;
using NUnit.Framework;
namespace mRemoteNGTests.Tools.Registry
{
internal class WindowsRegistryAdvancedTests : WindowsRegistryAdvanced
{
private const string _TestRootKey = @"Software\mRemoteNGTest";
private const RegistryHive _TestHive = RegistryHive.CurrentUser;
[SetUp]
public void Setup()
{
// GetBoolean && GetBoolValue (GetBoolValue -> Not Advanced but not tested jet)
SetRegistryValue(_TestHive, _TestRootKey, "TestBoolAsString", "true", RegistryValueKind.String);
SetRegistryValue(_TestHive, _TestRootKey, "TestBoolAsDWord", 0, RegistryValueKind.DWord);
// GetInteger Tests
SetRegistryValue(_TestHive, _TestRootKey, "TestInteger", "4711", RegistryValueKind.DWord);
// GetString Tests
SetRegistryValue(_TestHive, _TestRootKey, "TestString1", "Banane", RegistryValueKind.String);
SetRegistryValue(_TestHive, _TestRootKey, "TestString2", "Hund", RegistryValueKind.String);
}
[TearDown]
public void Cleanup()
{
// Delete the registry keys here
DeleteRegistryKey(_TestHive, _TestRootKey, true);
}
#region GetBoolean() Tests
// Non object returns
[Test]
public void GetBooleanFromString_ReturnsTrue()
{
var key = GetBoolean(_TestHive, _TestRootKey, "TestBoolAsString");
Assert.That(key.Value, Is.EqualTo(true));
}
[Test]
public void GetBooleanFromDword_ReturnsFalse()
{
var key = GetBoolean(_TestHive, _TestRootKey, "TestBoolAsDWord");
Assert.That(key.Value, Is.EqualTo(false));
}
[Test]
public void GetBooleanNotProvided_ReturnsDefaultTrue()
{
var key = GetBoolean(_TestHive, _TestRootKey, "TestBoolNotProvided", true);
Assert.Multiple(() =>
{
Assert.That(key.Value, Is.EqualTo(true), "Value should be the default (true)");
Assert.That(key.IsKeyPresent, Is.EqualTo(false), "IsProvided should be false");
});
}
#endregion
#region GetBoolValue()___No Object, just bool value returns
[Test]
public void GetBoolValueFromString_ReturnsTrue()
{
var key = GetBoolValue(_TestHive, _TestRootKey, "TestBoolAsString");
Assert.That(key, Is.EqualTo(true));
}
[Test]
public void GetBoolValueFromDword_ReturnsFalse()
{
var key = GetBoolValue(_TestHive, _TestRootKey, "TestBoolAsDWord", true);
Assert.That(key, Is.EqualTo(false));
}
[Test]
public void GetBoolValue_ReturnsDefaultTrue()
{
var key = GetBoolValue(_TestHive, _TestRootKey, "TestBoolNotProvided", true);
Assert.That(key, Is.EqualTo(true));
}
#endregion
#region GetInteger()
[Test]
public void GetInteger()
{
var key = GetInteger(_TestHive, _TestRootKey, "TestInteger");
Assert.Multiple(() =>
{
Assert.That(key.Value, Is.EqualTo(4711));
Assert.That(key.IsKeyPresent, Is.EqualTo(true));
});
}
[Test]
public void GetInteger_returnObjectDefault()
{
var key = GetInteger(_TestHive, _TestRootKey, "TestIntegerNotProvided");
Assert.Multiple(() =>
{
Assert.That(key.Value, Is.EqualTo(-1), "Value should be the default (-1)");
Assert.That(key.IsKeyPresent, Is.EqualTo(false));
});
}
[Test]
public void GetInteger_returnSpecifiedDefault()
{
var key = GetInteger(_TestHive, _TestRootKey, "TestIntegerNotProvided", 2096);
Assert.Multiple(() =>
{
Assert.That(key.Value, Is.EqualTo(-1), "Value should be the default (-1)");
Assert.That(key.IsKeyPresent, Is.EqualTo(false));
});
}
[Test]
public void GetDwordValue_returnIntegerValue()
{
int value = GetDwordValue(_TestHive, _TestRootKey, "TestInteger");
Assert.That(value, Is.EqualTo(4711));
}
#endregion
#region GetString()
[Test]
public void GetString()
{
var key = GetString(_TestHive, _TestRootKey, "TestString1");
Assert.Multiple(() =>
{
Assert.That(key.Value, Is.EqualTo("Banane"));
Assert.That(key.IsKeyPresent, Is.EqualTo(true));
});
}
[Test]
public void GetString_ReturnsDefault()
{
var key = GetString(_TestHive, _TestRootKey, "TestStringNotProvided", "Banane");
Assert.Multiple(() =>
{
Assert.That(key.Value, Is.EqualTo("Banane"));
Assert.That(key.IsKeyPresent, Is.EqualTo(false));
});
}
[Test]
public void GetStringValidated_Valid()
{
string[] fruits = { "Banane", "Erdbeere", "Apfel" };
var key = GetStringValidated(_TestHive, _TestRootKey, "TestString1", fruits);
Assert.Multiple(() =>
{
Assert.That(key.Value, Is.EqualTo("Banane"));
Assert.That(key.IsKeyPresent, Is.EqualTo(true));
Assert.That(key.IsValid, Is.EqualTo(true));
});
}
[Test]
public void GetStringValidated_NotValidNull()
{
string[] fruits = { "Banane", "Erdbeere", "Apfel" };
var key = GetStringValidated(_TestHive, _TestRootKey, "TestString2", fruits);
Assert.Multiple(() =>
{
Assert.That(key.Value, Is.EqualTo(null));
Assert.That(key.IsKeyPresent, Is.EqualTo(true));
Assert.That(key.IsValid, Is.EqualTo(false));
});
}
[Test]
public void GetStringValidated_NotValidDefault()
{
string[] fruits = { "Banane", "Erdbeere", "Apfel" };
var key = GetStringValidated(_TestHive, _TestRootKey, "TestString2", fruits, false, "Banane");
Assert.Multiple(() =>
{
Assert.That(key.Value, Is.EqualTo("Banane"));
Assert.That(key.IsKeyPresent, Is.EqualTo(true));
Assert.That(key.IsValid, Is.EqualTo(false));
});
}
#endregion
}
}

View File

@@ -1,386 +0,0 @@
using Microsoft.Win32;
using mRemoteNG.Tools.WindowsRegistry;
using NUnit.Framework;
using static System.Windows.Forms.VisualStyles.VisualStyleElement.Rebar;
namespace mRemoteNGTests.Tools.Registry
{
internal class WindowsRegistryKeyTests
{
private WindowsRegistryKey CompleteRegistryKey { get; set; }
private WindowsRegistryKey PartialRegistryKey { get; set; }
[SetUp]
public void Setup()
{
CompleteRegistryKey = new WindowsRegistryKey()
{
Hive = RegistryHive.CurrentUser,
ValueKind = RegistryValueKind.String,
Name = "Test",
Path = @"SOFTWARE\TEST\TEST\Test",
Value = "CompleteRegistryKey"
};
PartialRegistryKey = new WindowsRegistryKey()
{
Hive = RegistryHive.CurrentUser,
ValueKind = RegistryValueKind.DWord,
};
}
#region WindowsRegistryKey() tests
[Test]
public void WindowsRegistryKeyReadable()
{
Assert.That(CompleteRegistryKey.IsKeyReadable, Is.EqualTo(true));
}
[Test]
public void WindowsRegistryKeyNotReadable()
{
Assert.That(PartialRegistryKey.IsKeyReadable, Is.EqualTo(false));
}
[Test]
public void WindowsRegistryKeyWriteable()
{
Assert.That(CompleteRegistryKey.IsKeyWritable, Is.EqualTo(true));
}
[Test]
public void WindowsRegistryKeyNotWriteable()
{
Assert.That(PartialRegistryKey.IsKeyWritable, Is.EqualTo(false));
}
[Test]
public void WindowsRegistryKeyProvided()
{
Assert.That(CompleteRegistryKey.IsKeyPresent, Is.EqualTo(true));
}
[Test]
public void WindowsRegistryKeyNotProvided()
{
Assert.That(PartialRegistryKey.IsKeyPresent, Is.EqualTo(false));
}
#endregion
#region WindowsRegistryKeyBoolean tests
[Test]
public void WindowsRegistryKeyBoolean_FromStringTrue()
{
WindowsRegistryKey TestKey = new()
{
Hive = RegistryHive.CurrentUser,
ValueKind = RegistryValueKind.String,
Name = "TestBoolString",
Path = @"SOFTWARE\Test",
Value = "true"
};
WindowsRegistryKeyBoolean boolKey = new ();
boolKey.ConvertFromWindowsRegistryKey(TestKey);
Assert.That(boolKey.IsKeyPresent, Is.EqualTo(true));
Assert.That(boolKey.Value, Is.EqualTo(true));
}
[Test]
public void WindowsRegistryKeyBoolean_FromStringFalse()
{
WindowsRegistryKey TestKey = new()
{
Hive = RegistryHive.CurrentUser,
ValueKind = RegistryValueKind.String,
Name = "TestBoolString",
Path = @"SOFTWARE\Test",
Value = "false"
};
WindowsRegistryKeyBoolean boolKey = new();
boolKey.ConvertFromWindowsRegistryKey(TestKey);
Assert.That(boolKey.IsKeyPresent, Is.EqualTo(true));
Assert.That(boolKey.Value, Is.EqualTo(false));
}
[Test]
public void WindowsRegistryKeyBoolean_FromDwordTrue()
{
WindowsRegistryKey TestKey = new()
{
Hive = RegistryHive.CurrentUser,
ValueKind = RegistryValueKind.DWord,
Name = "TestBoolString",
Path = @"SOFTWARE\Test",
Value = "1"
};
WindowsRegistryKeyBoolean boolKey = new();
boolKey.ConvertFromWindowsRegistryKey(TestKey);
Assert.That(boolKey.IsKeyPresent, Is.EqualTo(true));
Assert.That(boolKey.Value, Is.EqualTo(true));
}
[Test]
public void WindowsRegistryKeyBoolean_FromDwordFalse()
{
WindowsRegistryKey TestKey = new()
{
Hive = RegistryHive.CurrentUser,
ValueKind = RegistryValueKind.DWord,
Name = "TestBoolString",
Path = @"SOFTWARE\Test",
Value = "0"
};
WindowsRegistryKeyBoolean boolKey = new();
boolKey.ConvertFromWindowsRegistryKey(TestKey);
Assert.That(boolKey.IsKeyPresent, Is.EqualTo(true));
Assert.That(boolKey.Value, Is.EqualTo(false));
}
[Test]
public void WindowsRegistryKeyBoolean_ReturnDefault()
{
WindowsRegistryKey TestKey = new()
{
Hive = RegistryHive.CurrentUser,
ValueKind = RegistryValueKind.DWord,
Name = "TestBoolString",
Path = @"SOFTWARE\Test",
Value = null
};
WindowsRegistryKeyBoolean boolKey = new();
boolKey.ConvertFromWindowsRegistryKey(TestKey, true);
Assert.That(boolKey.IsKeyPresent, Is.EqualTo(false));
Assert.That(boolKey.Value, Is.EqualTo(true));
}
#endregion
#region WindowsRegistryKeyInteger tests
[Test]
public void WindowsRegistryKeyInteger()
{
WindowsRegistryKey TestKey = new()
{
Hive = RegistryHive.CurrentUser,
ValueKind = RegistryValueKind.DWord,
Name = "TestIntigerString",
Path = @"SOFTWARE\Test",
Value = "4711"
};
WindowsRegistryKeyInteger IntKey = new();
IntKey.ConvertFromWindowsRegistryKey(TestKey);
Assert.That(IntKey.IsKeyPresent, Is.EqualTo(true));
Assert.That(IntKey.Value, Is.EqualTo(4711));
}
[Test]
public void WindowsRegistryKeyInteger_ReturnDefault()
{
WindowsRegistryKey TestKey = new()
{
Hive = RegistryHive.CurrentUser,
ValueKind = RegistryValueKind.DWord,
Name = "TestIntigerString",
Path = @"SOFTWARE\Test",
Value = null
};
WindowsRegistryKeyInteger IntKey = new();
IntKey.ConvertFromWindowsRegistryKey(TestKey, 2096);
Assert.That(IntKey.IsKeyPresent, Is.EqualTo(false));
Assert.That(IntKey.Value, Is.EqualTo(2096));
}
#endregion
#region WindowsRegistryKeyString tests
[Test]
public void WindowsRegistryKeyString()
{
WindowsRegistryKey TestKey = new()
{
Hive = RegistryHive.CurrentUser,
ValueKind = RegistryValueKind.DWord,
Name = "TestRegString",
Path = @"SOFTWARE\Test",
Value = "The Big Bang Theory"
};
WindowsRegistryKeyString StrKey = new();
StrKey.ConvertFromWindowsRegistryKey(TestKey);
Assert.That(StrKey.IsKeyPresent, Is.EqualTo(true));
Assert.That(StrKey.Value, Is.EqualTo("The Big Bang Theory"));
}
[Test]
public void WindowsRegistryKeyString_ReturnDefault()
{
WindowsRegistryKey TestKey = new()
{
Hive = RegistryHive.CurrentUser,
ValueKind = RegistryValueKind.DWord,
Name = "TestRegString",
Path = @"SOFTWARE\Test",
Value = null
};
WindowsRegistryKeyString StrKey = new();
StrKey.ConvertFromWindowsRegistryKey(TestKey, "South Park");
Assert.That(StrKey.IsKeyPresent, Is.EqualTo(false));
Assert.That(StrKey.Value, Is.EqualTo("South Park"));
}
[Test]
public void WindowsRegistryKeyString_ValidateSuccess()
{
WindowsRegistryKey TestKey = new()
{
Hive = RegistryHive.CurrentUser,
ValueKind = RegistryValueKind.DWord,
Name = "TestRegString",
Path = @"SOFTWARE\Test",
Value = "Big Bang"
};
WindowsRegistryKeyString StrKey = new()
{
AllowedValues = new[] { "Big Bang", "Big Bang Theory", "The Big Bang Theory" }
};
StrKey.ConvertFromWindowsRegistryKey(TestKey);
Assert.That(StrKey.IsKeyPresent, Is.EqualTo(true));
Assert.That(StrKey.IsValid, Is.EqualTo(true));
}
[Test]
public void WindowsRegistryKeyString_ValidateNotSuccess()
{
WindowsRegistryKey TestKey = new()
{
Hive = RegistryHive.CurrentUser,
ValueKind = RegistryValueKind.DWord,
Name = "TestRegString",
Path = @"SOFTWARE\Test",
Value = "ig ang"
};
WindowsRegistryKeyString StrKey = new()
{
AllowedValues = new[] { "Big Bang", "Big Bang Theory", "The Big Bang Theory" }
};
StrKey.ConvertFromWindowsRegistryKey(TestKey);
Assert.That(StrKey.IsKeyPresent, Is.EqualTo(true));
Assert.That(StrKey.IsValid, Is.EqualTo(false));
}
[Test]
public void WindowsRegistryKeyString_ValidateSuccessCase()
{
WindowsRegistryKey TestKey = new()
{
Hive = RegistryHive.CurrentUser,
ValueKind = RegistryValueKind.DWord,
Name = "TestRegString",
Path = @"SOFTWARE\Test",
Value = "BiG BAng"
};
WindowsRegistryKeyString StrKey = new()
{
AllowedValues = new[] { "BiG BAng", "Big Bang Theory", "The Big Bang Theory" },
IsCaseSensitiveValidation = true
};
StrKey.ConvertFromWindowsRegistryKey(TestKey);
Assert.That(StrKey.IsKeyPresent, Is.EqualTo(true));
Assert.That(StrKey.IsValid, Is.EqualTo(true));
}
[Test]
public void WindowsRegistryKeyString_ValidateNotSuccessCase()
{
WindowsRegistryKey TestKey = new()
{
Hive = RegistryHive.CurrentUser,
ValueKind = RegistryValueKind.DWord,
Name = "TestRegString",
Path = @"SOFTWARE\Test",
Value = "BiG BAng"
};
WindowsRegistryKeyString StrKey = new()
{
AllowedValues = new[] { "Big Bang", "Big Bang Theory", "The Big Bang Theory" },
IsCaseSensitiveValidation = true
};
StrKey.ConvertFromWindowsRegistryKey(TestKey);
Assert.That(StrKey.IsKeyPresent, Is.EqualTo(true));
Assert.That(StrKey.IsValid, Is.EqualTo(false));
}
[Test]
public void WindowsRegistryKeyString_ValidateNotSuccessReturnNull()
{
WindowsRegistryKey TestKey = new()
{
Hive = RegistryHive.CurrentUser,
ValueKind = RegistryValueKind.DWord,
Name = "TestRegString",
Path = @"SOFTWARE\Test",
Value = "ig ang"
};
WindowsRegistryKeyString StrKey = new()
{
AllowedValues = new[] { "Big Bang", "Big Bang Theory", "The Big Bang Theory" }
};
StrKey.ConvertFromWindowsRegistryKey(TestKey);
Assert.That(StrKey.IsKeyPresent, Is.EqualTo(true));
Assert.That(StrKey.IsValid, Is.EqualTo(false));
Assert.That(StrKey.Value, Is.EqualTo(null));
}
[Test]
public void WindowsRegistryKeyString_ValidateNotSuccessValidValue()
{
WindowsRegistryKey TestKey = new()
{
Hive = RegistryHive.CurrentUser,
ValueKind = RegistryValueKind.DWord,
Name = "TestRegString",
Path = @"SOFTWARE\Test",
Value = "ig ang"
};
WindowsRegistryKeyString StrKey = new()
{
AllowedValues = new[] { "Big Bang", "Big Bang Theory", "The Big Bang Theory" }
};
StrKey.ConvertFromWindowsRegistryKey(TestKey, "Big Bang Theory");
Assert.That(StrKey.IsKeyPresent, Is.EqualTo(true));
Assert.That(StrKey.IsValid, Is.EqualTo(false));
Assert.That(StrKey.Value, Is.EqualTo("Big Bang Theory"));
}
#endregion
}
}

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 = ""
};
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.");
}
WindowsRegistryKey keyValue = _registryReader.GetWindowsRegistryKey(key);
Assert.That(keyValue.Value, Is.EqualTo("{098f2470-bae0-11cd-b579-08002b30bfeb}"));
#endregion
#region WindowsRegistry.ThrowIfValueKindInvalid()
[Test]
public void ThrowIfValueKindInvalid_ValidValueKind_DoesNotThrow()
{
var method = GetPrivateStaticMethod("ThrowIfValueKindInvalid");
if (method != null)
Assert.DoesNotThrow(() => method.Invoke(null, new object[] { RegistryValueKind.String }));
else
Assert.Fail("The method ThrowIfValueKindInvalid could not be found.");
}
[Test]
public void CanGetWindowsRegistryKeyForKeyNotExists()
public void ThrowIfValueKindInvalid_InvalidValueKind_ThrowsArgumentException()
{
// No exception. Only null value
WindowsRegistryKey keyValue = _registryReader.GetWindowsRegistryKey(RegistryHive.LocalMachine, @"Software\Testabcdefg", "abcdefg");
Assert.That(keyValue.Value, Is.EqualTo(null));
var method = GetPrivateStaticMethod("ThrowIfValueKindInvalid");
if (method != null)
Assert.Throws<TargetInvocationException>(() => method.Invoke(null, new object[] { (RegistryValueKind)100 }));
else
Assert.Fail("The method ThrowIfValueKindInvalid could not be found.");
}
[Test]
public void GetWindowsRegistryThrowNotReadable()
public void ThrowIfValueKindInvalid_ValueKindUnknown_ThrowsArgumentException()
{
WindowsRegistryKey key = new()
{
Hive = RegistryHive.ClassesRoot,
};
var method = GetPrivateStaticMethod("ThrowIfValueKindInvalid");
if (method != null)
Assert.Throws<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 WindowsRegistry.SetValue()
[Test]
public void SetValue_NewKey_SetsValueSuccessfully()
{
const string testName = "TestValue";
const string testValue = "Test1";
const string testPath = TestPath + @"\SetValue";
Assert.DoesNotThrow(() => SetValue(TestHive, testPath, testName, testValue, RegistryValueKind.String));
string key = GetStringValue(TestHive, testPath, testName, testValue);
Assert.That(key, Is.EqualTo(testValue));
}
[Test]
public void SetValue_OverwriteValue_SetsValueSuccessfully()
{
const string testName = "TestValue";
const string testValue = "Test2";
const string testPath = TestPath + @"\SetValue";
Assert.DoesNotThrow(() => SetValue(TestHive, testPath, testName, testValue, RegistryValueKind.String));
string key = GetStringValue(TestHive, testPath, testName, testValue);
Assert.That(key, Is.EqualTo(testValue));
}
[Test]
public void SetValue_DefaultValueWithWrongValueKind_SetsValueSuccessfully()
{
const string? testName = null;
const string testValue = "SetDefault";
const string testPath = TestPath + @"\SetValue";
Assert.DoesNotThrow(() => SetValue(TestHive, testPath, testName, testValue, RegistryValueKind.Unknown));
string key = GetStringValue(TestHive, testPath, testName, testValue);
Assert.That(key, Is.EqualTo(testValue));
}
#endregion
#region WindowsRegistry.CreateKey()
[Test]
public void CreateKey_NewKey_CreatesKeySuccessfully()
{
const string testPath = TestPath + @"\TestSubKey";
Assert.DoesNotThrow(() => CreateKey(TestHive, testPath));
}
[Test]
public void CreateKey_ExistingKey_DoesNotThrow()
{
const string testPath = TestPath + @"\TestSubKey";
Assert.DoesNotThrow(() => CreateKey(TestHive, testPath));
}
[Test]
public void CreateKey_InvalidHive_ThrowsArgumentException()
{
Assert.Throws<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"));
}
Assert.Throws<InvalidOperationException>(() => _registryReader.GetWindowsRegistryKey(key));
[Test]
public void GetSubKeyNames_NonExistingKey_ReturnsEmptyArray()
{
string[] subKeyNames = GetSubKeyNames(TestHive, @"Software\NonExistingKey");
Assert.That(subKeyNames, Is.Empty);
}
#endregion
#region GetRegistryEntries() + Recurse tests
#region WindowsRegistry.GetValue()
[Test]
public void CanGetRegistryEntries()
public void GetValue_RetrieveValue_ReturnsValue()
{
List<WindowsRegistryKey> keys = _registryReader.GetRegistryEntries(RegistryHive.LocalMachine, @"HARDWARE\DESCRIPTION\System\BIOS");
Assert.That(keys.Count, Is.Not.EqualTo(0));
const string testPath = TestPath + @"\GetValue";
const string testName = "TestValue";
const string testValue = "Test";
Assert.DoesNotThrow(() => SetValue(TestHive, testPath, testName, testValue, RegistryValueKind.String));
string key = GetValue(TestHive, testPath, testName);
Assert.That(key, Is.EqualTo(testValue));
}
[Test]
public void CanGetRegistryEntriesRecurse()
public void GetValue_RetrieveDefault_RetrunsValue()
{
List<WindowsRegistryKey> keys = _registryReader.GetRegistryEntryiesRecursive(RegistryHive.LocalMachine, @"SOFTWARE\Microsoft\Windows\Windows Search");
Assert.That(keys.Count, Is.Not.EqualTo(0));
const string testPath = TestPath + @"\GetValue";
const string testName = "";
const string testValue = "TestDefault{098f2470-bae0-11cd-b579-08002b30bfeb}";
Assert.DoesNotThrow(() => SetValue(TestHive, testPath, testName, testValue, RegistryValueKind.String));
var key = GetValue(TestHive, testPath, testName);
Assert.That(key, Is.EqualTo(testValue));
}
[Test]
public void GetValue_NonExistingValue_ReturnsNull()
{
const string testPath = TestPath + @"\GetValue";
const string nonExistingName = "NonExistingValue";
string key = GetValue(TestHive, testPath, nonExistingName);
Assert.That(key, Is.Null);
}
[Test]
public void SetAndGetDefaultValue_RetrieveValue_ReturnsValue()
{
const string testPath = TestPath + @"\DefautValue";
const string testValue = "DefaultTestTestValue";
Assert.DoesNotThrow(() => SetDefaultValue(TestHive, testPath, testValue));
string key = GetDefaultValue(TestHive, testPath);
Assert.That(key, Is.EqualTo(testValue));
}
#endregion
#region new WindowsRegistryKey() tests
#region WindowsRegistry.GetStringValue()
[Test]
public void IsWindowsRegistryKeyValid()
public void GetStringValue_RetrieveValue_ReturnsValue()
{
// Tests property rules of WindowsRegistryKey
WindowsRegistryKey key = new();
const string testPath = TestPath + @"\GetStringValue";
const string testName = "TestValue";
const string testValue = "Test";
Assert.DoesNotThrow(() => SetValue(TestHive, testPath, testName, testValue, RegistryValueKind.String));
var key = GetStringValue(TestHive, testPath, testName);
Assert.That(key, Is.TypeOf<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));
}
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)
{
Assert.Multiple(() =>
{
Assert.That(entry.Name, Is.Not.Null & Is.Not.EqualTo(testNameSubKey)); //Subkeys should not be read (non-recursive).
Assert.That(entry.Hive, Is.EqualTo(TestHive));
Assert.That(entry.Path, Is.EqualTo(testPath));
Assert.That(entry.Value, Is.EqualTo(testValue));
Assert.That(entry.ValueKind, Is.EqualTo(RegistryValueKind.String));
});
}
}
#endregion
#region WinRegistryEC.GetRegistryEntriesRecursive()
[Test]
public void WindowsRegistryKeyThrowNameNullException()
public void GetRegistryEntriesRecursive_ReturnsCorrectEntries()
{
WindowsRegistryKey key = new();
Assert.Throws<ArgumentNullException>(() => key.Name = null, "Expected IsNameValid to throw ArgumentNullException");
const string testPath = TestPath + @"\GetRegistryEntriesRecursive";
const string testNamePrefix = "TestEntry";
const string testValue = "winRegEntriesTest";
for (int i = 1; i <= 10; i++)
{
string testName = $"{testNamePrefix}{i}";
Assert.DoesNotThrow(() => SetValue(TestHive, testPath, testName, testValue, RegistryValueKind.String));
}
const string testSubkeyPath = testPath + @"\Subkey";
const string testNameSubKey = "TestSubEntry";
Assert.DoesNotThrow(() => SetValue(TestHive, testSubkeyPath, testNameSubKey, testValue, RegistryValueKind.String));
var entries = GetEntriesRecursive(TestHive, testPath);
Assert.That(entries, Is.Not.Null);
Assert.That(entries, Is.Not.Empty);
Assert.That(entries, Is.InstanceOf<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
[Test]
public void CanSetRegistryValue()
{
Assert.DoesNotThrow(() => _registryWriter.SetRegistryValue(RegistryHive.CurrentUser, @"SOFTWARE\mRemoteNGTest", "TestKey", "A value string", RegistryValueKind.String));
}
#region WindowsRegistry.DeleteTree()
[Test]
public void SetRegistryValueThrowAccessDenied()
public void DeleteTree_RemovesKeyAndSubkeys()
{
Assert.Throws<InvalidOperationException>(() => _registryWriter.SetRegistryValue(RegistryHive.LocalMachine, @"SOFTWARE\mRemoteNGTest", "TestKey", "A value string", RegistryValueKind.String));
const string testPath = TestPath + @"\DeleteTree";
Assert.DoesNotThrow(() => SetValue(TestHive, $"{testPath}\\Subkey0", "Test", "Test", RegistryValueKind.String));
Assert.DoesNotThrow(() => SetValue(TestHive, $"{testPath}\\Subkey1", "Test", "Test", RegistryValueKind.String));
Assert.DoesNotThrow(() => SetValue(TestHive, $"{testPath}\\Subkey2", "Test", "Test", RegistryValueKind.String));
Assert.DoesNotThrow(() => SetValue(TestHive, $"{testPath}\\Subkey3", "Test", "Test", RegistryValueKind.String));
Assert.DoesNotThrow(() => DeleteTree(TestHive, testPath));
Assert.That(GetSubKeyNames(TestHive, testPath), Is.Empty);
}
#endregion
#region WindowsRegistry.ConvertStringToRegistryHive()
[Test]
public void ConvertStringToRegistryHive_ReturnsCorrectValue()
{
Assert.Multiple(() =>
{
Assert.That(ConvertStringToRegistryHive("HKCR"), Is.EqualTo(RegistryHive.ClassesRoot));
Assert.That(ConvertStringToRegistryHive("HKey_Classes_Root"), Is.EqualTo(RegistryHive.ClassesRoot));
Assert.That(ConvertStringToRegistryHive("ClassesRoot"), Is.EqualTo(RegistryHive.ClassesRoot));
Assert.That(ConvertStringToRegistryHive("HKCU"), Is.EqualTo(RegistryHive.CurrentUser));
Assert.That(ConvertStringToRegistryHive("HKey_Current_User"), Is.EqualTo(RegistryHive.CurrentUser));
Assert.That(ConvertStringToRegistryHive("currentuser"), Is.EqualTo(RegistryHive.CurrentUser));
Assert.That(ConvertStringToRegistryHive("HKLM"), Is.EqualTo(RegistryHive.LocalMachine));
Assert.That(ConvertStringToRegistryHive("HKey_Local_Machine"), Is.EqualTo(RegistryHive.LocalMachine));
Assert.That(ConvertStringToRegistryHive("LocalMachine"), Is.EqualTo(RegistryHive.LocalMachine));
Assert.That(ConvertStringToRegistryHive("HKU"), Is.EqualTo(RegistryHive.Users));
Assert.That(ConvertStringToRegistryHive("HKey_users"), Is.EqualTo(RegistryHive.Users));
Assert.That(ConvertStringToRegistryHive("Users"), Is.EqualTo(RegistryHive.Users));
Assert.That(ConvertStringToRegistryHive("HKCC"), Is.EqualTo(RegistryHive.CurrentConfig));
Assert.That(ConvertStringToRegistryHive("HKey_Current_Config"), Is.EqualTo(RegistryHive.CurrentConfig));
Assert.That(ConvertStringToRegistryHive("CurrentConfig"), Is.EqualTo(RegistryHive.CurrentConfig));
});
}
[Test]
public void ConvertStringToRegistryHive_InvalidHiveNull_ThrowArgumentNullException()
{
Assert.That(() => ConvertStringToRegistryHive(null), Throws.ArgumentNullException);
}
[Test]
public void ConvertStringToRegistryHive_InvalidHive_ThrowArgumentException()
{
Assert.That(() => ConvertStringToRegistryHive("InvalidHive"), Throws.ArgumentException);
}
#endregion
#region WindowsRegistry.ConvertStringToRegistryValueKind()
[Test]
public void ConvertStringToRegistryValueKind_ReturnsCorrectValue()
{
Assert.Multiple(() =>
{
Assert.That(ConvertStringToRegistryValueKind("string"), Is.EqualTo(RegistryValueKind.String));
Assert.That(ConvertStringToRegistryValueKind("reg_sz"), Is.EqualTo(RegistryValueKind.String));
Assert.That(ConvertStringToRegistryValueKind("dword"), Is.EqualTo(RegistryValueKind.DWord));
Assert.That(ConvertStringToRegistryValueKind("reg_dword"), Is.EqualTo(RegistryValueKind.DWord));
Assert.That(ConvertStringToRegistryValueKind("binary"), Is.EqualTo(RegistryValueKind.Binary));
Assert.That(ConvertStringToRegistryValueKind("reg_binary"), Is.EqualTo(RegistryValueKind.Binary));
Assert.That(ConvertStringToRegistryValueKind("qword"), Is.EqualTo(RegistryValueKind.QWord));
Assert.That(ConvertStringToRegistryValueKind("reg_qword"), Is.EqualTo(RegistryValueKind.QWord));
Assert.That(ConvertStringToRegistryValueKind("multistring"), Is.EqualTo(RegistryValueKind.MultiString));
Assert.That(ConvertStringToRegistryValueKind("reg_multi_sz"), Is.EqualTo(RegistryValueKind.MultiString));
Assert.That(ConvertStringToRegistryValueKind("expandstring"), Is.EqualTo(RegistryValueKind.ExpandString));
Assert.That(ConvertStringToRegistryValueKind("reg_expand_sz"), Is.EqualTo(RegistryValueKind.ExpandString));
});
}
[Test]
public void ConvertStringToRegistryValueKind_InvalidHiveNull_ThrowArgumentNullException()
{
Assert.That(() => ConvertStringToRegistryValueKind(null), Throws.ArgumentNullException);
}
[Test]
public void ConvertStringToRegistryValueKind_InvalidHive_ThrowArgumentException()
{
Assert.That(() => ConvertStringToRegistryValueKind("InvalidKind"), Throws.ArgumentException);
}
#endregion
#region WindowsRegistry.ConvertTypeToRegistryValueKind()
[Test]
public void ConvertTypeToRegistryValueKind_ReturnsCorrectValue()
{
Assert.Multiple(() =>
{
Assert.That(ConvertTypeToRegistryValueKind(typeof(string)), Is.EqualTo(RegistryValueKind.String));
Assert.That(ConvertTypeToRegistryValueKind(typeof(int)), Is.EqualTo(RegistryValueKind.DWord));
Assert.That(ConvertTypeToRegistryValueKind(typeof(long)), Is.EqualTo(RegistryValueKind.QWord));
Assert.That(ConvertTypeToRegistryValueKind(typeof(bool)), Is.EqualTo(RegistryValueKind.DWord));
Assert.That(ConvertTypeToRegistryValueKind(typeof(byte)), Is.EqualTo(RegistryValueKind.Binary));
});
}
[Test]
public void ConvertTypeToRegistryValueKind_UnknownType_ReturnsString()
{
Assert.That(ConvertTypeToRegistryValueKind(typeof(Single)), Is.EqualTo(RegistryValueKind.String));
}
[Test]
public void ConvertTypeToRegistryValueKind_WithNullValueType_ThrowsArgumentNullException()
{
Assert.Throws<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);
}
}
}