diff --git a/mRemoteV1/App/NativeMethods.cs b/mRemoteV1/App/NativeMethods.cs
index 0e15954b6..293379b0e 100644
--- a/mRemoteV1/App/NativeMethods.cs
+++ b/mRemoteV1/App/NativeMethods.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Drawing;
using System.Runtime.ConstrainedExecution;
using System.Runtime.InteropServices;
@@ -41,6 +41,12 @@ namespace mRemoteNG.App
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern int IsIconic(IntPtr hWnd);
+
+ internal delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
+ [DllImport("user32.dll")]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ internal static extern bool EnumChildWindows(IntPtr hwndParent, EnumWindowsProc lpEnumFunc, IntPtr lParam);
+
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool MoveWindow(IntPtr hWnd, int x, int y, int cx, int cy, bool repaint);
@@ -74,6 +80,9 @@ namespace mRemoteNG.App
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool SetForegroundWindow(IntPtr hWnd);
+ [DllImport("user32.dll", SetLastError = true)]
+ internal static extern IntPtr SetFocus(IntPtr hWnd);
+
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern bool SetMenuItemBitmaps(IntPtr hMenu,
int uPosition,
@@ -109,6 +118,16 @@ namespace mRemoteNG.App
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
internal static extern bool CloseHandle(IntPtr handle);
+ [DllImport("user32.dll")]
+ internal static extern IntPtr DefWindowProc(IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam);
+
+ [DllImport("user32.dll", SetLastError = true)]
+ internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
+
+ // When you don't want the ProcessId, use this overload and pass IntPtr.Zero for the second parameter
+ [DllImport("user32.dll")]
+ internal static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr processId);
+
#endregion
#region Structures
@@ -408,6 +427,11 @@ namespace mRemoteNG.App
///
public const int WM_WINDOWPOSCHANGED = 0x47;
+ ///
+ /// Sent to a window when its nonclient area needs to be changed to indicate an active or inactive state.
+ ///
+ public const int WM_NCACTIVATE = 0x86;
+
///
/// Posted to the window with the keyboard focus when a nonsystem key is pressed. A nonsystem key is a key that is pressed when the ALT key is not pressed.
///
@@ -423,6 +447,25 @@ namespace mRemoteNG.App
///
public const int WM_CHAR = 0x102;
+ ///
+ /// Posted to the window with the keyboard focus when the user presses the F10 key
+ /// (which activates the menu bar) or holds down the ALT key and then presses another
+ /// key. It also occurs when no window currently has the keyboard focus; in this case,
+ /// the WM_SYSKEYDOWN message is sent to the active window. The window that receives the
+ /// message can distinguish between these two contexts by checking the context code in
+ /// the lParam parameter.
+ ///
+ public const int WM_SYSKEYDOWN = 0x104;
+
+ ///
+ /// Posted to the window with the keyboard focus when the user releases a key that was
+ /// pressed while the ALT key was held down. It also occurs when no window currently
+ /// has the keyboard focus; in this case, the WM_SYSKEYUP message is sent to the active
+ /// window. The window that receives the message can distinguish between these two
+ /// contexts by checking the context code in the lParam parameter.
+ ///
+ public const int WM_SYSKEYUP = 0x105;
+
///
/// Sent when the user selects a command item from a menu, when a control sends a notification message to its parent window, or when an accelerator keystroke is translated.
///
@@ -542,6 +585,138 @@ namespace mRemoteNG.App
#endregion
+ #region WinEvent
+ ///
+ /// An application-defined callback (or hook) function that the system calls in
+ /// response to events generated by an accessible object. The hook function processes
+ /// the event notifications as required. Clients install the hook function and
+ /// request specific types of event notifications by calling SetWinEventHook.
+ ///
+ ///
+ /// Handle to an event hook function. This value is returned by
+ /// SetWinEventHook when the hook function is installed and
+ /// is specific to each instance of the hook function.
+ ///
+ ///
+ /// Specifies the event that occurred. This value is one of the event constants.
+ ///
+ ///
+ /// Handle to the window that generates the event, or NULL if no window is
+ /// associated with the event. For example, the mouse pointer is not associated
+ /// with a window.
+ ///
+ ///
+ /// Identifies the object associated with the event. This is one of the object
+ /// identifiers or a custom object ID.
+ ///
+ ///
+ /// Identifies whether the event was triggered by an object or a child element
+ /// of the object. If this value is CHILDID_SELF, the event was triggered by
+ /// the object; otherwise, this value is the child ID of the element that
+ /// triggered the event.
+ ///
+ ///
+ ///
+ /// Specifies the time, in milliseconds, that the event was generated.
+ ///
+ public delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd,
+ int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
+
+ ///
+ /// Sets an event hook function for a range of events.
+ ///
+ ///
+ /// Specifies the event constant for the lowest event value in the range of
+ /// events that are handled by the hook function. This parameter can be set
+ /// to EVENT_MIN to indicate the lowest possible event value.
+ ///
+ ///
+ /// Specifies the event constant for the highest event value in the range
+ /// of events that are handled by the hook function. This parameter can be
+ /// set to EVENT_MAX to indicate the highest possible event value.
+ ///
+ ///
+ /// Handle to the DLL that contains the hook function at lpfnWinEventProc,
+ /// if the WINEVENT_INCONTEXT flag is specified in the dwFlags parameter.
+ /// If the hook function is not located in a DLL, or if the WINEVENT_OUTOFCONTEXT
+ /// flag is specified, this parameter is NULL.
+ ///
+ ///
+ /// Pointer to the event hook function. For more information about this
+ /// function, see WinEventProc.
+ ///
+ ///
+ /// Specifies the ID of the process from which the hook function receives
+ /// events. Specify zero (0) to receive events from all processes on the
+ /// current desktop.
+ ///
+ ///
+ /// Specifies the ID of the thread from which the hook function receives
+ /// events. If this parameter is zero, the hook function is associated
+ /// with all existing threads on the current desktop.
+ ///
+ ///
+ /// Flag values that specify the location of the hook function and of
+ /// the events to be skipped. Valid values are: WINEVENT_INCONTEXT,
+ /// WINEVENT_OUTOFCONTEXT, WINEVENT_SKIPOWNPROCESS, WINEVENT_SKIPOWNTHREAD
+ ///
+ ///
+ /// If successful, returns an HWINEVENTHOOK value that identifies this
+ /// event hook instance. Applications save this return value to use it
+ /// with the UnhookWinEvent function. If unsuccessful, returns zero.
+ ///
+ [DllImport("user32.dll")]
+ public static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc,
+ WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
+
+ ///
+ /// Removes an event hook function created by a previous call to
+ /// SetWinEventHook.
+ ///
+ ///
+ /// Handle to the event hook returned in the previous call to
+ /// SetWinEventHook.
+ ///
+ ///
+ [DllImport("user32.dll")]
+ public static extern bool UnhookWinEvent(IntPtr hWinEventHook);
+
+ ///
+ /// The foreground window has changed. The system sends
+ /// this event even if the foreground window has changed
+ /// to another window in the same thread. Server applications
+ /// never send this event. For this event, the WinEventProc
+ /// callback function's hwnd parameter is the handle to the
+ /// window that is in the foreground, the idObject parameter
+ /// is OBJID_WINDOW, and the idChild parameter is CHILDID_SELF.
+ ///
+ public const uint EVENT_SYSTEM_FOREGROUND = 0x0003;
+
+ ///
+ /// The user has released ALT+TAB. This event is sent by the
+ /// system, never by servers. The hwnd parameter of the WinEventProc
+ /// callback function identifies the window to which the user
+ /// has switched. If only one application is running when the
+ /// user presses ALT+TAB, the system sends this event without
+ /// a corresponding EVENT_SYSTEM_SWITCHSTART event.
+ ///
+ public const uint EVENT_SYSTEM_SWITCHSTART = 0x0014;
+
+ ///
+ /// The user has pressed ALT+TAB, which activates the switch
+ /// window. This event is sent by the system, never by servers.
+ /// The hwnd parameter of the WinEventProc callback function
+ /// identifies the window to which the user is switching. If
+ /// only one application is running when the user presses
+ /// ALT+TAB, the system sends an EVENT_SYSTEM_SWITCHEND event
+ /// without a corresponding EVENT_SYSTEM_SWITCHSTART event.
+ ///
+ public const uint EVENT_SYSTEM_SWITCHEND = 0x0015;
+
+ public const uint WINEVENT_OUTOFCONTEXT = 0;
+
+ #endregion
+
#endregion
}
}
\ No newline at end of file
diff --git a/mRemoteV1/Connection/InterfaceControl.cs b/mRemoteV1/Connection/InterfaceControl.cs
index b21c31b8d..714e9fdfc 100644
--- a/mRemoteV1/Connection/InterfaceControl.cs
+++ b/mRemoteV1/Connection/InterfaceControl.cs
@@ -1,4 +1,4 @@
-using mRemoteNG.App;
+using mRemoteNG.App;
using mRemoteNG.Connection.Protocol;
using System;
using System.Drawing;
@@ -25,6 +25,8 @@ namespace mRemoteNG.Connection
Location = new Point(0, 0);
Size = Parent.Size;
Anchor = AnchorStyles.Bottom | AnchorStyles.Left | AnchorStyles.Right | AnchorStyles.Top;
+ GotFocus += OnGotFocus;
+ LostFocus += OnLostFocus;
InitializeComponent();
}
catch (Exception ex)
@@ -35,6 +37,16 @@ namespace mRemoteNG.Connection
}
}
+ private void OnLostFocus(object sender, EventArgs e)
+ {
+ Runtime.MessageCollector.AddMessage(Messages.MessageClass.DebugMsg, $"InterfaceControl lost focus '{Info.Name}'");
+ }
+
+ private void OnGotFocus(object sender, EventArgs e)
+ {
+ Runtime.MessageCollector.AddMessage(Messages.MessageClass.DebugMsg, $"InterfaceControl gained focus '{Info.Name}'");
+ }
+
public static InterfaceControl FindInterfaceControl(DockPanel DockPnl)
{
if (!(DockPnl.ActiveDocument is ConnectionTab ct)) return null;
diff --git a/mRemoteV1/Connection/Protocol/ExternalProcessProtocolBase.cs b/mRemoteV1/Connection/Protocol/ExternalProcessProtocolBase.cs
new file mode 100644
index 000000000..0b720e2d4
--- /dev/null
+++ b/mRemoteV1/Connection/Protocol/ExternalProcessProtocolBase.cs
@@ -0,0 +1,101 @@
+using System;
+using System.Diagnostics;
+using mRemoteNG.App;
+using mRemoteNG.Messages;
+
+namespace mRemoteNG.Connection.Protocol
+{
+ public class ExternalProcessProtocolBase : ProtocolBase
+ {
+ private IntPtr _winEventHook;
+ private NativeMethods.WinEventDelegate _setForegroundDelegate;
+
+ public override bool IsExternalProcess { get; } = true;
+
+ protected Process ProtocolProcess { get; set; }
+
+ protected IntPtr ProcessHandle { get; set; }
+
+ public int ThreadId => (int)NativeMethods.GetWindowThreadProcessId(ProcessHandle, IntPtr.Zero);
+
+
+ public override bool Connect()
+ {
+ _setForegroundDelegate = OnWinEventSetForeground;
+ _winEventHook = NativeMethods.SetWinEventHook(
+ NativeMethods.EVENT_SYSTEM_FOREGROUND,
+ NativeMethods.EVENT_SYSTEM_FOREGROUND,
+ IntPtr.Zero,
+ _setForegroundDelegate,
+ Convert.ToUInt32(ProtocolProcess.Id),
+ 0,
+ NativeMethods.WINEVENT_OUTOFCONTEXT);
+
+ return base.Connect();
+ }
+
+ public override void Close()
+ {
+ if (NativeMethods.UnhookWinEvent(_winEventHook))
+ {
+ Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"Successfully unhooked WinEvent listener from '{InterfaceControl.Info.Name}'");
+ }
+ else
+ {
+ Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"Failed to unhook WinEvent listener from '{InterfaceControl.Info.Name}'");
+ }
+
+ base.Close();
+ }
+
+ public override void Focus()
+ {
+ FocusChildProcessWindow();
+ }
+
+ private void FocusChildProcessWindow()
+ {
+ if (NativeMethods.GetForegroundWindow() == ProcessHandle)
+ {
+ Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "Process already focused - do nothing");
+ return;
+ }
+
+ var setForegroundSuccessful = NativeMethods.SetForegroundWindow(ProcessHandle);
+
+ var logMsg = setForegroundSuccessful
+ ? "External protocol window set to foreground. "
+ : "Failed to set external protocol window to foreground. ";
+
+ Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg,
+ logMsg +
+ $"name:'{InterfaceControl.Info.Name}', " +
+ $"protocol:'{InterfaceControl.Info.Protocol}', " +
+ $"pid:{ProtocolProcess.Id}, " +
+ $"hwnd:{ProcessHandle}");
+ }
+
+ ///
+ /// This callback will be called when the external process window managed by
+ /// this protocol is brought to the foreground.
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ void OnWinEventSetForeground(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
+ {
+ if (hwnd != ProtocolProcess.MainWindowHandle)
+ return;
+
+ //Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg,
+ // "Exernal protocol window set to foreground. " +
+ // $"protocol:{InterfaceControl.Info.Protocol} " +
+ // $"pid:{ProtocolProcess.Id}, " +
+ // $"hwnd:{ProtocolProcess.MainWindowHandle}");
+ }
+ }
+}
diff --git a/mRemoteV1/Connection/Protocol/IntegratedProgram.cs b/mRemoteV1/Connection/Protocol/IntegratedProgram.cs
index 2b296074d..0ec4cc407 100644
--- a/mRemoteV1/Connection/Protocol/IntegratedProgram.cs
+++ b/mRemoteV1/Connection/Protocol/IntegratedProgram.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.Diagnostics;
using System.Drawing;
using System.Threading;
@@ -9,7 +9,7 @@ using mRemoteNG.Tools;
namespace mRemoteNG.Connection.Protocol
{
- public class IntegratedProgram : ProtocolBase
+ public class IntegratedProgram : ExternalProcessProtocolBase
{
#region Private Fields
diff --git a/mRemoteV1/Connection/Protocol/ProtocolBase.cs b/mRemoteV1/Connection/Protocol/ProtocolBase.cs
index 3b673f2da..b9e7ba6db 100644
--- a/mRemoteV1/Connection/Protocol/ProtocolBase.cs
+++ b/mRemoteV1/Connection/Protocol/ProtocolBase.cs
@@ -63,6 +63,12 @@ namespace mRemoteNG.Connection.Protocol
public readonly System.Timers.Timer tmrReconnect = new System.Timers.Timer(2000);
protected ReconnectGroup ReconnectGroup;
+ ///
+ /// Whether this protocol runs as a thread within the main process or if
+ /// it is an external process that is running as a child process.
+ ///
+ public virtual bool IsExternalProcess { get; } = false;
+
protected ProtocolBase(string name)
{
Name = name;
diff --git a/mRemoteV1/Connection/Protocol/PuttyBase.cs b/mRemoteV1/Connection/Protocol/PuttyBase.cs
index 3897542df..97d616655 100644
--- a/mRemoteV1/Connection/Protocol/PuttyBase.cs
+++ b/mRemoteV1/Connection/Protocol/PuttyBase.cs
@@ -1,4 +1,4 @@
-using mRemoteNG.App;
+using mRemoteNG.App;
using mRemoteNG.Messages;
using mRemoteNG.Security.SymmetricEncryption;
using mRemoteNG.Tools;
@@ -14,27 +14,22 @@ using System.Windows.Forms;
namespace mRemoteNG.Connection.Protocol
{
- public class PuttyBase : ProtocolBase
+ public class PuttyBase : ExternalProcessProtocolBase
{
private const int IDM_RECONF = 0x50; // PuTTY Settings Menu ID
private bool _isPuttyNg;
private readonly DisplayProperties _display = new DisplayProperties();
#region Public Properties
-
protected Putty_Protocol PuttyProtocol { private get; set; }
protected Putty_SSHVersion PuttySSHVersion { private get; set; }
- public IntPtr PuttyHandle { get; set; }
-
- private Process PuttyProcess { get; set; }
-
public static string PuttyPath { get; set; }
public bool Focused
{
- get { return NativeMethods.GetForegroundWindow() == PuttyHandle; }
+ get { return NativeMethods.GetForegroundWindow() == ProcessHandle; }
}
#endregion
@@ -54,111 +49,39 @@ namespace mRemoteNG.Connection.Protocol
{
try
{
- _isPuttyNg = PuttyTypeDetector.GetPuttyType() == PuttyTypeDetector.PuttyType.PuttyNg;
+ var arguments = BuildPuttyCommandLineArguments(InterfaceControl.Info);
- PuttyProcess = new Process
+ ProtocolProcess = new Process
{
StartInfo =
{
UseShellExecute = false,
- FileName = PuttyPath
- }
+ FileName = PuttyPath,
+ Arguments = arguments.ToString()
+ },
+ EnableRaisingEvents = true
};
- var arguments = new CommandLineArguments {EscapeForShell = false};
+ ProtocolProcess.Exited += ProcessExited;
- arguments.Add("-load", InterfaceControl.Info.PuttySession);
-
- if (!(InterfaceControl.Info is PuttySessionInfo))
- {
- arguments.Add("-" + PuttyProtocol);
-
- if (PuttyProtocol == Putty_Protocol.ssh)
- {
- var username = "";
- var password = "";
-
- if (!string.IsNullOrEmpty(InterfaceControl.Info?.Username))
- {
- username = InterfaceControl.Info.Username;
- }
- else
- {
- // ReSharper disable once SwitchStatementMissingSomeCases
- switch (Settings.Default.EmptyCredentials)
- {
- case "windows":
- username = Environment.UserName;
- break;
- case "custom":
- username = Settings.Default.DefaultUsername;
- break;
- }
- }
-
- if (!string.IsNullOrEmpty(InterfaceControl.Info?.Password))
- {
- password = InterfaceControl.Info.Password;
- }
- else
- {
- if (Settings.Default.EmptyCredentials == "custom")
- {
- var cryptographyProvider = new LegacyRijndaelCryptographyProvider();
- password = cryptographyProvider.Decrypt(Settings.Default.DefaultPassword,
- Runtime.EncryptionKey);
- }
- }
-
- arguments.Add("-" + (int)PuttySSHVersion);
-
- if (!Force.HasFlag(ConnectionInfo.Force.NoCredentials))
- {
- if (!string.IsNullOrEmpty(username))
- {
- arguments.Add("-l", username);
- }
-
- if (!string.IsNullOrEmpty(password))
- {
- arguments.Add("-pw", password);
- }
- }
- }
-
- arguments.Add("-P", InterfaceControl.Info.Port.ToString());
- arguments.Add(InterfaceControl.Info.Hostname);
- }
-
- if (_isPuttyNg)
- {
- arguments.Add("-hwndparent", InterfaceControl.Handle.ToString());
- }
-
- PuttyProcess.StartInfo.Arguments = arguments.ToString();
-
- PuttyProcess.EnableRaisingEvents = true;
- PuttyProcess.Exited += ProcessExited;
-
- PuttyProcess.Start();
- PuttyProcess.WaitForInputIdle(Settings.Default.MaxPuttyWaitTime * 1000);
+ ProtocolProcess.Start();
+ ProtocolProcess.WaitForInputIdle(Settings.Default.MaxPuttyWaitTime * 1000);
var startTicks = Environment.TickCount;
- while (PuttyHandle.ToInt32() == 0 &
+ while (ProcessHandle.ToInt32() == 0 &
Environment.TickCount < startTicks + Settings.Default.MaxPuttyWaitTime * 1000)
{
if (_isPuttyNg)
{
- PuttyHandle = NativeMethods.FindWindowEx(
- InterfaceControl.Handle, new IntPtr(0), null, null);
+ ProcessHandle = NativeMethods.FindWindowEx(InterfaceControl.Handle, new IntPtr(0), null, null);
}
else
{
- PuttyProcess.Refresh();
- PuttyHandle = PuttyProcess.MainWindowHandle;
+ ProtocolProcess.Refresh();
+ ProcessHandle = ProtocolProcess.MainWindowHandle;
}
- if (PuttyHandle.ToInt32() == 0)
+ if (ProcessHandle.ToInt32() == 0)
{
Thread.Sleep(0);
}
@@ -166,14 +89,14 @@ namespace mRemoteNG.Connection.Protocol
if (!_isPuttyNg)
{
- NativeMethods.SetParent(PuttyHandle, InterfaceControl.Handle);
+ NativeMethods.SetParent(ProcessHandle, InterfaceControl.Handle);
}
Runtime.MessageCollector.AddMessage(MessageClass.InformationMsg, Language.strPuttyStuff, true);
Runtime.MessageCollector.AddMessage(MessageClass.InformationMsg,
- string.Format(Language.strPuttyHandle, PuttyHandle), true);
+ string.Format(Language.strPuttyHandle, ProcessHandle), true);
Runtime.MessageCollector.AddMessage(MessageClass.InformationMsg,
- string.Format(Language.strPuttyTitle, PuttyProcess.MainWindowTitle),
+ string.Format(Language.strPuttyTitle, ProtocolProcess.MainWindowTitle),
true);
Runtime.MessageCollector.AddMessage(MessageClass.InformationMsg,
string.Format(Language.strPuttyParentHandle,
@@ -192,18 +115,102 @@ namespace mRemoteNG.Connection.Protocol
}
}
+ private CommandLineArguments BuildPuttyCommandLineArguments(AbstractConnectionRecord connectionInfo)
+ {
+ var arguments = new CommandLineArguments { EscapeForShell = false };
+
+ arguments.Add("-load", connectionInfo.PuttySession);
+
+ if (!(connectionInfo is PuttySessionInfo))
+ {
+ arguments.Add("-" + PuttyProtocol);
+
+ if (PuttyProtocol == Putty_Protocol.ssh)
+ {
+ var username = "";
+ var password = "";
+
+ if (!string.IsNullOrEmpty(connectionInfo.Username))
+ {
+ username = connectionInfo.Username;
+ }
+ else
+ {
+ // ReSharper disable once SwitchStatementMissingSomeCases
+ switch (Settings.Default.EmptyCredentials)
+ {
+ case "windows":
+ username = Environment.UserName;
+ break;
+ case "custom":
+ username = Settings.Default.DefaultUsername;
+ break;
+ }
+ }
+
+ if (!string.IsNullOrEmpty(connectionInfo.Password))
+ {
+ password = connectionInfo.Password;
+ }
+ else
+ {
+ if (Settings.Default.EmptyCredentials == "custom")
+ {
+ var cryptographyProvider = new LegacyRijndaelCryptographyProvider();
+ password = cryptographyProvider.Decrypt(Settings.Default.DefaultPassword,
+ Runtime.EncryptionKey);
+ }
+ }
+
+ arguments.Add("-" + (int)PuttySSHVersion);
+
+ if (!Force.HasFlag(ConnectionInfo.Force.NoCredentials))
+ {
+ if (!string.IsNullOrEmpty(username))
+ {
+ arguments.Add("-l", username);
+ }
+
+ if (!string.IsNullOrEmpty(password))
+ {
+ arguments.Add("-pw", password);
+ }
+ }
+ }
+
+ arguments.Add("-P", connectionInfo.Port.ToString());
+ arguments.Add(connectionInfo.Hostname);
+ }
+
+ _isPuttyNg = PuttyTypeDetector.GetPuttyType() == PuttyTypeDetector.PuttyType.PuttyNg;
+ if (_isPuttyNg)
+ {
+ arguments.Add("-hwndparent", InterfaceControl.Handle.ToString());
+ }
+
+ return arguments;
+ }
+
public override void Focus()
{
- try
- {
- NativeMethods.SetForegroundWindow(PuttyHandle);
- }
- catch (Exception ex)
- {
- Runtime.MessageCollector.AddMessage(MessageClass.ErrorMsg,
- Language.strPuttyFocusFailed + Environment.NewLine + ex.Message,
- true);
- }
+ //try
+ //{
+ // if (NativeMethods.GetForegroundWindow() == PuttyHandle)
+ // {
+ // Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"Putty window already focused, ignoring focus request '{InterfaceControl.Info.Name}' (pid:{PuttyProcess.Id})");
+ // return;
+ // }
+
+ // NativeMethods.SetForegroundWindow(PuttyHandle);
+ // Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"Putty window focused '{InterfaceControl.Info.Name}' (pid:{PuttyProcess.Id})");
+ //}
+ //catch (Exception ex)
+ //{
+ // Runtime.MessageCollector.AddMessage(MessageClass.ErrorMsg,
+ // Language.strPuttyFocusFailed + Environment.NewLine + ex.Message,
+ // true);
+ //}
+ base.Focus();
}
public override void Resize(object sender, EventArgs e)
@@ -216,14 +223,14 @@ namespace mRemoteNG.Connection.Protocol
if (_isPuttyNg)
{
// PuTTYNG 0.70.0.1 and later doesn't have any window borders
- NativeMethods.MoveWindow(PuttyHandle, 0, 0, InterfaceControl.Width, InterfaceControl.Height, true);
+ NativeMethods.MoveWindow(ProcessHandle, 0, 0, InterfaceControl.Width, InterfaceControl.Height, true);
}
else
{
var scaledFrameBorderHeight = _display.ScaleHeight(SystemInformation.FrameBorderSize.Height);
var scaledFrameBorderWidth = _display.ScaleWidth(SystemInformation.FrameBorderSize.Width);
- NativeMethods.MoveWindow(PuttyHandle, -scaledFrameBorderWidth,
+ NativeMethods.MoveWindow(ProcessHandle, -scaledFrameBorderWidth,
-(SystemInformation.CaptionHeight + scaledFrameBorderHeight),
InterfaceControl.Width + scaledFrameBorderWidth * 2,
InterfaceControl.Height + SystemInformation.CaptionHeight +
@@ -243,9 +250,9 @@ namespace mRemoteNG.Connection.Protocol
{
try
{
- if (PuttyProcess.HasExited == false)
+ if (ProtocolProcess.HasExited == false)
{
- PuttyProcess.Kill();
+ ProtocolProcess.Kill();
}
}
catch (Exception ex)
@@ -257,7 +264,7 @@ namespace mRemoteNG.Connection.Protocol
try
{
- PuttyProcess.Dispose();
+ ProtocolProcess.Dispose();
}
catch (Exception ex)
{
@@ -273,8 +280,8 @@ namespace mRemoteNG.Connection.Protocol
{
try
{
- NativeMethods.PostMessage(PuttyHandle, NativeMethods.WM_SYSCOMMAND, (IntPtr)IDM_RECONF, (IntPtr)0);
- NativeMethods.SetForegroundWindow(PuttyHandle);
+ NativeMethods.PostMessage(ProcessHandle, NativeMethods.WM_SYSCOMMAND, (IntPtr)IDM_RECONF, IntPtr.Zero);
+ Focus();
}
catch (Exception ex)
{
@@ -284,6 +291,14 @@ namespace mRemoteNG.Connection.Protocol
}
}
+ ///
+ /// Sends an individual key stroke to this PuTTY session.
+ ///
+ public void SendKeyStroke(int keyType, int keyData)
+ {
+ NativeMethods.PostMessage(ProcessHandle, keyType, new IntPtr(keyData), new IntPtr(0));
+ }
+
#endregion
#region Enums
diff --git a/mRemoteV1/Connection/Protocol/RDP/RdpProtocol6.cs b/mRemoteV1/Connection/Protocol/RDP/RdpProtocol6.cs
index ca53457a9..60021fab7 100644
--- a/mRemoteV1/Connection/Protocol/RDP/RdpProtocol6.cs
+++ b/mRemoteV1/Connection/Protocol/RDP/RdpProtocol6.cs
@@ -1,6 +1,9 @@
using System;
+using System.Collections.Generic;
using System.Diagnostics;
+using System.Linq;
using System.Runtime.InteropServices;
+using System.Text;
using System.Threading;
using System.Timers;
using System.Windows.Forms;
@@ -35,6 +38,7 @@ namespace mRemoteNG.Connection.Protocol.RDP
private readonly FrmMain _frmMain = FrmMain.Default;
protected virtual RdpVersion RdpProtocolVersion => RdpVersion.Rdc6;
private AxHost AxHost => (AxHost)Control;
+ private readonly NativeMethods.EnumWindowsProc _enumWindowsProc;
#region Properties
@@ -101,6 +105,7 @@ namespace mRemoteNG.Connection.Protocol.RDP
{
_displayProperties = new DisplayProperties();
tmrReconnect.Elapsed += tmrReconnect_Elapsed;
+ _enumWindowsProc = LpEnumFunc;
}
#endregion
@@ -243,17 +248,54 @@ namespace mRemoteNG.Connection.Protocol.RDP
public override void Focus()
{
+ var result = new List();
+ var listHandle = GCHandle.Alloc(result);
try
{
if (Control.ContainsFocus == false)
{
- Control.Focus();
+ //AxHost.Focus();
+ //AxHost.Select();
+ //AxHost.DoVerb(-1);
+ //AxHost.DoVerb(-4);
+ //AxHost.DoVerb(-5);
+
+ NativeMethods.EnumChildWindows(AxHost.Handle, _enumWindowsProc, GCHandle.ToIntPtr(listHandle));
+ if (result.Any())
+ {
+ NativeMethods.SetFocus(result[0]);
+ Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"RDP connection focused '{connectionInfo.Name}'");
+ }
}
}
catch (Exception ex)
{
Runtime.MessageCollector.AddExceptionStackTrace(Language.strRdpFocusFailed, ex);
}
+ finally
+ {
+ if (listHandle.IsAllocated)
+ listHandle.Free();
+ }
+ }
+
+ private bool LpEnumFunc(IntPtr hwnd, IntPtr lparam)
+ {
+ var gch = GCHandle.FromIntPtr(lparam);
+ var list = gch.Target as List;
+ if (list == null)
+ throw new InvalidCastException("GCHandle Target could not be cast as List");
+
+ var sb = new StringBuilder();
+ NativeMethods.GetClassName(hwnd, sb, 64);
+
+ if (sb.ToString().Equals("IHWindowClass"))
+ {
+ list.Add(hwnd);
+ return false;
+ }
+
+ return true;
}
///
@@ -672,6 +714,9 @@ namespace mRemoteNG.Connection.Protocol.RDP
_rdpClient.OnDisconnected += RDPEvent_OnDisconnected;
_rdpClient.OnLeaveFullScreenMode += RDPEvent_OnLeaveFullscreenMode;
_rdpClient.OnIdleTimeoutNotification += RDPEvent_OnIdleTimeoutNotification;
+ //_rdpClient.OnFocusReleased += direction => Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"RDP control '{connectionInfo.Name}' released focus.");
+ //AxHost.GotFocus += (sender, args) => Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"RDP control '{connectionInfo.Name}' received focus.");
+ //AxHost.LostFocus += (sender, args) => Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"RDP control '{connectionInfo.Name}' lost focus.");
}
catch (Exception ex)
{
@@ -748,7 +793,7 @@ namespace mRemoteNG.Connection.Protocol.RDP
private void RdpClient_GotFocus(object sender, EventArgs e)
{
- ((ConnectionTab)Control.Parent.Parent).Focus();
+ //((ConnectionTab)Control.Parent.Parent).Focus();
}
#endregion
diff --git a/mRemoteV1/Messages/MessageWriters/DebugConsoleMessageWriter.cs b/mRemoteV1/Messages/MessageWriters/DebugConsoleMessageWriter.cs
index c168e39f8..1ab4387b1 100644
--- a/mRemoteV1/Messages/MessageWriters/DebugConsoleMessageWriter.cs
+++ b/mRemoteV1/Messages/MessageWriters/DebugConsoleMessageWriter.cs
@@ -6,7 +6,7 @@ namespace mRemoteNG.Messages.MessageWriters
{
public void Write(IMessage message)
{
- var textToPrint = $"{message.Class}: {message.Text}";
+ var textToPrint = $"[{message.Date.ToString("O")}] {message.Class}: {message.Text}";
Debug.Print(textToPrint);
}
}
diff --git a/mRemoteV1/Themes/ThemeInfo.cs b/mRemoteV1/Themes/ThemeInfo.cs
index eb0f2445e..1cb9c4e0e 100644
--- a/mRemoteV1/Themes/ThemeInfo.cs
+++ b/mRemoteV1/Themes/ThemeInfo.cs
@@ -1,4 +1,4 @@
-using System;
+using System;
using System.ComponentModel;
using System.Linq;
using WeifenLuo.WinFormsUI.Docking;
diff --git a/mRemoteV1/Tools/MultiSSHController.cs b/mRemoteV1/Tools/MultiSSHController.cs
index d2dc91b8c..162f46789 100644
--- a/mRemoteV1/Tools/MultiSSHController.cs
+++ b/mRemoteV1/Tools/MultiSSHController.cs
@@ -63,7 +63,7 @@ namespace mRemoteNG.Tools
foreach (PuttyBase proc in processHandlers)
{
- NativeMethods.PostMessage(proc.PuttyHandle, keyType, new IntPtr(keyData), new IntPtr(0));
+ proc.SendKeyStroke(keyType, keyData);
}
}
diff --git a/mRemoteV1/UI/Forms/frmMain.Designer.cs b/mRemoteV1/UI/Forms/frmMain.Designer.cs
index dc8c75a75..de63b2247 100644
--- a/mRemoteV1/UI/Forms/frmMain.Designer.cs
+++ b/mRemoteV1/UI/Forms/frmMain.Designer.cs
@@ -31,193 +31,191 @@
[System.Diagnostics.DebuggerStepThrough()]
private void InitializeComponent()
{
- this.components = new System.ComponentModel.Container();
- mRemoteNG.Connection.ConnectionInitiator connectionInitiator1 = new mRemoteNG.Connection.ConnectionInitiator();
- System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmMain));
- this.pnlDock = new WeifenLuo.WinFormsUI.Docking.DockPanel();
- this.msMain = new System.Windows.Forms.MenuStrip();
- this.fileMenu = new mRemoteNG.UI.Menu.MainFileMenu();
- this.viewMenu = new mRemoteNG.UI.Menu.ViewMenu();
- this.toolsMenu = new mRemoteNG.UI.Menu.ToolsMenu();
- this.helpMenu = new mRemoteNG.UI.Menu.HelpMenu();
- this.mMenSep3 = new System.Windows.Forms.ToolStripSeparator();
- this.tsContainer = new System.Windows.Forms.ToolStripContainer();
- this._quickConnectToolStrip = new mRemoteNG.UI.Controls.QuickConnectToolStrip();
- this._multiSshToolStrip = new mRemoteNG.UI.Controls.MultiSshToolStrip();
- this._externalToolsToolStrip = new mRemoteNG.UI.Controls.ExternalToolsToolStrip();
- this.tmrAutoSave = new System.Windows.Forms.Timer(this.components);
- this.vsToolStripExtender = new WeifenLuo.WinFormsUI.Docking.VisualStudioToolStripExtender(this.components);
- this.msMain.SuspendLayout();
- this.tsContainer.ContentPanel.SuspendLayout();
- this.tsContainer.TopToolStripPanel.SuspendLayout();
- this.tsContainer.SuspendLayout();
- this.SuspendLayout();
- //
- // pnlDock
- //
- this.pnlDock.Dock = System.Windows.Forms.DockStyle.Fill;
- this.pnlDock.DockBackColor = System.Drawing.SystemColors.Control;
- this.pnlDock.DockLeftPortion = 230D;
- this.pnlDock.DockRightPortion = 230D;
- this.pnlDock.DocumentStyle = WeifenLuo.WinFormsUI.Docking.DocumentStyle.DockingSdi;
- this.pnlDock.Location = new System.Drawing.Point(0, 0);
- this.pnlDock.Name = "pnlDock";
- this.pnlDock.Size = new System.Drawing.Size(1129, 471);
- this.pnlDock.TabIndex = 13;
- this.pnlDock.ActiveDocumentChanged += new System.EventHandler(this.pnlDock_ActiveDocumentChanged);
- //
- // msMain
- //
- this.msMain.Anchor = System.Windows.Forms.AnchorStyles.Left;
- this.msMain.Dock = System.Windows.Forms.DockStyle.None;
- this.msMain.GripMargin = new System.Windows.Forms.Padding(2);
- this.msMain.GripStyle = System.Windows.Forms.ToolStripGripStyle.Visible;
- this.msMain.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
+ this.components = new System.ComponentModel.Container();
+ System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmMain));
+ this.pnlDock = new WeifenLuo.WinFormsUI.Docking.DockPanel();
+ this.msMain = new System.Windows.Forms.MenuStrip();
+ this.fileMenu = new mRemoteNG.UI.Menu.MainFileMenu();
+ this.viewMenu = new mRemoteNG.UI.Menu.ViewMenu();
+ this.toolsMenu = new mRemoteNG.UI.Menu.ToolsMenu();
+ this.helpMenu = new mRemoteNG.UI.Menu.HelpMenu();
+ this.mMenSep3 = new System.Windows.Forms.ToolStripSeparator();
+ this.tsContainer = new System.Windows.Forms.ToolStripContainer();
+ this._quickConnectToolStrip = new mRemoteNG.UI.Controls.QuickConnectToolStrip();
+ this._multiSshToolStrip = new mRemoteNG.UI.Controls.MultiSshToolStrip();
+ this._externalToolsToolStrip = new mRemoteNG.UI.Controls.ExternalToolsToolStrip();
+ this.tmrAutoSave = new System.Windows.Forms.Timer(this.components);
+ this.vsToolStripExtender = new WeifenLuo.WinFormsUI.Docking.VisualStudioToolStripExtender(this.components);
+ this.msMain.SuspendLayout();
+ this.tsContainer.ContentPanel.SuspendLayout();
+ this.tsContainer.TopToolStripPanel.SuspendLayout();
+ this.tsContainer.SuspendLayout();
+ this.SuspendLayout();
+ //
+ // pnlDock
+ //
+ this.pnlDock.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.pnlDock.DockBackColor = System.Drawing.SystemColors.Control;
+ this.pnlDock.DockLeftPortion = 230D;
+ this.pnlDock.DockRightPortion = 230D;
+ this.pnlDock.DocumentStyle = WeifenLuo.WinFormsUI.Docking.DocumentStyle.DockingSdi;
+ this.pnlDock.Location = new System.Drawing.Point(0, 0);
+ this.pnlDock.Name = "pnlDock";
+ this.pnlDock.Size = new System.Drawing.Size(1129, 471);
+ this.pnlDock.TabIndex = 13;
+ this.pnlDock.ActiveDocumentChanged += new System.EventHandler(this.pnlDock_ActiveDocumentChanged);
+ //
+ // msMain
+ //
+ this.msMain.Anchor = System.Windows.Forms.AnchorStyles.Left;
+ this.msMain.Dock = System.Windows.Forms.DockStyle.None;
+ this.msMain.GripMargin = new System.Windows.Forms.Padding(2);
+ this.msMain.GripStyle = System.Windows.Forms.ToolStripGripStyle.Visible;
+ this.msMain.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
this.fileMenu,
this.viewMenu,
this.toolsMenu,
this.helpMenu});
- this.msMain.Location = new System.Drawing.Point(3, 50);
- this.msMain.Name = "msMain";
- this.msMain.Padding = new System.Windows.Forms.Padding(0, 0, 1, 0);
- this.msMain.Size = new System.Drawing.Size(184, 25);
- this.msMain.Stretch = false;
- this.msMain.TabIndex = 0;
- this.msMain.Text = "Main Toolbar";
- //
- // fileMenu
- //
- this.fileMenu.ConnectionInitiator = null;
- this.fileMenu.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
- this.fileMenu.Name = "mMenFile";
- this.fileMenu.Size = new System.Drawing.Size(37, 19);
- this.fileMenu.Text = "&File";
- this.fileMenu.TreeWindow = null;
- this.fileMenu.DropDownOpening += new System.EventHandler(this.mainFileMenu1_DropDownOpening);
- //
- // viewMenu
- //
- this.viewMenu.FullscreenHandler = null;
- this.viewMenu.MainForm = null;
- this.viewMenu.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
- this.viewMenu.Name = "mMenView";
- this.viewMenu.Size = new System.Drawing.Size(44, 19);
- this.viewMenu.Text = "&View";
- this.viewMenu.TsExternalTools = null;
- this.viewMenu.TsMultiSsh = null;
- this.viewMenu.TsQuickConnect = null;
- this.viewMenu.DropDownOpening += new System.EventHandler(this.ViewMenu_Opening);
- //
- // toolsMenu
- //
- this.toolsMenu.CredentialProviderCatalog = null;
- this.toolsMenu.MainForm = null;
- this.toolsMenu.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
- this.toolsMenu.Name = "mMenTools";
- this.toolsMenu.Size = new System.Drawing.Size(47, 19);
- this.toolsMenu.Text = "&Tools";
- //
- // helpMenu
- //
- this.helpMenu.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
- this.helpMenu.Name = "mMenInfo";
- this.helpMenu.Size = new System.Drawing.Size(44, 19);
- this.helpMenu.Text = "&Help";
- this.helpMenu.TextDirection = System.Windows.Forms.ToolStripTextDirection.Horizontal;
- //
- // mMenSep3
- //
- this.mMenSep3.Name = "mMenSep3";
- this.mMenSep3.Size = new System.Drawing.Size(211, 6);
- //
- // tsContainer
- //
- //
- // tsContainer.ContentPanel
- //
- this.tsContainer.ContentPanel.Controls.Add(this.pnlDock);
- this.tsContainer.ContentPanel.Size = new System.Drawing.Size(1129, 471);
- this.tsContainer.Dock = System.Windows.Forms.DockStyle.Fill;
- this.tsContainer.Location = new System.Drawing.Point(0, 0);
- this.tsContainer.Name = "tsContainer";
- this.tsContainer.Size = new System.Drawing.Size(1129, 571);
- this.tsContainer.TabIndex = 17;
- this.tsContainer.Text = "ToolStripContainer1";
- //
- // tsContainer.TopToolStripPanel
- //
- this.tsContainer.TopToolStripPanel.Controls.Add(this._quickConnectToolStrip);
- this.tsContainer.TopToolStripPanel.Controls.Add(this._multiSshToolStrip);
- this.tsContainer.TopToolStripPanel.Controls.Add(this.msMain);
- this.tsContainer.TopToolStripPanel.Controls.Add(this._externalToolsToolStrip);
- this.tsContainer.TopToolStripPanel.RenderMode = System.Windows.Forms.ToolStripRenderMode.Professional;
- //
- // _quickConnectToolStrip
- //
- this._quickConnectToolStrip.BackColor = System.Drawing.SystemColors.Control;
- this._quickConnectToolStrip.ConnectionInitiator = connectionInitiator1;
- this._quickConnectToolStrip.Dock = System.Windows.Forms.DockStyle.None;
- this._quickConnectToolStrip.ForeColor = System.Drawing.SystemColors.ControlText;
- this._quickConnectToolStrip.Location = new System.Drawing.Point(3, 0);
- this._quickConnectToolStrip.Name = "_quickConnectToolStrip";
- this._quickConnectToolStrip.Size = new System.Drawing.Size(364, 25);
- this._quickConnectToolStrip.TabIndex = 18;
- //
- // _multiSshToolStrip
- //
- this._multiSshToolStrip.Dock = System.Windows.Forms.DockStyle.None;
- this._multiSshToolStrip.Location = new System.Drawing.Point(3, 25);
- this._multiSshToolStrip.MinimumSize = new System.Drawing.Size(300, 0);
- this._multiSshToolStrip.Name = "_multiSshToolStrip";
- this._multiSshToolStrip.Size = new System.Drawing.Size(376, 25);
- this._multiSshToolStrip.TabIndex = 1;
- //
- // _externalToolsToolStrip
- //
- this._externalToolsToolStrip.BackColor = System.Drawing.SystemColors.Control;
- this._externalToolsToolStrip.Dock = System.Windows.Forms.DockStyle.None;
- this._externalToolsToolStrip.ForeColor = System.Drawing.SystemColors.ControlText;
- this._externalToolsToolStrip.Location = new System.Drawing.Point(3, 75);
- this._externalToolsToolStrip.Name = "_externalToolsToolStrip";
- this._externalToolsToolStrip.Size = new System.Drawing.Size(111, 25);
- this._externalToolsToolStrip.TabIndex = 17;
- //
- // tmrAutoSave
- //
- this.tmrAutoSave.Interval = 10000;
- this.tmrAutoSave.Tick += new System.EventHandler(this.tmrAutoSave_Tick);
- //
- // vsToolStripExtender
- //
- this.vsToolStripExtender.DefaultRenderer = null;
- //
- // FrmMain
- //
- this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
- this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
- this.ClientSize = new System.Drawing.Size(1129, 571);
- this.Controls.Add(this.tsContainer);
- this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
- this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
- this.MainMenuStrip = this.msMain;
- this.MinimumSize = new System.Drawing.Size(400, 400);
- this.Name = "FrmMain";
- this.Opacity = 0D;
- this.Text = " ";
- this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.frmMain_FormClosing);
- this.Load += new System.EventHandler(this.frmMain_Load);
- this.Shown += new System.EventHandler(this.frmMain_Shown);
- this.ResizeBegin += new System.EventHandler(this.frmMain_ResizeBegin);
- this.ResizeEnd += new System.EventHandler(this.frmMain_ResizeEnd);
- this.Resize += new System.EventHandler(this.frmMain_Resize);
- this.msMain.ResumeLayout(false);
- this.msMain.PerformLayout();
- this.tsContainer.ContentPanel.ResumeLayout(false);
- this.tsContainer.TopToolStripPanel.ResumeLayout(false);
- this.tsContainer.TopToolStripPanel.PerformLayout();
- this.tsContainer.ResumeLayout(false);
- this.tsContainer.PerformLayout();
- this.ResumeLayout(false);
+ this.msMain.Location = new System.Drawing.Point(3, 50);
+ this.msMain.Name = "msMain";
+ this.msMain.Padding = new System.Windows.Forms.Padding(0, 0, 1, 0);
+ this.msMain.Size = new System.Drawing.Size(184, 25);
+ this.msMain.Stretch = false;
+ this.msMain.TabIndex = 0;
+ this.msMain.Text = "Main Toolbar";
+ //
+ // fileMenu
+ //
+ this.fileMenu.ConnectionInitiator = null;
+ this.fileMenu.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
+ this.fileMenu.Name = "mMenFile";
+ this.fileMenu.Size = new System.Drawing.Size(37, 19);
+ this.fileMenu.Text = "&File";
+ this.fileMenu.TreeWindow = null;
+ this.fileMenu.DropDownOpening += new System.EventHandler(this.mainFileMenu1_DropDownOpening);
+ //
+ // viewMenu
+ //
+ this.viewMenu.FullscreenHandler = null;
+ this.viewMenu.MainForm = null;
+ this.viewMenu.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
+ this.viewMenu.Name = "mMenView";
+ this.viewMenu.Size = new System.Drawing.Size(44, 19);
+ this.viewMenu.Text = "&View";
+ this.viewMenu.TsExternalTools = null;
+ this.viewMenu.TsMultiSsh = null;
+ this.viewMenu.TsQuickConnect = null;
+ this.viewMenu.DropDownOpening += new System.EventHandler(this.ViewMenu_Opening);
+ //
+ // toolsMenu
+ //
+ this.toolsMenu.CredentialProviderCatalog = null;
+ this.toolsMenu.MainForm = null;
+ this.toolsMenu.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
+ this.toolsMenu.Name = "mMenTools";
+ this.toolsMenu.Size = new System.Drawing.Size(47, 19);
+ this.toolsMenu.Text = "&Tools";
+ //
+ // helpMenu
+ //
+ this.helpMenu.Margin = new System.Windows.Forms.Padding(0, 3, 0, 3);
+ this.helpMenu.Name = "mMenInfo";
+ this.helpMenu.Size = new System.Drawing.Size(44, 19);
+ this.helpMenu.Text = "&Help";
+ this.helpMenu.TextDirection = System.Windows.Forms.ToolStripTextDirection.Horizontal;
+ //
+ // mMenSep3
+ //
+ this.mMenSep3.Name = "mMenSep3";
+ this.mMenSep3.Size = new System.Drawing.Size(211, 6);
+ //
+ // tsContainer
+ //
+ //
+ // tsContainer.ContentPanel
+ //
+ this.tsContainer.ContentPanel.Controls.Add(this.pnlDock);
+ this.tsContainer.ContentPanel.Size = new System.Drawing.Size(1129, 471);
+ this.tsContainer.Dock = System.Windows.Forms.DockStyle.Fill;
+ this.tsContainer.Location = new System.Drawing.Point(0, 0);
+ this.tsContainer.Name = "tsContainer";
+ this.tsContainer.Size = new System.Drawing.Size(1129, 571);
+ this.tsContainer.TabIndex = 17;
+ this.tsContainer.Text = "ToolStripContainer1";
+ //
+ // tsContainer.TopToolStripPanel
+ //
+ this.tsContainer.TopToolStripPanel.Controls.Add(this._quickConnectToolStrip);
+ this.tsContainer.TopToolStripPanel.Controls.Add(this._multiSshToolStrip);
+ this.tsContainer.TopToolStripPanel.Controls.Add(this.msMain);
+ this.tsContainer.TopToolStripPanel.Controls.Add(this._externalToolsToolStrip);
+ this.tsContainer.TopToolStripPanel.RenderMode = System.Windows.Forms.ToolStripRenderMode.Professional;
+ //
+ // _quickConnectToolStrip
+ //
+ this._quickConnectToolStrip.BackColor = System.Drawing.SystemColors.Control;
+ this._quickConnectToolStrip.Dock = System.Windows.Forms.DockStyle.None;
+ this._quickConnectToolStrip.ForeColor = System.Drawing.SystemColors.ControlText;
+ this._quickConnectToolStrip.Location = new System.Drawing.Point(3, 0);
+ this._quickConnectToolStrip.Name = "_quickConnectToolStrip";
+ this._quickConnectToolStrip.Size = new System.Drawing.Size(364, 25);
+ this._quickConnectToolStrip.TabIndex = 18;
+ //
+ // _multiSshToolStrip
+ //
+ this._multiSshToolStrip.Dock = System.Windows.Forms.DockStyle.None;
+ this._multiSshToolStrip.Location = new System.Drawing.Point(3, 25);
+ this._multiSshToolStrip.MinimumSize = new System.Drawing.Size(300, 0);
+ this._multiSshToolStrip.Name = "_multiSshToolStrip";
+ this._multiSshToolStrip.Size = new System.Drawing.Size(376, 25);
+ this._multiSshToolStrip.TabIndex = 1;
+ //
+ // _externalToolsToolStrip
+ //
+ this._externalToolsToolStrip.BackColor = System.Drawing.SystemColors.Control;
+ this._externalToolsToolStrip.Dock = System.Windows.Forms.DockStyle.None;
+ this._externalToolsToolStrip.ForeColor = System.Drawing.SystemColors.ControlText;
+ this._externalToolsToolStrip.Location = new System.Drawing.Point(3, 75);
+ this._externalToolsToolStrip.Name = "_externalToolsToolStrip";
+ this._externalToolsToolStrip.Size = new System.Drawing.Size(111, 25);
+ this._externalToolsToolStrip.TabIndex = 17;
+ //
+ // tmrAutoSave
+ //
+ this.tmrAutoSave.Interval = 10000;
+ this.tmrAutoSave.Tick += new System.EventHandler(this.tmrAutoSave_Tick);
+ //
+ // vsToolStripExtender
+ //
+ this.vsToolStripExtender.DefaultRenderer = null;
+ //
+ // FrmMain
+ //
+ this.AutoScaleDimensions = new System.Drawing.SizeF(96F, 96F);
+ this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Dpi;
+ this.ClientSize = new System.Drawing.Size(1129, 571);
+ this.Controls.Add(this.tsContainer);
+ this.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0)));
+ this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon")));
+ this.MainMenuStrip = this.msMain;
+ this.MinimumSize = new System.Drawing.Size(400, 400);
+ this.Name = "FrmMain";
+ this.Opacity = 0D;
+ this.Text = " ";
+ this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.frmMain_FormClosing);
+ this.Load += new System.EventHandler(this.frmMain_Load);
+ this.Shown += new System.EventHandler(this.frmMain_Shown);
+ this.ResizeBegin += new System.EventHandler(this.frmMain_ResizeBegin);
+ this.ResizeEnd += new System.EventHandler(this.frmMain_ResizeEnd);
+ this.Resize += new System.EventHandler(this.frmMain_Resize);
+ this.msMain.ResumeLayout(false);
+ this.msMain.PerformLayout();
+ this.tsContainer.ContentPanel.ResumeLayout(false);
+ this.tsContainer.TopToolStripPanel.ResumeLayout(false);
+ this.tsContainer.TopToolStripPanel.PerformLayout();
+ this.tsContainer.ResumeLayout(false);
+ this.tsContainer.PerformLayout();
+ this.ResumeLayout(false);
}
internal WeifenLuo.WinFormsUI.Docking.DockPanel pnlDock;
diff --git a/mRemoteV1/UI/Forms/frmMain.cs b/mRemoteV1/UI/Forms/frmMain.cs
index 4ec22b45a..0474b08d2 100644
--- a/mRemoteV1/UI/Forms/frmMain.cs
+++ b/mRemoteV1/UI/Forms/frmMain.cs
@@ -1,4 +1,4 @@
-using Microsoft.Win32;
+using Microsoft.Win32;
using mRemoteNG.App;
using mRemoteNG.App.Info;
using mRemoteNG.App.Initialization;
@@ -23,11 +23,14 @@ using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.IO;
+using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
+using mRemoteNG.Connection.Protocol;
using mRemoteNG.UI.Panels;
using WeifenLuo.WinFormsUI.Docking;
+using Message = System.Windows.Forms.Message;
// ReSharper disable MemberCanBePrivate.Global
@@ -206,6 +209,18 @@ namespace mRemoteNG.UI.Forms
panelAdder.AddPanel(panelName);
}
+ TabHelper.Instance.ActiveConnectionTabChanged += OnActiveConnectionTabChanged;
+ TabHelper.Instance.TabClicked += OnTabClicked;
+ }
+
+ private void OnTabClicked(object sender, EventArgs e)
+ {
+ ActivateConnection();
+ }
+
+ private void OnActiveConnectionTabChanged(object sender, EventArgs e)
+ {
+ ActivateConnection();
}
private void ApplyLanguage()
@@ -437,6 +452,7 @@ namespace mRemoteNG.UI.Forms
private void frmMain_ResizeBegin(object sender, EventArgs e)
{
_inSizeMove = true;
+ Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "Begin app window move/resize");
}
private void frmMain_Resize(object sender, EventArgs e)
@@ -460,12 +476,17 @@ namespace mRemoteNG.UI.Forms
private void frmMain_ResizeEnd(object sender, EventArgs e)
{
_inSizeMove = false;
+ Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "End app window move/resize");
// This handles activations from clicks that started a size/move operation
ActivateConnection();
}
+
+ // Maybe after starting putty, remove its ability to show up in alt-tab?
+ // SetWindowLong(this.Handle, GWL_EXSTYLE, (GetWindowLong(this.Handle,GWL_EXSTYLE) | WS_EX_TOOLWINDOW) & ~WS_EX_APPWINDOW);
protected override void WndProc(ref System.Windows.Forms.Message m)
{
+ const int tabKey = 0x09;
// Listen for and handle operating system messages
try
{
@@ -474,63 +495,124 @@ namespace mRemoteNG.UI.Forms
{
case NativeMethods.WM_MOUSEACTIVATE:
_inMouseActivate = true;
+ Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"_inMouseActivate = {_inMouseActivate}");
+
+ var controlThatWasClicked2 = FromChildHandle(NativeMethods.WindowFromPoint(MousePosition))
+ ?? GetChildAtPoint(MousePosition);
+
+ if (controlThatWasClicked2 == null)
+ break;
+
+ Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"Clicked control: {controlThatWasClicked2}");
+ break;
+ case NativeMethods.WM_KEYDOWN:
+ case NativeMethods.WM_SYSKEYDOWN:
+ if (m.WParam.ToInt32() != tabKey)
+ break;
+
+ if ((m.LParam.ToInt32() & 0b00100000000000000000000000000000) == 0) // 29th bit ON means ALT key down
+ break;
+
+ Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "ALT-TAB PRESSED");
+
+ break;
+ case NativeMethods.WM_SYSKEYUP:
+ if (m.WParam.ToInt32() != tabKey)
+ break;
+
+ if ((m.LParam.ToInt32() & 0b00100000000000000000000000000000) == 0) // 29th bit ON means ALT key down
+ break;
+
+ Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "ALT-TAB RELEASED");
+
break;
case NativeMethods.WM_ACTIVATEAPP:
- var candidateTabToFocus = FromChildHandle(NativeMethods.WindowFromPoint(MousePosition))
- ?? GetChildAtPoint(MousePosition);
-
- if (candidateTabToFocus is InterfaceControl)
+ if (m.WParam.ToInt32() == 0) // mRemoteNG is being deactivated
{
- candidateTabToFocus.Parent.Focus();
+ //var threadWhichIsActivating = m.LParam.ToInt32();
+ //var activatingChildProcessWindow = _connectionInitiator.ActiveConnections
+ // .OfType()
+ // .Any(prot => prot.ThreadId == threadWhichIsActivating);
+
+ _inMouseActivate = false;
+ Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "mRemoteNG main window lost focus");
+ break;
}
- _inMouseActivate = false;
+ Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "mRemoteNG main window received focus");
+
+ //var candidateTabToFocus = FromChildHandle(NativeMethods.WindowFromPoint(MousePosition))
+ // ?? GetChildAtPoint(MousePosition);
+ //if (candidateTabToFocus is InterfaceControl)
+ //{
+ // candidateTabToFocus.Parent.Focus();
+ //}
+
break;
case NativeMethods.WM_ACTIVATE:
- // Only handle this msg if it was triggered by a click
- if (NativeMethods.LOWORD(m.WParam) == NativeMethods.WA_CLICKACTIVE)
+ if (NativeMethods.LOWORD(m.WParam) == NativeMethods.WA_ACTIVE)
{
- var controlThatWasClicked = FromChildHandle(NativeMethods.WindowFromPoint(MousePosition))
- ?? GetChildAtPoint(MousePosition);
- if (controlThatWasClicked != null)
- {
- if (controlThatWasClicked is TreeView ||
- controlThatWasClicked is ComboBox ||
- controlThatWasClicked is TextBox ||
- controlThatWasClicked is FrmMain)
- {
- controlThatWasClicked.Focus();
- }
- else if (controlThatWasClicked.CanSelect ||
- controlThatWasClicked is MenuStrip ||
- controlThatWasClicked is ToolStrip)
- {
- // Simulate a mouse event since one wasn't generated by Windows
- SimulateClick(controlThatWasClicked);
- controlThatWasClicked.Focus();
- }
- else if (controlThatWasClicked is AutoHideStripBase)
- {
- // only focus the autohide toolstrip
- controlThatWasClicked.Focus();
- }
- else
- {
- // This handles activations from clicks that did not start a size/move operation
- ActivateConnection();
- }
- }
+ Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "KB ACTIVATE");
}
+ // Only handle this msg if it was triggered by a click
+ if (NativeMethods.LOWORD(m.WParam) != NativeMethods.WA_CLICKACTIVE)
+ return;
+
+ var controlThatWasClicked = FromChildHandle(NativeMethods.WindowFromPoint(MousePosition))
+ ?? GetChildAtPoint(MousePosition);
+
+ if (controlThatWasClicked == null)
+ break;
+
+ Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"Click activate: {controlThatWasClicked}");
+
+ if (controlThatWasClicked is TreeView ||
+ controlThatWasClicked is ComboBox ||
+ controlThatWasClicked is TextBox ||
+ controlThatWasClicked is FrmMain ||
+ controlThatWasClicked is AutoHideStripBase)
+ {
+ controlThatWasClicked.Focus();
+ }
+ else if (controlThatWasClicked.CanSelect ||
+ controlThatWasClicked is MenuStrip ||
+ controlThatWasClicked is ToolStrip)
+ {
+ // Simulate a mouse event since one wasn't generated by Windows
+ SimulateClick(controlThatWasClicked);
+ controlThatWasClicked.Focus();
+ }
+ //else
+ //{
+ // // This handles activations from clicks that did not start a size/move operation
+ // ActivateConnection();
+ //}
+
break;
+ case NativeMethods.WM_NCACTIVATE:
+ //if (m.WParam.ToInt32() == 1)
+ // break;
+
+ //// Never allow the mRemoteNG window to display itself as inactive. By doing this,
+ //// we ensure focus events can propagate to child connection windows
+ NativeMethods.DefWindowProc(Handle, Convert.ToUInt32(m.Msg), (IntPtr)1, m.LParam);
+ m.Result = (IntPtr)1;
+ Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "Fixed main app NCACTIVATE");
+ return;
case NativeMethods.WM_WINDOWPOSCHANGED:
// Ignore this message if the window wasn't activated
- var windowPos =
- (NativeMethods.WINDOWPOS)Marshal.PtrToStructure(m.LParam, typeof(NativeMethods.WINDOWPOS));
+ if (!_inMouseActivate)
+ break;
+
+ var windowPos = (NativeMethods.WINDOWPOS)Marshal.PtrToStructure(m.LParam, typeof(NativeMethods.WINDOWPOS));
if ((windowPos.flags & NativeMethods.SWP_NOACTIVATE) == 0)
{
if (!_inMouseActivate && !_inSizeMove)
+ {
+ Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "WM_WINDOWPOSCHANGED DONE");
ActivateConnection();
+ }
}
break;
@@ -558,6 +640,18 @@ namespace mRemoteNG.UI.Forms
base.WndProc(ref m);
}
+ protected override bool ProcessKeyMessage(ref Message m)
+ {
+ if (m.WParam.ToInt32() != 0x09)
+ return false;
+
+ if ((m.LParam.ToInt32() & 0b00100000000000000000000000000000) == 0) // 29th bit ON means ALT key down
+ return false;
+
+ Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "ALT-TAB PRESSED");
+ return base.ProcessKeyMessage(ref m);
+ }
+
private void SimulateClick(Control control)
{
var clientMousePosition = control.PointToClient(MousePosition);
@@ -571,12 +665,24 @@ namespace mRemoteNG.UI.Forms
private void ActivateConnection()
{
- var cw = pnlDock.ActiveDocument as ConnectionWindow;
- var dp = cw?.ActiveControl as DockPane;
+ Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "Performing special connection focus logic");
+ //var cw = pnlDock.ActiveDocument as ConnectionWindow;
+ //var dp = cw?.ActiveControl as DockPane;
- if (!(dp?.ActiveContent is ConnectionTab tab)) return;
- var ifc = InterfaceControl.FindInterfaceControl(tab);
- if (ifc == null) return;
+ //if (!(dp?.ActiveContent is ConnectionTab tab))
+ //{
+ // Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "Active content is not a tab. We won't focus a specific connection.");
+ // return;
+ //}
+
+ //var ifc = InterfaceControl.FindInterfaceControl(tab);
+ var tab = TabHelper.Instance.CurrentTab;
+ var ifc = tab.InterfaceControl;
+ if (ifc == null)
+ {
+ Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"InterfaceControl for tab '{tab.Name}' was not found. We won't focus that connection.");
+ return;
+ }
ifc.Protocol.Focus();
var conFormWindow = ifc.FindForm();
@@ -585,7 +691,7 @@ namespace mRemoteNG.UI.Forms
private void pnlDock_ActiveDocumentChanged(object sender, EventArgs e)
{
- ActivateConnection();
+ //ActivateConnection();
}
internal void UpdateWindowTitle()
diff --git a/mRemoteV1/UI/Tabs/ConnectionTab.cs b/mRemoteV1/UI/Tabs/ConnectionTab.cs
index 5788efb0c..731391132 100644
--- a/mRemoteV1/UI/Tabs/ConnectionTab.cs
+++ b/mRemoteV1/UI/Tabs/ConnectionTab.cs
@@ -7,6 +7,7 @@ using mRemoteNG.Config;
using mRemoteNG.Connection;
using mRemoteNG.Connection.Protocol;
using mRemoteNG.Connection.Protocol.VNC;
+using mRemoteNG.Messages;
using mRemoteNG.UI.TaskDialog;
using WeifenLuo.WinFormsUI.Docking;
@@ -14,7 +15,7 @@ namespace mRemoteNG.UI.Tabs
{
public partial class ConnectionTab : DockContent
{
- private InterfaceControl InterfaceControl => Tag as InterfaceControl;
+ public InterfaceControl InterfaceControl => Tag as InterfaceControl;
///
///Silent close ignores the popup asking for confirmation
@@ -30,11 +31,33 @@ namespace mRemoteNG.UI.Tabs
{
InitializeComponent();
GotFocus += ConnectionTab_GotFocus;
+ Activated += OnActivated;
+ Click += OnClick;
+ }
+
+ private void OnClick(object sender, EventArgs e)
+ {
+ Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"Tab clicked: '{TabText}'");
+ }
+
+ private void OnActivated(object sender, EventArgs e)
+ {
+ Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"Tab activated: '{TabText}'");
}
private void ConnectionTab_GotFocus(object sender, EventArgs e)
{
- TabHelper.Instance.CurrentTab = this;
+ Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"Tab received focused: '{TabText}'");
+ //TabHelper.Instance.CurrentTab = this;
+ //if (TabHelper.Instance.FocusConnection)
+ //{
+ // Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"Focusing connection in tab: '{TabText}'");
+ // InterfaceControl?.Protocol.Focus();
+ //}
+ //else
+ //{
+ // Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "Dont focus connection");
+ //}
}
protected override void OnFormClosing(FormClosingEventArgs e)
diff --git a/mRemoteV1/UI/Tabs/DockPaneStripNG.cs b/mRemoteV1/UI/Tabs/DockPaneStripNG.cs
index 4e234dfe9..b45888359 100644
--- a/mRemoteV1/UI/Tabs/DockPaneStripNG.cs
+++ b/mRemoteV1/UI/Tabs/DockPaneStripNG.cs
@@ -1088,11 +1088,19 @@ namespace mRemoteNG.UI.Tabs
protected override void OnMouseDown(MouseEventArgs e)
{
+ App.Runtime.MessageCollector.AddMessage(Messages.MessageClass.DebugMsg, "Mouse down");
base.OnMouseDown(e);
// suspend drag if mouse is down on active close button.
m_suspendDrag = ActiveCloseHitTest(e.Location);
if (!IsMouseDown)
IsMouseDown = true;
+
+ var tabIndex = HitTest(e.Location);
+ var tab = Tabs[tabIndex].Content as ConnectionTab;
+ if (tab == null)
+ return;
+ TabHelper.Instance.CurrentTab = tab;
+ TabHelper.Instance.RaiseTabClickedEvent();
}
protected override void OnMouseMove(MouseEventArgs e)
@@ -1149,6 +1157,7 @@ namespace mRemoteNG.UI.Tabs
protected override void OnMouseClick(MouseEventArgs e)
{
+ App.Runtime.MessageCollector.AddMessage(Messages.MessageClass.DebugMsg, "Mouse click");
base.OnMouseClick(e);
if (e.Button != MouseButtons.Left || Appearance != DockPane.AppearanceStyle.Document)
return;
diff --git a/mRemoteV1/UI/Tabs/TabHelper.cs b/mRemoteV1/UI/Tabs/TabHelper.cs
index 62e102d86..0c1bee828 100644
--- a/mRemoteV1/UI/Tabs/TabHelper.cs
+++ b/mRemoteV1/UI/Tabs/TabHelper.cs
@@ -4,12 +4,18 @@ using System;
namespace mRemoteNG.UI.Tabs
{
- class TabHelper
+ public class TabHelper
{
private static readonly Lazy lazyHelper = new Lazy(() => new TabHelper());
public static TabHelper Instance => lazyHelper.Value;
+ ///
+ /// Should focus events on a connection tab automatically focus
+ /// its child connection?
+ ///
+ public bool FocusConnection { get; set; } = true;
+
private TabHelper()
{
}
@@ -21,10 +27,16 @@ namespace mRemoteNG.UI.Tabs
get => currentTab;
set
{
+ if (currentTab == value)
+ {
+ Runtime.MessageCollector.AddMessage(Messages.MessageClass.DebugMsg, $"Tab already current: '{currentTab.TabText}'");
+ return;
+ }
+
currentTab = value;
findCurrentPanel();
- Runtime.MessageCollector.AddMessage(Messages.MessageClass.DebugMsg,
- "Tab got focused: " + currentTab.TabText);
+ Runtime.MessageCollector.AddMessage(Messages.MessageClass.DebugMsg, $"Current tab changed: '{currentTab.TabText}'");
+ RaiseActiveConnectionTabChangedEvent();
}
}
@@ -36,7 +48,7 @@ namespace mRemoteNG.UI.Tabs
currentForm = currentForm.Parent;
}
- if (currentForm != null)
+ if (currentForm != null && CurrentPanel != currentForm)
CurrentPanel = (ConnectionWindow)currentForm;
}
@@ -47,10 +59,31 @@ namespace mRemoteNG.UI.Tabs
get => currentPanel;
set
{
+ if (currentPanel == value)
+ return;
+
currentPanel = value;
- Runtime.MessageCollector.AddMessage(Messages.MessageClass.DebugMsg,
- "Panel got focused: " + currentPanel.TabText);
+ Runtime.MessageCollector.AddMessage(Messages.MessageClass.DebugMsg, $"Current panel changed: '{currentPanel.TabText}'");
+ RaiseActivePanelChangedEvent();
}
}
+
+ public event EventHandler ActivePanelChanged;
+ protected virtual void RaiseActivePanelChangedEvent()
+ {
+ ActivePanelChanged?.Invoke(this, EventArgs.Empty);
+ }
+
+ public event EventHandler ActiveConnectionTabChanged;
+ protected virtual void RaiseActiveConnectionTabChangedEvent()
+ {
+ ActiveConnectionTabChanged?.Invoke(this, EventArgs.Empty);
+ }
+
+ public event EventHandler TabClicked;
+ public void RaiseTabClickedEvent()
+ {
+ TabClicked?.Invoke(this, EventArgs.Empty);
+ }
}
}
\ No newline at end of file
diff --git a/mRemoteV1/UI/Window/ConnectionWindow.cs b/mRemoteV1/UI/Window/ConnectionWindow.cs
index 2e759ed75..d7a673378 100644
--- a/mRemoteV1/UI/Window/ConnectionWindow.cs
+++ b/mRemoteV1/UI/Window/ConnectionWindow.cs
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
+using System.Diagnostics;
using System.Linq;
using System.Windows.Forms;
using mRemoteNG.App;
@@ -97,6 +98,7 @@ namespace mRemoteNG.UI.Window
private void ConnectionWindow_GotFocus(object sender, EventArgs e)
{
TabHelper.Instance.CurrentPanel = this;
+ Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"Connection window focused: '{TabText}'");
}
public ConnectionTab AddConnectionTab(ConnectionInfo connectionInfo)
diff --git a/mRemoteV1/mRemoteV1.csproj b/mRemoteV1/mRemoteV1.csproj
index 7dc17adb0..36d5dd3d1 100644
--- a/mRemoteV1/mRemoteV1.csproj
+++ b/mRemoteV1/mRemoteV1.csproj
@@ -96,6 +96,9 @@
+
+ ..\packages\Utf8Json.1.3.7\lib\net45\Utf8Json.dll
+
False
References\VncSharp.dll
@@ -255,6 +258,7 @@
+