Compare commits

..

11 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
5b59518364 Fix RDP connections for AD Protected Users by supporting Kerberos-only modes
Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com>
2026-02-25 17:37:30 +00:00
copilot-swe-agent[bot]
ba72c1666c Initial plan 2026-02-25 17:28:12 +00:00
Dimitrij
43735b1d04 Merge pull request #3169 from mRemoteNG/renovate/vstest-monorepo
Update dependency Microsoft.NET.Test.Sdk to 18.3.0
2026-02-24 17:25:53 +00:00
renovate[bot]
002f6cb290 Update dependency Microsoft.NET.Test.Sdk to 18.3.0 2026-02-24 13:30:48 +00:00
Dimitrij
92c617d442 Merge pull request #3168 from mRemoteNG/renovate/aws-sdk-net-monorepo
Update aws-sdk-net monorepo
2026-02-24 10:20:28 +00:00
renovate[bot]
764b96f864 Update aws-sdk-net monorepo 2026-02-23 21:57:17 +00:00
Dimitrij
e30a8ad3f0 Merge pull request #3164 from mRemoteNG/renovate/chromiumembeddedframework.runtime.win-arm64-145.x
Update dependency chromiumembeddedframework.runtime.win-arm64 to v145
2026-02-22 16:26:38 +00:00
Dimitrij
1e85969e3a Merge pull request #3165 from mRemoteNG/renovate/chromiumembeddedframework.runtime.win-x64-145.x
Update dependency chromiumembeddedframework.runtime.win-x64 to v145
2026-02-22 16:26:24 +00:00
renovate[bot]
1a47bba982 Update dependency chromiumembeddedframework.runtime.win-x64 to v145 2026-02-22 16:24:37 +00:00
renovate[bot]
1cc5f05bf5 Update dependency chromiumembeddedframework.runtime.win-arm64 to v145 2026-02-22 16:24:32 +00:00
Dimitrij
7e0277f85d Update GitHub regex to exclude user-attachments
allow attachments
2026-02-22 16:24:08 +00:00
15 changed files with 44 additions and 311 deletions

View File

