From 9ea0a53447d4a4bcd5fb1db7890467cfe52ec31d Mon Sep 17 00:00:00 2001 From: Danilo Del Busso Date: Tue, 11 Jul 2023 10:21:46 +0100 Subject: [PATCH] Catch `HRESULT E_FAIL` exceptions for `IRDPClient.Connect` calls The `Connect` method returns `E_FAIL` if it is called while the control is already connected or in the connecting state. This can be hit when a lot of connections are being opened at the same time, and it's there as a failsafe. Also adds `IsAttemptingConnection` as a new field Signed-off-by: Danilo Del Busso --- XenAdmin/ConsoleView/RdpClient.cs | 80 ++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 27 deletions(-) diff --git a/XenAdmin/ConsoleView/RdpClient.cs b/XenAdmin/ConsoleView/RdpClient.cs index 0e950bd9d..65c285fdc 100644 --- a/XenAdmin/ConsoleView/RdpClient.cs +++ b/XenAdmin/ConsoleView/RdpClient.cs @@ -97,6 +97,11 @@ namespace XenAdmin.ConsoleView rdpControl.Resize += resizeHandler; } + private bool _connecting; + private bool _authWarningVisible; + + public bool IsAttemptingConnection => _connecting || _authWarningVisible; + private void RDPConfigure(Size currentConsoleSize) { rdpControl.BeginInit(); @@ -104,7 +109,7 @@ namespace XenAdmin.ConsoleView rdpControl.Dock = DockStyle.None; rdpControl.Anchor = AnchorStyles.None; rdpControl.Size = currentConsoleSize; - RDPAddOnDisconnected(); + AddRDPEventHandlers(); rdpControl.Enter += RdpEnter; rdpControl.Leave += rdpClient_Leave; rdpControl.GotFocus += rdpClient_GotFocus; @@ -123,15 +128,28 @@ namespace XenAdmin.ConsoleView } } - private void RDPAddOnDisconnected() + private void AddRDPEventHandlers() { if (rdpControl == null) return; - if (rdpClient9 == null) - rdpClient6.OnDisconnected += rdpClient_OnDisconnected; - else - rdpClient9.OnDisconnected += rdpClient_OnDisconnected; + var rdpClient = (IRdpClient)rdpClient9 ?? rdpClient6; + if (rdpClient == null) + { + return; + } + + rdpClient.OnDisconnected += (_, e) => + { + Program.AssertOnEventThread(); + OnDisconnected?.Invoke(this, EventArgs.Empty); + }; + rdpClient.OnConnected += (_1, _2) => _connecting = false; + rdpClient.OnConnecting += (_1, _2) => _connecting = true; + rdpClient.OnDisconnected += (_1, _2) => _connecting = _authWarningVisible = false; + rdpClient.OnAuthenticationWarningDisplayed += (_1, _2) => _authWarningVisible = true; + rdpClient.OnAuthenticationWarningDismissed += (_1, _2) => _authWarningVisible = false; + } private void RDPSetSettings() @@ -166,21 +184,38 @@ namespace XenAdmin.ConsoleView if (rdpControl == null) return; - if (rdpClient9 == null) + var rdpClientName = rdpClient9 == null ? "RDPClient6" : "RDPClient9"; + var rdpClient = (IRdpClient) rdpClient9 ?? rdpClient6; + + Log.Debug($"Connecting {rdpClientName} using server '{rdpIP}', width '{w}' and height '{h}'"); + + if (rdpClient == null) { - Log.Debug($"Connecting RDPClient6 using server '{rdpIP}', width '{w}' and height '{h}'"); - rdpClient6.Server = rdpIP; - rdpClient6.DesktopWidth = w; - rdpClient6.DesktopHeight = h; - rdpClient6.Connect(); + Log.Warn("RDPConnect called with an uninitialized RDP client. Aborting connection attempt."); + return; } - else + + rdpClient.Server = rdpIP; + rdpClient.DesktopWidth = w; + rdpClient.DesktopHeight = h; + try { - Log.Debug($"Connecting RDPClient9 using server '{rdpIP}', width '{w}' and height '{h}'"); - rdpClient9.Server = rdpIP; - rdpClient9.DesktopWidth = w; - rdpClient9.DesktopHeight = h; - rdpClient9.Connect(); + rdpClient.Connect(); + } + catch (COMException comException) + { + // The Connect method returns E_FAIL if it is called while the control is already connected or in the connecting state. + // see https://learn.microsoft.com/en-us/windows/win32/termserv/imstscax-connect#remarks for more information. + // The HRESULT value is taken from https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/705fb797-2175-4a90-b5a3-3918024b10b8 + var eFailHResultValue = Convert.ToInt32("0x80004005", 16); + if (comException.ErrorCode == eFailHResultValue) + { + Log.Warn("Attempted connection while RDP client was connected or connected already."); + } + else + { + throw; + } } } @@ -223,15 +258,6 @@ namespace XenAdmin.ConsoleView get { return rdpControl == null ? 0 : (rdpClient9 == null ? rdpClient6.DesktopWidth : rdpClient9.DesktopWidth); } } - void rdpClient_OnDisconnected(object sender, AxMSTSCLib.IMsTscAxEvents_OnDisconnectedEvent e) - { - Program.AssertOnEventThread(); - - if (OnDisconnected != null) - OnDisconnected(this, null); - - } - //refresh to draw focus border in correct position after display is updated void rdpClient_OnRemoteDesktopSizeChange(object sender, AxMSTSCLib.IMsTscAxEvents_OnRemoteDesktopSizeChangeEvent e) {