From f7dc0918ebc3bd3b26fa624883b0746298028926 Mon Sep 17 00:00:00 2001 From: David Sparer Date: Sun, 25 Aug 2019 15:59:51 -0500 Subject: [PATCH] fixed most focus issues when working with putty still need to resolve refocusing putty in some cases --- mRemoteV1/App/NativeMethods.cs | 1018 +++++++++++++++++++++++++++- mRemoteV1/App/Runtime.cs | 2 +- mRemoteV1/UI/Forms/frmMain.cs | 112 ++- mRemoteV1/UI/SystemKeyboardHook.cs | 60 ++ mRemoteV1/mRemoteV1.csproj | 1 + 5 files changed, 1154 insertions(+), 39 deletions(-) create mode 100644 mRemoteV1/UI/SystemKeyboardHook.cs diff --git a/mRemoteV1/App/NativeMethods.cs b/mRemoteV1/App/NativeMethods.cs index 293379b0e..73e5b6a36 100644 --- a/mRemoteV1/App/NativeMethods.cs +++ b/mRemoteV1/App/NativeMethods.cs @@ -398,7 +398,14 @@ namespace mRemoteNG.App public const int WM_CLOSE = 0x10; /// - /// Sent when a window belonging to a different application than the active window is about to be activated. The message is sent to the application whose window is being activated and to the application whose window is being deactivated. + /// Sent when a window belonging to a different application than the active window is about to be activated. + /// The message is sent to the application whose window is being activated and to the application whose window + /// is being deactivated. + /// The wParam indicates whether the window is being activated or deactivated (TRUE if the + /// window is being activated; FALSE if the window is being deactivated). + /// The lParam is the thread identifier. If the wParam parameter is TRUE, lParam is the identifier of the + /// thread that owns the window being deactivated. If wParam is FALSE, lParam is the identifier of the + /// thread that owns the window being activated. /// public const int WM_ACTIVATEAPP = 0x1C; @@ -557,6 +564,998 @@ namespace mRemoteNG.App #endregion + #region Keyboard + public const int WH_KEYBOARD_LL = 13; + + [StructLayout(LayoutKind.Sequential)] + public class KBDLLHOOKSTRUCT + { + public uint vkCode; + public uint scanCode; + public KBDLLHOOKSTRUCTFlags flags; + public uint time; + public UIntPtr dwExtraInfo; + } + + [Flags] + public enum KBDLLHOOKSTRUCTFlags : uint + { + LLKHF_EXTENDED = 0x01, + LLKHF_INJECTED = 0x10, + LLKHF_ALTDOWN = 0x20, + LLKHF_UP = 0x80, + } + + [StructLayout(LayoutKind.Sequential)] + public struct INPUT + { + internal uint type; + internal InputUnion U; + internal static int Size + { + get { return Marshal.SizeOf(typeof(INPUT)); } + } + } + + [StructLayout(LayoutKind.Explicit)] + internal struct InputUnion + { + [FieldOffset(0)] + internal MOUSEINPUT mi; + [FieldOffset(0)] + internal KEYBDINPUT ki; + [FieldOffset(0)] + internal HARDWAREINPUT hi; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct MOUSEINPUT + { + internal int dx; + internal int dy; + internal int mouseData; + internal MOUSEEVENTF dwFlags; + internal uint time; + internal UIntPtr dwExtraInfo; + } + + [Flags] + internal enum MOUSEEVENTF : uint + { + ABSOLUTE = 0x8000, + HWHEEL = 0x01000, + MOVE = 0x0001, + MOVE_NOCOALESCE = 0x2000, + LEFTDOWN = 0x0002, + LEFTUP = 0x0004, + RIGHTDOWN = 0x0008, + RIGHTUP = 0x0010, + MIDDLEDOWN = 0x0020, + MIDDLEUP = 0x0040, + VIRTUALDESK = 0x4000, + WHEEL = 0x0800, + XDOWN = 0x0080, + XUP = 0x0100 + } + + [StructLayout(LayoutKind.Sequential)] + internal struct KEYBDINPUT + { + internal VirtualKeyShort wVk; + internal ScanCodeShort wScan; + internal KEYEVENTF dwFlags; + internal int time; + internal UIntPtr dwExtraInfo; + } + + [Flags] + internal enum KEYEVENTF : uint + { + EXTENDEDKEY = 0x0001, + KEYUP = 0x0002, + SCANCODE = 0x0008, + UNICODE = 0x0004 + } + + internal enum VirtualKeyShort : short + { + /// + ///Left mouse button + /// + LBUTTON = 0x01, + /// + ///Right mouse button + /// + RBUTTON = 0x02, + /// + ///Control-break processing + /// + CANCEL = 0x03, + /// + ///Middle mouse button (three-button mouse) + /// + MBUTTON = 0x04, + /// + ///Windows 2000/XP: X1 mouse button + /// + XBUTTON1 = 0x05, + /// + ///Windows 2000/XP: X2 mouse button + /// + XBUTTON2 = 0x06, + /// + ///BACKSPACE key + /// + BACK = 0x08, + /// + ///TAB key + /// + TAB = 0x09, + /// + ///CLEAR key + /// + CLEAR = 0x0C, + /// + ///ENTER key + /// + RETURN = 0x0D, + /// + ///SHIFT key + /// + SHIFT = 0x10, + /// + ///CTRL key + /// + CONTROL = 0x11, + /// + ///ALT key + /// + MENU = 0x12, + /// + ///PAUSE key + /// + PAUSE = 0x13, + /// + ///CAPS LOCK key + /// + CAPITAL = 0x14, + /// + ///Input Method Editor (IME) Kana mode + /// + KANA = 0x15, + /// + ///IME Hangul mode + /// + HANGUL = 0x15, + /// + ///IME Junja mode + /// + JUNJA = 0x17, + /// + ///IME final mode + /// + FINAL = 0x18, + /// + ///IME Hanja mode + /// + HANJA = 0x19, + /// + ///IME Kanji mode + /// + KANJI = 0x19, + /// + ///ESC key + /// + ESCAPE = 0x1B, + /// + ///IME convert + /// + CONVERT = 0x1C, + /// + ///IME nonconvert + /// + NONCONVERT = 0x1D, + /// + ///IME accept + /// + ACCEPT = 0x1E, + /// + ///IME mode change request + /// + MODECHANGE = 0x1F, + /// + ///SPACEBAR + /// + SPACE = 0x20, + /// + ///PAGE UP key + /// + PRIOR = 0x21, + /// + ///PAGE DOWN key + /// + NEXT = 0x22, + /// + ///END key + /// + END = 0x23, + /// + ///HOME key + /// + HOME = 0x24, + /// + ///LEFT ARROW key + /// + LEFT = 0x25, + /// + ///UP ARROW key + /// + UP = 0x26, + /// + ///RIGHT ARROW key + /// + RIGHT = 0x27, + /// + ///DOWN ARROW key + /// + DOWN = 0x28, + /// + ///SELECT key + /// + SELECT = 0x29, + /// + ///PRINT key + /// + PRINT = 0x2A, + /// + ///EXECUTE key + /// + EXECUTE = 0x2B, + /// + ///PRINT SCREEN key + /// + SNAPSHOT = 0x2C, + /// + ///INS key + /// + INSERT = 0x2D, + /// + ///DEL key + /// + DELETE = 0x2E, + /// + ///HELP key + /// + HELP = 0x2F, + /// + ///0 key + /// + KEY_0 = 0x30, + /// + ///1 key + /// + KEY_1 = 0x31, + /// + ///2 key + /// + KEY_2 = 0x32, + /// + ///3 key + /// + KEY_3 = 0x33, + /// + ///4 key + /// + KEY_4 = 0x34, + /// + ///5 key + /// + KEY_5 = 0x35, + /// + ///6 key + /// + KEY_6 = 0x36, + /// + ///7 key + /// + KEY_7 = 0x37, + /// + ///8 key + /// + KEY_8 = 0x38, + /// + ///9 key + /// + KEY_9 = 0x39, + /// + ///A key + /// + KEY_A = 0x41, + /// + ///B key + /// + KEY_B = 0x42, + /// + ///C key + /// + KEY_C = 0x43, + /// + ///D key + /// + KEY_D = 0x44, + /// + ///E key + /// + KEY_E = 0x45, + /// + ///F key + /// + KEY_F = 0x46, + /// + ///G key + /// + KEY_G = 0x47, + /// + ///H key + /// + KEY_H = 0x48, + /// + ///I key + /// + KEY_I = 0x49, + /// + ///J key + /// + KEY_J = 0x4A, + /// + ///K key + /// + KEY_K = 0x4B, + /// + ///L key + /// + KEY_L = 0x4C, + /// + ///M key + /// + KEY_M = 0x4D, + /// + ///N key + /// + KEY_N = 0x4E, + /// + ///O key + /// + KEY_O = 0x4F, + /// + ///P key + /// + KEY_P = 0x50, + /// + ///Q key + /// + KEY_Q = 0x51, + /// + ///R key + /// + KEY_R = 0x52, + /// + ///S key + /// + KEY_S = 0x53, + /// + ///T key + /// + KEY_T = 0x54, + /// + ///U key + /// + KEY_U = 0x55, + /// + ///V key + /// + KEY_V = 0x56, + /// + ///W key + /// + KEY_W = 0x57, + /// + ///X key + /// + KEY_X = 0x58, + /// + ///Y key + /// + KEY_Y = 0x59, + /// + ///Z key + /// + KEY_Z = 0x5A, + /// + ///Left Windows key (Microsoft Natural keyboard) + /// + LWIN = 0x5B, + /// + ///Right Windows key (Natural keyboard) + /// + RWIN = 0x5C, + /// + ///Applications key (Natural keyboard) + /// + APPS = 0x5D, + /// + ///Computer Sleep key + /// + SLEEP = 0x5F, + /// + ///Numeric keypad 0 key + /// + NUMPAD0 = 0x60, + /// + ///Numeric keypad 1 key + /// + NUMPAD1 = 0x61, + /// + ///Numeric keypad 2 key + /// + NUMPAD2 = 0x62, + /// + ///Numeric keypad 3 key + /// + NUMPAD3 = 0x63, + /// + ///Numeric keypad 4 key + /// + NUMPAD4 = 0x64, + /// + ///Numeric keypad 5 key + /// + NUMPAD5 = 0x65, + /// + ///Numeric keypad 6 key + /// + NUMPAD6 = 0x66, + /// + ///Numeric keypad 7 key + /// + NUMPAD7 = 0x67, + /// + ///Numeric keypad 8 key + /// + NUMPAD8 = 0x68, + /// + ///Numeric keypad 9 key + /// + NUMPAD9 = 0x69, + /// + ///Multiply key + /// + MULTIPLY = 0x6A, + /// + ///Add key + /// + ADD = 0x6B, + /// + ///Separator key + /// + SEPARATOR = 0x6C, + /// + ///Subtract key + /// + SUBTRACT = 0x6D, + /// + ///Decimal key + /// + DECIMAL = 0x6E, + /// + ///Divide key + /// + DIVIDE = 0x6F, + /// + ///F1 key + /// + F1 = 0x70, + /// + ///F2 key + /// + F2 = 0x71, + /// + ///F3 key + /// + F3 = 0x72, + /// + ///F4 key + /// + F4 = 0x73, + /// + ///F5 key + /// + F5 = 0x74, + /// + ///F6 key + /// + F6 = 0x75, + /// + ///F7 key + /// + F7 = 0x76, + /// + ///F8 key + /// + F8 = 0x77, + /// + ///F9 key + /// + F9 = 0x78, + /// + ///F10 key + /// + F10 = 0x79, + /// + ///F11 key + /// + F11 = 0x7A, + /// + ///F12 key + /// + F12 = 0x7B, + /// + ///F13 key + /// + F13 = 0x7C, + /// + ///F14 key + /// + F14 = 0x7D, + /// + ///F15 key + /// + F15 = 0x7E, + /// + ///F16 key + /// + F16 = 0x7F, + /// + ///F17 key + /// + F17 = 0x80, + /// + ///F18 key + /// + F18 = 0x81, + /// + ///F19 key + /// + F19 = 0x82, + /// + ///F20 key + /// + F20 = 0x83, + /// + ///F21 key + /// + F21 = 0x84, + /// + ///F22 key, (PPC only) Key used to lock device. + /// + F22 = 0x85, + /// + ///F23 key + /// + F23 = 0x86, + /// + ///F24 key + /// + F24 = 0x87, + /// + ///NUM LOCK key + /// + NUMLOCK = 0x90, + /// + ///SCROLL LOCK key + /// + SCROLL = 0x91, + /// + ///Left SHIFT key + /// + LSHIFT = 0xA0, + /// + ///Right SHIFT key + /// + RSHIFT = 0xA1, + /// + ///Left CONTROL key + /// + LCONTROL = 0xA2, + /// + ///Right CONTROL key + /// + RCONTROL = 0xA3, + /// + ///Left MENU key + /// + LMENU = 0xA4, + /// + ///Right MENU key + /// + RMENU = 0xA5, + /// + ///Windows 2000/XP: Browser Back key + /// + BROWSER_BACK = 0xA6, + /// + ///Windows 2000/XP: Browser Forward key + /// + BROWSER_FORWARD = 0xA7, + /// + ///Windows 2000/XP: Browser Refresh key + /// + BROWSER_REFRESH = 0xA8, + /// + ///Windows 2000/XP: Browser Stop key + /// + BROWSER_STOP = 0xA9, + /// + ///Windows 2000/XP: Browser Search key + /// + BROWSER_SEARCH = 0xAA, + /// + ///Windows 2000/XP: Browser Favorites key + /// + BROWSER_FAVORITES = 0xAB, + /// + ///Windows 2000/XP: Browser Start and Home key + /// + BROWSER_HOME = 0xAC, + /// + ///Windows 2000/XP: Volume Mute key + /// + VOLUME_MUTE = 0xAD, + /// + ///Windows 2000/XP: Volume Down key + /// + VOLUME_DOWN = 0xAE, + /// + ///Windows 2000/XP: Volume Up key + /// + VOLUME_UP = 0xAF, + /// + ///Windows 2000/XP: Next Track key + /// + MEDIA_NEXT_TRACK = 0xB0, + /// + ///Windows 2000/XP: Previous Track key + /// + MEDIA_PREV_TRACK = 0xB1, + /// + ///Windows 2000/XP: Stop Media key + /// + MEDIA_STOP = 0xB2, + /// + ///Windows 2000/XP: Play/Pause Media key + /// + MEDIA_PLAY_PAUSE = 0xB3, + /// + ///Windows 2000/XP: Start Mail key + /// + LAUNCH_MAIL = 0xB4, + /// + ///Windows 2000/XP: Select Media key + /// + LAUNCH_MEDIA_SELECT = 0xB5, + /// + ///Windows 2000/XP: Start Application 1 key + /// + LAUNCH_APP1 = 0xB6, + /// + ///Windows 2000/XP: Start Application 2 key + /// + LAUNCH_APP2 = 0xB7, + /// + ///Used for miscellaneous characters; it can vary by keyboard. + /// + OEM_1 = 0xBA, + /// + ///Windows 2000/XP: For any country/region, the '+' key + /// + OEM_PLUS = 0xBB, + /// + ///Windows 2000/XP: For any country/region, the ',' key + /// + OEM_COMMA = 0xBC, + /// + ///Windows 2000/XP: For any country/region, the '-' key + /// + OEM_MINUS = 0xBD, + /// + ///Windows 2000/XP: For any country/region, the '.' key + /// + OEM_PERIOD = 0xBE, + /// + ///Used for miscellaneous characters; it can vary by keyboard. + /// + OEM_2 = 0xBF, + /// + ///Used for miscellaneous characters; it can vary by keyboard. + /// + OEM_3 = 0xC0, + /// + ///Used for miscellaneous characters; it can vary by keyboard. + /// + OEM_4 = 0xDB, + /// + ///Used for miscellaneous characters; it can vary by keyboard. + /// + OEM_5 = 0xDC, + /// + ///Used for miscellaneous characters; it can vary by keyboard. + /// + OEM_6 = 0xDD, + /// + ///Used for miscellaneous characters; it can vary by keyboard. + /// + OEM_7 = 0xDE, + /// + ///Used for miscellaneous characters; it can vary by keyboard. + /// + OEM_8 = 0xDF, + /// + ///Windows 2000/XP: Either the angle bracket key or the backslash key on the RT 102-key keyboard + /// + OEM_102 = 0xE2, + /// + ///Windows 95/98/Me, Windows NT 4.0, Windows 2000/XP: IME PROCESS key + /// + PROCESSKEY = 0xE5, + /// + ///Windows 2000/XP: Used to pass Unicode characters as if they were keystrokes. + ///The VK_PACKET key is the low word of a 32-bit Virtual Key value used for non-keyboard input methods. For more information, + ///see Remark in KEYBDINPUT, SendInput, WM_KEYDOWN, and WM_KEYUP + /// + PACKET = 0xE7, + /// + ///Attn key + /// + ATTN = 0xF6, + /// + ///CrSel key + /// + CRSEL = 0xF7, + /// + ///ExSel key + /// + EXSEL = 0xF8, + /// + ///Erase EOF key + /// + EREOF = 0xF9, + /// + ///Play key + /// + PLAY = 0xFA, + /// + ///Zoom key + /// + ZOOM = 0xFB, + /// + ///Reserved + /// + NONAME = 0xFC, + /// + ///PA1 key + /// + PA1 = 0xFD, + /// + ///Clear key + /// + OEM_CLEAR = 0xFE + } + internal enum ScanCodeShort : short + { + LBUTTON = 0, + RBUTTON = 0, + CANCEL = 70, + MBUTTON = 0, + XBUTTON1 = 0, + XBUTTON2 = 0, + BACK = 14, + TAB = 15, + CLEAR = 76, + RETURN = 28, + SHIFT = 42, + CONTROL = 29, + MENU = 56, + PAUSE = 0, + CAPITAL = 58, + KANA = 0, + HANGUL = 0, + JUNJA = 0, + FINAL = 0, + HANJA = 0, + KANJI = 0, + ESCAPE = 1, + CONVERT = 0, + NONCONVERT = 0, + ACCEPT = 0, + MODECHANGE = 0, + SPACE = 57, + PRIOR = 73, + NEXT = 81, + END = 79, + HOME = 71, + LEFT = 75, + UP = 72, + RIGHT = 77, + DOWN = 80, + SELECT = 0, + PRINT = 0, + EXECUTE = 0, + SNAPSHOT = 84, + INSERT = 82, + DELETE = 83, + HELP = 99, + KEY_0 = 11, + KEY_1 = 2, + KEY_2 = 3, + KEY_3 = 4, + KEY_4 = 5, + KEY_5 = 6, + KEY_6 = 7, + KEY_7 = 8, + KEY_8 = 9, + KEY_9 = 10, + KEY_A = 30, + KEY_B = 48, + KEY_C = 46, + KEY_D = 32, + KEY_E = 18, + KEY_F = 33, + KEY_G = 34, + KEY_H = 35, + KEY_I = 23, + KEY_J = 36, + KEY_K = 37, + KEY_L = 38, + KEY_M = 50, + KEY_N = 49, + KEY_O = 24, + KEY_P = 25, + KEY_Q = 16, + KEY_R = 19, + KEY_S = 31, + KEY_T = 20, + KEY_U = 22, + KEY_V = 47, + KEY_W = 17, + KEY_X = 45, + KEY_Y = 21, + KEY_Z = 44, + LWIN = 91, + RWIN = 92, + APPS = 93, + SLEEP = 95, + NUMPAD0 = 82, + NUMPAD1 = 79, + NUMPAD2 = 80, + NUMPAD3 = 81, + NUMPAD4 = 75, + NUMPAD5 = 76, + NUMPAD6 = 77, + NUMPAD7 = 71, + NUMPAD8 = 72, + NUMPAD9 = 73, + MULTIPLY = 55, + ADD = 78, + SEPARATOR = 0, + SUBTRACT = 74, + DECIMAL = 83, + DIVIDE = 53, + F1 = 59, + F2 = 60, + F3 = 61, + F4 = 62, + F5 = 63, + F6 = 64, + F7 = 65, + F8 = 66, + F9 = 67, + F10 = 68, + F11 = 87, + F12 = 88, + F13 = 100, + F14 = 101, + F15 = 102, + F16 = 103, + F17 = 104, + F18 = 105, + F19 = 106, + F20 = 107, + F21 = 108, + F22 = 109, + F23 = 110, + F24 = 118, + NUMLOCK = 69, + SCROLL = 70, + LSHIFT = 42, + RSHIFT = 54, + LCONTROL = 29, + RCONTROL = 29, + LMENU = 56, + RMENU = 56, + BROWSER_BACK = 106, + BROWSER_FORWARD = 105, + BROWSER_REFRESH = 103, + BROWSER_STOP = 104, + BROWSER_SEARCH = 101, + BROWSER_FAVORITES = 102, + BROWSER_HOME = 50, + VOLUME_MUTE = 32, + VOLUME_DOWN = 46, + VOLUME_UP = 48, + MEDIA_NEXT_TRACK = 25, + MEDIA_PREV_TRACK = 16, + MEDIA_STOP = 36, + MEDIA_PLAY_PAUSE = 34, + LAUNCH_MAIL = 108, + LAUNCH_MEDIA_SELECT = 109, + LAUNCH_APP1 = 107, + LAUNCH_APP2 = 33, + OEM_1 = 39, + OEM_PLUS = 13, + OEM_COMMA = 51, + OEM_MINUS = 12, + OEM_PERIOD = 52, + OEM_2 = 53, + OEM_3 = 41, + OEM_4 = 26, + OEM_5 = 43, + OEM_6 = 27, + OEM_7 = 40, + OEM_8 = 0, + OEM_102 = 86, + PROCESSKEY = 0, + PACKET = 0, + ATTN = 0, + CRSEL = 0, + EXSEL = 0, + EREOF = 93, + PLAY = 0, + ZOOM = 98, + NONAME = 0, + PA1 = 0, + OEM_CLEAR = 0, + } + + [StructLayout(LayoutKind.Sequential)] + internal struct HARDWAREINPUT + { + internal int uMsg; + internal short wParamL; + internal short wParamH; + } + + /// + /// Synthesizes keystrokes, mouse motions, and button clicks. + /// + /// + /// The number of structures in the pInputs array. + /// + /// + /// An array of INPUT structures. Each structure represents an event to be inserted into the keyboard or mouse input stream. + /// + /// + /// The size, in bytes, of an INPUT structure. If cbSize is not the size of an INPUT structure, the function fails. + /// + /// + [DllImport("user32.dll")] + internal static extern uint SendInput(uint nInputs, + [MarshalAs(UnmanagedType.LPArray), In] INPUT[] pInputs, + int cbSize); + + [DllImport("user32.dll")] + public static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo); + #endregion + #region Virtual Key Codes public const int VK_CONTROL = 0x11; @@ -681,6 +1680,23 @@ namespace mRemoteNG.App [DllImport("user32.dll")] public static extern bool UnhookWinEvent(IntPtr hWinEventHook); + public delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam); + + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern IntPtr SetWindowsHookEx(int idHook, + LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId); + + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool UnhookWindowsHookEx(IntPtr hhk); + + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, + IntPtr wParam, IntPtr lParam); + + [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] + public static extern IntPtr GetModuleHandle(string lpModuleName); + /// /// The foreground window has changed. The system sends /// this event even if the foreground window has changed diff --git a/mRemoteV1/App/Runtime.cs b/mRemoteV1/App/Runtime.cs index 2c86269c8..be41c80d8 100644 --- a/mRemoteV1/App/Runtime.cs +++ b/mRemoteV1/App/Runtime.cs @@ -1,4 +1,4 @@ -using mRemoteNG.App.Info; +using mRemoteNG.App.Info; using mRemoteNG.Config.Putty; using mRemoteNG.Connection; using mRemoteNG.Credential; diff --git a/mRemoteV1/UI/Forms/frmMain.cs b/mRemoteV1/UI/Forms/frmMain.cs index 0474b08d2..fdeadf3e0 100644 --- a/mRemoteV1/UI/Forms/frmMain.cs +++ b/mRemoteV1/UI/Forms/frmMain.cs @@ -53,6 +53,14 @@ namespace mRemoteNG.UI.Forms private readonly ThemeManager _themeManager; private readonly FileBackupPruner _backupPruner = new FileBackupPruner(); private readonly IConnectionInitiator _connectionInitiator = new ConnectionInitiator(); + private readonly SystemKeyboardHook _keyboardHook; + private bool _childProcessHeldLastFocus; + private bool _currentlyFixingAltTab; + + /// + /// TRUE if any part of mrng has focus - the main window or child processes + /// + private bool _mrngFocused; internal FullscreenHandler Fullscreen { get; set; } @@ -71,6 +79,7 @@ namespace mRemoteNG.UI.Forms ApplyTheme(); _screenSystemMenu = new ScreenSelectionSystemMenu(this); + _keyboardHook = new SystemKeyboardHook(KeyboardHookCallback); } #region Properties @@ -380,6 +389,8 @@ namespace mRemoteNG.UI.Forms private void frmMain_FormClosing(object sender, FormClosingEventArgs e) { + _keyboardHook?.Dispose(); + if (!(Runtime.WindowList == null || Runtime.WindowList.Count == 0)) { var openConnections = 0; @@ -484,9 +495,8 @@ namespace mRemoteNG.UI.Forms // 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) + protected override void WndProc(ref Message m) { - const int tabKey = 0x09; // Listen for and handle operating system messages try { @@ -504,42 +514,30 @@ namespace mRemoteNG.UI.Forms 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: if (m.WParam.ToInt32() == 0) // mRemoteNG is being deactivated { - //var threadWhichIsActivating = m.LParam.ToInt32(); - //var activatingChildProcessWindow = _connectionInitiator.ActiveConnections - // .OfType() - // .Any(prot => prot.ThreadId == threadWhichIsActivating); - + var threadWhichIsActivating = m.LParam.ToInt32(); + _childProcessHeldLastFocus = _connectionInitiator + .ActiveConnections + .OfType() + .Any(proc => proc.ThreadId == threadWhichIsActivating); + _inMouseActivate = false; - Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "mRemoteNG main window lost focus"); + _mrngFocused = _childProcessHeldLastFocus; + Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"mRemoteNG main window lost focus (_childProcessHeldLastFocus={_childProcessHeldLastFocus})"); break; } - Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "mRemoteNG main window received focus"); + if (_childProcessHeldLastFocus && !_mrngFocused) + { + ActivateConnection(); + } + + _childProcessHeldLastFocus = false; + _mrngFocused = true; + Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"mRemoteNG main window received focus (_childProcessHeldLastFocus={_childProcessHeldLastFocus})"); //var candidateTabToFocus = FromChildHandle(NativeMethods.WindowFromPoint(MousePosition)) // ?? GetChildAtPoint(MousePosition); @@ -640,16 +638,53 @@ namespace mRemoteNG.UI.Forms base.WndProc(ref m); } - protected override bool ProcessKeyMessage(ref Message m) + public int KeyboardHookCallback(int msg, NativeMethods.KBDLLHOOKSTRUCT kbd) { - if (m.WParam.ToInt32() != 0x09) - return false; + var key = (Keys) kbd.vkCode; + if (key.HasFlag(Keys.Tab) && kbd.flags.HasFlag(NativeMethods.KBDLLHOOKSTRUCTFlags.LLKHF_ALTDOWN)) + { + if (msg == NativeMethods.WM_SYSKEYDOWN || msg == NativeMethods.WM_KEYDOWN) + { + Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, $"ALT-TAB PRESSED (CPF={_childProcessHeldLastFocus}, MRNGF={_mrngFocused}, IMA={_inMouseActivate}, CFAT={_currentlyFixingAltTab})"); + if (_childProcessHeldLastFocus && _mrngFocused && !_inMouseActivate && !_currentlyFixingAltTab) + { + Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "FIXING ALT-TAB FOR EXTAPP"); + _currentlyFixingAltTab = true; - if ((m.LParam.ToInt32() & 0b00100000000000000000000000000000) == 0) // 29th bit ON means ALT key down - return false; + // simulate an extra TAB key press. This skips focus of the mrng main window. + NativeMethods.keybd_event((byte)Keys.Tab, 0, (uint)NativeMethods.KEYEVENTF.KEYUP, 0); + NativeMethods.keybd_event((byte)Keys.Tab, 0, 0, 0); - Runtime.MessageCollector.AddMessage(MessageClass.DebugMsg, "ALT-TAB PRESSED"); - return base.ProcessKeyMessage(ref m); + // WndProc will never get an event when we switch from a child proc to a completely different program since the main mrng window never had focus to begin with. + // Assume mrng as a whole will lose focus, even though the user could choose to retain focus on us. When Alt-tab completes, the mrng main window will + // receive the focus event and we will handle the child process focusing as necessary. + _mrngFocused = false; + _currentlyFixingAltTab = false; + } + } + } + + // alt + right-shift + if (key.HasFlag(Keys.RShiftKey) && kbd.flags.HasFlag(NativeMethods.KBDLLHOOKSTRUCTFlags.LLKHF_ALTDOWN)) + { + if (msg != NativeMethods.WM_SYSKEYUP && msg != NativeMethods.WM_KEYUP) + return 0; + + if (!_mrngFocused) + return 0; + + if (_childProcessHeldLastFocus) + { + Focus(); // focus main window + return 1; + } + + // focus connection + ActivateConnection(); + return 1; + } + + return 0; } private void SimulateClick(Control control) @@ -677,6 +712,9 @@ namespace mRemoteNG.UI.Forms //var ifc = InterfaceControl.FindInterfaceControl(tab); var tab = TabHelper.Instance.CurrentTab; + if (tab == null) + return; + var ifc = tab.InterfaceControl; if (ifc == null) { diff --git a/mRemoteV1/UI/SystemKeyboardHook.cs b/mRemoteV1/UI/SystemKeyboardHook.cs new file mode 100644 index 000000000..2e8463989 --- /dev/null +++ b/mRemoteV1/UI/SystemKeyboardHook.cs @@ -0,0 +1,60 @@ +using mRemoteNG.App; +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; + +namespace mRemoteNG.UI +{ + public class SystemKeyboardHook : IDisposable + { + private readonly IntPtr _hookId; + private readonly Func _userCallback; + private readonly NativeMethods.LowLevelKeyboardProc _sysCallback; + private bool _disposed; + + public SystemKeyboardHook(Func proc) + { + _userCallback = proc; + _sysCallback = SystemCallback; + using (var curProcess = Process.GetCurrentProcess()) + using (var curModule = curProcess.MainModule) + { + _hookId = NativeMethods.SetWindowsHookEx( + NativeMethods.WH_KEYBOARD_LL, + _sysCallback, + NativeMethods.GetModuleHandle(curModule.ModuleName), + 0); + } + } + + private IntPtr SystemCallback(int nCode, IntPtr wParam, IntPtr lParam) + { + if (nCode >= 0) + { + var kdb = Marshal.PtrToStructure(lParam); + return new IntPtr(_userCallback(wParam.ToInt32(), kdb)); + } + + return NativeMethods.CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (_disposed) + return; + + if (disposing) + { + NativeMethods.UnhookWindowsHookEx(_hookId); + } + + _disposed = true; + } + } +} diff --git a/mRemoteV1/mRemoteV1.csproj b/mRemoteV1/mRemoteV1.csproj index 36d5dd3d1..af39dc198 100644 --- a/mRemoteV1/mRemoteV1.csproj +++ b/mRemoteV1/mRemoteV1.csproj @@ -736,6 +736,7 @@ Component + Form