@@ -20,7 +20,7 @@ jobs:
with:
script: |
const githubRepoRegex =
/\[[^\]]*\]\(https?:\/\/github\.com\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+[^\s)]*\)|https?:\/\/github\.com\/[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+[^\s)]*/gi;
/\[[^\]]*\]\((?!https?:\/\/github\.com\/user-attachments\/assets\/)[^\)]*https?:\/\/github\.com\/(?!user-attachments\/assets\/)[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+[^\s)]*\)|https?:\/\/github\.com\/(?!user-attachments\/assets\/)[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+[^\s)]*/gi;
// CASE 1: Comment
if (context.payload.comment) {

View File

@@ -5,8 +5,8 @@
<NoWarn>$(NoWarn);NU1507;NU1701</NoWarn>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="AWSSDK.Core" Version="4.0.3.14" />
<PackageVersion Include="AWSSDK.EC2" Version="4.0.76" />
<PackageVersion Include="AWSSDK.Core" Version="4.0.3.15" />
<PackageVersion Include="AWSSDK.EC2" Version="4.0.76.1" />
<PackageVersion Include="BouncyCastle.Cryptography" Version="2.6.2" />
<PackageVersion Include="Castle.Core" Version="5.2.1" />
<PackageVersion Include="ConsoleControl" Version="1.3.0" />
@@ -25,7 +25,7 @@
<PackageVersion Include="Microsoft.Data.SqlClient.SNI.runtime" Version="6.0.2" />
<PackageVersion Include="Microsoft.Extensions.DependencyModel" Version="10.0.3" />
<PackageVersion Include="Microsoft.Extensions.Configuration.UserSecrets" Version="10.0.3" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
<PackageVersion Include="Microsoft.NETCore.Platforms" Version="7.0.4" />
<PackageVersion Include="Microsoft.NETCore.Targets" Version="5.0.0" />
<PackageVersion Include="Microsoft.VisualStudio.TextTemplating.VSHost" Version="17.14.40265" />

View File

@@ -56,20 +56,6 @@ namespace mRemoteNG.Connection
[Browsable(false)]
public bool PleaseConnect { get; set; }
private HostStatus _hostStatus = HostStatus.Unknown;
[Browsable(false)]
public HostStatus HostStatus
{
get => _hostStatus;
set
{
if (_hostStatus == value) return;
_hostStatus = value;
RaisePropertyChangedEvent(this, new PropertyChangedEventArgs(nameof(HostStatus)));
}
}
#endregion
#region Constructors

View File

@@ -1,9 +0,0 @@
namespace mRemoteNG.Connection
{
public enum HostStatus
{
Unknown,
Online,
Offline
}
}

View File

@@ -641,20 +641,27 @@ namespace mRemoteNG.Connection.Protocol.RDP
_rdpClient.UserName = userName;
}
if (string.IsNullOrEmpty(password))
// Restricted Admin and Remote Credential Guard modes use the current user's Kerberos
// credentials and do not forward explicit passwords to the remote host.
// Skipping password assignment avoids potential NTLM fallback attempts that would
// fail for accounts in the AD Protected Users security group.
if (!connectionInfo.UseRestrictedAdmin && !connectionInfo.UseRCG)
{
if (Properties.OptionsCredentialsPage.Default.EmptyCredentials == "custom")
if (string.IsNullOrEmpty(password))
{
if (Properties.OptionsCredentialsPage.Default.DefaultPassword != "")
if (Properties.OptionsCredentialsPage.Default.EmptyCredentials == "custom")
{
LegacyRijndaelCryptographyProvider cryptographyProvider = new();
_rdpClient.AdvancedSettings2.ClearTextPassword = cryptographyProvider.Decrypt(Properties.OptionsCredentialsPage.Default.DefaultPassword, Runtime.EncryptionKey);
if (Properties.OptionsCredentialsPage.Default.DefaultPassword != "")
{
LegacyRijndaelCryptographyProvider cryptographyProvider = new();
_rdpClient.AdvancedSettings2.ClearTextPassword = cryptographyProvider.Decrypt(Properties.OptionsCredentialsPage.Default.DefaultPassword, Runtime.EncryptionKey);
}
}
}
}
else
{
_rdpClient.AdvancedSettings2.ClearTextPassword = password;
else
{
_rdpClient.AdvancedSettings2.ClearTextPassword = password;
}
}
if (string.IsNullOrEmpty(domain))

View File

