debug putty focus issues

This commit is contained in:
David Sparer
2019-08-25 10:39:27 -05:00
parent 53c534aa93
commit 26d9e3c2ff
17 changed files with 890 additions and 361 deletions

View File

@@ -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
/// </summary>
public const int WM_WINDOWPOSCHANGED = 0x47;
/// <summary>
/// Sent to a window when its nonclient area needs to be changed to indicate an active or inactive state.
/// </summary>
public const int WM_NCACTIVATE = 0x86;
/// <summary>
/// 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.
/// </summary>
@@ -423,6 +447,25 @@ namespace mRemoteNG.App
/// </summary>
public const int WM_CHAR = 0x102;
/// <summary>
/// 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.
/// </summary>
public const int WM_SYSKEYDOWN = 0x104;
/// <summary>
/// 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.
/// </summary>
public const int WM_SYSKEYUP = 0x105;
/// <summary>
/// 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.
/// </summary>
@@ -542,6 +585,138 @@ namespace mRemoteNG.App
#endregion
#region WinEvent
/// <summary>
/// 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.
/// </summary>
/// <param name="hWinEventHook">
/// 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.
/// </param>
/// <param name="eventType">
/// Specifies the event that occurred. This value is one of the event constants.
/// </param>
/// <param name="hwnd">
/// 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.
/// </param>
/// <param name="idObject">
/// Identifies the object associated with the event. This is one of the object
/// identifiers or a custom object ID.
/// </param>
/// <param name="idChild">
/// 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.
/// </param>
/// <param name="dwEventThread"></param>
/// <param name="dwmsEventTime">
/// Specifies the time, in milliseconds, that the event was generated.
/// </param>
public delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd,
int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
/// <summary>
/// Sets an event hook function for a range of events.
/// </summary>
/// <param name="eventMin">
/// 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.
/// </param>
/// <param name="eventMax">
/// 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.
/// </param>
/// <param name="hmodWinEventProc">
/// 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.
/// </param>
/// <param name="lpfnWinEventProc">
/// Pointer to the event hook function. For more information about this
/// function, see WinEventProc.
/// </param>
/// <param name="idProcess">
/// 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.
/// </param>
/// <param name="idThread">
/// 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.
/// </param>
/// <param name="dwFlags">
/// 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
/// </param>
/// <returns>
/// 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.
/// </returns>
[DllImport("user32.dll")]
public static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc,
WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
/// <summary>
/// Removes an event hook function created by a previous call to
/// SetWinEventHook.
/// </summary>
/// <param name="hWinEventHook">
/// Handle to the event hook returned in the previous call to
/// SetWinEventHook.
/// </param>
/// <returns></returns>
[DllImport("user32.dll")]
public static extern bool UnhookWinEvent(IntPtr hWinEventHook);
/// <summary>
/// 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.
/// </summary>
public const uint EVENT_SYSTEM_FOREGROUND = 0x0003;
/// <summary>
/// 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.
/// </summary>
public const uint EVENT_SYSTEM_SWITCHSTART = 0x0014;
/// <summary>
/// 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.
/// </summary>
public const uint EVENT_SYSTEM_SWITCHEND = 0x0015;
public const uint WINEVENT_OUTOFCONTEXT = 0;
#endregion
#endregion
}
}

View File

@@ -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;

View File

@@ -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}");
}
/// <summary>
/// This callback will be called when the external process window managed by
/// this protocol is brought to the foreground.
/// </summary>
/// <param name="hWinEventHook"></param>
/// <param name="eventType"></param>
/// <param name="hwnd"></param>
/// <param name="idObject"></param>
/// <param name="idChild"></param>
/// <param name="dwEventThread"></param>
/// <param name="dwmsEventTime"></param>
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}");
}
}
}

View File

@@ -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

View File

@@ -63,6 +63,12 @@ namespace mRemoteNG.Connection.Protocol
public readonly System.Timers.Timer tmrReconnect = new System.Timers.Timer(2000);
protected ReconnectGroup ReconnectGroup;
/// <summary>
/// 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.
/// </summary>
public virtual bool IsExternalProcess { get; } = false;
protected ProtocolBase(string name)
{
Name = name;

View File

@@ -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
}
}
/// <summary>
/// Sends an individual key stroke to this PuTTY session.
/// </summary>
public void SendKeyStroke(int keyType, int keyData)
{
NativeMethods.PostMessage(ProcessHandle, keyType, new IntPtr(keyData), new IntPtr(0));
}
#endregion
#region Enums

View File

@@ -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<IntPtr>();
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<IntPtr>;
if (list == null)
throw new InvalidCastException("GCHandle Target could not be cast as List<IntPtr>");
var sb = new StringBuilder();
NativeMethods.GetClassName(hwnd, sb, 64);
if (sb.ToString().Equals("IHWindowClass"))
{
list.Add(hwnd);
return false;
}
return true;
}
/// <summary>
@@ -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

View File

@@ -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);
}
}

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.ComponentModel;
using System.Linq;
using WeifenLuo.WinFormsUI.Docking;

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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<ExternalProcessProtocolBase>()
// .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()

View File

@@ -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;
/// <summary>
///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)

View File

@@ -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;

View File

@@ -4,12 +4,18 @@ using System;
namespace mRemoteNG.UI.Tabs
{
class TabHelper
public class TabHelper
{
private static readonly Lazy<TabHelper> lazyHelper = new Lazy<TabHelper>(() => new TabHelper());
public static TabHelper Instance => lazyHelper.Value;
/// <summary>
/// Should focus events on a connection tab automatically focus
/// its child connection?
/// </summary>
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);
}
}
}

View File

@@ -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)

View File

@@ -96,6 +96,9 @@
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="Utf8Json">
<HintPath>..\packages\Utf8Json.1.3.7\lib\net45\Utf8Json.dll</HintPath>
</Reference>
<Reference Include="VncSharp, Version=1.0.0.0, Culture=neutral, PublicKeyToken=61974755c7bfea7c, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>References\VncSharp.dll</HintPath>
@@ -255,6 +258,7 @@
<Compile Include="Connection\IConnectionInitiator.cs" />
<Compile Include="Connection\IInheritable.cs" />
<Compile Include="Connection\IHasParent.cs" />
<Compile Include="Connection\Protocol\ExternalProcessProtocolBase.cs" />
<Compile Include="Connection\Protocol\Http\Connection.Protocol.HTTPS.CertEvent.cs" />
<Compile Include="Connection\Protocol\ProtocolFactory.cs" />
<Compile Include="Connection\Protocol\RDP\AuthenticationLevel.cs" />