mirror of
https://github.com/mRemoteNG/mRemoteNG.git
synced 2026-02-17 14:07:46 +08:00
feat: Introduce RegistryLoader class with Lazy<T> and async registry settings loading
- Added the RegistryLoader class to handle loading of registry settings. - Implemented Lazy<T> for singleton pattern to ensure only one instance of RegistryLoader is created. - Added support for asynchronous loading of registry settings to improve performance in async workflows. - Designed the class with thread safety in mind, leveraging ConcurrentDictionary for storage. - Included logging of debug messages to track the load process and timing.
This commit is contained in:
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.ComponentModel;
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
@@ -8,6 +7,7 @@ using mRemoteNG.App.Info;
|
|||||||
using mRemoteNG.App.Initialization;
|
using mRemoteNG.App.Initialization;
|
||||||
using mRemoteNG.App.Update;
|
using mRemoteNG.App.Update;
|
||||||
using mRemoteNG.Config.Connections.Multiuser;
|
using mRemoteNG.Config.Connections.Multiuser;
|
||||||
|
using mRemoteNG.Config.Settings.Registry;
|
||||||
using mRemoteNG.Connection;
|
using mRemoteNG.Connection;
|
||||||
using mRemoteNG.Messages;
|
using mRemoteNG.Messages;
|
||||||
using mRemoteNG.Properties;
|
using mRemoteNG.Properties;
|
||||||
@@ -22,6 +22,7 @@ namespace mRemoteNG.App
|
|||||||
[SupportedOSPlatform("windows")]
|
[SupportedOSPlatform("windows")]
|
||||||
public class Startup
|
public class Startup
|
||||||
{
|
{
|
||||||
|
private RegistryLoader _RegistryLoader;
|
||||||
private AppUpdater _appUpdate;
|
private AppUpdater _appUpdate;
|
||||||
private readonly ConnectionIconLoader _connectionIconLoader;
|
private readonly ConnectionIconLoader _connectionIconLoader;
|
||||||
private readonly FrmMain _frmMain = FrmMain.Default;
|
private readonly FrmMain _frmMain = FrmMain.Default;
|
||||||
@@ -30,7 +31,8 @@ namespace mRemoteNG.App
|
|||||||
|
|
||||||
private Startup()
|
private Startup()
|
||||||
{
|
{
|
||||||
_appUpdate = new AppUpdater();
|
_RegistryLoader = RegistryLoader.Instance; //created instance
|
||||||
|
_appUpdate = new AppUpdater();
|
||||||
_connectionIconLoader = new ConnectionIconLoader(GeneralAppInfo.HomePath + "\\Icons\\");
|
_connectionIconLoader = new ConnectionIconLoader(GeneralAppInfo.HomePath + "\\Icons\\");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
187
mRemoteNG/Config/Settings/Registry/RegistryLoader.cs
Normal file
187
mRemoteNG/Config/Settings/Registry/RegistryLoader.cs
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
using mRemoteNG.App;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.Versioning;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace mRemoteNG.Config.Settings.Registry
|
||||||
|
{
|
||||||
|
[SupportedOSPlatform("windows")]
|
||||||
|
public class RegistryLoader : IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This instance is used to load registry settings at program startup.cs, ensuring that
|
||||||
|
/// these settings are applied to regular configurations and can override them as needed.
|
||||||
|
/// Once the settings are loaded, this lazy instance will primarily be used for configuring
|
||||||
|
/// the options page. After this initialization phase, the lazy instance may no longer be necessary and get disposed.
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Lazy<RegistryLoader> instance =
|
||||||
|
new(() => new RegistryLoader());
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Singleton instance of the RegistryMultiLoader class.
|
||||||
|
/// </summary>
|
||||||
|
public static RegistryLoader Instance => instance.Value;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dictionary to store settings for all registry pages.
|
||||||
|
/// </summary>
|
||||||
|
public static ConcurrentDictionary<Type, object> RegistrySettings { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Static constructor that initializes the registry settings dictionary
|
||||||
|
/// and starts loading settings asynchronously at startup.
|
||||||
|
/// </summary>
|
||||||
|
static RegistryLoader()
|
||||||
|
{
|
||||||
|
RegistrySettings = new ConcurrentDictionary<Type, object>();
|
||||||
|
// Start loading asynchronously on startup
|
||||||
|
_ = LoadAllAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cleans up the registry settings by removing entries of a specified type.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="deleteEntries">The type of entries to remove from the dictionary.</param>
|
||||||
|
/// <remarks>
|
||||||
|
/// If the dictionary becomes empty after removal and the singleton instance has been created,
|
||||||
|
/// it disposes of the instance to free resources.
|
||||||
|
/// </remarks>
|
||||||
|
public static void Cleanup(Type deleteEntries)
|
||||||
|
{
|
||||||
|
// Remove the registry setting from the dictionary
|
||||||
|
RegistrySettings?.TryRemove(deleteEntries, out _);
|
||||||
|
|
||||||
|
if (RegistrySettings.IsEmpty && instance.IsValueCreated)
|
||||||
|
{
|
||||||
|
Instance.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously loads and applies multiple registry classes in parallel.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>A task representing the asynchronous operation.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method creates a list of tasks, each responsible for initializing
|
||||||
|
/// and applying a specific registry class. It awaits the completion of all tasks
|
||||||
|
/// using Task.WhenAll.
|
||||||
|
/// </remarks>
|
||||||
|
private static async Task LoadAllAsync()
|
||||||
|
{
|
||||||
|
#if DEBUG
|
||||||
|
Stopwatch stopwatch = Stopwatch.StartNew();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// Create a list of tasks to initialize and apply each registry class asynchronously
|
||||||
|
var tasks = new List<Task>
|
||||||
|
{
|
||||||
|
LoadAndApplyAsync<OptRegistryAppearancePage>(),
|
||||||
|
LoadAndApplyAsync<OptRegistryConnectionsPage>(),
|
||||||
|
LoadAndApplyAsync<OptRegistryCredentialsPage>(),
|
||||||
|
LoadAndApplyAsync<OptRegistryNotificationsPage>(),
|
||||||
|
LoadAndApplyAsync<OptRegistrySecurityPage>(),
|
||||||
|
LoadAndApplyAsync<OptRegistrySqlServerPage>(),
|
||||||
|
LoadAndApplyAsync<OptRegistryStartupExitPage>(),
|
||||||
|
LoadAndApplyAsync<OptRegistryTabsPanelsPage>(),
|
||||||
|
LoadAndApplyAsync<OptRegistryUpdatesPage>()
|
||||||
|
};
|
||||||
|
|
||||||
|
// Await all tasks to complete
|
||||||
|
await Task.WhenAll(tasks);
|
||||||
|
|
||||||
|
Runtime.MessageCollector.AddMessage(Messages.MessageClass.DebugMsg,
|
||||||
|
$"Registry settings loaded and applied asynchronously in {typeof(RegistryLoader).Name}", true);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Runtime.MessageCollector.AddMessage(Messages.MessageClass.DebugMsg,
|
||||||
|
$"Registry settings error during load: {ex.Message}", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
stopwatch.Stop();
|
||||||
|
Runtime.MessageCollector.AddMessage(Messages.MessageClass.DebugMsg,
|
||||||
|
$"Registry settings total async load time: {stopwatch.ElapsedMilliseconds} ms", true);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Asynchronously initializes and applies registry settings for a specified type.
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">The type of the registry settings class, which must have a parameterless constructor.</typeparam>
|
||||||
|
/// <returns>A task representing the asynchronous operation.</returns>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method creates an instance of the specified type T, stores it in a dictionary
|
||||||
|
/// using its type as the key, and completes the task.
|
||||||
|
/// </remarks>
|
||||||
|
private static async Task LoadAndApplyAsync<T>() where T : new()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
// create an instance of setting
|
||||||
|
var instance = new T();
|
||||||
|
|
||||||
|
// Store the instance in the dictionary using its type as key
|
||||||
|
RegistrySettings[typeof(T)] = instance;
|
||||||
|
|
||||||
|
// complete task
|
||||||
|
await Task.CompletedTask;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
Runtime.MessageCollector.AddMessage(Messages.MessageClass.DebugMsg,
|
||||||
|
$"Registry settings error during load {typeof(T).Name}: {ex.Message}", true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#region IDisposable Support
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Implements the IDisposable pattern for resource cleanup.
|
||||||
|
/// </summary>
|
||||||
|
private bool disposedValue = false; // To detect redundant calls
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Releases the resources used by the object.
|
||||||
|
/// </summary>
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (disposedValue)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
|
||||||
|
disposedValue = true;
|
||||||
|
|
||||||
|
Runtime.MessageCollector.AddMessage(Messages.MessageClass.DebugMsg,
|
||||||
|
$"Registry settings lazy instance of {typeof(RegistryLoader).Name} has been disposed.", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Performs the actual resource cleanup.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="disposing">True if managed resources should be released.</param>
|
||||||
|
|
||||||
|
protected virtual void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (disposing)
|
||||||
|
RegistrySettings?.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Finalizer that is called to free unmanaged resources.
|
||||||
|
/// </summary>
|
||||||
|
~RegistryLoader()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion IDisposable Support
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -300,6 +300,9 @@ namespace mRemoteNG.Tools.WindowsRegistry
|
|||||||
AllowedValues = allowedValues;
|
AllowedValues = allowedValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ElementType == typeof(string) && privateValue != null)
|
||||||
|
privateValue = EnforceStringInputValidity(privateValue);
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user