@@ -6099,15 +6099,6 @@ namespace mRemoteNG.Resources.Language {
}
}
/// <summary>
/// Looks up a localized string similar to Show host status indicator in connection tree.
/// </summary>
internal static string ShowStatusIndicatorInTree {
get {
return ResourceManager.GetString("ShowStatusIndicatorInTree", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to &amp;Show Help Text.
/// </summary>

View File

@@ -1114,10 +1114,10 @@ If you run into such an error, please create a new connection file!</value>
<value>Use the Credential Security Support Provider (CredSSP) for authentication if it is available.</value>
</data>
<data name="PropertyDescriptionUseRestrictedAdmin" xml:space="preserve">
<value>Use restricted admin mode on the target host (local system context).</value>
<value>Use restricted admin mode on the target host (local system context). Credentials are not forwarded to the remote host; the current user's Kerberos ticket is used instead. Recommended for AD Protected Users accounts where NTLM authentication is disabled. Requires the connecting user to have administrative rights on the target.</value>
</data>
<data name="PropertyDescriptionUseRCG" xml:space="preserve">
<value>Use Remote Credential Guard to tunnel authentication on target back to source through the RDP channel.</value>
<value>Use Remote Credential Guard to tunnel authentication on target back to source through the RDP channel. Kerberos requests are redirected back to the connecting device, so credentials are never sent to the remote host. Recommended for AD Protected Users accounts where NTLM authentication is disabled. Requires both client and server to be domain-joined.</value>
</data>
<data name="PropertyDescriptionUser1" xml:space="preserve">
<value>Feel free to enter any information you need here.</value>
@@ -1606,9 +1606,6 @@ If you run into such an error, please create a new connection file!</value>
<data name="ShowFullConsFilePath" xml:space="preserve">
<value>Show full connections file path in window title</value>
</data>
<data name="ShowStatusIndicatorInTree" xml:space="preserve">
<value>Show host status indicator in connection tree</value>
</data>
<data name="ShowLogonInfoOnTabs" xml:space="preserve">
<value>Show logon information on tab names</value>
</data>

View File

@@ -94,17 +94,5 @@ namespace mRemoteNG.Properties {
this["cbAppearancePageInOptionMenu"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("False")]
public bool ShowStatusIndicatorInTree {
get {
return ((bool)(this["ShowStatusIndicatorInTree"]));
}
set {
this["ShowStatusIndicatorInTree"] = value;
}
}
}
}

View File

@@ -20,8 +20,5 @@
<Setting Name="cbAppearancePageInOptionMenu" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">True</Value>
</Setting>
<Setting Name="ShowStatusIndicatorInTree" Type="System.Boolean" Scope="User">
<Value Profile="(Default)">False</Value>
</Setting>
</Settings>
</SettingsFile>

View File

@@ -1,133 +0,0 @@
using System;
using System.Collections.Generic;
using System.Net.NetworkInformation;
using System.Threading;
using System.Threading.Tasks;
using mRemoteNG.Connection;
using mRemoteNG.Container;
using mRemoteNG.Properties;
using mRemoteNG.Tree;
namespace mRemoteNG.Tools
{
public class ConnectionStatusChecker : IDisposable
{
private Timer _timer;
private ConnectionTreeModel _model;
private bool _disposed;
private const int CheckIntervalMs = 30000;
private const int PingTimeoutMs = 5000;
private const int MaxConcurrentChecks = 10;
private readonly SemaphoreSlim _throttle = new(MaxConcurrentChecks, MaxConcurrentChecks);
public ConnectionStatusChecker(ConnectionTreeModel model)
{
_model = model;
_timer = new Timer(CheckAllConnections, null, 0, CheckIntervalMs);
}
public void UpdateModel(ConnectionTreeModel model)
{
_model = model;
}
private void CheckAllConnections(object state)
{
if (!OptionsAppearancePage.Default.ShowStatusIndicatorInTree)
return;
if (_model == null)
return;
var connections = GetAllConnections(_model);
foreach (var connection in connections)
{
Task.Run(() => CheckConnectionStatusThrottled(connection));
}
}
private async Task CheckConnectionStatusThrottled(ConnectionInfo connection)
{
await _throttle.WaitAsync().ConfigureAwait(false);
try
{
CheckConnectionStatus(connection);
}
finally
{
_throttle.Release();
}
}
private static void CheckConnectionStatus(ConnectionInfo connection)
{
if (string.IsNullOrEmpty(connection.Hostname))
{
connection.HostStatus = HostStatus.Unknown;
return;
}
try
{
using var ping = new Ping();
PingReply reply = ping.Send(connection.Hostname, PingTimeoutMs);
connection.HostStatus = reply?.Status == IPStatus.Success
? HostStatus.Online
: HostStatus.Offline;
}
catch (PingException)
{
connection.HostStatus = HostStatus.Offline;
}
catch (Exception)
{
connection.HostStatus = HostStatus.Unknown;
}
}
private static IEnumerable<ConnectionInfo> GetAllConnections(ConnectionTreeModel model)
{
var result = new List<ConnectionInfo>();
foreach (var root in model.RootNodes)
{
CollectConnections(root, result);
}
return result;
}
private static void CollectConnections(ConnectionInfo node, List<ConnectionInfo> result)
{
if (node is ContainerInfo container)
{
foreach (var child in container.Children)
{
CollectConnections(child, result);
}
}
else
{
result.Add(node);
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing)
{
_timer?.Dispose();
_timer = null;
_throttle?.Dispose();
}
_disposed = true;
}
}
}

View File

@@ -11,7 +11,6 @@ using mRemoteNG.Connection;
using mRemoteNG.Container;
using mRemoteNG.Properties;
using mRemoteNG.Themes;
using mRemoteNG.Tools;
using mRemoteNG.Tools.Clipboard;
using mRemoteNG.Tree;
using mRemoteNG.Tree.ClickHandlers;
@@ -30,7 +29,6 @@ namespace mRemoteNG.UI.Controls.ConnectionTree
private readonly PuttySessionsManager _puttySessionsManager = PuttySessionsManager.Instance;
private readonly StatusImageList _statusImageList = new();
private ThemeManager _themeManager;
private ConnectionStatusChecker _connectionStatusChecker;
private readonly ConnectionTreeSearchTextFilter _connectionTreeSearchTextFilter = new();
@@ -103,7 +101,6 @@ namespace mRemoteNG.UI.Controls.ConnectionTree
{
components?.Dispose();
_statusImageList?.Dispose();
_connectionStatusChecker?.Dispose();
_themeManager.ThemeChanged -= ThemeManagerOnThemeChanged;
}
@@ -205,9 +202,6 @@ namespace mRemoteNG.UI.Controls.ConnectionTree
NodeSearcher = new NodeSearcher(newModel);
ExecutePostSetupActions();
AutoResizeColumn(Columns[0]);
_connectionStatusChecker?.Dispose();
_connectionStatusChecker = new ConnectionStatusChecker(newModel);
}
private void RegisterModelUpdateHandlers(ConnectionTreeModel newModel)
@@ -240,8 +234,7 @@ namespace mRemoteNG.UI.Controls.ConnectionTree
string property = propertyChangedEventArgs.PropertyName;
if (property != nameof(ConnectionInfo.Name)
&& property != nameof(ConnectionInfo.OpenConnections)
&& property != nameof(ConnectionInfo.Icon)
&& property != nameof(ConnectionInfo.HostStatus))
&& property != nameof(ConnectionInfo.Icon))
{
return;
}

