From e4b4400aa5df8d2c93a559229f249c52c3b044d7 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 19 Oct 2025 20:22:29 +0000
Subject: [PATCH 1/7] Initial plan
From 435e6f46c10f223d78516d6c2e4904f8894ffc9e Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 19 Oct 2025 20:29:42 +0000
Subject: [PATCH 2/7] Add Terminal protocol support to mRemoteNG
Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com>
---
.../Connection/AbstractConnectionRecord.cs | 2 +-
mRemoteNG/Connection/ConnectionInfo.cs | 3 +
.../Connection/Protocol/ProtocolFactory.cs | 3 +
mRemoteNG/Connection/Protocol/ProtocolType.cs | 5 +-
.../Terminal/Connection.Protocol.Terminal.cs | 130 ++++++++++++++++++
mRemoteNG/Language/Language.resx | 3 +
6 files changed, 144 insertions(+), 2 deletions(-)
create mode 100644 mRemoteNG/Connection/Protocol/Terminal/Connection.Protocol.Terminal.cs
diff --git a/mRemoteNG/Connection/AbstractConnectionRecord.cs b/mRemoteNG/Connection/AbstractConnectionRecord.cs
index 6b6b6178..c58fbdab 100644
--- a/mRemoteNG/Connection/AbstractConnectionRecord.cs
+++ b/mRemoteNG/Connection/AbstractConnectionRecord.cs
@@ -295,7 +295,7 @@ namespace mRemoteNG.Connection
[LocalizedAttributes.LocalizedCategory(nameof(Language.Connection), 2),
LocalizedAttributes.LocalizedDisplayName(nameof(Language.Domain)),
LocalizedAttributes.LocalizedDescription(nameof(Language.PropertyDescriptionDomain)),
- AttributeUsedInProtocol(ProtocolType.RDP, ProtocolType.IntApp, ProtocolType.PowerShell)]
+ AttributeUsedInProtocol(ProtocolType.RDP, ProtocolType.IntApp, ProtocolType.PowerShell, ProtocolType.Terminal)]
public string Domain
{
get => GetPropertyValue("Domain", _domain).Trim();
diff --git a/mRemoteNG/Connection/ConnectionInfo.cs b/mRemoteNG/Connection/ConnectionInfo.cs
index 1ae22813..a81e779d 100644
--- a/mRemoteNG/Connection/ConnectionInfo.cs
+++ b/mRemoteNG/Connection/ConnectionInfo.cs
@@ -8,6 +8,7 @@ using mRemoteNG.Connection.Protocol;
using mRemoteNG.Connection.Protocol.ARD;
using mRemoteNG.Connection.Protocol.Http;
using mRemoteNG.Connection.Protocol.PowerShell;
+using mRemoteNG.Connection.Protocol.Terminal;
using mRemoteNG.Connection.Protocol.RAW;
using mRemoteNG.Connection.Protocol.RDP;
using mRemoteNG.Connection.Protocol.Rlogin;
@@ -273,6 +274,8 @@ namespace mRemoteNG.Connection
return (int)ProtocolHTTPS.Defaults.Port;
case ProtocolType.PowerShell:
return (int)ProtocolPowerShell.Defaults.Port;
+ case ProtocolType.Terminal:
+ return (int)ProtocolTerminal.Defaults.Port;
case ProtocolType.IntApp:
return (int)IntegratedProgram.Defaults.Port;
}
diff --git a/mRemoteNG/Connection/Protocol/ProtocolFactory.cs b/mRemoteNG/Connection/Protocol/ProtocolFactory.cs
index b6d7fbba..b2761220 100644
--- a/mRemoteNG/Connection/Protocol/ProtocolFactory.cs
+++ b/mRemoteNG/Connection/Protocol/ProtocolFactory.cs
@@ -8,6 +8,7 @@ using mRemoteNG.Connection.Protocol.VNC;
using mRemoteNG.Connection.Protocol.ARD;
using System;
using mRemoteNG.Connection.Protocol.PowerShell;
+using mRemoteNG.Connection.Protocol.Terminal;
using mRemoteNG.Resources.Language;
using System.Runtime.Versioning;
@@ -47,6 +48,8 @@ namespace mRemoteNG.Connection.Protocol
return new ProtocolHTTPS(connectionInfo.RenderingEngine);
case ProtocolType.PowerShell:
return new ProtocolPowerShell(connectionInfo);
+ case ProtocolType.Terminal:
+ return new ProtocolTerminal(connectionInfo);
case ProtocolType.IntApp:
if (connectionInfo.ExtApp == "")
{
diff --git a/mRemoteNG/Connection/Protocol/ProtocolType.cs b/mRemoteNG/Connection/Protocol/ProtocolType.cs
index c795cd8e..be856518 100644
--- a/mRemoteNG/Connection/Protocol/ProtocolType.cs
+++ b/mRemoteNG/Connection/Protocol/ProtocolType.cs
@@ -38,6 +38,9 @@ namespace mRemoteNG.Connection.Protocol
[LocalizedAttributes.LocalizedDescription(nameof(Language.Ard))]
ARD = 11,
+ [LocalizedAttributes.LocalizedDescription(nameof(Language.Terminal))]
+ Terminal = 12,
+
[LocalizedAttributes.LocalizedDescription(nameof(Language.ExternalTool))]
IntApp = 20
}
@@ -46,7 +49,7 @@ namespace mRemoteNG.Connection.Protocol
{
public static bool SupportBlankHostname(ProtocolType protocolType)
{
- return (protocolType == ProtocolType.IntApp || protocolType == ProtocolType.PowerShell);
+ return (protocolType == ProtocolType.IntApp || protocolType == ProtocolType.PowerShell || protocolType == ProtocolType.Terminal);
}
}
}
\ No newline at end of file
diff --git a/mRemoteNG/Connection/Protocol/Terminal/Connection.Protocol.Terminal.cs b/mRemoteNG/Connection/Protocol/Terminal/Connection.Protocol.Terminal.cs
new file mode 100644
index 00000000..f66137c8
--- /dev/null
+++ b/mRemoteNG/Connection/Protocol/Terminal/Connection.Protocol.Terminal.cs
@@ -0,0 +1,130 @@
+using System;
+using System.Drawing;
+using System.Runtime.Versioning;
+using System.Windows.Forms;
+using mRemoteNG.App;
+using mRemoteNG.Messages;
+using mRemoteNG.Resources.Language;
+
+namespace mRemoteNG.Connection.Protocol.Terminal
+{
+ [SupportedOSPlatform("windows")]
+ public class ProtocolTerminal(ConnectionInfo connectionInfo) : ProtocolBase
+ {
+ #region Private Fields
+
+ private IntPtr _handle;
+ private readonly ConnectionInfo _connectionInfo = connectionInfo;
+ private ConsoleControl.ConsoleControl _consoleControl;
+
+ #endregion
+
+ #region Public Methods
+
+ public override bool Connect()
+ {
+ try
+ {
+ Runtime.MessageCollector?.AddMessage(MessageClass.InformationMsg, "Attempting to start Windows Terminal session.", true);
+
+ _consoleControl = new ConsoleControl.ConsoleControl
+ {
+ Dock = DockStyle.Fill,
+ BackColor = ColorTranslator.FromHtml("#012456"),
+ ForeColor = Color.White,
+ IsInputEnabled = true,
+ Padding = new Padding(0, 20, 0, 0)
+ };
+
+ // Path to Windows Terminal executable
+ string terminalExe = @"%LocalAppData%\Microsoft\WindowsApps\wt.exe";
+
+ // Expand environment variables
+ terminalExe = Environment.ExpandEnvironmentVariables(terminalExe);
+
+ // Setup arguments based on whether hostname is provided
+ string arguments = "";
+ string hostname = _connectionInfo.Hostname.Trim().ToLower();
+ bool useLocalHost = hostname == "" || hostname.Equals("localhost");
+
+ if (!useLocalHost)
+ {
+ // If hostname is provided, try to connect via SSH
+ string username = _connectionInfo.Username;
+ if (!string.IsNullOrEmpty(_connectionInfo.Domain))
+ {
+ username = $"{_connectionInfo.Domain}\\{username}";
+ }
+
+ if (!string.IsNullOrEmpty(username))
+ {
+ arguments = $"ssh {username}@{_connectionInfo.Hostname}";
+ }
+ else
+ {
+ arguments = $"ssh {_connectionInfo.Hostname}";
+ }
+ }
+ // If no hostname or localhost, just open a local terminal session
+
+ _consoleControl.StartProcess(terminalExe, arguments);
+
+ while (!_consoleControl.IsHandleCreated) break;
+ _handle = _consoleControl.Handle;
+ NativeMethods.SetParent(_handle, InterfaceControl.Handle);
+
+ Resize(this, new EventArgs());
+ base.Connect();
+ return true;
+ }
+ catch (Exception ex)
+ {
+ Runtime.MessageCollector?.AddExceptionMessage(Language.ConnectionFailed, ex);
+ return false;
+ }
+ }
+
+ public override void Focus()
+ {
+ try
+ {
+ NativeMethods.SetForegroundWindow(_handle);
+ }
+ catch (Exception ex)
+ {
+ Runtime.MessageCollector.AddExceptionMessage(Language.IntAppFocusFailed, ex);
+ }
+ }
+
+ protected override void Resize(object sender, EventArgs e)
+ {
+ try
+ {
+ if (InterfaceControl.Size == Size.Empty) return;
+ // Use ClientRectangle to account for padding (for connection frame color)
+ Rectangle clientRect = InterfaceControl.ClientRectangle;
+ NativeMethods.MoveWindow(_handle,
+ clientRect.X - SystemInformation.FrameBorderSize.Width,
+ clientRect.Y - (SystemInformation.CaptionHeight + SystemInformation.FrameBorderSize.Height),
+ clientRect.Width + SystemInformation.FrameBorderSize.Width * 2,
+ clientRect.Height + SystemInformation.CaptionHeight +
+ SystemInformation.FrameBorderSize.Height * 2, true);
+ }
+ catch (Exception ex)
+ {
+ Runtime.MessageCollector.AddExceptionMessage(Language.IntAppResizeFailed, ex);
+ }
+ }
+
+ #endregion
+
+ #region Enumerations
+
+ public enum Defaults
+ {
+ Port = 22
+ }
+
+ #endregion
+ }
+}
diff --git a/mRemoteNG/Language/Language.resx b/mRemoteNG/Language/Language.resx
index 6b803b06..a2fdf733 100644
--- a/mRemoteNG/Language/Language.resx
+++ b/mRemoteNG/Language/Language.resx
@@ -2194,6 +2194,9 @@ Nightly Channel includes Alphas, Betas & Release Candidates.
PowerShell
+
+ Terminal
+
Changelog
From 895e16d4cbdcafae86a0e53f8eeb4cbcb307df30 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 19 Oct 2025 20:33:20 +0000
Subject: [PATCH 3/7] Add Terminal language entries to all language files
Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com>
---
mRemoteNG/Language/Language.fr.resx | 3 +++
mRemoteNG/Language/Language.pl.resx | 3 +++
mRemoteNG/Language/Language.ta.resx | 3 +++
mRemoteNG/Language/Language.zh-CN.resx | 3 +++
4 files changed, 12 insertions(+)
diff --git a/mRemoteNG/Language/Language.fr.resx b/mRemoteNG/Language/Language.fr.resx
index ea7ed8ff..0ae353b5 100644
--- a/mRemoteNG/Language/Language.fr.resx
+++ b/mRemoteNG/Language/Language.fr.resx
@@ -2105,6 +2105,9 @@ Le canal nightly inclut les versions alpha, beta et release candidates.
PowerShell
+
+ Terminal
+
Historique des modifications
diff --git a/mRemoteNG/Language/Language.pl.resx b/mRemoteNG/Language/Language.pl.resx
index a9c3fb64..b4c3b8d3 100644
--- a/mRemoteNG/Language/Language.pl.resx
+++ b/mRemoteNG/Language/Language.pl.resx
@@ -2152,6 +2152,9 @@ Kanał nocny obejmuje wersje alfa, beta i RC (gotowe do wydania).
PowerShell
+
+ Terminal
+
Dziennik zmian
diff --git a/mRemoteNG/Language/Language.ta.resx b/mRemoteNG/Language/Language.ta.resx
index c2aec0f8..08d2406d 100644
--- a/mRemoteNG/Language/Language.ta.resx
+++ b/mRemoteNG/Language/Language.ta.resx
@@ -2155,6 +2155,9 @@
ஆற்றல்ஓடு
+
+ Terminal
+
மாற்றபதிவு
diff --git a/mRemoteNG/Language/Language.zh-CN.resx b/mRemoteNG/Language/Language.zh-CN.resx
index 20d5517d..683a3ee1 100644
--- a/mRemoteNG/Language/Language.zh-CN.resx
+++ b/mRemoteNG/Language/Language.zh-CN.resx
@@ -2074,6 +2074,9 @@ mRemoteNG 将退出并安装更新。
PowerShell
+
+ Terminal
+
网关
From 0a381e0a4458665e824cb1e9cb79584b9a6e01c1 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 19 Oct 2025 20:34:22 +0000
Subject: [PATCH 4/7] Improve Terminal protocol to use cmd.exe instead of
wt.exe
Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com>
---
.../Terminal/Connection.Protocol.Terminal.cs | 20 ++++++++++---------
1 file changed, 11 insertions(+), 9 deletions(-)
diff --git a/mRemoteNG/Connection/Protocol/Terminal/Connection.Protocol.Terminal.cs b/mRemoteNG/Connection/Protocol/Terminal/Connection.Protocol.Terminal.cs
index f66137c8..636a5aaa 100644
--- a/mRemoteNG/Connection/Protocol/Terminal/Connection.Protocol.Terminal.cs
+++ b/mRemoteNG/Connection/Protocol/Terminal/Connection.Protocol.Terminal.cs
@@ -25,7 +25,7 @@ namespace mRemoteNG.Connection.Protocol.Terminal
{
try
{
- Runtime.MessageCollector?.AddMessage(MessageClass.InformationMsg, "Attempting to start Windows Terminal session.", true);
+ Runtime.MessageCollector?.AddMessage(MessageClass.InformationMsg, "Attempting to start Terminal session.", true);
_consoleControl = new ConsoleControl.ConsoleControl
{
@@ -36,11 +36,9 @@ namespace mRemoteNG.Connection.Protocol.Terminal
Padding = new Padding(0, 20, 0, 0)
};
- // Path to Windows Terminal executable
- string terminalExe = @"%LocalAppData%\Microsoft\WindowsApps\wt.exe";
-
- // Expand environment variables
- terminalExe = Environment.ExpandEnvironmentVariables(terminalExe);
+ // Path to command prompt or PowerShell - can be configured through options
+ // Using cmd.exe as default for Terminal protocol
+ string terminalExe = @"C:\Windows\System32\cmd.exe";
// Setup arguments based on whether hostname is provided
string arguments = "";
@@ -58,14 +56,18 @@ namespace mRemoteNG.Connection.Protocol.Terminal
if (!string.IsNullOrEmpty(username))
{
- arguments = $"ssh {username}@{_connectionInfo.Hostname}";
+ arguments = $"/K ssh {username}@{_connectionInfo.Hostname}";
}
else
{
- arguments = $"ssh {_connectionInfo.Hostname}";
+ arguments = $"/K ssh {_connectionInfo.Hostname}";
}
}
- // If no hostname or localhost, just open a local terminal session
+ else
+ {
+ // For local sessions, just start cmd with /K to keep it open
+ arguments = "/K";
+ }
_consoleControl.StartProcess(terminalExe, arguments);
From 8fc3682cc955ce27130492628572e3dca547666a Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 19 Oct 2025 20:36:52 +0000
Subject: [PATCH 5/7] Address code review feedback - improve Terminal protocol
implementation
Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com>
---
.../Connection/AbstractConnectionRecord.cs | 2 +-
.../Terminal/Connection.Protocol.Terminal.cs | 24 +++++++++++++------
2 files changed, 18 insertions(+), 8 deletions(-)
diff --git a/mRemoteNG/Connection/AbstractConnectionRecord.cs b/mRemoteNG/Connection/AbstractConnectionRecord.cs
index c58fbdab..6b6b6178 100644
--- a/mRemoteNG/Connection/AbstractConnectionRecord.cs
+++ b/mRemoteNG/Connection/AbstractConnectionRecord.cs
@@ -295,7 +295,7 @@ namespace mRemoteNG.Connection
[LocalizedAttributes.LocalizedCategory(nameof(Language.Connection), 2),
LocalizedAttributes.LocalizedDisplayName(nameof(Language.Domain)),
LocalizedAttributes.LocalizedDescription(nameof(Language.PropertyDescriptionDomain)),
- AttributeUsedInProtocol(ProtocolType.RDP, ProtocolType.IntApp, ProtocolType.PowerShell, ProtocolType.Terminal)]
+ AttributeUsedInProtocol(ProtocolType.RDP, ProtocolType.IntApp, ProtocolType.PowerShell)]
public string Domain
{
get => GetPropertyValue("Domain", _domain).Trim();
diff --git a/mRemoteNG/Connection/Protocol/Terminal/Connection.Protocol.Terminal.cs b/mRemoteNG/Connection/Protocol/Terminal/Connection.Protocol.Terminal.cs
index 636a5aaa..d9c1c0c9 100644
--- a/mRemoteNG/Connection/Protocol/Terminal/Connection.Protocol.Terminal.cs
+++ b/mRemoteNG/Connection/Protocol/Terminal/Connection.Protocol.Terminal.cs
@@ -36,9 +36,9 @@ namespace mRemoteNG.Connection.Protocol.Terminal
Padding = new Padding(0, 20, 0, 0)
};
- // Path to command prompt or PowerShell - can be configured through options
- // Using cmd.exe as default for Terminal protocol
- string terminalExe = @"C:\Windows\System32\cmd.exe";
+ // Path to command prompt - dynamically determined from system
+ // Using COMSPEC environment variable which points to the system's command processor
+ string terminalExe = Environment.GetEnvironmentVariable("COMSPEC") ?? @"C:\Windows\System32\cmd.exe";
// Setup arguments based on whether hostname is provided
string arguments = "";
@@ -48,20 +48,30 @@ namespace mRemoteNG.Connection.Protocol.Terminal
if (!useLocalHost)
{
// If hostname is provided, try to connect via SSH
+ // Note: Domain field is not used for SSH as it's Windows-specific
+ // SSH authentication will use standard SSH mechanisms (password prompt, keys, etc.)
string username = _connectionInfo.Username;
- if (!string.IsNullOrEmpty(_connectionInfo.Domain))
+ int port = _connectionInfo.Port;
+
+ // Build SSH command
+ string sshCommand = "ssh";
+
+ // Add port if it's not the default SSH port (22)
+ if (port > 0 && port != 22)
{
- username = $"{_connectionInfo.Domain}\\{username}";
+ sshCommand += $" -p {port}";
}
if (!string.IsNullOrEmpty(username))
{
- arguments = $"/K ssh {username}@{_connectionInfo.Hostname}";
+ sshCommand += $" {username}@{_connectionInfo.Hostname}";
}
else
{
- arguments = $"/K ssh {_connectionInfo.Hostname}";
+ sshCommand += $" {_connectionInfo.Hostname}";
}
+
+ arguments = $"/K {sshCommand}";
}
else
{
From a8efba1ed4722874e8d7f05fdbd4afa7cedd7f1d Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 19 Oct 2025 20:38:36 +0000
Subject: [PATCH 6/7] Fix handle creation wait loop in Terminal protocol
Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com>
---
.../Terminal/Connection.Protocol.Terminal.cs | 15 ++++++++++++++-
1 file changed, 14 insertions(+), 1 deletion(-)
diff --git a/mRemoteNG/Connection/Protocol/Terminal/Connection.Protocol.Terminal.cs b/mRemoteNG/Connection/Protocol/Terminal/Connection.Protocol.Terminal.cs
index d9c1c0c9..d9542d2a 100644
--- a/mRemoteNG/Connection/Protocol/Terminal/Connection.Protocol.Terminal.cs
+++ b/mRemoteNG/Connection/Protocol/Terminal/Connection.Protocol.Terminal.cs
@@ -81,7 +81,20 @@ namespace mRemoteNG.Connection.Protocol.Terminal
_consoleControl.StartProcess(terminalExe, arguments);
- while (!_consoleControl.IsHandleCreated) break;
+ // Wait for the console control to create its handle
+ int maxWaitMs = 5000; // 5 seconds timeout
+ int startTicks = Environment.TickCount;
+ while (!_consoleControl.IsHandleCreated &&
+ Environment.TickCount < startTicks + maxWaitMs)
+ {
+ System.Threading.Thread.Sleep(10);
+ }
+
+ if (!_consoleControl.IsHandleCreated)
+ {
+ throw new Exception("Console control failed to create handle within timeout period");
+ }
+
_handle = _consoleControl.Handle;
NativeMethods.SetParent(_handle, InterfaceControl.Handle);
From f983f5de02e4b9427ad44647eda586ba3b2e8ca2 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Sun, 19 Oct 2025 20:40:22 +0000
Subject: [PATCH 7/7] Improve timeout handling and error messaging in Terminal
protocol
Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com>
---
.../Protocol/Terminal/Connection.Protocol.Terminal.cs | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/mRemoteNG/Connection/Protocol/Terminal/Connection.Protocol.Terminal.cs b/mRemoteNG/Connection/Protocol/Terminal/Connection.Protocol.Terminal.cs
index d9542d2a..4116975a 100644
--- a/mRemoteNG/Connection/Protocol/Terminal/Connection.Protocol.Terminal.cs
+++ b/mRemoteNG/Connection/Protocol/Terminal/Connection.Protocol.Terminal.cs
@@ -83,16 +83,16 @@ namespace mRemoteNG.Connection.Protocol.Terminal
// Wait for the console control to create its handle
int maxWaitMs = 5000; // 5 seconds timeout
- int startTicks = Environment.TickCount;
+ long startTicks = Environment.TickCount64;
while (!_consoleControl.IsHandleCreated &&
- Environment.TickCount < startTicks + maxWaitMs)
+ Environment.TickCount64 < startTicks + maxWaitMs)
{
- System.Threading.Thread.Sleep(10);
+ System.Threading.Thread.Sleep(50);
}
if (!_consoleControl.IsHandleCreated)
{
- throw new Exception("Console control failed to create handle within timeout period");
+ throw new Exception("Failed to initialize terminal console within 5 seconds. This may indicate system resource constraints or permission issues.");
}
_handle = _consoleControl.Handle;