From 9ef81e6e72653c3a4fb40e0d28e090ce12909f8a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 19 Oct 2025 20:23:35 +0000 Subject: [PATCH 1/4] Initial plan From f5fcc7c206e8faea39094db4c319ae277373561c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 19 Oct 2025 20:30:48 +0000 Subject: [PATCH 2/4] Add WSL protocol support with installation check Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com> --- mRemoteNG/Connection/ConnectionInfo.cs | 2 + .../Connection/Protocol/ProtocolFactory.cs | 3 + mRemoteNG/Connection/Protocol/ProtocolType.cs | 5 +- .../Protocol/WSL/Connection.Protocol.WSL.cs | 160 ++++++++++++++++++ mRemoteNG/Icons/WSL.ico | Bin 0 -> 1150 bytes mRemoteNG/Language/Language.resx | 3 + mRemoteNG/mRemoteNG.csproj | 3 + 7 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 mRemoteNG/Connection/Protocol/WSL/Connection.Protocol.WSL.cs create mode 100644 mRemoteNG/Icons/WSL.ico diff --git a/mRemoteNG/Connection/ConnectionInfo.cs b/mRemoteNG/Connection/ConnectionInfo.cs index 1ae22813f..095fc473f 100644 --- a/mRemoteNG/Connection/ConnectionInfo.cs +++ b/mRemoteNG/Connection/ConnectionInfo.cs @@ -273,6 +273,8 @@ namespace mRemoteNG.Connection return (int)ProtocolHTTPS.Defaults.Port; case ProtocolType.PowerShell: return (int)ProtocolPowerShell.Defaults.Port; + case ProtocolType.WSL: + return (int)ProtocolWSL.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 b6d7fbbad..42017f56f 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.WSL; 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.WSL: + return new ProtocolWSL(connectionInfo); case ProtocolType.IntApp: if (connectionInfo.ExtApp == "") { diff --git a/mRemoteNG/Connection/Protocol/ProtocolType.cs b/mRemoteNG/Connection/Protocol/ProtocolType.cs index c795cd8e6..b9a32a8f8 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.Wsl))] + WSL = 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.WSL); } } } \ No newline at end of file diff --git a/mRemoteNG/Connection/Protocol/WSL/Connection.Protocol.WSL.cs b/mRemoteNG/Connection/Protocol/WSL/Connection.Protocol.WSL.cs new file mode 100644 index 000000000..3a48882a8 --- /dev/null +++ b/mRemoteNG/Connection/Protocol/WSL/Connection.Protocol.WSL.cs @@ -0,0 +1,160 @@ +using System; +using System.Drawing; +using System.IO; +using System.Runtime.Versioning; +using System.Windows.Forms; +using mRemoteNG.App; +using mRemoteNG.Messages; +using mRemoteNG.Resources.Language; + +namespace mRemoteNG.Connection.Protocol.WSL +{ + [SupportedOSPlatform("windows")] + public class ProtocolWSL(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 + { + // Check if WSL is installed + if (!IsWslInstalled()) + { + Runtime.MessageCollector?.AddMessage(MessageClass.ErrorMsg, + "WSL is not installed on this system. Please install WSL to use this protocol.", true); + return false; + } + + Runtime.MessageCollector?.AddMessage(MessageClass.InformationMsg, + "Attempting to start WSL session.", true); + + _consoleControl = new ConsoleControl.ConsoleControl + { + Dock = DockStyle.Fill, + BackColor = ColorTranslator.FromHtml("#300A24"), // Ubuntu terminal color + ForeColor = Color.White, + IsInputEnabled = true, + Padding = new Padding(0, 20, 0, 0) + }; + + // Path to wsl.exe + string wslExe = @"C:\Windows\System32\wsl.exe"; + + // Build arguments based on connection info + string arguments = BuildWslArguments(); + + _consoleControl.StartProcess(wslExe, 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; + } + } + + private bool IsWslInstalled() + { + try + { + // Check if wsl.exe exists + string wslPath = @"C:\Windows\System32\wsl.exe"; + if (!File.Exists(wslPath)) + { + return false; + } + + // Additional check: Try to execute wsl.exe --status to verify it's properly installed + // For now, just check if the file exists + return true; + } + catch + { + return false; + } + } + + private string BuildWslArguments() + { + string arguments = ""; + + // If a hostname is specified, treat it as a distribution name + if (!string.IsNullOrEmpty(_connectionInfo.Hostname)) + { + string hostname = _connectionInfo.Hostname.Trim(); + // Check if it's not localhost (WSL doesn't use localhost as a distribution name) + if (!hostname.Equals("localhost", StringComparison.OrdinalIgnoreCase)) + { + arguments = $"-d {hostname}"; + } + } + + // If username is specified, we can try to use it + if (!string.IsNullOrEmpty(_connectionInfo.Username)) + { + arguments += $" -u {_connectionInfo.Username}"; + } + + return arguments.Trim(); + } + + 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 = 0 // WSL doesn't use a traditional port + } + + #endregion + } +} diff --git a/mRemoteNG/Icons/WSL.ico b/mRemoteNG/Icons/WSL.ico new file mode 100644 index 0000000000000000000000000000000000000000..cb8f13a90ff4dce2ace2024522ff4d25dbca9f77 GIT binary patch literal 1150 zcma)+dq`7Z7{*T-Ei6mCEK6;sSccd|vWs~k(Ml^bBubI~s8B1^%81NbYKC2`)KU}0 zOVg=snRzEmmd#vRp{QXh>qxYkBb}{nCmnk_XJIVcAH9d~ocDXq^LyWq_rM{DIrL>| zNzivEB6c1@unB^2qZcjF%5R;17{YoXK`_fWv}%>7bg)0Ep|&>venq1;`C9(Zj+f%f zp`oD!#h5(d!-%x&$^(;Y;GWhFyW1aO9oK>lUVaGl z^F@%4H@1Yv!kXI$_St3}vt^*z*Q6 zQBmIxn+M}qQBS{b&>_Bk3`*r>2*XR&a2Lsu%-B$XTrc3kf*|0v!3#x2#o@** zlaGD*9I`Jc5RU}{1EW%@{^AAvax7Y44)2X#Xls4;56{%NayjK~;^{;KuhZ%N^k$N@ zlvK`3xjw%e4z{-7m6W!b@|x04M#l*EC*>mKmH@|+(&4e% z4gB&7p((E^Eh-{XcsM)^K5N|(9}@$Y6;5bwZ2E6Lm3vv}K+oFB!V*iZY!Kk Purple (Custom) + + WSL + \ No newline at end of file diff --git a/mRemoteNG/mRemoteNG.csproj b/mRemoteNG/mRemoteNG.csproj index c5a2931e5..f92b19332 100644 --- a/mRemoteNG/mRemoteNG.csproj +++ b/mRemoteNG/mRemoteNG.csproj @@ -399,6 +399,9 @@ Always + + Always + Always From 6657fa3ad0b8cf2845ae16392684780f95e2becb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sun, 19 Oct 2025 20:32:40 +0000 Subject: [PATCH 3/4] Update attributes and tests for WSL protocol Co-authored-by: Kvarkas <3611964+Kvarkas@users.noreply.github.com> --- mRemoteNG/Connection/AbstractConnectionRecord.cs | 2 +- .../Window/ConfigWindowTests/ConfigWindowGeneralTests.cs | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/mRemoteNG/Connection/AbstractConnectionRecord.cs b/mRemoteNG/Connection/AbstractConnectionRecord.cs index 6b6b61786..ba68d1cb2 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.WSL)] public string Domain { get => GetPropertyValue("Domain", _domain).Trim(); diff --git a/mRemoteNGTests/UI/Window/ConfigWindowTests/ConfigWindowGeneralTests.cs b/mRemoteNGTests/UI/Window/ConfigWindowTests/ConfigWindowGeneralTests.cs index 3b7675b8c..a617b2f08 100644 --- a/mRemoteNGTests/UI/Window/ConfigWindowTests/ConfigWindowGeneralTests.cs +++ b/mRemoteNGTests/UI/Window/ConfigWindowTests/ConfigWindowGeneralTests.cs @@ -332,6 +332,14 @@ namespace mRemoteNGTests.UI.Window.ConfigWindowTests nameof(ConnectionInfo.Port), }); break; + case ProtocolType.WSL: + expectedProperties.AddRange(new[] + { + nameof(ConnectionInfo.Password), + nameof(ConnectionInfo.Domain), + nameof(ConnectionInfo.Port), + }); + break; case ProtocolType.IntApp: expectedProperties.AddRange(new[] { From 4ade871aba7beb88f970d80160943a8f948e6ce5 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:40 +0000 Subject: [PATCH 4/4] Add WSL translations to language resource 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 ea7ed8ffc..b6d30075a 100644 --- a/mRemoteNG/Language/Language.fr.resx +++ b/mRemoteNG/Language/Language.fr.resx @@ -2187,4 +2187,7 @@ Le canal nightly inclut les versions alpha, beta et release candidates. + + WSL + \ No newline at end of file diff --git a/mRemoteNG/Language/Language.pl.resx b/mRemoteNG/Language/Language.pl.resx index a9c3fb645..d35c89400 100644 --- a/mRemoteNG/Language/Language.pl.resx +++ b/mRemoteNG/Language/Language.pl.resx @@ -2393,4 +2393,7 @@ Kanał nocny obejmuje wersje alfa, beta i RC (gotowe do wydania). + + WSL + \ No newline at end of file diff --git a/mRemoteNG/Language/Language.ta.resx b/mRemoteNG/Language/Language.ta.resx index c2aec0f87..f213fc512 100644 --- a/mRemoteNG/Language/Language.ta.resx +++ b/mRemoteNG/Language/Language.ta.resx @@ -2450,4 +2450,7 @@ + + WSL + \ No newline at end of file diff --git a/mRemoteNG/Language/Language.zh-CN.resx b/mRemoteNG/Language/Language.zh-CN.resx index 20d5517d5..be5715ab2 100644 --- a/mRemoteNG/Language/Language.zh-CN.resx +++ b/mRemoteNG/Language/Language.zh-CN.resx @@ -2114,4 +2114,7 @@ mRemoteNG 将退出并安装更新。 + + WSL + \ No newline at end of file