View File

@@ -39,7 +39,6 @@ namespace mRemoteNG.UI.Forms.OptionsPages
lblLanguage = new MrngLabel();
chkShowFullConnectionsFilePathInTitle = new MrngCheckBox();
chkShowDescriptionTooltipsInTree = new MrngCheckBox();
chkShowStatusIndicatorInTree = new MrngCheckBox();
chkShowSystemTrayIcon = new MrngCheckBox();
chkMinimizeToSystemTray = new MrngCheckBox();
chkCloseToSystemTray = new MrngCheckBox();
@@ -77,6 +76,18 @@ namespace mRemoteNG.UI.Forms.OptionsPages
lblLanguage.TabIndex = 0;
lblLanguage.Text = "Language";
//
// chkShowFullConnectionsFilePathInTitle
//
chkShowFullConnectionsFilePathInTitle._mice = MrngCheckBox.MouseState.OUT;
chkShowFullConnectionsFilePathInTitle.AutoSize = true;
chkShowFullConnectionsFilePathInTitle.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
chkShowFullConnectionsFilePathInTitle.Location = new System.Drawing.Point(3, 130);
chkShowFullConnectionsFilePathInTitle.Name = "chkShowFullConnectionsFilePathInTitle";
chkShowFullConnectionsFilePathInTitle.Size = new System.Drawing.Size(268, 17);
chkShowFullConnectionsFilePathInTitle.TabIndex = 4;
chkShowFullConnectionsFilePathInTitle.Text = "Show full connections file path in window title";
chkShowFullConnectionsFilePathInTitle.UseVisualStyleBackColor = true;
//
// chkShowDescriptionTooltipsInTree
//
chkShowDescriptionTooltipsInTree._mice = MrngCheckBox.MouseState.OUT;
@@ -89,36 +100,12 @@ namespace mRemoteNG.UI.Forms.OptionsPages
chkShowDescriptionTooltipsInTree.Text = "Show description tooltips in connection tree";
chkShowDescriptionTooltipsInTree.UseVisualStyleBackColor = true;
//
// chkShowStatusIndicatorInTree
//
chkShowStatusIndicatorInTree._mice = MrngCheckBox.MouseState.OUT;
chkShowStatusIndicatorInTree.AutoSize = true;
chkShowStatusIndicatorInTree.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
chkShowStatusIndicatorInTree.Location = new System.Drawing.Point(3, 130);
chkShowStatusIndicatorInTree.Name = "chkShowStatusIndicatorInTree";
chkShowStatusIndicatorInTree.Size = new System.Drawing.Size(290, 17);
chkShowStatusIndicatorInTree.TabIndex = 10;
chkShowStatusIndicatorInTree.Text = "Show host status indicator in connection tree";
chkShowStatusIndicatorInTree.UseVisualStyleBackColor = true;
//
// chkShowFullConnectionsFilePathInTitle
//
chkShowFullConnectionsFilePathInTitle._mice = MrngCheckBox.MouseState.OUT;
chkShowFullConnectionsFilePathInTitle.AutoSize = true;
chkShowFullConnectionsFilePathInTitle.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
chkShowFullConnectionsFilePathInTitle.Location = new System.Drawing.Point(3, 153);
chkShowFullConnectionsFilePathInTitle.Name = "chkShowFullConnectionsFilePathInTitle";
chkShowFullConnectionsFilePathInTitle.Size = new System.Drawing.Size(268, 17);
chkShowFullConnectionsFilePathInTitle.TabIndex = 4;
chkShowFullConnectionsFilePathInTitle.Text = "Show full connections file path in window title";
chkShowFullConnectionsFilePathInTitle.UseVisualStyleBackColor = true;
//
// chkShowSystemTrayIcon
//
chkShowSystemTrayIcon._mice = MrngCheckBox.MouseState.OUT;
chkShowSystemTrayIcon.AutoSize = true;
chkShowSystemTrayIcon.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
chkShowSystemTrayIcon.Location = new System.Drawing.Point(3, 199);
chkShowSystemTrayIcon.Location = new System.Drawing.Point(3, 176);
chkShowSystemTrayIcon.Name = "chkShowSystemTrayIcon";
chkShowSystemTrayIcon.Size = new System.Drawing.Size(178, 17);
chkShowSystemTrayIcon.TabIndex = 5;
@@ -130,7 +117,7 @@ namespace mRemoteNG.UI.Forms.OptionsPages
chkMinimizeToSystemTray._mice = MrngCheckBox.MouseState.OUT;
chkMinimizeToSystemTray.AutoSize = true;
chkMinimizeToSystemTray.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
chkMinimizeToSystemTray.Location = new System.Drawing.Point(3, 222);
chkMinimizeToSystemTray.Location = new System.Drawing.Point(3, 199);
chkMinimizeToSystemTray.Name = "chkMinimizeToSystemTray";
chkMinimizeToSystemTray.Size = new System.Drawing.Size(147, 17);
chkMinimizeToSystemTray.TabIndex = 6;
@@ -142,7 +129,7 @@ namespace mRemoteNG.UI.Forms.OptionsPages
chkCloseToSystemTray._mice = MrngCheckBox.MouseState.OUT;
chkCloseToSystemTray.AutoSize = true;
chkCloseToSystemTray.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point);
chkCloseToSystemTray.Location = new System.Drawing.Point(3, 245);
chkCloseToSystemTray.Location = new System.Drawing.Point(3, 222);
chkCloseToSystemTray.Name = "chkCloseToSystemTray";
chkCloseToSystemTray.Size = new System.Drawing.Size(129, 17);
chkCloseToSystemTray.TabIndex = 7;
@@ -156,14 +143,13 @@ namespace mRemoteNG.UI.Forms.OptionsPages
pnlOptions.Controls.Add(chkMinimizeToSystemTray);
pnlOptions.Controls.Add(lblLanguageRestartRequired);
pnlOptions.Controls.Add(chkShowSystemTrayIcon);
pnlOptions.Controls.Add(chkShowStatusIndicatorInTree);
pnlOptions.Controls.Add(chkShowDescriptionTooltipsInTree);
pnlOptions.Controls.Add(lblLanguage);
pnlOptions.Controls.Add(chkShowFullConnectionsFilePathInTitle);
pnlOptions.Dock = System.Windows.Forms.DockStyle.Top;
pnlOptions.Location = new System.Drawing.Point(0, 30);
pnlOptions.Name = "pnlOptions";
pnlOptions.Size = new System.Drawing.Size(610, 290);
pnlOptions.Size = new System.Drawing.Size(610, 267);
pnlOptions.TabIndex = 8;
//
// lblRegistrySettingsUsedInfo
@@ -197,7 +183,6 @@ namespace mRemoteNG.UI.Forms.OptionsPages
internal Controls.MrngLabel lblLanguage;
internal MrngCheckBox chkShowFullConnectionsFilePathInTitle;
internal MrngCheckBox chkShowDescriptionTooltipsInTree;
internal MrngCheckBox chkShowStatusIndicatorInTree;
internal MrngCheckBox chkShowSystemTrayIcon;
internal MrngCheckBox chkMinimizeToSystemTray;
internal MrngCheckBox chkCloseToSystemTray;

