diff --git a/Directory.Packages.props b/Directory.Packages.props
index 0fff8254..13432064 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -5,8 +5,8 @@
$(NoWarn);NU1507
-
-
+
+
diff --git a/mRemoteNG/App/ProgramRoot.cs b/mRemoteNG/App/ProgramRoot.cs
index ee9af1c1..848b4fe7 100644
--- a/mRemoteNG/App/ProgramRoot.cs
+++ b/mRemoteNG/App/ProgramRoot.cs
@@ -1,14 +1,20 @@
-using System;
+using mRemoteNG.Config.Settings;
+using mRemoteNG.DotNet.Update;
+using mRemoteNG.UI.Forms;
+
+using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Reflection;
+using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Threading;
using System.Windows.Forms;
-using mRemoteNG.Config.Settings;
-using mRemoteNG.UI.Forms;
-using System.Runtime.InteropServices;
+using System.Threading.Tasks;
+using mRemoteNG.DotNet.Update;
+
+
namespace mRemoteNG.App
{
@@ -25,13 +31,48 @@ namespace mRemoteNG.App
[STAThread]
public static void Main(string[] args)
{
- Trace.WriteLine("!!!!!!=============== TEST ==================!!!!!!!!!!!!!");
- // Forcing to load System.Configuration.ConfigurationManager before any other assembly to be able to check settings
try
{
- string assemblyFile = "System.Configuration.ConfigurationManager" + ".dll";
- string assemblyPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Assemblies", assemblyFile);
+ // FIX: Awaited Task synchronously to obtain bool result
+ bool isInstalled = DotNetRuntimeCheck
+ .IsDotnetRuntimeInstalled(DotNetRuntimeCheck.RequiredDotnetVersion)
+ .GetAwaiter()
+ .GetResult();
+ if (!isInstalled)
+ {
+ Trace.WriteLine($".NET Desktop Runtime {DotNetRuntimeCheck.RequiredDotnetVersion} is NOT installed.");
+ Trace.WriteLine("Please download and install it from:");
+ Trace.WriteLine("https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-9.0.8-windows-x64-installer");
+
+ try
+ {
+ MessageBox.Show(
+ $".NET Desktop Runtime {DotNetRuntimeCheck.RequiredDotnetVersion} is required.\n" +
+ "The application will now exit.\n\nDownload:\nhttps://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-9.0.8-windows-x64-installer",
+ "Missing .NET Runtime",
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Error);
+ } catch {
+ // Ignore UI issues
+ }
+
+ Environment.Exit(1);
+ return;
+ }
+
+ Trace.WriteLine($".NET Desktop Runtime {DotNetRuntimeCheck.RequiredDotnetVersion} is installed.");
+ } catch (Exception ex) {
+ Trace.WriteLine("Runtime check failed: " + ex);
+ Environment.Exit(1);
+ return;
+ }
+
+ Trace.WriteLine("!!!!!!=============== TEST ==================!!!!!!!!!!!!!");
+ try
+ {
+ string assemblyFile = "System.Configuration.ConfigurationManager.dll";
+ string assemblyPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Assemblies", assemblyFile);
if (File.Exists(assemblyPath))
{
@@ -40,13 +81,11 @@ namespace mRemoteNG.App
}
catch (FileNotFoundException ex)
{
- Trace.WriteLine("Error occured: " + ex.Message);
+ Trace.WriteLine("Error occured: " + ex.Message);
}
- //Subscribe to AssemblyResolve event
AppDomain.CurrentDomain.AssemblyResolve += OnAssemblyResolve;
- //Check if needed runtime is installed
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
string runtimeVersion = RuntimeInformation.FrameworkDescription;
@@ -66,12 +105,9 @@ namespace mRemoteNG.App
Console.WriteLine("This application requires the .NET Desktop Runtime 9.0.2 on Windows.");
}
-
-
- //Check if local settings DB exist or accessible
CheckLockalDB();
- Lazy singleInstanceOption = new Lazy(() => Properties.OptionsStartupExitPage.Default.SingleInstance);
+ Lazy singleInstanceOption = new(() => Properties.OptionsStartupExitPage.Default.SingleInstance);
if (singleInstanceOption.Value)
{
@@ -87,20 +123,20 @@ namespace mRemoteNG.App
{
LocalDBManager settingsManager = new LocalDBManager(dbPath: "mRemoteNG.appSettings", useEncryption: false, schemaFilePath: "");
}
+
private static Assembly OnAssemblyResolve(object sender, ResolveEventArgs resolveArgs)
{
string assemblyName = new AssemblyName(resolveArgs.Name).Name.Replace(".resources", string.Empty);
string assemblyFile = assemblyName + ".dll";
string assemblyPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Assemblies", assemblyFile);
-
if (File.Exists(assemblyPath))
{
return Assembly.LoadFrom(assemblyPath);
}
return null;
}
-
+
private static void StartApplication()
{
CatchAllUnhandledExceptions();
@@ -114,7 +150,6 @@ namespace mRemoteNG.App
Rectangle viewport = targetScreen.WorkingArea;
_frmSplashScreen.Top = viewport.Top;
_frmSplashScreen.Left = viewport.Left;
- // normally it should be screens[1] however due DPI apply 1 size "same" as default with 100%
_frmSplashScreen.Left = viewport.Left + (targetScreen.Bounds.Size.Width - _frmSplashScreen.Width) / 2;
_frmSplashScreen.Top = viewport.Top + (targetScreen.Bounds.Size.Height - _frmSplashScreen.Height) / 2;
_frmSplashScreen.ShowInTaskbar = false;
@@ -168,14 +203,13 @@ namespace mRemoteNG.App
private static void CatchAllUnhandledExceptions()
{
- System.Windows.Forms.Application.ThreadException += ApplicationOnThreadException;
- System.Windows.Forms.Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
+ Application.ThreadException += ApplicationOnThreadException;
+ Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);
AppDomain.CurrentDomain.UnhandledException += CurrentDomainOnUnhandledException;
}
private static void ApplicationOnThreadException(object sender, ThreadExceptionEventArgs e)
{
- // if (PresentationSource.FromVisual(FrmSplashScreenNew))
FrmSplashScreenNew.GetInstance().Close();
if (FrmMain.Default.IsDisposed) return;
@@ -186,13 +220,8 @@ namespace mRemoteNG.App
private static void CurrentDomainOnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
- //TODO: Check if splash closed properly
- //if (!FrmSplashScreenNew.GetInstance().IsDisposed)
- // FrmSplashScreenNew.GetInstance().Close();
-
FrmUnhandledException window = new(e.ExceptionObject as Exception, e.IsTerminating);
window.ShowDialog(FrmMain.Default);
}
-
}
}
\ No newline at end of file
diff --git a/mRemoteNG/App/Update/DotNetRuntimeCheck.cs b/mRemoteNG/App/Update/DotNetRuntimeCheck.cs
new file mode 100644
index 00000000..019343a8
--- /dev/null
+++ b/mRemoteNG/App/Update/DotNetRuntimeCheck.cs
@@ -0,0 +1,66 @@
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Linq;
+using System.Net.Http;
+using System.Threading.Tasks;
+
+namespace mRemoteNG.DotNet.Update
+{
+ public class DotNetRuntimeCheck
+ {
+ public const string RequiredDotnetVersion = "9.0.8";
+ public const string DotnetInstallerUrl = "https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-9.0.8-windows-x64-installer";
+ public const string DotnetInstallerFileName = "windowsdesktop-runtime-9.0.8-win-x64.exe";
+
+ public static async Task Main(string[] args)
+ {
+ if (await IsDotnetRuntimeInstalled(RequiredDotnetVersion))
+ {
+ Console.WriteLine($".NET Desktop Runtime {RequiredDotnetVersion} is installed. Launching application...");
+ }
+ else
+ {
+ Console.WriteLine($".NET Desktop Runtime {RequiredDotnetVersion} is not installed.");
+ }
+ }
+ ///
+ /// Checks if a specific version of the .NET runtime is installed by running `dotnet --list-runtimes`.
+ ///
+ public static async Task IsDotnetRuntimeInstalled(string version)
+ {
+ try
+ {
+ // Set up a process to run the 'dotnet' command.
+ var process = new Process
+ {
+ StartInfo = new ProcessStartInfo
+ {
+ FileName = "dotnet",
+ Arguments = "--list-runtimes",
+ RedirectStandardOutput = true,
+ UseShellExecute = false,
+ CreateNoWindow = true,
+ }
+ };
+
+ process.Start();
+
+ // Read the output from the command.
+ var output = await process.StandardOutput.ReadToEndAsync();
+ process.WaitForExit();
+
+ // Check if the output contains the required runtime and version.
+ // The format is typically: Microsoft.NETCore.App 9.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
+ return output.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
+ .Any(line => line.Trim().StartsWith($"Microsoft.NETCore.App {version}") ||
+ line.Trim().StartsWith($"Microsoft.WindowsDesktop.App {version}"));
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine($"Could not check .NET runtimes. Please ensure 'dotnet' is in your PATH. Error: {ex.Message}");
+ return false;
+ }
+ }
+ } //Check
+}