View File

@@ -34,7 +34,6 @@ namespace mRemoteNG.UI.Forms.OptionsPages
lblLanguageRestartRequired.Text =
string.Format(Language.LanguageRestartRequired, Application.ProductName);
chkShowDescriptionTooltipsInTree.Text = Language.ShowDescriptionTooltips;
chkShowStatusIndicatorInTree.Text = Language.ShowStatusIndicatorInTree;
chkShowFullConnectionsFilePathInTitle.Text = Language.ShowFullConsFilePath;
chkShowSystemTrayIcon.Text = Language.AlwaysShowSysTrayIcon;
chkMinimizeToSystemTray.Text = Language.MinimizeToSysTray;
@@ -64,7 +63,6 @@ namespace mRemoteNG.UI.Forms.OptionsPages
}
chkShowDescriptionTooltipsInTree.Checked = Properties.OptionsAppearancePage.Default.ShowDescriptionTooltipsInTree;
chkShowStatusIndicatorInTree.Checked = Properties.OptionsAppearancePage.Default.ShowStatusIndicatorInTree;
chkShowFullConnectionsFilePathInTitle.Checked = Properties.OptionsAppearancePage.Default.ShowCompleteConsPathInTitle;
chkShowSystemTrayIcon.Checked = Properties.OptionsAppearancePage.Default.ShowSystemTrayIcon;
chkMinimizeToSystemTray.Checked = Properties.OptionsAppearancePage.Default.MinimizeToTray;
@@ -84,7 +82,6 @@ namespace mRemoteNG.UI.Forms.OptionsPages
}
Properties.OptionsAppearancePage.Default.ShowDescriptionTooltipsInTree = chkShowDescriptionTooltipsInTree.Checked;
Properties.OptionsAppearancePage.Default.ShowStatusIndicatorInTree = chkShowStatusIndicatorInTree.Checked;
Properties.OptionsAppearancePage.Default.ShowCompleteConsPathInTitle = chkShowFullConnectionsFilePathInTitle.Checked;
FrmMain.Default.ShowFullPathInTitle = chkShowFullConnectionsFilePathInTitle.Checked;

View File

@@ -57,11 +57,6 @@ namespace mRemoteNG.UI
return $"Connection_{icon}_{status}";
}
private static string BuildStatusIconName(string baseKey, HostStatus hostStatus)
{
return $"{baseKey}_{hostStatus}";
}
private const string DefaultConnectionIcon = "";
private string GetConnectionIcon(ConnectionInfo connection)
@@ -72,80 +67,19 @@ namespace mRemoteNG.UI
}
bool connected = connection.OpenConnections.Count > 0;
string baseKey = BuildConnectionIconName(connection.Icon, connected);
bool showStatusIndicator = Properties.OptionsAppearancePage.Default.ShowStatusIndicatorInTree;
string name = showStatusIndicator
? BuildStatusIconName(baseKey, connection.HostStatus)
: baseKey;
string name = BuildConnectionIconName(connection.Icon, connected);
if (ImageList.Images.ContainsKey(name)) return name;
Icon image = ConnectionIcon.FromString(connection.Icon);
if (image == null)
{
return DefaultConnectionIcon;
}
Bitmap defaultBitmap = image.ToBitmap();
Bitmap playBitmap = Overlay(image, Properties.Resources.ConnectedOverlay);
if (showStatusIndicator)
{
foreach (HostStatus status in Enum.GetValues<HostStatus>())
{
Color barColor = GetStatusColor(status);
string defaultStatusKey = BuildStatusIconName(BuildConnectionIconName(connection.Icon, false), status);
string playStatusKey = BuildStatusIconName(BuildConnectionIconName(connection.Icon, true), status);
if (!ImageList.Images.ContainsKey(defaultStatusKey))
ImageList.Images.Add(defaultStatusKey, AddStatusBar(defaultBitmap, barColor));
if (!ImageList.Images.ContainsKey(playStatusKey))
ImageList.Images.Add(playStatusKey, AddStatusBar(playBitmap, barColor));
}
defaultBitmap.Dispose();
playBitmap.Dispose();
}
else
{
if (!ImageList.Images.ContainsKey(BuildConnectionIconName(connection.Icon, false)))
ImageList.Images.Add(BuildConnectionIconName(connection.Icon, false), defaultBitmap);
else
defaultBitmap.Dispose();
if (!ImageList.Images.ContainsKey(BuildConnectionIconName(connection.Icon, true)))
ImageList.Images.Add(BuildConnectionIconName(connection.Icon, true), playBitmap);
else
playBitmap.Dispose();
}
ImageList.Images.Add(BuildConnectionIconName(connection.Icon, false), image);
ImageList.Images.Add(BuildConnectionIconName(connection.Icon, true), Overlay(image, Properties.Resources.ConnectedOverlay));
return name;
}
private static Color GetStatusColor(HostStatus status)
{
return status switch
{
HostStatus.Online => Color.FromArgb(0, 180, 0),
HostStatus.Offline => Color.FromArgb(200, 0, 0),
_ => Color.FromArgb(160, 160, 160)
};
}
private static Bitmap AddStatusBar(Bitmap source, Color barColor)
{
Bitmap result = new(source.Width, source.Height);
using (Graphics gr = Graphics.FromImage(result))
{
gr.DrawImage(source, new Rectangle(0, 0, source.Width, source.Height));
using SolidBrush brush = new(barColor);
gr.FillRectangle(brush, 0, 0, 3, source.Height);
}
return result;
}
private static Bitmap Overlay(Icon background, Image foreground)
{
Bitmap result = new(background.ToBitmap(), new Size(16, 16));

View File

@@ -553,10 +553,10 @@
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<PackageReference Update="chromiumembeddedframework.runtime.win-x64" Version="144.0.12" />
<PackageReference Update="chromiumembeddedframework.runtime.win-x64" Version="145.0.26" />
</ItemGroup>
<ItemGroup Condition="'$(Platform)'=='arm64'">
<PackageReference Update="chromiumembeddedframework.runtime.win-arm64" Version="144.0.12" />
<PackageReference Update="chromiumembeddedframework.runtime.win-arm64" Version="145.0.26" />
</ItemGroup>
<ItemGroup>
<Service Include="{508349b6-6b84-4df5-91f0-309beebad82d}" />