From 08a74151850981d1ee7d277c4f770c41513fe01d Mon Sep 17 00:00:00 2001 From: d3agle Date: Wed, 29 Jul 2015 06:57:52 -0500 Subject: [PATCH 1/8] Added Mouse Scrolling to RDP Added Keyboard/Mouse hook to server to access mouse data and for future keyboard integration to RDP --- Client/Core/Commands/SurveillanceHandler.cs | 6 + Client/Core/Helper/NativeMethodsHelper.cs | 15 +- Client/Core/Utilities/NativeMethods.cs | 2 +- Client/Enums/MouseAction.cs | 2 + Server/Core/Helper/MouseKeyHook/Hook.cs | 38 ++ .../Helper/MouseKeyHook/HotKeys/HotKeyArgs.cs | 33 ++ .../Helper/MouseKeyHook/HotKeys/HotKeySet.cs | 287 ++++++++++++++ .../HotKeys/HotKeySetCollection.cs | 48 +++ .../HotKeys/HotKeySetsListener.cs | 4 + .../Helper/MouseKeyHook/HotKeys/ReadMe.txt | 85 ++++ .../Helper/MouseKeyHook/IKeyboardEvents.cs | 43 ++ .../MouseKeyHook/IKeyboardMouseEvents.cs | 15 + .../Core/Helper/MouseKeyHook/IMouseEvents.cs | 73 ++++ .../Implementation/AppEventFacade.cs | 19 + .../Implementation/AppKeyListener.cs | 27 ++ .../Implementation/AppMouseListener.cs | 21 + .../Implementation/BaseListener.cs | 26 ++ .../MouseKeyHook/Implementation/ButtonSet.cs | 33 ++ .../MouseKeyHook/Implementation/Callback.cs | 10 + .../Implementation/EventFacade.cs | 114 ++++++ .../Implementation/GlobalEventFacade.cs | 19 + .../Implementation/GlobalKeyListener.cs | 27 ++ .../Implementation/GlobalMouseListener.cs | 73 ++++ .../Implementation/KeyListener.cs | 71 ++++ .../Implementation/KeyboardState.cs | 111 ++++++ .../Implementation/MouseListener.cs | 179 +++++++++ .../MouseKeyHook/Implementation/Subscribe.cs | 10 + .../Helper/MouseKeyHook/KeyEventArgsExt.cs | 128 ++++++ .../MouseKeyHook/KeyPressEventArgsExt.cs | 119 ++++++ .../Helper/MouseKeyHook/MouseEventExtArgs.cs | 211 ++++++++++ .../MouseKeyHook/WinApi/AppMouseStruct.cs | 70 ++++ .../MouseKeyHook/WinApi/CallbackData.cs | 30 ++ .../Helper/MouseKeyHook/WinApi/HookHelper.cs | 101 +++++ .../Helper/MouseKeyHook/WinApi/HookIds.cs | 30 ++ .../MouseKeyHook/WinApi/HookNativeMethods.cs | 89 +++++ .../MouseKeyHook/WinApi/HookProcedure.cs | 40 ++ .../WinApi/HookProcedureHandle.cs | 31 ++ .../Helper/MouseKeyHook/WinApi/HookResult.cs | 35 ++ .../MouseKeyHook/WinApi/KeyboardHookStruct.cs | 43 ++ .../WinApi/KeyboardNativeMethods.cs | 366 ++++++++++++++++++ .../Helper/MouseKeyHook/WinApi/Messages.cs | 123 ++++++ .../MouseKeyHook/WinApi/MouseNativeMethods.cs | 27 ++ .../Helper/MouseKeyHook/WinApi/MouseStruct.cs | 54 +++ .../Core/Helper/MouseKeyHook/WinApi/Point.cs | 64 +++ .../WinApi/ThreadNativeMethods.cs | 46 +++ Server/Enums/MouseAction.cs | 2 + Server/Forms/FrmRemoteDesktop.cs | 26 ++ Server/Server.csproj | 41 ++ 48 files changed, 3062 insertions(+), 5 deletions(-) create mode 100644 Server/Core/Helper/MouseKeyHook/Hook.cs create mode 100644 Server/Core/Helper/MouseKeyHook/HotKeys/HotKeyArgs.cs create mode 100644 Server/Core/Helper/MouseKeyHook/HotKeys/HotKeySet.cs create mode 100644 Server/Core/Helper/MouseKeyHook/HotKeys/HotKeySetCollection.cs create mode 100644 Server/Core/Helper/MouseKeyHook/HotKeys/HotKeySetsListener.cs create mode 100644 Server/Core/Helper/MouseKeyHook/HotKeys/ReadMe.txt create mode 100644 Server/Core/Helper/MouseKeyHook/IKeyboardEvents.cs create mode 100644 Server/Core/Helper/MouseKeyHook/IKeyboardMouseEvents.cs create mode 100644 Server/Core/Helper/MouseKeyHook/IMouseEvents.cs create mode 100644 Server/Core/Helper/MouseKeyHook/Implementation/AppEventFacade.cs create mode 100644 Server/Core/Helper/MouseKeyHook/Implementation/AppKeyListener.cs create mode 100644 Server/Core/Helper/MouseKeyHook/Implementation/AppMouseListener.cs create mode 100644 Server/Core/Helper/MouseKeyHook/Implementation/BaseListener.cs create mode 100644 Server/Core/Helper/MouseKeyHook/Implementation/ButtonSet.cs create mode 100644 Server/Core/Helper/MouseKeyHook/Implementation/Callback.cs create mode 100644 Server/Core/Helper/MouseKeyHook/Implementation/EventFacade.cs create mode 100644 Server/Core/Helper/MouseKeyHook/Implementation/GlobalEventFacade.cs create mode 100644 Server/Core/Helper/MouseKeyHook/Implementation/GlobalKeyListener.cs create mode 100644 Server/Core/Helper/MouseKeyHook/Implementation/GlobalMouseListener.cs create mode 100644 Server/Core/Helper/MouseKeyHook/Implementation/KeyListener.cs create mode 100644 Server/Core/Helper/MouseKeyHook/Implementation/KeyboardState.cs create mode 100644 Server/Core/Helper/MouseKeyHook/Implementation/MouseListener.cs create mode 100644 Server/Core/Helper/MouseKeyHook/Implementation/Subscribe.cs create mode 100644 Server/Core/Helper/MouseKeyHook/KeyEventArgsExt.cs create mode 100644 Server/Core/Helper/MouseKeyHook/KeyPressEventArgsExt.cs create mode 100644 Server/Core/Helper/MouseKeyHook/MouseEventExtArgs.cs create mode 100644 Server/Core/Helper/MouseKeyHook/WinApi/AppMouseStruct.cs create mode 100644 Server/Core/Helper/MouseKeyHook/WinApi/CallbackData.cs create mode 100644 Server/Core/Helper/MouseKeyHook/WinApi/HookHelper.cs create mode 100644 Server/Core/Helper/MouseKeyHook/WinApi/HookIds.cs create mode 100644 Server/Core/Helper/MouseKeyHook/WinApi/HookNativeMethods.cs create mode 100644 Server/Core/Helper/MouseKeyHook/WinApi/HookProcedure.cs create mode 100644 Server/Core/Helper/MouseKeyHook/WinApi/HookProcedureHandle.cs create mode 100644 Server/Core/Helper/MouseKeyHook/WinApi/HookResult.cs create mode 100644 Server/Core/Helper/MouseKeyHook/WinApi/KeyboardHookStruct.cs create mode 100644 Server/Core/Helper/MouseKeyHook/WinApi/KeyboardNativeMethods.cs create mode 100644 Server/Core/Helper/MouseKeyHook/WinApi/Messages.cs create mode 100644 Server/Core/Helper/MouseKeyHook/WinApi/MouseNativeMethods.cs create mode 100644 Server/Core/Helper/MouseKeyHook/WinApi/MouseStruct.cs create mode 100644 Server/Core/Helper/MouseKeyHook/WinApi/Point.cs create mode 100644 Server/Core/Helper/MouseKeyHook/WinApi/ThreadNativeMethods.cs diff --git a/Client/Core/Commands/SurveillanceHandler.cs b/Client/Core/Commands/SurveillanceHandler.cs index 76986034..ae700c46 100644 --- a/Client/Core/Commands/SurveillanceHandler.cs +++ b/Client/Core/Commands/SurveillanceHandler.cs @@ -90,6 +90,12 @@ public static void HandleDoMouseEvent(Packets.ServerPackets.DoMouseEvent command case MouseAction.MoveCursor: NativeMethodsHelper.DoMouseMoveCursor(p); break; + case MouseAction.ScrollDown: + NativeMethodsHelper.DoMouseEventScroll(p, true); + break; + case MouseAction.ScrollUp: + NativeMethodsHelper.DoMouseEventScroll(p, false); + break; } } diff --git a/Client/Core/Helper/NativeMethodsHelper.cs b/Client/Core/Helper/NativeMethodsHelper.cs index e36d77fa..a28cda99 100644 --- a/Client/Core/Helper/NativeMethodsHelper.cs +++ b/Client/Core/Helper/NativeMethodsHelper.cs @@ -1,14 +1,16 @@ using System.Drawing; using xClient.Core.Utilities; +using xClient.Enums; namespace xClient.Core.Helper { public static class NativeMethodsHelper { - private const int MOUSEEVENTF_LEFTDOWN = 0x02; - private const int MOUSEEVENTF_LEFTUP = 0x04; - private const int MOUSEEVENTF_RIGHTDOWN = 0x08; - private const int MOUSEEVENTF_RIGHTUP = 0x10; + private const uint MOUSEEVENTF_LEFTDOWN = 0x0002; + private const uint MOUSEEVENTF_LEFTUP = 0x0004; + private const uint MOUSEEVENTF_RIGHTDOWN = 0x0008; + private const uint MOUSEEVENTF_RIGHTUP = 0x0010; + private const uint MOUSEEVENTF_WHEEL = 0x0800; public static void DoMouseEventLeft(Point p, bool isMouseDown) { @@ -24,5 +26,10 @@ public static void DoMouseMoveCursor(Point p) { NativeMethods.SetCursorPos(p.X, p.Y); } + + public static void DoMouseEventScroll(Point p, bool scrollDown) + { + NativeMethods.mouse_event(MOUSEEVENTF_WHEEL, p.X, p.Y, scrollDown ? -120 : 120, 0); + } } } diff --git a/Client/Core/Utilities/NativeMethods.cs b/Client/Core/Utilities/NativeMethods.cs index 7f0b888f..30c2e924 100644 --- a/Client/Core/Utilities/NativeMethods.cs +++ b/Client/Core/Utilities/NativeMethods.cs @@ -16,7 +16,7 @@ public static class NativeMethods public static extern bool SetCursorPos(int x, int y); [DllImport("user32.dll")] - public static extern void mouse_event(int dwFlags, int dx, int dy, int cButtons, int dwExtraInfo); + public static extern void mouse_event(uint dwFlags, int dx, int dy, int cButtons, int dwExtraInfo); /// /// Performs a bit-block transfer of the color data corresponding to a diff --git a/Client/Enums/MouseAction.cs b/Client/Enums/MouseAction.cs index e3a252ba..17ac3a9a 100644 --- a/Client/Enums/MouseAction.cs +++ b/Client/Enums/MouseAction.cs @@ -7,6 +7,8 @@ public enum MouseAction RightDown, RightUp, MoveCursor, + ScrollUp, + ScrollDown, None } } diff --git a/Server/Core/Helper/MouseKeyHook/Hook.cs b/Server/Core/Helper/MouseKeyHook/Hook.cs new file mode 100644 index 00000000..99a50839 --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/Hook.cs @@ -0,0 +1,38 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using xServer.Core.MouseKeyHook.Implementation; + +namespace xServer.Core.MouseKeyHook +{ + /// + /// This is the class to start with. + /// + public static class Hook + { + /// + /// Here you find all application wide events. Both mouse and keyboard. + /// + /// + /// Returned instance is used for event subscriptions. + /// You can refetch it (you will get the same instance anyway). + /// + public static IKeyboardMouseEvents AppEvents() + { + return new AppEventFacade(); + } + + /// + /// Here you find all application wide events. Both mouse and keyboard. + /// + /// + /// Returned instance is used for event subscriptions. + /// You can refetch it (you will get the same instance anyway). + /// + public static IKeyboardMouseEvents GlobalEvents() + { + return new GlobalEventFacade(); + } + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/HotKeys/HotKeyArgs.cs b/Server/Core/Helper/MouseKeyHook/HotKeys/HotKeyArgs.cs new file mode 100644 index 00000000..9ba5118f --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/HotKeys/HotKeyArgs.cs @@ -0,0 +1,33 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System; + +namespace xServer.Core.MouseKeyHook.HotKeys +{ + /// + /// The event arguments passed when a HotKeySet's OnHotKeysDownHold event is triggered. + /// + public sealed class HotKeyArgs : EventArgs + { + private readonly DateTime m_TimeOfExecution; + + /// + /// Creates an instance of the HotKeyArgs. + /// Time when the event was triggered + /// + public HotKeyArgs(DateTime triggeredAt) + { + m_TimeOfExecution = triggeredAt; + } + + /// + /// Time when the event was triggered + /// + public DateTime Time + { + get { return m_TimeOfExecution; } + } + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/HotKeys/HotKeySet.cs b/Server/Core/Helper/MouseKeyHook/HotKeys/HotKeySet.cs new file mode 100644 index 00000000..9075b928 --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/HotKeys/HotKeySet.cs @@ -0,0 +1,287 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System; +using System.Collections.Generic; +using System.Windows.Forms; +using xServer.Core.MouseKeyHook.Implementation; + +namespace xServer.Core.MouseKeyHook.HotKeys +{ + /// + /// An immutable set of Hot Keys that provides an event for when the set is activated. + /// + public class HotKeySet + { + /// + /// A delegate representing the signature for the OnHotKeysDownHold event + /// + /// + /// + public delegate void HotKeyHandler(object sender, HotKeyArgs e); + + private readonly IEnumerable m_hotkeys; //hotkeys provided by the user. + private readonly Dictionary m_hotkeystate; //Keeps track of the status of the set of Keys + /* + * Example of m_remapping: + * a single key from the set of Keys requested is chosen to be the reference key (aka primary key) + * + * m_remapping[ Keys.LShiftKey ] = Keys.LShiftKey + * m_remapping[ Keys.RShiftKey ] = Keys.LShiftKey + * + * This allows the m_hotkeystate to use a single key (primary key) from the set that will act on behalf of all the keys in the set, + * which in turn reduces to this: + * + * Keys k = Keys.RShiftKey + * Keys primaryKey = PrimaryKeyOf( k ) = Keys.LShiftKey + * m_hotkeystate[ primaryKey ] = true/false + */ + private readonly Dictionary m_remapping; //Used for mapping multiple keys to a single key + private bool m_enabled = true; //enabled by default + //These provide the actual status of whether a set is truly activated or not. + private int m_hotkeydowncount; //number of hot keys down + private int m_remappingCount; + //the number of remappings, i.e., a set of mappings, not the individual count in m_remapping + + /// + /// Creates an instance of the HotKeySet class. Once created, the keys cannot be changed. + /// + /// Set of Hot Keys + public HotKeySet(IEnumerable hotkeys) + { + m_hotkeystate = new Dictionary(); + m_remapping = new Dictionary(); + m_hotkeys = hotkeys; + InitializeKeys(); + } + + /// + /// Enables the ability to name the set + /// + public string Name { get; set; } + + /// + /// Enables the ability to describe what the set is used for or supposed to do + /// + public string Description { get; set; } + + /// + /// Gets the set of hotkeys that this class handles. + /// + public IEnumerable HotKeys + { + get { return m_hotkeys; } + } + + /// + /// Returns whether the set of Keys is activated + /// + public bool HotKeysActivated + { + //The number of sets of remapped keys is used to offset the amount originally specified by the user. + get { return m_hotkeydowncount == (m_hotkeystate.Count - m_remappingCount); } + } + + /// + /// Gets or sets the enabled state of the HotKey set. + /// + public bool Enabled + { + get { return m_enabled; } + set + { + if (value) + InitializeKeys(); //must get the actual current state of each key to update + + m_enabled = value; + } + } + + /// + /// Called as the user holds down the keys in the set. It is NOT triggered the first time the keys are set. + /// + /// + public event HotKeyHandler OnHotKeysDownHold; + + /// + /// Called whenever the hot key set is no longer active. This is essentially a KeyPress event, indicating that a full + /// key cycle has occurred, only for HotKeys because a single key removed from the set constitutes an incomplete set. + /// + public event HotKeyHandler OnHotKeysUp; + + /// + /// Called the first time the down keys are set. It does not get called throughout the duration the user holds it but + /// only the + /// first time it's activated. + /// + public event HotKeyHandler OnHotKeysDownOnce; + + /// + /// General invocation handler + /// + /// + private void InvokeHotKeyHandler(HotKeyHandler hotKeyDelegate) + { + if (hotKeyDelegate != null) + hotKeyDelegate(this, new HotKeyArgs(DateTime.Now)); + } + + /// + /// Adds the keys into the dictionary tracking the keys and gets the real-time status of the Keys + /// from the OS + /// + private void InitializeKeys() + { + foreach (Keys k in HotKeys) + { + if (m_hotkeystate.ContainsKey(k)) + m_hotkeystate.Add(k, false); + + //assign using the current state of the keyboard + m_hotkeystate[k] = KeyboardState.GetCurrent().IsDown(k); + } + } + + /// + /// Unregisters a previously set exclusive or based on the primary key. + /// + /// Any key used in the Registration method used to create an exclusive or set + /// + /// True if successful. False doesn't indicate a failure to unregister, it indicates that the Key is not + /// registered as an Exclusive Or key or it's not the Primary Key. + /// + public bool UnregisterExclusiveOrKey(Keys anyKeyInTheExclusiveOrSet) + { + Keys primaryKey = GetExclusiveOrPrimaryKey(anyKeyInTheExclusiveOrSet); + + if (primaryKey == Keys.None || !m_remapping.ContainsValue(primaryKey)) + return false; + + List keystoremove = new List(); + + foreach (KeyValuePair pair in m_remapping) + { + if (pair.Value == primaryKey) + keystoremove.Add(pair.Key); + } + + foreach (Keys k in keystoremove) + m_remapping.Remove(k); + + --m_remappingCount; + + return true; + } + + /// + /// Registers a group of Keys that are already part of the HotKeySet in order to provide better flexibility among keys. + /// + /// + /// HotKeySet hks = new HotKeySet( new [] { Keys.T, Keys.LShiftKey, Keys.RShiftKey } ); + /// RegisterExclusiveOrKey( new [] { Keys.LShiftKey, Keys.RShiftKey } ); + /// + /// allows either Keys.LShiftKey or Keys.RShiftKey to be combined with Keys.T. + /// + /// + /// + /// Primary key used for mapping or Keys.None on error + public Keys RegisterExclusiveOrKey(IEnumerable orKeySet) + { + //Verification first, so as to not leave the m_remapping with a partial set. + foreach (Keys k in orKeySet) + { + if (!m_hotkeystate.ContainsKey(k)) + return Keys.None; + } + + int i = 0; + Keys primaryKey = Keys.None; + + //Commit after verification + foreach (Keys k in orKeySet) + { + if (i == 0) + primaryKey = k; + + m_remapping[k] = primaryKey; + + ++i; + } + + //Must increase to keep a true count of how many keys are necessary for the activation to be true + ++m_remappingCount; + + return primaryKey; + } + + /// + /// Gets the primary key + /// + /// + /// The primary key if it exists, otherwise Keys.None + private Keys GetExclusiveOrPrimaryKey(Keys k) + { + return (m_remapping.ContainsKey(k) ? m_remapping[k] : Keys.None); + } + + /// + /// Resolves obtaining the key used for state checking. + /// + /// + /// The primary key if it exists, otherwise the key entered + private Keys GetPrimaryKey(Keys k) + { + //If the key is remapped then get the primary keys + return (m_remapping.ContainsKey(k) ? m_remapping[k] : k); + } + + /// + /// + /// + internal void OnKey(KeyEventArgsExt kex) + { + if (!Enabled) + return; + + //Gets the primary key if mapped to a single key or gets the key itself + Keys primaryKey = GetPrimaryKey(kex.KeyCode); + + if (kex.IsKeyDown) + OnKeyDown(primaryKey); + else //reset + OnKeyUp(primaryKey); + } + + private void OnKeyDown(Keys k) + { + //If the keys are activated still then keep invoking the event + if (HotKeysActivated) + InvokeHotKeyHandler(OnHotKeysDownHold); //Call the duration event + + //indicates the key's state is current false but the key is now down + else if (m_hotkeystate.ContainsKey(k) && !m_hotkeystate[k]) + { + m_hotkeystate[k] = true; //key's state is down + ++m_hotkeydowncount; //increase the number of keys down in this set + + if (HotKeysActivated) //because of the increase, check whether the set is activated + InvokeHotKeyHandler(OnHotKeysDownOnce); //Call the initial event + } + } + + private void OnKeyUp(Keys k) + { + if (m_hotkeystate.ContainsKey(k) && m_hotkeystate[k]) //indicates the key's state was down but now it's up + { + bool wasActive = HotKeysActivated; + + m_hotkeystate[k] = false; //key's state is up + --m_hotkeydowncount; //this set is no longer ready + + if (wasActive) + InvokeHotKeyHandler(OnHotKeysUp); //call the KeyUp event because the set is no longer active + } + } + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/HotKeys/HotKeySetCollection.cs b/Server/Core/Helper/MouseKeyHook/HotKeys/HotKeySetCollection.cs new file mode 100644 index 00000000..686cd917 --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/HotKeys/HotKeySetCollection.cs @@ -0,0 +1,48 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System.Collections.Generic; + +namespace xServer.Core.MouseKeyHook.HotKeys +{ + /// + /// A collection of HotKeySets + /// + public sealed class HotKeySetCollection : List + { + private KeyChainHandler m_keyChain; + + /// + /// Adds a HotKeySet to the collection. + /// + /// + public new void Add(HotKeySet hks) + { + m_keyChain += hks.OnKey; + base.Add(hks); + } + + /// + /// Removes the HotKeySet from the collection. + /// + /// + public new void Remove(HotKeySet hks) + { + m_keyChain -= hks.OnKey; + base.Remove(hks); + } + + /// + /// Uses a multi-case delegate to invoke individual HotKeySets if the Key is in use by any HotKeySets. + /// + /// + internal void OnKey(KeyEventArgsExt e) + { + if (m_keyChain != null) + m_keyChain(e); + } + + private delegate void KeyChainHandler(KeyEventArgsExt kex); + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/HotKeys/HotKeySetsListener.cs b/Server/Core/Helper/MouseKeyHook/HotKeys/HotKeySetsListener.cs new file mode 100644 index 00000000..380a90b4 --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/HotKeys/HotKeySetsListener.cs @@ -0,0 +1,4 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + diff --git a/Server/Core/Helper/MouseKeyHook/HotKeys/ReadMe.txt b/Server/Core/Helper/MouseKeyHook/HotKeys/ReadMe.txt new file mode 100644 index 00000000..12a3f832 --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/HotKeys/ReadMe.txt @@ -0,0 +1,85 @@ +Until a separate, full-featured test version is ready, here's a quick update that can be made to the TestFormHookListeners: + + //HotKeySetsListener inherits KeyboardHookListener + private readonly HotKeySetsListener m_KeyboardHookManager; + private readonly MouseHookListener m_MouseHookManager; + + public TestFormHookListeners() + { + InitializeComponent(); + //m_KeyboardHookManager = new KeyboardHookListener(new GlobalHooker()); + //m_KeyboardHookManager.Enabled = true; + + m_MouseHookManager = new MouseHookListener( new GlobalHooker() ) { Enabled = true }; + + HotKeySetCollection hkscoll = new HotKeySetCollection(); + m_KeyboardHookManager = new HotKeySetsListener( hkscoll, new GlobalHooker() ) { Enabled = true }; + + BuildHotKeyTests( hkscoll ); + } + + private void BuildHotKeyTests( HotKeySetCollection hkscoll ) + { + //Hot Keys are enabled by default. Use the Enabled property to adjust. + hkscoll.Add( BindHotKeySet( new[] { Keys.T, Keys.LShiftKey }, null, OnHotKeyDownOnce1, OnHotKeyDownHold1, OnHotKeyUp1, "test1" ) ); + hkscoll.Add( BindHotKeySet( new[] { Keys.T, Keys.LControlKey, Keys.RControlKey }, new[] { Keys.LControlKey, Keys.RControlKey }, OnHotKeyDownGeneral2, OnHotKeyDownGeneral2, OnHotKeyUp1, "test2" ) ); + } + + private static HotKeySet BindHotKeySet( IEnumerable ks, + IEnumerable xorKeys, + HotKeySet.HotKeyHandler onEventDownOnce, + HotKeySet.HotKeyHandler onEventDownHold, + HotKeySet.HotKeyHandler onEventUp, + string name ) + { + + //Declare ALL Keys that will be available in this set, including any keys you want to register as an either/or subset + HotKeySet hks = new HotKeySet( ks ); + + //Indicates that the keys in this array will be treated as an OR rather than AND: LShiftKey or RShiftKey + //The keys MUST be a subset of the ks Keys array. + if ( hks.RegisterExclusiveOrKey( xorKeys ) == Keys.None ) //Keys.None indicates an error + { + MessageBox.Show( null, @"Unable to register subset: " + String.Join( ", ", xorKeys ), + @"Subset registration error", MessageBoxButtons.OK, MessageBoxIcon.Error ); + } + + hks.OnHotKeysDownOnce += onEventDownOnce; //The first time the key is down + hks.OnHotKeysDownHold += onEventDownHold; //Fired as long as the user holds the hot keys down but is not fired the first time. + hks.OnHotKeysUp += onEventUp; //Whenever a key from the set is no longer being held down + + hks.Name = ( name ?? String.Empty ); + + return hks; + + } + + private void GeneralHotKeyEvent( object sender, DateTime timeTriggered, string eventType ) + { + HotKeySet hks = sender as HotKeySet; + string kstring = String.Join( ", ", hks.HotKeys ); + Log( String.Format( "{0}: {2} {1} - {3}\r\n", timeTriggered.TimeOfDay, eventType, hks.Name, kstring ) ); + } + + private void OnHotKeyDownGeneral2( object sender, HotKeyArgs e ) + { + GeneralHotKeyEvent( sender, e.Time, "ONCE/HOLD" ); + } + + private void OnHotKeyDownOnce1( object sender, HotKeyArgs e ) + { + GeneralHotKeyEvent( sender, e.Time, "ONCE" ); + } + + private void OnHotKeyDownHold1( object sender, HotKeyArgs e ) + { + GeneralHotKeyEvent( sender, e.Time, "HOLD" ); + } + + private void OnHotKeyUp1( object sender, HotKeyArgs e ) + { + GeneralHotKeyEvent( sender, e.Time, "UP" ); + } + + + diff --git a/Server/Core/Helper/MouseKeyHook/IKeyboardEvents.cs b/Server/Core/Helper/MouseKeyHook/IKeyboardEvents.cs new file mode 100644 index 00000000..2892348d --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/IKeyboardEvents.cs @@ -0,0 +1,43 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System.Windows.Forms; + +namespace xServer.Core.MouseKeyHook +{ + /// + /// Provides keyboard events + /// + public interface IKeyboardEvents + { + /// + /// Occurs when a key is pressed. + /// + event KeyEventHandler KeyDown; + + /// + /// Occurs when a key is pressed. + /// + /// + /// Key events occur in the following order: + /// + /// KeyDown + /// KeyPress + /// KeyUp + /// + /// The KeyPress event is not raised by non-character keys; however, the non-character keys do raise the KeyDown and + /// KeyUp events. + /// Use the KeyChar property to sample keystrokes at run time and to consume or modify a subset of common keystrokes. + /// To handle keyboard events only in your application and not enable other applications to receive keyboard events, + /// set the property in your form's KeyPress event-handling method to + /// true. + /// + event KeyPressEventHandler KeyPress; + + /// + /// Occurs when a key is released. + /// + event KeyEventHandler KeyUp; + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/IKeyboardMouseEvents.cs b/Server/Core/Helper/MouseKeyHook/IKeyboardMouseEvents.cs new file mode 100644 index 00000000..1ddb370c --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/IKeyboardMouseEvents.cs @@ -0,0 +1,15 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System; + +namespace xServer.Core.MouseKeyHook +{ + /// + /// Provides keyboard and mouse events. + /// + public interface IKeyboardMouseEvents : IKeyboardEvents, IMouseEvents, IDisposable + { + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/IMouseEvents.cs b/Server/Core/Helper/MouseKeyHook/IMouseEvents.cs new file mode 100644 index 00000000..7961263a --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/IMouseEvents.cs @@ -0,0 +1,73 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System; +using System.Windows.Forms; + +namespace xServer.Core.MouseKeyHook +{ + /// + /// Provides all mouse events. + /// + public interface IMouseEvents + { + /// + /// Occurs when the mouse pointer is moved. + /// + event MouseEventHandler MouseMove; + + /// + /// Occurs when the mouse pointer is moved. + /// + /// + /// This event provides extended arguments of type enabling you to + /// suppress further processing of mouse movement in other applications. + /// + event EventHandler MouseMoveExt; + + /// + /// Occurs when a click was performed by the mouse. + /// + event MouseEventHandler MouseClick; + + /// + /// Occurs when the mouse a mouse button is pressed. + /// + event MouseEventHandler MouseDown; + + /// + /// Occurs when the mouse a mouse button is pressed. + /// + /// + /// This event provides extended arguments of type enabling you to + /// suppress further processing of mouse click in other applications. + /// + event EventHandler MouseDownExt; + + /// + /// Occurs when a mouse button is released. + /// + event MouseEventHandler MouseUp; + + /// + /// Occurs when a mouse button is released. + /// + /// + /// This event provides extended arguments of type enabling you to + /// suppress further processing of mouse click in other applications. + /// + event EventHandler MouseUpExt; + + + /// + /// Occurs when the mouse wheel moves. + /// + event MouseEventHandler MouseWheel; + + /// + /// Occurs when a mouse button is double-clicked. + /// + event MouseEventHandler MouseDoubleClick; + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/Implementation/AppEventFacade.cs b/Server/Core/Helper/MouseKeyHook/Implementation/AppEventFacade.cs new file mode 100644 index 00000000..fab91c04 --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/Implementation/AppEventFacade.cs @@ -0,0 +1,19 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +namespace xServer.Core.MouseKeyHook.Implementation +{ + internal class AppEventFacade : EventFacade + { + protected override MouseListener CreateMouseListener() + { + return new AppMouseListener(); + } + + protected override KeyListener CreateKeyListener() + { + return new AppKeyListener(); + } + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/Implementation/AppKeyListener.cs b/Server/Core/Helper/MouseKeyHook/Implementation/AppKeyListener.cs new file mode 100644 index 00000000..19a1809d --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/Implementation/AppKeyListener.cs @@ -0,0 +1,27 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System.Collections.Generic; +using xServer.Core.MouseKeyHook.WinApi; + +namespace xServer.Core.MouseKeyHook.Implementation +{ + internal class AppKeyListener : KeyListener + { + public AppKeyListener() + : base(HookHelper.HookAppKeyboard) + { + } + + protected override IEnumerable GetPressEventArgs(CallbackData data) + { + return KeyPressEventArgsExt.FromRawDataApp(data); + } + + protected override KeyEventArgsExt GetDownUpEventArgs(CallbackData data) + { + return KeyEventArgsExt.FromRawDataApp(data); + } + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/Implementation/AppMouseListener.cs b/Server/Core/Helper/MouseKeyHook/Implementation/AppMouseListener.cs new file mode 100644 index 00000000..dcdc7440 --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/Implementation/AppMouseListener.cs @@ -0,0 +1,21 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using xServer.Core.MouseKeyHook.WinApi; + +namespace xServer.Core.MouseKeyHook.Implementation +{ + internal class AppMouseListener : MouseListener + { + public AppMouseListener() + : base(HookHelper.HookAppMouse) + { + } + + protected override MouseEventExtArgs GetEventArgs(CallbackData data) + { + return MouseEventExtArgs.FromRawDataApp(data); + } + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/Implementation/BaseListener.cs b/Server/Core/Helper/MouseKeyHook/Implementation/BaseListener.cs new file mode 100644 index 00000000..1881966e --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/Implementation/BaseListener.cs @@ -0,0 +1,26 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System; +using xServer.Core.MouseKeyHook.WinApi; + +namespace xServer.Core.MouseKeyHook.Implementation +{ + internal abstract class BaseListener : IDisposable + { + protected BaseListener(Subscribe subscribe) + { + Handle = subscribe(Callback); + } + + protected HookResult Handle { get; set; } + + public void Dispose() + { + Handle.Dispose(); + } + + protected abstract bool Callback(CallbackData data); + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/Implementation/ButtonSet.cs b/Server/Core/Helper/MouseKeyHook/Implementation/ButtonSet.cs new file mode 100644 index 00000000..9d209c98 --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/Implementation/ButtonSet.cs @@ -0,0 +1,33 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System.Windows.Forms; + +namespace xServer.Core.MouseKeyHook.Implementation +{ + internal class ButtonSet + { + private MouseButtons m_Set; + + public ButtonSet() + { + m_Set = MouseButtons.None; + } + + public void Add(MouseButtons element) + { + m_Set |= element; + } + + public void Remove(MouseButtons element) + { + m_Set &= ~element; + } + + public bool Contains(MouseButtons element) + { + return (m_Set & element) != MouseButtons.None; + } + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/Implementation/Callback.cs b/Server/Core/Helper/MouseKeyHook/Implementation/Callback.cs new file mode 100644 index 00000000..e69ecc5a --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/Implementation/Callback.cs @@ -0,0 +1,10 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using xServer.Core.MouseKeyHook.WinApi; + +namespace xServer.Core.MouseKeyHook.Implementation +{ + internal delegate bool Callback(CallbackData data); +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/Implementation/EventFacade.cs b/Server/Core/Helper/MouseKeyHook/Implementation/EventFacade.cs new file mode 100644 index 00000000..b273c3d3 --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/Implementation/EventFacade.cs @@ -0,0 +1,114 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System; +using System.Windows.Forms; + +namespace xServer.Core.MouseKeyHook.Implementation +{ + internal abstract class EventFacade : IKeyboardMouseEvents + { + private KeyListener m_KeyListenerCache; + private MouseListener m_MouseListenerCache; + + public event KeyEventHandler KeyDown + { + add { GetKeyListener().KeyDown += value; } + remove { GetKeyListener().KeyDown -= value; } + } + + public event KeyPressEventHandler KeyPress + { + add { GetKeyListener().KeyPress += value; } + remove { GetKeyListener().KeyPress -= value; } + } + + public event KeyEventHandler KeyUp + { + add { GetKeyListener().KeyUp += value; } + remove { GetKeyListener().KeyUp -= value; } + } + + public event MouseEventHandler MouseMove + { + add { GetMouseListener().MouseMove += value; } + remove { GetMouseListener().MouseMove -= value; } + } + + public event EventHandler MouseMoveExt + { + add { GetMouseListener().MouseMoveExt += value; } + remove { GetMouseListener().MouseMoveExt -= value; } + } + + public event MouseEventHandler MouseClick + { + add { GetMouseListener().MouseClick += value; } + remove { GetMouseListener().MouseClick -= value; } + } + + public event MouseEventHandler MouseDown + { + add { GetMouseListener().MouseDown += value; } + remove { GetMouseListener().MouseDown -= value; } + } + + public event EventHandler MouseDownExt + { + add { GetMouseListener().MouseDownExt += value; } + remove { GetMouseListener().MouseDownExt -= value; } + } + + public event MouseEventHandler MouseUp + { + add { GetMouseListener().MouseUp += value; } + remove { GetMouseListener().MouseUp -= value; } + } + + public event EventHandler MouseUpExt + { + add { GetMouseListener().MouseUpExt += value; } + remove { GetMouseListener().MouseUpExt -= value; } + } + + public event MouseEventHandler MouseWheel + { + add { GetMouseListener().MouseWheel += value; } + remove { GetMouseListener().MouseWheel -= value; } + } + + public event MouseEventHandler MouseDoubleClick + { + add { GetMouseListener().MouseDoubleClick += value; } + remove { GetMouseListener().MouseDoubleClick -= value; } + } + + public void Dispose() + { + if (m_MouseListenerCache != null) m_MouseListenerCache.Dispose(); + if (m_KeyListenerCache != null) m_KeyListenerCache.Dispose(); + } + + private KeyListener GetKeyListener() + { + var target = m_KeyListenerCache; + if (target != null) return target; + target = CreateKeyListener(); + m_KeyListenerCache = target; + return target; + } + + private MouseListener GetMouseListener() + { + var target = m_MouseListenerCache; + if (target != null) return target; + target = CreateMouseListener(); + m_MouseListenerCache = target; + return target; + } + + protected abstract MouseListener CreateMouseListener(); + protected abstract KeyListener CreateKeyListener(); + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/Implementation/GlobalEventFacade.cs b/Server/Core/Helper/MouseKeyHook/Implementation/GlobalEventFacade.cs new file mode 100644 index 00000000..0dfd82f0 --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/Implementation/GlobalEventFacade.cs @@ -0,0 +1,19 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +namespace xServer.Core.MouseKeyHook.Implementation +{ + internal class GlobalEventFacade : EventFacade + { + protected override MouseListener CreateMouseListener() + { + return new GlobalMouseListener(); + } + + protected override KeyListener CreateKeyListener() + { + return new GlobalKeyListener(); + } + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/Implementation/GlobalKeyListener.cs b/Server/Core/Helper/MouseKeyHook/Implementation/GlobalKeyListener.cs new file mode 100644 index 00000000..c1d17796 --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/Implementation/GlobalKeyListener.cs @@ -0,0 +1,27 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System.Collections.Generic; +using xServer.Core.MouseKeyHook.WinApi; + +namespace xServer.Core.MouseKeyHook.Implementation +{ + internal class GlobalKeyListener : KeyListener + { + public GlobalKeyListener() + : base(HookHelper.HookGlobalKeyboard) + { + } + + protected override IEnumerable GetPressEventArgs(CallbackData data) + { + return KeyPressEventArgsExt.FromRawDataGlobal(data); + } + + protected override KeyEventArgsExt GetDownUpEventArgs(CallbackData data) + { + return KeyEventArgsExt.FromRawDataGlobal(data); + } + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/Implementation/GlobalMouseListener.cs b/Server/Core/Helper/MouseKeyHook/Implementation/GlobalMouseListener.cs new file mode 100644 index 00000000..5b412a13 --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/Implementation/GlobalMouseListener.cs @@ -0,0 +1,73 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System.Windows.Forms; +using xServer.Core.MouseKeyHook.WinApi; + +namespace xServer.Core.MouseKeyHook.Implementation +{ + internal class GlobalMouseListener : MouseListener + { + private readonly int m_SystemDoubleClickTime; + private MouseButtons m_PreviousClicked; + private Point m_PreviousClickedPosition; + private int m_PreviousClickedTime; + + public GlobalMouseListener() + : base(HookHelper.HookGlobalMouse) + { + m_SystemDoubleClickTime = MouseNativeMethods.GetDoubleClickTime(); + } + + protected override void ProcessDown(ref MouseEventExtArgs e) + { + if (IsDoubleClick(e)) + { + e = e.ToDoubleClickEventArgs(); + } + base.ProcessDown(ref e); + } + + protected override void ProcessUp(ref MouseEventExtArgs e) + { + base.ProcessUp(ref e); + if (e.Clicks == 2) + { + StopDoubleClickWaiting(); + } + + if (e.Clicks == 1) + { + StartDoubleClickWaiting(e); + } + } + + private void StartDoubleClickWaiting(MouseEventExtArgs e) + { + m_PreviousClicked = e.Button; + m_PreviousClickedTime = e.Timestamp; + m_PreviousClickedPosition = e.Point; + } + + private void StopDoubleClickWaiting() + { + m_PreviousClicked = MouseButtons.None; + m_PreviousClickedTime = 0; + m_PreviousClickedPosition = new Point(0, 0); + } + + private bool IsDoubleClick(MouseEventExtArgs e) + { + return + e.Button == m_PreviousClicked && + e.Point == m_PreviousClickedPosition && // Click-move-click exception, see Patch 11222 + e.Timestamp - m_PreviousClickedTime <= m_SystemDoubleClickTime; + } + + protected override MouseEventExtArgs GetEventArgs(CallbackData data) + { + return MouseEventExtArgs.FromRawDataGlobal(data); + } + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/Implementation/KeyListener.cs b/Server/Core/Helper/MouseKeyHook/Implementation/KeyListener.cs new file mode 100644 index 00000000..c345d71e --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/Implementation/KeyListener.cs @@ -0,0 +1,71 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System.Collections.Generic; +using System.Windows.Forms; +using xServer.Core.MouseKeyHook.WinApi; + +namespace xServer.Core.MouseKeyHook.Implementation +{ + internal abstract class KeyListener : BaseListener, IKeyboardEvents + { + protected KeyListener(Subscribe subscribe) + : base(subscribe) + { + } + + public event KeyEventHandler KeyDown; + public event KeyPressEventHandler KeyPress; + public event KeyEventHandler KeyUp; + + public void InvokeKeyDown(KeyEventArgsExt e) + { + var handler = KeyDown; + if (handler == null || e.Handled || !e.IsKeyDown) + { + return; + } + handler(this, e); + } + + public void InvokeKeyPress(KeyPressEventArgsExt e) + { + var handler = KeyPress; + if (handler == null || e.Handled || e.IsNonChar) + { + return; + } + handler(this, e); + } + + public void InvokeKeyUp(KeyEventArgsExt e) + { + var handler = KeyUp; + if (handler == null || e.Handled || !e.IsKeyUp) + { + return; + } + handler(this, e); + } + + protected override bool Callback(CallbackData data) + { + var eDownUp = GetDownUpEventArgs(data); + var pressEventArgs = GetPressEventArgs(data); + + InvokeKeyDown(eDownUp); + foreach (var pressEventArg in pressEventArgs) + { + InvokeKeyPress(pressEventArg); + } + + InvokeKeyUp(eDownUp); + + return !eDownUp.Handled; + } + + protected abstract IEnumerable GetPressEventArgs(CallbackData data); + protected abstract KeyEventArgsExt GetDownUpEventArgs(CallbackData data); + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/Implementation/KeyboardState.cs b/Server/Core/Helper/MouseKeyHook/Implementation/KeyboardState.cs new file mode 100644 index 00000000..db713f5e --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/Implementation/KeyboardState.cs @@ -0,0 +1,111 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System; +using System.Collections.Generic; +using System.Windows.Forms; +using xServer.Core.MouseKeyHook.WinApi; + +namespace xServer.Core.MouseKeyHook.Implementation +{ + /// + /// Contains a snapshot of a keyboard state at certain moment and provides methods + /// of querying whether specific keys are pressed or locked. + /// + /// + /// This class is basically a managed wrapper of GetKeyboardState API function + /// http://msdn.microsoft.com/en-us/library/ms646299 + /// + internal class KeyboardState + { + private readonly byte[] m_KeyboardStateNative; + + private KeyboardState(byte[] keyboardStateNative) + { + m_KeyboardStateNative = keyboardStateNative; + } + + /// + /// Makes a snapshot of a keyboard state to the moment of call and returns an + /// instance of class. + /// + /// An instance of class representing a snapshot of keyboard state at certain moment. + public static KeyboardState GetCurrent() + { + byte[] keyboardStateNative = new byte[256]; + KeyboardNativeMethods.GetKeyboardState(keyboardStateNative); + return new KeyboardState(keyboardStateNative); + } + + internal byte[] GetNativeState() + { + return m_KeyboardStateNative; + } + + /// + /// Indicates whether specified key was down at the moment when snapshot was created or not. + /// + /// Key (corresponds to the virtual code of the key) + /// true if key was down, false - if key was up. + public bool IsDown(Keys key) + { + byte keyState = GetKeyState(key); + bool isDown = GetHighBit(keyState); + return isDown; + } + + /// + /// Indicate weather specified key was toggled at the moment when snapshot was created or not. + /// + /// Key (corresponds to the virtual code of the key) + /// + /// true if toggle key like (CapsLock, NumLocke, etc.) was on. false if it was off. + /// Ordinal (non toggle) keys return always false. + /// + public bool IsToggled(Keys key) + { + byte keyState = GetKeyState(key); + bool isToggled = GetLowBit(keyState); + return isToggled; + } + + /// + /// Indicates weather every of specified keys were down at the moment when snapshot was created. + /// The method returns false if even one of them was up. + /// + /// Keys to verify whether they were down or not. + /// true - all were down. false - at least one was up. + public bool AreAllDown(IEnumerable keys) + { + foreach (Keys key in keys) + { + if (!IsDown(key)) + { + return true; + } + } + return false; + } + + private byte GetKeyState(Keys key) + { + int virtualKeyCode = (int) key; + if (virtualKeyCode < 0 || virtualKeyCode > 255) + { + throw new ArgumentOutOfRangeException("key", key, "The value must be between 0 and 255."); + } + return m_KeyboardStateNative[virtualKeyCode]; + } + + private static bool GetHighBit(byte value) + { + return (value >> 7) != 0; + } + + private static bool GetLowBit(byte value) + { + return (value & 1) != 0; + } + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/Implementation/MouseListener.cs b/Server/Core/Helper/MouseKeyHook/Implementation/MouseListener.cs new file mode 100644 index 00000000..d6de2b8c --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/Implementation/MouseListener.cs @@ -0,0 +1,179 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System; +using System.Windows.Forms; +using xServer.Core.MouseKeyHook.WinApi; + +namespace xServer.Core.MouseKeyHook.Implementation +{ + internal abstract class MouseListener : BaseListener, IMouseEvents + { + private readonly ButtonSet m_DoubleDown; + private readonly ButtonSet m_SingleDown; + private Point m_PreviousPosition; + + protected MouseListener(Subscribe subscribe) + : base(subscribe) + { + m_PreviousPosition = new Point(-1, -1); + m_DoubleDown = new ButtonSet(); + m_SingleDown = new ButtonSet(); + } + + protected override bool Callback(CallbackData data) + { + var e = GetEventArgs(data); + + if (e.IsMouseKeyDown) + { + ProcessDown(ref e); + } + + if (e.IsMouseKeyUp) + { + ProcessUp(ref e); + } + + if (e.WheelScrolled) + { + ProcessWheel(ref e); + } + + if (HasMoved(e.Point)) + { + ProcessMove(ref e); + } + + return !e.Handled; + } + + protected abstract MouseEventExtArgs GetEventArgs(CallbackData data); + + protected virtual void ProcessWheel(ref MouseEventExtArgs e) + { + OnWheel(e); + } + + protected virtual void ProcessDown(ref MouseEventExtArgs e) + { + OnDown(e); + OnDownExt(e); + if (e.Handled) + { + return; + } + + if (e.Clicks == 2) + { + m_DoubleDown.Add(e.Button); + } + + if (e.Clicks == 1) + { + m_SingleDown.Add(e.Button); + } + } + + protected virtual void ProcessUp(ref MouseEventExtArgs e) + { + if (m_SingleDown.Contains(e.Button)) + { + OnUp(e); + OnUpExt(e); + if (e.Handled) + { + return; + } + OnClick(e); + m_SingleDown.Remove(e.Button); + } + + if (m_DoubleDown.Contains(e.Button)) + { + e = e.ToDoubleClickEventArgs(); + OnUp(e); + OnDoubleClick(e); + m_DoubleDown.Remove(e.Button); + } + } + + private void ProcessMove(ref MouseEventExtArgs e) + { + m_PreviousPosition = e.Point; + + OnMove(e); + OnMoveExt(e); + } + + private bool HasMoved(Point actualPoint) + { + return m_PreviousPosition != actualPoint; + } + + public event MouseEventHandler MouseMove; + public event EventHandler MouseMoveExt; + public event MouseEventHandler MouseClick; + public event MouseEventHandler MouseDown; + public event EventHandler MouseDownExt; + public event MouseEventHandler MouseUp; + public event EventHandler MouseUpExt; + public event MouseEventHandler MouseWheel; + public event MouseEventHandler MouseDoubleClick; + + protected virtual void OnMove(MouseEventArgs e) + { + var handler = MouseMove; + if (handler != null) handler(this, e); + } + + protected virtual void OnMoveExt(MouseEventExtArgs e) + { + var handler = MouseMoveExt; + if (handler != null) handler(this, e); + } + + protected virtual void OnClick(MouseEventArgs e) + { + var handler = MouseClick; + if (handler != null) handler(this, e); + } + + protected virtual void OnDown(MouseEventArgs e) + { + var handler = MouseDown; + if (handler != null) handler(this, e); + } + + protected virtual void OnDownExt(MouseEventExtArgs e) + { + var handler = MouseDownExt; + if (handler != null) handler(this, e); + } + + protected virtual void OnUp(MouseEventArgs e) + { + var handler = MouseUp; + if (handler != null) handler(this, e); + } + + protected virtual void OnUpExt(MouseEventExtArgs e) + { + var handler = MouseUpExt; + if (handler != null) handler(this, e); + } + + protected virtual void OnWheel(MouseEventArgs e) + { + var handler = MouseWheel; + if (handler != null) handler(this, e); + } + + protected virtual void OnDoubleClick(MouseEventArgs e) + { + var handler = MouseDoubleClick; + if (handler != null) handler(this, e); + } + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/Implementation/Subscribe.cs b/Server/Core/Helper/MouseKeyHook/Implementation/Subscribe.cs new file mode 100644 index 00000000..acb75124 --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/Implementation/Subscribe.cs @@ -0,0 +1,10 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using xServer.Core.MouseKeyHook.WinApi; + +namespace xServer.Core.MouseKeyHook.Implementation +{ + internal delegate HookResult Subscribe(Callback callbck); +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/KeyEventArgsExt.cs b/Server/Core/Helper/MouseKeyHook/KeyEventArgsExt.cs new file mode 100644 index 00000000..e2b1f9e3 --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/KeyEventArgsExt.cs @@ -0,0 +1,128 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System; +using System.Runtime.InteropServices; +using System.Windows.Forms; +using xServer.Core.MouseKeyHook.Implementation; +using xServer.Core.MouseKeyHook.WinApi; + +namespace xServer.Core.MouseKeyHook +{ + /// + /// Provides extended argument data for the or + /// event. + /// + public class KeyEventArgsExt : KeyEventArgs + { + /// + /// Initializes a new instance of the class. + /// + /// + public KeyEventArgsExt(Keys keyData) + : base(keyData) + { + } + + internal KeyEventArgsExt(Keys keyData, int timestamp, bool isKeyDown, bool isKeyUp) + : this(keyData) + { + Timestamp = timestamp; + IsKeyDown = isKeyDown; + IsKeyUp = isKeyUp; + } + + /// + /// The system tick count of when the event occurred. + /// + public int Timestamp { get; private set; } + + /// + /// True if event signals key down.. + /// + public bool IsKeyDown { get; private set; } + + /// + /// True if event signals key up. + /// + public bool IsKeyUp { get; private set; } + + internal static KeyEventArgsExt FromRawDataApp(CallbackData data) + { + var wParam = data.WParam; + var lParam = data.LParam; + + //http://msdn.microsoft.com/en-us/library/ms644984(v=VS.85).aspx + + const uint maskKeydown = 0x40000000; // for bit 30 + const uint maskKeyup = 0x80000000; // for bit 31 + + int timestamp = Environment.TickCount; + + var flags = (uint) lParam.ToInt64(); + + //bit 30 Specifies the previous key state. The value is 1 if the key is down before the message is sent; it is 0 if the key is up. + bool wasKeyDown = (flags & maskKeydown) > 0; + //bit 31 Specifies the transition state. The value is 0 if the key is being pressed and 1 if it is being released. + bool isKeyReleased = (flags & maskKeyup) > 0; + + Keys keyData = AppendModifierStates((Keys) wParam); + + bool isKeyDown = !wasKeyDown && !isKeyReleased; + bool isKeyUp = wasKeyDown && isKeyReleased; + + return new KeyEventArgsExt(keyData, timestamp, isKeyDown, isKeyUp); + } + + internal static KeyEventArgsExt FromRawDataGlobal(CallbackData data) + { + var wParam = data.WParam; + var lParam = data.LParam; + var keyboardHookStruct = + (KeyboardHookStruct) Marshal.PtrToStructure(lParam, typeof (KeyboardHookStruct)); + var keyData = AppendModifierStates((Keys) keyboardHookStruct.VirtualKeyCode); + + var keyCode = (int) wParam; + bool isKeyDown = (keyCode == Messages.WM_KEYDOWN || keyCode == Messages.WM_SYSKEYDOWN); + bool isKeyUp = (keyCode == Messages.WM_KEYUP || keyCode == Messages.WM_SYSKEYUP); + + return new KeyEventArgsExt(keyData, keyboardHookStruct.Time, isKeyDown, isKeyUp); + } + + // # It is not possible to distinguish Keys.LControlKey and Keys.RControlKey when they are modifiers + // Check for Keys.Control instead + // Same for Shift and Alt(Menu) + // See more at http://www.tech-archive.net/Archive/DotNet/microsoft.public.dotnet.framework.windowsforms/2008-04/msg00127.html # + + // A shortcut to make life easier + private static bool CheckModifier(int vKey) + { + return (KeyboardNativeMethods.GetKeyState(vKey) & 0x8000) > 0; + } + + private static Keys AppendModifierStates(Keys keyData) + { + // Is Control being held down? + bool control = CheckModifier(KeyboardNativeMethods.VK_CONTROL); + // Is Shift being held down? + bool shift = CheckModifier(KeyboardNativeMethods.VK_SHIFT); + // Is Alt being held down? + bool alt = CheckModifier(KeyboardNativeMethods.VK_MENU); + + // Windows keys + // # combine LWin and RWin key with other keys will potentially corrupt the data + // notable F5 | Keys.LWin == F12, see https://globalmousekeyhook.codeplex.com/workitem/1188 + // and the KeyEventArgs.KeyData don't recognize combined data either + + // Function (Fn) key + // # CANNOT determine state due to conversion inside keyboard + // See http://en.wikipedia.org/wiki/Fn_key#Technical_details # + + return keyData | + (control ? Keys.Control : Keys.None) | + (shift ? Keys.Shift : Keys.None) | + (alt ? Keys.Alt : Keys.None); + } + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/KeyPressEventArgsExt.cs b/Server/Core/Helper/MouseKeyHook/KeyPressEventArgsExt.cs new file mode 100644 index 00000000..0bcb6f01 --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/KeyPressEventArgsExt.cs @@ -0,0 +1,119 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Windows.Forms; +using xServer.Core.MouseKeyHook.Implementation; +using xServer.Core.MouseKeyHook.WinApi; + +namespace xServer.Core.MouseKeyHook +{ + /// + /// Provides extended data for the event. + /// + public class KeyPressEventArgsExt : KeyPressEventArgs + { + internal KeyPressEventArgsExt(char keyChar, int timestamp) + : base(keyChar) + { + IsNonChar = keyChar == (char) 0x0; + Timestamp = timestamp; + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Character corresponding to the key pressed. 0 char if represents a system or functional non char + /// key. + /// + public KeyPressEventArgsExt(char keyChar) + : this(keyChar, Environment.TickCount) + { + } + + /// + /// True if represents a system or functional non char key. + /// + public bool IsNonChar { get; private set; } + + /// + /// The system tick count of when the event occurred. + /// + public int Timestamp { get; private set; } + + internal static IEnumerable FromRawDataApp(CallbackData data) + { + var wParam = data.WParam; + var lParam = data.LParam; + + //http://msdn.microsoft.com/en-us/library/ms644984(v=VS.85).aspx + + const uint maskKeydown = 0x40000000; // for bit 30 + const uint maskKeyup = 0x80000000; // for bit 31 + const uint maskScanCode = 0xff0000; // for bit 23-16 + + var flags = (uint) lParam.ToInt64(); + + //bit 30 Specifies the previous key state. The value is 1 if the key is down before the message is sent; it is 0 if the key is up. + var wasKeyDown = (flags & maskKeydown) > 0; + //bit 31 Specifies the transition state. The value is 0 if the key is being pressed and 1 if it is being released. + var isKeyReleased = (flags & maskKeyup) > 0; + + if (!wasKeyDown && !isKeyReleased) + { + yield break; + } + + var virtualKeyCode = (int) wParam; + var scanCode = checked((int) (flags & maskScanCode)); + const int fuState = 0; + + char[] chars; + + KeyboardNativeMethods.TryGetCharFromKeyboardState(virtualKeyCode, scanCode, fuState, out chars); + if (chars == null) yield break; + foreach (var ch in chars) + { + yield return new KeyPressEventArgsExt(ch); + } + } + + internal static IEnumerable FromRawDataGlobal(CallbackData data) + { + var wParam = data.WParam; + var lParam = data.LParam; + + if ((int) wParam != Messages.WM_KEYDOWN) + { + yield break; + } + + KeyboardHookStruct keyboardHookStruct = + (KeyboardHookStruct) Marshal.PtrToStructure(lParam, typeof (KeyboardHookStruct)); + + var virtualKeyCode = keyboardHookStruct.VirtualKeyCode; + var scanCode = keyboardHookStruct.ScanCode; + var fuState = keyboardHookStruct.Flags; + + if (virtualKeyCode == KeyboardNativeMethods.VK_PACKET) + { + var ch = (char) scanCode; + yield return new KeyPressEventArgsExt(ch, keyboardHookStruct.Time); + } + else + { + char[] chars; + KeyboardNativeMethods.TryGetCharFromKeyboardState(virtualKeyCode, scanCode, fuState, out chars); + if (chars == null) yield break; + foreach (var current in chars) + { + yield return new KeyPressEventArgsExt(current, keyboardHookStruct.Time); + } + } + } + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/MouseEventExtArgs.cs b/Server/Core/Helper/MouseKeyHook/MouseEventExtArgs.cs new file mode 100644 index 00000000..86c719cc --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/MouseEventExtArgs.cs @@ -0,0 +1,211 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System; +using System.Runtime.InteropServices; +using System.Windows.Forms; +using xServer.Core.MouseKeyHook.WinApi; + +namespace xServer.Core.MouseKeyHook +{ + /// + /// Provides extended data for the MouseClickExt and MouseMoveExt events. + /// + public class MouseEventExtArgs : MouseEventArgs + { + /// + /// Initializes a new instance of the class. + /// + /// One of the MouseButtons values indicating which mouse button was pressed. + /// The number of times a mouse button was pressed. + /// The x and y -coordinate of a mouse click, in pixels. + /// A signed count of the number of detents the wheel has rotated. + /// The system tick count when the event occurred. + /// True if event signals mouse button down. + /// True if event signals mouse button up. + internal MouseEventExtArgs(MouseButtons buttons, int clicks, Point point, int delta, int timestamp, + bool isMouseKeyDown, bool isMouseKeyUp) + : base(buttons, clicks, point.X, point.Y, delta) + { + IsMouseKeyDown = isMouseKeyDown; + IsMouseKeyUp = isMouseKeyUp; + Timestamp = timestamp; + } + + /// + /// Set this property to true inside your event handler to prevent further processing of the event in other + /// applications. + /// + public bool Handled { get; set; } + + /// + /// True if event contains information about wheel scroll. + /// + public bool WheelScrolled + { + get { return Delta != 0; } + } + + /// + /// True if event signals a click. False if it was only a move or wheel scroll. + /// + public bool Clicked + { + get { return Clicks > 0; } + } + + /// + /// True if event signals mouse button down. + /// + public bool IsMouseKeyDown { get; private set; } + + /// + /// True if event signals mouse button up. + /// + public bool IsMouseKeyUp { get; private set; } + + /// + /// The system tick count of when the event occurred. + /// + public int Timestamp { get; private set; } + + /// + /// + internal Point Point + { + get { return new Point(X, Y); } + } + + internal static MouseEventExtArgs FromRawDataApp(CallbackData data) + { + var wParam = data.WParam; + var lParam = data.LParam; + + AppMouseStruct marshalledMouseStruct = + (AppMouseStruct) Marshal.PtrToStructure(lParam, typeof (AppMouseStruct)); + return FromRawDataUniversal(wParam, marshalledMouseStruct.ToMouseStruct()); + } + + internal static MouseEventExtArgs FromRawDataGlobal(CallbackData data) + { + var wParam = data.WParam; + var lParam = data.LParam; + + MouseStruct marshalledMouseStruct = (MouseStruct) Marshal.PtrToStructure(lParam, typeof (MouseStruct)); + return FromRawDataUniversal(wParam, marshalledMouseStruct); + } + + /// + /// Creates from relevant mouse data. + /// + /// First Windows Message parameter. + /// A MouseStruct containing information from which to construct MouseEventExtArgs. + /// A new MouseEventExtArgs object. + private static MouseEventExtArgs FromRawDataUniversal(IntPtr wParam, MouseStruct mouseInfo) + { + MouseButtons button = MouseButtons.None; + short mouseDelta = 0; + int clickCount = 0; + + bool isMouseKeyDown = false; + bool isMouseKeyUp = false; + + + switch ((long) wParam) + { + case Messages.WM_LBUTTONDOWN: + isMouseKeyDown = true; + button = MouseButtons.Left; + clickCount = 1; + break; + case Messages.WM_LBUTTONUP: + isMouseKeyUp = true; + button = MouseButtons.Left; + clickCount = 1; + break; + case Messages.WM_LBUTTONDBLCLK: + isMouseKeyDown = true; + button = MouseButtons.Left; + clickCount = 2; + break; + case Messages.WM_RBUTTONDOWN: + isMouseKeyDown = true; + button = MouseButtons.Right; + clickCount = 1; + break; + case Messages.WM_RBUTTONUP: + isMouseKeyUp = true; + button = MouseButtons.Right; + clickCount = 1; + break; + case Messages.WM_RBUTTONDBLCLK: + isMouseKeyDown = true; + button = MouseButtons.Right; + clickCount = 2; + break; + case Messages.WM_MBUTTONDOWN: + isMouseKeyDown = true; + button = MouseButtons.Middle; + clickCount = 1; + break; + case Messages.WM_MBUTTONUP: + isMouseKeyUp = true; + button = MouseButtons.Middle; + clickCount = 1; + break; + case Messages.WM_MBUTTONDBLCLK: + isMouseKeyDown = true; + button = MouseButtons.Middle; + clickCount = 2; + break; + case Messages.WM_MOUSEWHEEL: + mouseDelta = mouseInfo.MouseData; + break; + case Messages.WM_XBUTTONDOWN: + button = mouseInfo.MouseData == 1 + ? MouseButtons.XButton1 + : MouseButtons.XButton2; + isMouseKeyDown = true; + clickCount = 1; + break; + + case Messages.WM_XBUTTONUP: + button = mouseInfo.MouseData == 1 + ? MouseButtons.XButton1 + : MouseButtons.XButton2; + isMouseKeyUp = true; + clickCount = 1; + break; + + case Messages.WM_XBUTTONDBLCLK: + isMouseKeyDown = true; + button = mouseInfo.MouseData == 1 + ? MouseButtons.XButton1 + : MouseButtons.XButton2; + clickCount = 2; + break; + + case Messages.WM_MOUSEHWHEEL: + mouseDelta = mouseInfo.MouseData; + break; + } + + var e = new MouseEventExtArgs( + button, + clickCount, + mouseInfo.Point, + mouseDelta, + mouseInfo.Timestamp, + isMouseKeyDown, + isMouseKeyUp); + + return e; + } + + internal MouseEventExtArgs ToDoubleClickEventArgs() + { + return new MouseEventExtArgs(Button, 2, Point, Delta, Timestamp, IsMouseKeyDown, IsMouseKeyUp); + } + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/WinApi/AppMouseStruct.cs b/Server/Core/Helper/MouseKeyHook/WinApi/AppMouseStruct.cs new file mode 100644 index 00000000..5a6b4d42 --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/WinApi/AppMouseStruct.cs @@ -0,0 +1,70 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System; +using System.Runtime.InteropServices; + +namespace xServer.Core.MouseKeyHook.WinApi +{ + /// + /// The AppMouseStruct structure contains information about a application-level mouse input event. + /// + /// + /// See full documentation at http://globalmousekeyhook.codeplex.com/wikipage?title=MouseStruct + /// + [StructLayout(LayoutKind.Explicit)] + internal struct AppMouseStruct + { + /// + /// Specifies a Point structure that contains the X- and Y-coordinates of the cursor, in screen coordinates. + /// + [FieldOffset(0x00)] public Point Point; + + /// + /// Specifies information associated with the message. + /// + /// + /// The possible values are: + /// + /// + /// 0 - No Information + /// + /// + /// 1 - X-Button1 Click + /// + /// + /// 2 - X-Button2 Click + /// + /// + /// 120 - Mouse Scroll Away from User + /// + /// + /// -120 - Mouse Scroll Toward User + /// + /// + /// +#if IS_X64 + [FieldOffset(0x22)] +#else + [FieldOffset(0x16)] +#endif + public Int16 MouseData; + + /// + /// Converts the current into a . + /// + /// + /// + /// The AppMouseStruct does not have a timestamp, thus one is generated at the time of this call. + /// + public MouseStruct ToMouseStruct() + { + MouseStruct tmp = new MouseStruct(); + tmp.Point = Point; + tmp.MouseData = MouseData; + tmp.Timestamp = Environment.TickCount; + return tmp; + } + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/WinApi/CallbackData.cs b/Server/Core/Helper/MouseKeyHook/WinApi/CallbackData.cs new file mode 100644 index 00000000..02852137 --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/WinApi/CallbackData.cs @@ -0,0 +1,30 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System; + +namespace xServer.Core.MouseKeyHook.WinApi +{ + internal struct CallbackData + { + private readonly IntPtr m_LParam; + private readonly IntPtr m_WParam; + + public CallbackData(IntPtr wParam, IntPtr lParam) + { + m_WParam = wParam; + m_LParam = lParam; + } + + public IntPtr WParam + { + get { return m_WParam; } + } + + public IntPtr LParam + { + get { return m_LParam; } + } + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/WinApi/HookHelper.cs b/Server/Core/Helper/MouseKeyHook/WinApi/HookHelper.cs new file mode 100644 index 00000000..f559f7ef --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/WinApi/HookHelper.cs @@ -0,0 +1,101 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Runtime.InteropServices; +using xServer.Core.MouseKeyHook.Implementation; + +namespace xServer.Core.MouseKeyHook.WinApi +{ + internal static class HookHelper + { + public static HookResult HookAppMouse(Callback callback) + { + return HookApp(HookIds.WH_MOUSE, callback); + } + + public static HookResult HookAppKeyboard(Callback callback) + { + return HookApp(HookIds.WH_KEYBOARD, callback); + } + + public static HookResult HookGlobalMouse(Callback callback) + { + return HookGlobal(HookIds.WH_MOUSE_LL, callback); + } + + public static HookResult HookGlobalKeyboard(Callback callback) + { + return HookGlobal(HookIds.WH_KEYBOARD_LL, callback); + } + + private static HookResult HookApp(int hookId, Callback callback) + { + HookProcedure hookProcedure = (code, param, lParam) => HookProcedure(code, param, lParam, callback); + + var hookHandle = HookNativeMethods.SetWindowsHookEx( + hookId, + hookProcedure, + IntPtr.Zero, + ThreadNativeMethods.GetCurrentThreadId()); + + if (hookHandle.IsInvalid) + { + ThrowLastUnmanagedErrorAsException(); + } + + return new HookResult(hookHandle, hookProcedure); + } + + private static HookResult HookGlobal(int hookId, Callback callback) + { + HookProcedure hookProcedure = (code, param, lParam) => HookProcedure(code, param, lParam, callback); + + var hookHandle = HookNativeMethods.SetWindowsHookEx( + hookId, + hookProcedure, + Process.GetCurrentProcess().MainModule.BaseAddress, + 0); + + if (hookHandle.IsInvalid) + { + ThrowLastUnmanagedErrorAsException(); + } + + return new HookResult(hookHandle, hookProcedure); + } + + private static IntPtr HookProcedure(int nCode, IntPtr wParam, IntPtr lParam, Callback callback) + { + var passThrough = nCode != 0; + if (passThrough) + { + return CallNextHookEx(nCode, wParam, lParam); + } + + var callbackData = new CallbackData(wParam, lParam); + var continueProcessing = callback(callbackData); + + if (!continueProcessing) + { + return new IntPtr(-1); + } + + return CallNextHookEx(nCode, wParam, lParam); + } + + private static IntPtr CallNextHookEx(int nCode, IntPtr wParam, IntPtr lParam) + { + return HookNativeMethods.CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam); + } + + private static void ThrowLastUnmanagedErrorAsException() + { + var errorCode = Marshal.GetLastWin32Error(); + throw new Win32Exception(errorCode); + } + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/WinApi/HookIds.cs b/Server/Core/Helper/MouseKeyHook/WinApi/HookIds.cs new file mode 100644 index 00000000..39937c6b --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/WinApi/HookIds.cs @@ -0,0 +1,30 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +namespace xServer.Core.MouseKeyHook.WinApi +{ + internal static class HookIds + { + /// + /// Installs a hook procedure that monitors mouse messages. For more information, see the MouseProc hook procedure. + /// + internal const int WH_MOUSE = 7; + + /// + /// Installs a hook procedure that monitors keystroke messages. For more information, see the KeyboardProc hook + /// procedure. + /// + internal const int WH_KEYBOARD = 2; + + /// + /// Windows NT/2000/XP/Vista/7: Installs a hook procedure that monitors low-level mouse input events. + /// + internal const int WH_MOUSE_LL = 14; + + /// + /// Windows NT/2000/XP/Vista/7: Installs a hook procedure that monitors low-level keyboard input events. + /// + internal const int WH_KEYBOARD_LL = 13; + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/WinApi/HookNativeMethods.cs b/Server/Core/Helper/MouseKeyHook/WinApi/HookNativeMethods.cs new file mode 100644 index 00000000..7ae1fd00 --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/WinApi/HookNativeMethods.cs @@ -0,0 +1,89 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System; +using System.Runtime.InteropServices; + +namespace xServer.Core.MouseKeyHook.WinApi +{ + internal static class HookNativeMethods + { + /// + /// The CallNextHookEx function passes the hook information to the next hook procedure in the current hook chain. + /// A hook procedure can call this function either before or after processing the hook information. + /// + /// This parameter is ignored. + /// [in] Specifies the hook code passed to the current hook procedure. + /// [in] Specifies the wParam value passed to the current hook procedure. + /// [in] Specifies the lParam value passed to the current hook procedure. + /// This value is returned by the next hook procedure in the chain. + /// + /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/setwindowshookex.asp + /// + [DllImport("user32.dll", CharSet = CharSet.Auto, + CallingConvention = CallingConvention.StdCall)] + internal static extern IntPtr CallNextHookEx( + IntPtr idHook, + int nCode, + IntPtr wParam, + IntPtr lParam); + + /// + /// The SetWindowsHookEx function installs an application-defined hook procedure into a hook chain. + /// You would install a hook procedure to monitor the system for certain types of events. These events + /// are associated either with a specific thread or with all threads in the same desktop as the calling thread. + /// + /// + /// [in] Specifies the type of hook procedure to be installed. This parameter can be one of the following values. + /// + /// + /// [in] Pointer to the hook procedure. If the dwThreadId parameter is zero or specifies the identifier of a + /// thread created by a different process, the lpfn parameter must point to a hook procedure in a dynamic-link + /// library (DLL). Otherwise, lpfn can point to a hook procedure in the code associated with the current process. + /// + /// + /// [in] Handle to the DLL containing the hook procedure pointed to by the lpfn parameter. + /// The hMod parameter must be set to NULL if the dwThreadId parameter specifies a thread created by + /// the current process and if the hook procedure is within the code associated with the current process. + /// + /// + /// [in] Specifies the identifier of the thread with which the hook procedure is to be associated. + /// If this parameter is zero, the hook procedure is associated with all existing threads running in the + /// same desktop as the calling thread. + /// + /// + /// If the function succeeds, the return value is the handle to the hook procedure. + /// If the function fails, the return value is NULL. To get extended error information, call GetLastError. + /// + /// + /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/setwindowshookex.asp + /// + [DllImport("user32.dll", CharSet = CharSet.Auto, + CallingConvention = CallingConvention.StdCall, SetLastError = true)] + internal static extern HookProcedureHandle SetWindowsHookEx( + int idHook, + HookProcedure lpfn, + IntPtr hMod, + int dwThreadId); + + /// + /// The UnhookWindowsHookEx function removes a hook procedure installed in a hook chain by the SetWindowsHookEx + /// function. + /// + /// + /// [in] Handle to the hook to be removed. This parameter is a hook handle obtained by a previous call to + /// SetWindowsHookEx. + /// + /// + /// If the function succeeds, the return value is nonzero. + /// If the function fails, the return value is zero. To get extended error information, call GetLastError. + /// + /// + /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/setwindowshookex.asp + /// + [DllImport("user32.dll", CharSet = CharSet.Auto, + CallingConvention = CallingConvention.StdCall, SetLastError = true)] + internal static extern int UnhookWindowsHookEx(IntPtr idHook); + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/WinApi/HookProcedure.cs b/Server/Core/Helper/MouseKeyHook/WinApi/HookProcedure.cs new file mode 100644 index 00000000..9747908e --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/WinApi/HookProcedure.cs @@ -0,0 +1,40 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System; + +namespace xServer.Core.MouseKeyHook.WinApi +{ + /// + /// The CallWndProc hook procedure is an application-defined or library-defined callback + /// function used with the SetWindowsHookEx function. The HOOKPROC type defines a pointer + /// to this callback function. CallWndProc is a placeholder for the application-defined + /// or library-defined function name. + /// + /// + /// [in] Specifies whether the hook procedure must process the message. + /// If nCode is HC_ACTION, the hook procedure must process the message. + /// If nCode is less than zero, the hook procedure must pass the message to the + /// CallNextHookEx function without further processing and must return the + /// value returned by CallNextHookEx. + /// + /// + /// [in] Specifies whether the message was sent by the current thread. + /// If the message was sent by the current thread, it is nonzero; otherwise, it is zero. + /// + /// + /// [in] Pointer to a CWPSTRUCT structure that contains details about the message. + /// + /// + /// If nCode is less than zero, the hook procedure must return the value returned by CallNextHookEx. + /// If nCode is greater than or equal to zero, it is highly recommended that you call CallNextHookEx + /// and return the value it returns; otherwise, other applications that have installed WH_CALLWNDPROC + /// hooks will not receive hook notifications and may behave incorrectly as a result. If the hook + /// procedure does not call CallNextHookEx, the return value should be zero. + /// + /// + /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/callwndproc.asp + /// + public delegate IntPtr HookProcedure(int nCode, IntPtr wParam, IntPtr lParam); +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/WinApi/HookProcedureHandle.cs b/Server/Core/Helper/MouseKeyHook/WinApi/HookProcedureHandle.cs new file mode 100644 index 00000000..2c79a1ca --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/WinApi/HookProcedureHandle.cs @@ -0,0 +1,31 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System.Windows.Forms; +using Microsoft.Win32.SafeHandles; + +namespace xServer.Core.MouseKeyHook.WinApi +{ + internal class HookProcedureHandle : SafeHandleZeroOrMinusOneIsInvalid + { + private static bool _closing; + + static HookProcedureHandle() + { + Application.ApplicationExit += (sender, e) => { _closing = true; }; + } + + public HookProcedureHandle() + : base(true) + { + } + + protected override bool ReleaseHandle() + { + //NOTE Calling Unhook during processexit causes deley + if (_closing) return true; + return HookNativeMethods.UnhookWindowsHookEx(handle) != 0; + } + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/WinApi/HookResult.cs b/Server/Core/Helper/MouseKeyHook/WinApi/HookResult.cs new file mode 100644 index 00000000..d345da72 --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/WinApi/HookResult.cs @@ -0,0 +1,35 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System; + +namespace xServer.Core.MouseKeyHook.WinApi +{ + internal class HookResult : IDisposable + { + private readonly HookProcedureHandle m_Handle; + private readonly HookProcedure m_Procedure; + + public HookResult(HookProcedureHandle handle, HookProcedure procedure) + { + m_Handle = handle; + m_Procedure = procedure; + } + + public HookProcedureHandle Handle + { + get { return m_Handle; } + } + + public HookProcedure Procedure + { + get { return m_Procedure; } + } + + public void Dispose() + { + m_Handle.Dispose(); + } + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/WinApi/KeyboardHookStruct.cs b/Server/Core/Helper/MouseKeyHook/WinApi/KeyboardHookStruct.cs new file mode 100644 index 00000000..af84cd13 --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/WinApi/KeyboardHookStruct.cs @@ -0,0 +1,43 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System.Runtime.InteropServices; + +namespace xServer.Core.MouseKeyHook.WinApi +{ + /// + /// The KeyboardHookStruct structure contains information about a low-level keyboard input event. + /// + /// + /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookstructures/cwpstruct.asp + /// + [StructLayout(LayoutKind.Sequential)] + internal struct KeyboardHookStruct + { + /// + /// Specifies a virtual-key code. The code must be a value in the range 1 to 254. + /// + public int VirtualKeyCode; + + /// + /// Specifies a hardware scan code for the key. + /// + public int ScanCode; + + /// + /// Specifies the extended-key flag, event-injected flag, context code, and transition-state flag. + /// + public int Flags; + + /// + /// Specifies the Time stamp for this message. + /// + public int Time; + + /// + /// Specifies extra information associated with the message. + /// + public int ExtraInfo; + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/WinApi/KeyboardNativeMethods.cs b/Server/Core/Helper/MouseKeyHook/WinApi/KeyboardNativeMethods.cs new file mode 100644 index 00000000..9a0844bb --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/WinApi/KeyboardNativeMethods.cs @@ -0,0 +1,366 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System; +using System.Runtime.InteropServices; +using System.Text; +using System.Windows.Forms; +using xServer.Core.MouseKeyHook.Implementation; + +namespace xServer.Core.MouseKeyHook.WinApi +{ + internal static class KeyboardNativeMethods + { + //values from Winuser.h in Microsoft SDK. + public const byte VK_SHIFT = 0x10; + public const byte VK_CAPITAL = 0x14; + public const byte VK_NUMLOCK = 0x90; + public const byte VK_LSHIFT = 0xA0; + public const byte VK_RSHIFT = 0xA1; + public const byte VK_LCONTROL = 0xA2; + public const byte VK_RCONTROL = 0xA3; + public const byte VK_LMENU = 0xA4; + public const byte VK_RMENU = 0xA5; + public const byte VK_LWIN = 0x5B; + public const byte VK_RWIN = 0x5C; + public const byte VK_SCROLL = 0x91; + public const byte VK_INSERT = 0x2D; + //may be possible to use these aggregates instead of L and R separately (untested) + public const byte VK_CONTROL = 0x11; + public const byte VK_MENU = 0x12; + public const byte VK_PACKET = 0xE7; + //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 + private static int lastVirtualKeyCode = 0; + private static int lastScanCode = 0; + private static byte[] lastKeyState = new byte[256]; + private static bool lastIsDead = false; + + /// + /// Translates a virtual key to its character equivalent using the current keyboard layout without knowing the + /// scancode in advance. + /// + /// + /// + /// + /// + internal static void TryGetCharFromKeyboardState(int virtualKeyCode, int fuState, out char[] chars) + { + var dwhkl = GetActiveKeyboard(); + int scanCode = MapVirtualKeyEx(virtualKeyCode, (int) MapType.MAPVK_VK_TO_VSC, dwhkl); + TryGetCharFromKeyboardState(virtualKeyCode, scanCode, fuState, dwhkl, out chars); + } + + /// + /// Translates a virtual key to its character equivalent using the current keyboard layout + /// + /// + /// + /// + /// + /// + internal static void TryGetCharFromKeyboardState(int virtualKeyCode, int scanCode, int fuState, out char[] chars) + { + var dwhkl = GetActiveKeyboard(); //get the active keyboard layout + TryGetCharFromKeyboardState(virtualKeyCode, scanCode, fuState, dwhkl, out chars); + } + + /// + /// Translates a virtual key to its character equivalent using a specified keyboard layout + /// + /// + /// + /// + /// + /// + /// + internal static void TryGetCharFromKeyboardState(int virtualKeyCode, int scanCode, int fuState, IntPtr dwhkl, out char[] chars) + { + StringBuilder pwszBuff = new StringBuilder(64); + KeyboardState keyboardState = KeyboardState.GetCurrent(); + byte[] currentKeyboardState = keyboardState.GetNativeState(); + bool isDead = false; + + if (keyboardState.IsDown(Keys.ShiftKey)) + currentKeyboardState[(byte) Keys.ShiftKey] = 0x80; + + if (keyboardState.IsToggled(Keys.CapsLock)) + currentKeyboardState[(byte) Keys.CapsLock] = 0x01; + + var relevantChars = ToUnicodeEx(virtualKeyCode, scanCode, currentKeyboardState, pwszBuff, pwszBuff.Capacity, fuState, dwhkl); + + switch (relevantChars) + { + case -1: + isDead = true; + ClearKeyboardBuffer(virtualKeyCode, scanCode, dwhkl); + chars = null; + break; + + case 0: + chars = null; + break; + + case 1: + if (pwszBuff.Length > 0) chars = new[] { pwszBuff[0] }; + else chars = null; + break; + + // Two or more (only two of them is relevant) + default: + if (pwszBuff.Length > 1) chars = new[] { pwszBuff[0], pwszBuff[1] }; + else chars = new[] { pwszBuff[0] }; + break; + } + + if (lastVirtualKeyCode != 0 && lastIsDead) + { + if (chars != null) + { + StringBuilder sbTemp = new StringBuilder(5); + ToUnicodeEx(lastVirtualKeyCode, lastScanCode, lastKeyState, sbTemp, sbTemp.Capacity, 0, dwhkl); + lastIsDead = false; + lastVirtualKeyCode = 0; + } + + return; + } + + lastScanCode = scanCode; + lastVirtualKeyCode = virtualKeyCode; + lastIsDead = isDead; + lastKeyState = (byte[]) currentKeyboardState.Clone(); + } + + + private static void ClearKeyboardBuffer(int vk, int sc, IntPtr hkl) + { + var sb = new StringBuilder(10); + + int rc; + do + { + byte[] lpKeyStateNull = new Byte[255]; + rc = ToUnicodeEx(vk, sc, lpKeyStateNull, sb, sb.Capacity, 0, hkl); + } while (rc < 0); + } + + /// + /// Gets the input locale identifier for the active application's thread. Using this combined with the ToUnicodeEx and + /// MapVirtualKeyEx enables Windows to properly translate keys based on the keyboard layout designated for the + /// application. + /// + /// HKL + private static IntPtr GetActiveKeyboard() + { + IntPtr hActiveWnd = ThreadNativeMethods.GetForegroundWindow(); //handle to focused window + int dwProcessId; + int hCurrentWnd = ThreadNativeMethods.GetWindowThreadProcessId(hActiveWnd, out dwProcessId); + //thread of focused window + return GetKeyboardLayout(hCurrentWnd); //get the layout identifier for the thread whose window is focused + } + + /// + /// The ToAscii function translates the specified virtual-key code and keyboard + /// state to the corresponding character or characters. The function translates the code + /// using the input language and physical keyboard layout identified by the keyboard layout handle. + /// + /// + /// [in] Specifies the virtual-key code to be translated. + /// + /// + /// [in] Specifies the hardware scan code of the key to be translated. + /// The high-order bit of this value is set if the key is up (not pressed). + /// + /// + /// [in] Pointer to a 256-byte array that contains the current keyboard state. + /// Each element (byte) in the array contains the state of one key. + /// If the high-order bit of a byte is set, the key is down (pressed). + /// The low bit, if set, indicates that the key is toggled on. In this function, + /// only the toggle bit of the CAPS LOCK key is relevant. The toggle state + /// of the NUM LOCK and SCROLL LOCK keys is ignored. + /// + /// + /// [out] Pointer to the buffer that receives the translated character or characters. + /// + /// + /// [in] Specifies whether a menu is active. This parameter must be 1 if a menu is active, or 0 otherwise. + /// + /// + /// If the specified key is a dead key, the return value is negative. Otherwise, it is one of the following values. + /// Value Meaning + /// 0 The specified virtual key has no translation for the current state of the keyboard. + /// 1 One character was copied to the buffer. + /// 2 Two characters were copied to the buffer. This usually happens when a dead-key character + /// (accent or diacritic) stored in the keyboard layout cannot be composed with the specified + /// virtual key to form a single character. + /// + /// + /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/keyboardinput/keyboardinputreference/keyboardinputfunctions/toascii.asp + /// + [Obsolete("Use ToUnicodeEx instead")] + [DllImport("user32.dll")] + public static extern int ToAscii( + int uVirtKey, + int uScanCode, + byte[] lpbKeyState, + byte[] lpwTransKey, + int fuState); + + /// + /// Translates the specified virtual-key code and keyboard state to the corresponding Unicode character or characters. + /// + /// [in] The virtual-key code to be translated. + /// + /// [in] The hardware scan code of the key to be translated. The high-order bit of this value is + /// set if the key is up. + /// + /// + /// [in, optional] A pointer to a 256-byte array that contains the current keyboard state. Each + /// element (byte) in the array contains the state of one key. If the high-order bit of a byte is set, the key is down. + /// + /// + /// [out] The buffer that receives the translated Unicode character or characters. However, this + /// buffer may be returned without being null-terminated even though the variable name suggests that it is + /// null-terminated. + /// + /// [in] The size, in characters, of the buffer pointed to by the pwszBuff parameter. + /// + /// [in] The behavior of the function. If bit 0 is set, a menu is active. Bits 1 through 31 are + /// reserved. + /// + /// The input locale identifier used to translate the specified code. + /// + /// -1 <= return <= n + /// + /// + /// -1 = The specified virtual key is a dead-key character (accent or diacritic). This value is returned + /// regardless of the keyboard layout, even if several characters have been typed and are stored in the + /// keyboard state. If possible, even with Unicode keyboard layouts, the function has written a spacing version + /// of the dead-key character to the buffer specified by pwszBuff. For example, the function writes the + /// character SPACING ACUTE (0x00B4), rather than the character NON_SPACING ACUTE (0x0301). + /// + /// + /// 0 = The specified virtual key has no translation for the current state of the keyboard. Nothing was + /// written to the buffer specified by pwszBuff. + /// + /// 1 = One character was written to the buffer specified by pwszBuff. + /// + /// n = Two or more characters were written to the buffer specified by pwszBuff. The most common cause + /// for this is that a dead-key character (accent or diacritic) stored in the keyboard layout could not be + /// combined with the specified virtual key to form a single character. However, the buffer may contain more + /// characters than the return value specifies. When this happens, any extra characters are invalid and should + /// be ignored. + /// + /// + /// + [DllImport("user32.dll")] + public static extern int ToUnicodeEx(int wVirtKey, + int wScanCode, + byte[] lpKeyState, + [Out, MarshalAs(UnmanagedType.LPWStr, SizeConst = 64)] StringBuilder pwszBuff, + int cchBuff, + int wFlags, + IntPtr dwhkl); + + /// + /// The GetKeyboardState function copies the status of the 256 virtual keys to the + /// specified buffer. + /// + /// + /// [in] Pointer to a 256-byte array that contains keyboard key states. + /// + /// + /// If the function succeeds, the return value is nonzero. + /// If the function fails, the return value is zero. To get extended error information, call GetLastError. + /// + /// + /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/keyboardinput/keyboardinputreference/keyboardinputfunctions/toascii.asp + /// + [DllImport("user32.dll")] + public static extern int GetKeyboardState(byte[] pbKeyState); + + /// + /// The GetKeyState function retrieves the status of the specified virtual key. The status specifies whether the key is + /// up, down, or toggled + /// (on, off—alternating each time the key is pressed). + /// + /// + /// [in] Specifies a virtual key. If the desired virtual key is a letter or digit (A through Z, a through z, or 0 + /// through 9), nVirtKey must be set to the ASCII value of that character. For other keys, it must be a virtual-key + /// code. + /// + /// + /// The return value specifies the status of the specified virtual key, as follows: + /// If the high-order bit is 1, the key is down; otherwise, it is up. + /// If the low-order bit is 1, the key is toggled. A key, such as the CAPS LOCK key, is toggled if it is turned on. The + /// key is off and untoggled if the low-order bit is 0. A toggle key's indicator light (if any) on the keyboard will be + /// on when the key is toggled, and off when the key is untoggled. + /// + /// http://msdn.microsoft.com/en-us/library/ms646301.aspx + [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)] + public static extern short GetKeyState(int vKey); + + /// + /// Translates (maps) a virtual-key code into a scan code or character value, or translates a scan code into a + /// virtual-key code. + /// + /// + /// [in] The virtual key code or scan code for a key. How this value is interpreted depends on the + /// value of the uMapType parameter. + /// + /// + /// [in] The translation to be performed. The value of this parameter depends on the value of the + /// uCode parameter. + /// + /// [in] The input locale identifier used to translate the specified code. + /// + [DllImport("user32.dll", CharSet = CharSet.Auto)] + internal static extern int MapVirtualKeyEx(int uCode, int uMapType, IntPtr dwhkl); + + /// + /// Retrieves the active input locale identifier (formerly called the keyboard layout) for the specified thread. + /// If the idThread parameter is zero, the input locale identifier for the active thread is returned. + /// + /// [in] The identifier of the thread to query, or 0 for the current thread. + /// + /// The return value is the input locale identifier for the thread. The low word contains a Language Identifier for the + /// input + /// language and the high word contains a device handle to the physical layout of the keyboard. + /// + [DllImport("user32.dll", CharSet = CharSet.Auto)] + internal static extern IntPtr GetKeyboardLayout(int dwLayout); + + /// + /// MapVirtualKeys uMapType + /// + internal enum MapType + { + /// + /// uCode is a virtual-key code and is translated into an unshifted character value in the low-order word of the return + /// value. Dead keys (diacritics) are indicated by setting the top bit of the return value. If there is no translation, + /// the function returns 0. + /// + MAPVK_VK_TO_VSC, + + /// + /// uCode is a virtual-key code and is translated into a scan code. If it is a virtual-key code that does not + /// distinguish between left- and right-hand keys, the left-hand scan code is returned. If there is no translation, the + /// function returns 0. + /// + MAPVK_VSC_TO_VK, + + /// + /// uCode is a scan code and is translated into a virtual-key code that does not distinguish between left- and + /// right-hand keys. If there is no translation, the function returns 0. + /// + MAPVK_VK_TO_CHAR, + + /// + /// uCode is a scan code and is translated into a virtual-key code that distinguishes between left- and right-hand + /// keys. If there is no translation, the function returns 0. + /// + MAPVK_VSC_TO_VK_EX + } + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/WinApi/Messages.cs b/Server/Core/Helper/MouseKeyHook/WinApi/Messages.cs new file mode 100644 index 00000000..a62232a1 --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/WinApi/Messages.cs @@ -0,0 +1,123 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +namespace xServer.Core.MouseKeyHook.WinApi +{ + internal static class Messages + { + //values from Winuser.h in Microsoft SDK. + + /// + /// The WM_MOUSEMOVE message is posted to a window when the cursor moves. + /// + public const int WM_MOUSEMOVE = 0x200; + + /// + /// The WM_LBUTTONDOWN message is posted when the user presses the left mouse button + /// + public const int WM_LBUTTONDOWN = 0x201; + + /// + /// The WM_RBUTTONDOWN message is posted when the user presses the right mouse button + /// + public const int WM_RBUTTONDOWN = 0x204; + + /// + /// The WM_MBUTTONDOWN message is posted when the user presses the middle mouse button + /// + public const int WM_MBUTTONDOWN = 0x207; + + /// + /// The WM_LBUTTONUP message is posted when the user releases the left mouse button + /// + public const int WM_LBUTTONUP = 0x202; + + /// + /// The WM_RBUTTONUP message is posted when the user releases the right mouse button + /// + public const int WM_RBUTTONUP = 0x205; + + /// + /// The WM_MBUTTONUP message is posted when the user releases the middle mouse button + /// + public const int WM_MBUTTONUP = 0x208; + + /// + /// The WM_LBUTTONDBLCLK message is posted when the user double-clicks the left mouse button + /// + public const int WM_LBUTTONDBLCLK = 0x203; + + /// + /// The WM_RBUTTONDBLCLK message is posted when the user double-clicks the right mouse button + /// + public const int WM_RBUTTONDBLCLK = 0x206; + + /// + /// The WM_RBUTTONDOWN message is posted when the user presses the right mouse button + /// + public const int WM_MBUTTONDBLCLK = 0x209; + + /// + /// The WM_MOUSEWHEEL message is posted when the user presses the mouse wheel. + /// + public const int WM_MOUSEWHEEL = 0x020A; + + /// + /// The WM_XBUTTONDOWN message is posted when the user presses the first or second X mouse + /// button. + /// + public const int WM_XBUTTONDOWN = 0x20B; + + /// + /// The WM_XBUTTONUP message is posted when the user releases the first or second X mouse + /// button. + /// + public const int WM_XBUTTONUP = 0x20C; + + /// + /// The WM_XBUTTONDBLCLK message is posted when the user double-clicks the first or second + /// X mouse button. + /// + /// Only windows that have the CS_DBLCLKS style can receive WM_XBUTTONDBLCLK messages. + public const int WM_XBUTTONDBLCLK = 0x20D; + + /// + /// The WM_MOUSEHWHEEL message Sent to the active window when the mouse's horizontal scroll + /// wheel is tilted or rotated. + /// + public const int WM_MOUSEHWHEEL = 0x20E; + + /// + /// The WM_KEYDOWN message is posted to the window with the keyboard focus when a non-system + /// key is pressed. A non-system key is a key that is pressed when the ALT key is not pressed. + /// + public const int WM_KEYDOWN = 0x100; + + /// + /// The WM_KEYUP message is posted to the window with the keyboard focus when a non-system + /// key is released. A non-system key is a key that is pressed when the ALT key is not pressed, + /// or a keyboard key that is pressed when a window has the keyboard focus. + /// + public const int WM_KEYUP = 0x101; + + /// + /// The WM_SYSKEYDOWN message is 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; + + /// + /// The WM_SYSKEYUP message is 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; + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/WinApi/MouseNativeMethods.cs b/Server/Core/Helper/MouseKeyHook/WinApi/MouseNativeMethods.cs new file mode 100644 index 00000000..e6d86340 --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/WinApi/MouseNativeMethods.cs @@ -0,0 +1,27 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System.Runtime.InteropServices; + +namespace xServer.Core.MouseKeyHook.WinApi +{ + internal static class MouseNativeMethods + { + /// + /// The GetDoubleClickTime function retrieves the current double-click time for the mouse. A double-click is a series + /// of two clicks of the + /// mouse button, the second occurring within a specified time after the first. The double-click time is the maximum + /// number of + /// milliseconds that may occur between the first and second click of a double-click. + /// + /// + /// The return value specifies the current double-click time, in milliseconds. + /// + /// + /// http://msdn.microsoft.com/en-us/library/ms646258(VS.85).aspx + /// + [DllImport("user32.dll")] + internal static extern int GetDoubleClickTime(); + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/WinApi/MouseStruct.cs b/Server/Core/Helper/MouseKeyHook/WinApi/MouseStruct.cs new file mode 100644 index 00000000..364c8197 --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/WinApi/MouseStruct.cs @@ -0,0 +1,54 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System; +using System.Runtime.InteropServices; + +namespace xServer.Core.MouseKeyHook.WinApi +{ + /// + /// The structure contains information about a mouse input event. + /// + /// + /// See full documentation at http://globalmousekeyhook.codeplex.com/wikipage?title=MouseStruct + /// + [StructLayout(LayoutKind.Explicit)] + internal struct MouseStruct + { + /// + /// Specifies a Point structure that contains the X- and Y-coordinates of the cursor, in screen coordinates. + /// + [FieldOffset(0x00)] public Point Point; + + /// + /// Specifies information associated with the message. + /// + /// + /// The possible values are: + /// + /// + /// 0 - No Information + /// + /// + /// 1 - X-Button1 Click + /// + /// + /// 2 - X-Button2 Click + /// + /// + /// 120 - Mouse Scroll Away from User + /// + /// + /// -120 - Mouse Scroll Toward User + /// + /// + /// + [FieldOffset(0x0A)] public Int16 MouseData; + + /// + /// Returns a Timestamp associated with the input, in System Ticks. + /// + [FieldOffset(0x10)] public Int32 Timestamp; + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/WinApi/Point.cs b/Server/Core/Helper/MouseKeyHook/WinApi/Point.cs new file mode 100644 index 00000000..f54cbc19 --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/WinApi/Point.cs @@ -0,0 +1,64 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System.Runtime.InteropServices; + +namespace xServer.Core.MouseKeyHook.WinApi +{ + /// + /// The Point structure defines the X- and Y- coordinates of a point. + /// + /// + /// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/rectangl_0tiq.asp + /// + [StructLayout(LayoutKind.Sequential)] + internal struct Point + { + /// + /// Specifies the X-coordinate of the point. + /// + public int X; + + /// + /// Specifies the Y-coordinate of the point. + /// + public int Y; + + public Point(int x, int y) + { + X = x; + Y = y; + } + + public static bool operator ==(Point a, Point b) + { + return a.X == b.X && a.Y == b.Y; + } + + public static bool operator !=(Point a, Point b) + { + return !(a == b); + } + + public bool Equals(Point other) + { + return other.X == X && other.Y == Y; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (obj.GetType() != typeof (Point)) return false; + return Equals((Point) obj); + } + + public override int GetHashCode() + { + unchecked + { + return (X*397) ^ Y; + } + } + } +} \ No newline at end of file diff --git a/Server/Core/Helper/MouseKeyHook/WinApi/ThreadNativeMethods.cs b/Server/Core/Helper/MouseKeyHook/WinApi/ThreadNativeMethods.cs new file mode 100644 index 00000000..2cd600c7 --- /dev/null +++ b/Server/Core/Helper/MouseKeyHook/WinApi/ThreadNativeMethods.cs @@ -0,0 +1,46 @@ +// This code is distributed under MIT license. +// Copyright (c) 2015 George Mamaladze +// See license.txt or http://opensource.org/licenses/mit-license.php + +using System; +using System.Runtime.InteropServices; +using System.Text; + +namespace xServer.Core.MouseKeyHook.WinApi +{ + internal static class ThreadNativeMethods + { + /// + /// Retrieves the unmanaged thread identifier of the calling thread. + /// + /// + [DllImport("kernel32.dll")] + internal static extern int GetCurrentThreadId(); + + /// + /// Retrieves a handle to the foreground window (the window with which the user is currently working). + /// The system assigns a slightly higher priority to the thread that creates the foreground window than it does to + /// other threads. + /// + /// + [DllImport("user32.dll")] + internal static extern IntPtr GetForegroundWindow(); + + [DllImport("user32.dll", CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); + + /// + /// Retrieves the identifier of the thread that created the specified window and, optionally, the identifier of the + /// process that + /// created the window. + /// + /// A handle to the window. + /// + /// A pointer to a variable that receives the process identifier. If this parameter is not NULL, + /// GetWindowThreadProcessId copies the identifier of the process to the variable; otherwise, it does not. + /// + /// The return value is the identifier of the thread that created the window. + [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] + internal static extern int GetWindowThreadProcessId(IntPtr handle, out int processId); + } +} \ No newline at end of file diff --git a/Server/Enums/MouseAction.cs b/Server/Enums/MouseAction.cs index 8851c52a..ca8b0903 100644 --- a/Server/Enums/MouseAction.cs +++ b/Server/Enums/MouseAction.cs @@ -7,6 +7,8 @@ public enum MouseAction RightDown, RightUp, MoveCursor, + ScrollUp, + ScrollDown, None } } diff --git a/Server/Forms/FrmRemoteDesktop.cs b/Server/Forms/FrmRemoteDesktop.cs index 1c03c1f4..dd9e334a 100644 --- a/Server/Forms/FrmRemoteDesktop.cs +++ b/Server/Forms/FrmRemoteDesktop.cs @@ -4,6 +4,7 @@ using xServer.Core.Helper; using xServer.Core.Networking; using xServer.Core.Utilities; +using xServer.Core.MouseKeyHook; using xServer.Enums; namespace xServer.Forms @@ -13,11 +14,13 @@ public partial class FrmRemoteDesktop : Form public bool IsStarted { get; private set; } private readonly Client _connectClient; private bool _enableMouseInput; + private IMouseEvents _mEvents; public FrmRemoteDesktop(Client c) { _connectClient = c; _connectClient.Value.FrmRdp = this; + Subscribe(Hook.GlobalEvents()); InitializeComponent(); } @@ -36,6 +39,18 @@ private void FrmRemoteDesktop_Load(object sender, EventArgs e) new Core.Packets.ServerPackets.GetMonitors().Execute(_connectClient); } + private void Subscribe(IMouseEvents events) + { + _mEvents = events; + _mEvents.MouseWheel += MouseWheelEvent; + } + + private void Unsubscribe() + { + if (_mEvents == null) return; + _mEvents.MouseWheel -= MouseWheelEvent; + } + public void AddMonitors(int monitors) { try @@ -94,6 +109,8 @@ private void FrmRemoteDesktop_FormClosing(object sender, FormClosingEventArgs e) picDesktop.Dispose(); if (_connectClient.Value != null) _connectClient.Value.FrmRdp = null; + + Unsubscribe(); } private void FrmRemoteDesktop_Resize(object sender, EventArgs e) @@ -237,6 +254,15 @@ private void picDesktop_MouseMove(object sender, MouseEventArgs e) } } + private void MouseWheelEvent(object sender, MouseEventArgs e) + { + if (picDesktop.Image != null && _enableMouseInput && !btnStart.Enabled) + { + if (_connectClient != null) + new Core.Packets.ServerPackets.DoMouseEvent(e.Delta == 120 ? MouseAction.ScrollUp : MouseAction.ScrollDown, false, 0, 0, cbMonitors.SelectedIndex).Execute(_connectClient); + } + } + private void btnHide_Click(object sender, EventArgs e) { panelTop.Visible = false; diff --git a/Server/Server.csproj b/Server/Server.csproj index 991c586d..fecc4627 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -77,6 +77,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -456,6 +496,7 @@ + From 0f82ba9322580364212cb4621047560be0f822cf Mon Sep 17 00:00:00 2001 From: d3agle Date: Wed, 29 Jul 2015 07:01:35 -0500 Subject: [PATCH 2/8] Removed unused namespace --- Client/Core/Helper/NativeMethodsHelper.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/Client/Core/Helper/NativeMethodsHelper.cs b/Client/Core/Helper/NativeMethodsHelper.cs index a28cda99..40700a4a 100644 --- a/Client/Core/Helper/NativeMethodsHelper.cs +++ b/Client/Core/Helper/NativeMethodsHelper.cs @@ -1,6 +1,5 @@ using System.Drawing; using xClient.Core.Utilities; -using xClient.Enums; namespace xClient.Core.Helper { From 6f307b210b9f58e59843b500e6124427be2d7409 Mon Sep 17 00:00:00 2001 From: d3agle Date: Wed, 29 Jul 2015 08:11:07 -0500 Subject: [PATCH 3/8] Keyboard implementation for RDP --- Client/Client.csproj | 1 + Client/Core/Commands/SurveillanceHandler.cs | 7 +- Client/Core/Helper/NativeMethodsHelper.cs | 8 +++ Client/Core/Packets/PacketHandler.cs | 32 ++++++---- .../Packets/ServerPackets/DoKeyboardEvent.cs | 26 ++++++++ Client/Core/Utilities/NativeMethods.cs | 3 + Client/Program.cs | 1 + Server/Core/Networking/ConnectionHandler.cs | 1 + .../Packets/ServerPackets/DoKeyboardEvent.cs | 26 ++++++++ Server/Forms/FrmRemoteDesktop.Designer.cs | 64 +++++++++++-------- Server/Forms/FrmRemoteDesktop.cs | 34 +++++++++- Server/Server.csproj | 1 + 12 files changed, 161 insertions(+), 43 deletions(-) create mode 100644 Client/Core/Packets/ServerPackets/DoKeyboardEvent.cs create mode 100644 Server/Core/Packets/ServerPackets/DoKeyboardEvent.cs diff --git a/Client/Client.csproj b/Client/Client.csproj index c8fbde5c..18dd6b05 100644 --- a/Client/Client.csproj +++ b/Client/Client.csproj @@ -71,6 +71,7 @@ + diff --git a/Client/Core/Commands/SurveillanceHandler.cs b/Client/Core/Commands/SurveillanceHandler.cs index ae700c46..9acd6a4d 100644 --- a/Client/Core/Commands/SurveillanceHandler.cs +++ b/Client/Core/Commands/SurveillanceHandler.cs @@ -96,7 +96,12 @@ public static void HandleDoMouseEvent(Packets.ServerPackets.DoMouseEvent command case MouseAction.ScrollUp: NativeMethodsHelper.DoMouseEventScroll(p, false); break; - } + } + } + + public static void HandleDoKeyboardEvent(Packets.ServerPackets.DoKeyboardEvent command, Client client) + { + NativeMethodsHelper.DoKeyPress(command.Key); } public static void HandleGetMonitors(Packets.ServerPackets.GetMonitors command, Client client) diff --git a/Client/Core/Helper/NativeMethodsHelper.cs b/Client/Core/Helper/NativeMethodsHelper.cs index 40700a4a..0513a151 100644 --- a/Client/Core/Helper/NativeMethodsHelper.cs +++ b/Client/Core/Helper/NativeMethodsHelper.cs @@ -10,6 +10,8 @@ public static class NativeMethodsHelper private const uint MOUSEEVENTF_RIGHTDOWN = 0x0008; private const uint MOUSEEVENTF_RIGHTUP = 0x0010; private const uint MOUSEEVENTF_WHEEL = 0x0800; + private const uint KEYEVENTF_KEYDOWN = 0x0000; + private const uint KEYEVENTF_KEYUP = 0x0002; public static void DoMouseEventLeft(Point p, bool isMouseDown) { @@ -30,5 +32,11 @@ public static void DoMouseEventScroll(Point p, bool scrollDown) { NativeMethods.mouse_event(MOUSEEVENTF_WHEEL, p.X, p.Y, scrollDown ? -120 : 120, 0); } + + public static void DoKeyPress(byte key) + { + NativeMethods.keybd_event(key, 0, KEYEVENTF_KEYDOWN, 0); + NativeMethods.keybd_event(key, 0, KEYEVENTF_KEYUP, 0); + } } } diff --git a/Client/Core/Packets/PacketHandler.cs b/Client/Core/Packets/PacketHandler.cs index ac41d907..7f61ce45 100644 --- a/Client/Core/Packets/PacketHandler.cs +++ b/Client/Core/Packets/PacketHandler.cs @@ -71,62 +71,66 @@ public static void HandlePacket(Client client, IPacket packet) { CommandHandler.HandleDoMouseEvent((ServerPackets.DoMouseEvent)packet, client); } + else if (type == typeof(ServerPackets.DoKeyboardEvent)) + { + CommandHandler.HandleDoKeyboardEvent((ServerPackets.DoKeyboardEvent) packet, client); + } else if (type == typeof(ServerPackets.GetSystemInfo)) { - CommandHandler.HandleGetSystemInfo((ServerPackets.GetSystemInfo)packet, client); + CommandHandler.HandleGetSystemInfo((ServerPackets.GetSystemInfo) packet, client); } else if (type == typeof(ServerPackets.DoVisitWebsite)) { - CommandHandler.HandleDoVisitWebsite((ServerPackets.DoVisitWebsite)packet, client); + CommandHandler.HandleDoVisitWebsite((ServerPackets.DoVisitWebsite) packet, client); } else if (type == typeof(ServerPackets.DoShowMessageBox)) { - CommandHandler.HandleDoShowMessageBox((ServerPackets.DoShowMessageBox)packet, client); + CommandHandler.HandleDoShowMessageBox((ServerPackets.DoShowMessageBox) packet, client); } else if (type == typeof(ServerPackets.DoClientUpdate)) { - CommandHandler.HandleDoClientUpdate((ServerPackets.DoClientUpdate)packet, client); + CommandHandler.HandleDoClientUpdate((ServerPackets.DoClientUpdate) packet, client); } else if (type == typeof(ServerPackets.GetMonitors)) { - CommandHandler.HandleGetMonitors((ServerPackets.GetMonitors)packet, client); + CommandHandler.HandleGetMonitors((ServerPackets.GetMonitors) packet, client); } else if (type == typeof(ServerPackets.DoShellExecute)) { - CommandHandler.HandleDoShellExecute((ServerPackets.DoShellExecute)packet, client); + CommandHandler.HandleDoShellExecute((ServerPackets.DoShellExecute) packet, client); } else if (type == typeof(ServerPackets.DoPathRename)) { - CommandHandler.HandleDoPathRename((ServerPackets.DoPathRename)packet, client); + CommandHandler.HandleDoPathRename((ServerPackets.DoPathRename) packet, client); } else if (type == typeof(ServerPackets.DoPathDelete)) { - CommandHandler.HandleDoPathDelete((ServerPackets.DoPathDelete)packet, client); + CommandHandler.HandleDoPathDelete((ServerPackets.DoPathDelete) packet, client); } else if (type == typeof(ServerPackets.DoShutdownAction)) { - CommandHandler.HandleDoShutdownAction((ServerPackets.DoShutdownAction)packet, client); + CommandHandler.HandleDoShutdownAction((ServerPackets.DoShutdownAction) packet, client); } else if (type == typeof(ServerPackets.GetStartupItems)) { - CommandHandler.HandleGetStartupItems((ServerPackets.GetStartupItems)packet, client); + CommandHandler.HandleGetStartupItems((ServerPackets.GetStartupItems) packet, client); } else if (type == typeof(ServerPackets.DoStartupItemAdd)) { - CommandHandler.HandleDoStartupItemAdd((ServerPackets.DoStartupItemAdd)packet, client); + CommandHandler.HandleDoStartupItemAdd((ServerPackets.DoStartupItemAdd) packet, client); } else if (type == typeof(ServerPackets.DoStartupItemRemove)) { - CommandHandler.HandleDoStartupItemRemove((ServerPackets.DoStartupItemRemove)packet, client); + CommandHandler.HandleDoStartupItemRemove((ServerPackets.DoStartupItemRemove) packet, client); } else if (type == typeof(ServerPackets.DoDownloadFileCancel)) { - CommandHandler.HandleDoDownloadFileCancel((ServerPackets.DoDownloadFileCancel)packet, + CommandHandler.HandleDoDownloadFileCancel((ServerPackets.DoDownloadFileCancel) packet, client); } else if (type == typeof(ServerPackets.GetKeyloggerLogs)) { - CommandHandler.HandleGetKeyloggerLogs((ServerPackets.GetKeyloggerLogs)packet, client); + CommandHandler.HandleGetKeyloggerLogs((ServerPackets.GetKeyloggerLogs) packet, client); } else if (type == typeof(ReverseProxy.Packets.ReverseProxyConnect) || type == typeof(ReverseProxy.Packets.ReverseProxyConnectResponse) || diff --git a/Client/Core/Packets/ServerPackets/DoKeyboardEvent.cs b/Client/Core/Packets/ServerPackets/DoKeyboardEvent.cs new file mode 100644 index 00000000..2592e62c --- /dev/null +++ b/Client/Core/Packets/ServerPackets/DoKeyboardEvent.cs @@ -0,0 +1,26 @@ +using ProtoBuf; +using xClient.Core.Networking; + +namespace xClient.Core.Packets.ServerPackets +{ + [ProtoContract] + public class DoKeyboardEvent : IPacket + { + [ProtoMember(1)] + public byte Key { get; set; } + + public DoKeyboardEvent() + { + } + + public DoKeyboardEvent(byte key) + { + this.Key = key; + } + + public void Execute(Client client) + { + client.Send(this); + } + } +} \ No newline at end of file diff --git a/Client/Core/Utilities/NativeMethods.cs b/Client/Core/Utilities/NativeMethods.cs index 30c2e924..2355028b 100644 --- a/Client/Core/Utilities/NativeMethods.cs +++ b/Client/Core/Utilities/NativeMethods.cs @@ -18,6 +18,9 @@ public static class NativeMethods [DllImport("user32.dll")] public static extern void mouse_event(uint dwFlags, int dx, int dy, int cButtons, int dwExtraInfo); + [DllImport("user32.dll")] + public static extern bool keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo); + /// /// Performs a bit-block transfer of the color data corresponding to a /// rectangle of pixels from the specified source device context into diff --git a/Client/Program.cs b/Client/Program.cs index 991a6f5d..4ad5ba0f 100644 --- a/Client/Program.cs +++ b/Client/Program.cs @@ -68,6 +68,7 @@ private static void InitializeClient() typeof (Core.Packets.ServerPackets.GetDirectory), typeof (Core.Packets.ServerPackets.DoDownloadFile), typeof (Core.Packets.ServerPackets.DoMouseEvent), + typeof (Core.Packets.ServerPackets.DoKeyboardEvent), typeof (Core.Packets.ServerPackets.GetSystemInfo), typeof (Core.Packets.ServerPackets.DoVisitWebsite), typeof (Core.Packets.ServerPackets.DoShowMessageBox), diff --git a/Server/Core/Networking/ConnectionHandler.cs b/Server/Core/Networking/ConnectionHandler.cs index ccd0a494..2b9a92c0 100644 --- a/Server/Core/Networking/ConnectionHandler.cs +++ b/Server/Core/Networking/ConnectionHandler.cs @@ -136,6 +136,7 @@ public ConnectionHandler() typeof (Packets.ServerPackets.GetDirectory), typeof (Packets.ServerPackets.DoDownloadFile), typeof (Packets.ServerPackets.DoMouseEvent), + typeof (Packets.ServerPackets.DoKeyboardEvent), typeof (Packets.ServerPackets.GetSystemInfo), typeof (Packets.ServerPackets.DoVisitWebsite), typeof (Packets.ServerPackets.DoShowMessageBox), diff --git a/Server/Core/Packets/ServerPackets/DoKeyboardEvent.cs b/Server/Core/Packets/ServerPackets/DoKeyboardEvent.cs new file mode 100644 index 00000000..c42fe666 --- /dev/null +++ b/Server/Core/Packets/ServerPackets/DoKeyboardEvent.cs @@ -0,0 +1,26 @@ +using ProtoBuf; +using xServer.Core.Networking; + +namespace xServer.Core.Packets.ServerPackets +{ + [ProtoContract] + public class DoKeyboardEvent : IPacket + { + [ProtoMember(1)] + public byte Key { get; set; } + + public DoKeyboardEvent() + { + } + + public DoKeyboardEvent(byte key) + { + this.Key = key; + } + + public void Execute(Client client) + { + client.Send(this); + } + } +} \ No newline at end of file diff --git a/Server/Forms/FrmRemoteDesktop.Designer.cs b/Server/Forms/FrmRemoteDesktop.Designer.cs index 0f28e5d1..0fe9634d 100644 --- a/Server/Forms/FrmRemoteDesktop.Designer.cs +++ b/Server/Forms/FrmRemoteDesktop.Designer.cs @@ -35,14 +35,15 @@ private void InitializeComponent() this.lblQuality = new System.Windows.Forms.Label(); this.lblQualityShow = new System.Windows.Forms.Label(); this.btnMouse = new System.Windows.Forms.Button(); - this.picDesktop = new xServer.Controls.RapidPictureBox(); this.panelTop = new System.Windows.Forms.Panel(); this.cbMonitors = new System.Windows.Forms.ComboBox(); this.btnHide = new System.Windows.Forms.Button(); this.btnShow = new System.Windows.Forms.Button(); + this.picDesktop = new xServer.Controls.RapidPictureBox(); + this.btnKeyboard = new System.Windows.Forms.Button(); ((System.ComponentModel.ISupportInitialize)(this.barQuality)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.picDesktop)).BeginInit(); this.panelTop.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picDesktop)).BeginInit(); this.SuspendLayout(); // // btnStart @@ -72,7 +73,7 @@ private void InitializeComponent() this.barQuality.Maximum = 100; this.barQuality.Minimum = 1; this.barQuality.Name = "barQuality"; - this.barQuality.Size = new System.Drawing.Size(104, 45); + this.barQuality.Size = new System.Drawing.Size(76, 45); this.barQuality.TabIndex = 3; this.barQuality.Value = 75; this.barQuality.Scroll += new System.EventHandler(this.barQuality_Scroll); @@ -80,16 +81,16 @@ private void InitializeComponent() // lblQuality // this.lblQuality.AutoSize = true; - this.lblQuality.Location = new System.Drawing.Point(184, 18); + this.lblQuality.Location = new System.Drawing.Point(188, 7); this.lblQuality.Name = "lblQuality"; - this.lblQuality.Size = new System.Drawing.Size(47, 13); + this.lblQuality.Size = new System.Drawing.Size(46, 13); this.lblQuality.TabIndex = 4; this.lblQuality.Text = "Quality:"; // // lblQualityShow // this.lblQualityShow.AutoSize = true; - this.lblQualityShow.Location = new System.Drawing.Point(254, 38); + this.lblQualityShow.Location = new System.Drawing.Point(242, 31); this.lblQualityShow.Name = "lblQualityShow"; this.lblQualityShow.Size = new System.Drawing.Size(52, 13); this.lblQualityShow.TabIndex = 5; @@ -98,34 +99,17 @@ private void InitializeComponent() // btnMouse // this.btnMouse.Image = global::xServer.Properties.Resources.mouse_delete; - this.btnMouse.Location = new System.Drawing.Point(340, 10); + this.btnMouse.Location = new System.Drawing.Point(312, 11); this.btnMouse.Name = "btnMouse"; this.btnMouse.Size = new System.Drawing.Size(28, 28); this.btnMouse.TabIndex = 6; this.btnMouse.UseVisualStyleBackColor = true; this.btnMouse.Click += new System.EventHandler(this.btnMouse_Click); // - // picDesktop - // - this.picDesktop.BackColor = System.Drawing.Color.Black; - this.picDesktop.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.picDesktop.Cursor = System.Windows.Forms.Cursors.Default; - this.picDesktop.Dock = System.Windows.Forms.DockStyle.Fill; - this.picDesktop.GetImageSafe = null; - this.picDesktop.Location = new System.Drawing.Point(0, 0); - this.picDesktop.Name = "picDesktop"; - this.picDesktop.Running = false; - this.picDesktop.Size = new System.Drawing.Size(784, 562); - this.picDesktop.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; - this.picDesktop.TabIndex = 0; - this.picDesktop.TabStop = false; - this.picDesktop.MouseDown += new System.Windows.Forms.MouseEventHandler(this.picDesktop_MouseDown); - this.picDesktop.MouseMove += new System.Windows.Forms.MouseEventHandler(this.picDesktop_MouseMove); - this.picDesktop.MouseUp += new System.Windows.Forms.MouseEventHandler(this.picDesktop_MouseUp); - // // panelTop // this.panelTop.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.panelTop.Controls.Add(this.btnKeyboard); this.panelTop.Controls.Add(this.cbMonitors); this.panelTop.Controls.Add(this.btnHide); this.panelTop.Controls.Add(this.lblQualityShow); @@ -169,6 +153,33 @@ private void InitializeComponent() this.btnShow.Visible = false; this.btnShow.Click += new System.EventHandler(this.btnShow_Click); // + // picDesktop + // + this.picDesktop.BackColor = System.Drawing.Color.Black; + this.picDesktop.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.picDesktop.Cursor = System.Windows.Forms.Cursors.Default; + this.picDesktop.Dock = System.Windows.Forms.DockStyle.Fill; + this.picDesktop.GetImageSafe = null; + this.picDesktop.Location = new System.Drawing.Point(0, 0); + this.picDesktop.Name = "picDesktop"; + this.picDesktop.Running = false; + this.picDesktop.Size = new System.Drawing.Size(784, 562); + this.picDesktop.SizeMode = System.Windows.Forms.PictureBoxSizeMode.StretchImage; + this.picDesktop.TabIndex = 0; + this.picDesktop.TabStop = false; + this.picDesktop.MouseDown += new System.Windows.Forms.MouseEventHandler(this.picDesktop_MouseDown); + this.picDesktop.MouseMove += new System.Windows.Forms.MouseEventHandler(this.picDesktop_MouseMove); + this.picDesktop.MouseUp += new System.Windows.Forms.MouseEventHandler(this.picDesktop_MouseUp); + // + // btnKeyboard + // + this.btnKeyboard.Location = new System.Drawing.Point(346, 11); + this.btnKeyboard.Name = "btnKeyboard"; + this.btnKeyboard.Size = new System.Drawing.Size(28, 28); + this.btnKeyboard.TabIndex = 9; + this.btnKeyboard.UseVisualStyleBackColor = true; + this.btnKeyboard.Click += new System.EventHandler(this.btnKeyboard_Click); + // // FrmRemoteDesktop // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -188,9 +199,9 @@ private void InitializeComponent() this.Load += new System.EventHandler(this.FrmRemoteDesktop_Load); this.Resize += new System.EventHandler(this.FrmRemoteDesktop_Resize); ((System.ComponentModel.ISupportInitialize)(this.barQuality)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.picDesktop)).EndInit(); this.panelTop.ResumeLayout(false); this.panelTop.PerformLayout(); + ((System.ComponentModel.ISupportInitialize)(this.picDesktop)).EndInit(); this.ResumeLayout(false); } @@ -208,5 +219,6 @@ private void InitializeComponent() private System.Windows.Forms.Button btnShow; private xServer.Controls.RapidPictureBox picDesktop; private System.Windows.Forms.ComboBox cbMonitors; + private System.Windows.Forms.Button btnKeyboard; } } \ No newline at end of file diff --git a/Server/Forms/FrmRemoteDesktop.cs b/Server/Forms/FrmRemoteDesktop.cs index dd9e334a..892a2681 100644 --- a/Server/Forms/FrmRemoteDesktop.cs +++ b/Server/Forms/FrmRemoteDesktop.cs @@ -14,7 +14,8 @@ public partial class FrmRemoteDesktop : Form public bool IsStarted { get; private set; } private readonly Client _connectClient; private bool _enableMouseInput; - private IMouseEvents _mEvents; + private bool _enableKeyboardInput; + private IKeyboardMouseEvents _mEvents; public FrmRemoteDesktop(Client c) { @@ -35,20 +36,24 @@ private void FrmRemoteDesktop_Load(object sender, EventArgs e) btnShow.Location = new Point(377, 0); btnShow.Left = (this.Width / 2) - (btnShow.Width / 2); + _enableKeyboardInput = false; + if (_connectClient.Value != null) new Core.Packets.ServerPackets.GetMonitors().Execute(_connectClient); } - private void Subscribe(IMouseEvents events) + private void Subscribe(IKeyboardMouseEvents events) { _mEvents = events; _mEvents.MouseWheel += MouseWheelEvent; + _mEvents.KeyDown += OnKeyDown; } private void Unsubscribe() { if (_mEvents == null) return; _mEvents.MouseWheel -= MouseWheelEvent; + _mEvents.KeyDown -= OnKeyDown; } public void AddMonitors(int monitors) @@ -135,6 +140,8 @@ private void btnStart_Click(object sender, EventArgs e) // Subscribe to the new frame counter. picDesktop.SetFrameUpdatedEvent(_frameCounter_FrameUpdated); + this.ActiveControl = picDesktop; + new Core.Packets.ServerPackets.GetDesktop(barQuality.Value, cbMonitors.SelectedIndex).Execute(_connectClient); } @@ -146,6 +153,8 @@ private void btnStop_Click(object sender, EventArgs e) // Unsubscribe from the frame counter. It will be re-created when starting again. picDesktop.UnsetFrameUpdatedEvent(_frameCounter_FrameUpdated); + + this.ActiveControl = picDesktop; } private void barQuality_Scroll(object sender, EventArgs e) @@ -161,6 +170,8 @@ private void barQuality_Scroll(object sender, EventArgs e) lblQualityShow.Text += " (high)"; else if (value >= 25) lblQualityShow.Text += " (mid)"; + + this.ActiveControl = picDesktop; } private void btnMouse_Click(object sender, EventArgs e) @@ -177,6 +188,14 @@ private void btnMouse_Click(object sender, EventArgs e) btnMouse.Image = Properties.Resources.mouse_add; _enableMouseInput = true; } + + this.ActiveControl = picDesktop; + } + + private void btnKeyboard_Click(object sender, EventArgs e) + { + _enableKeyboardInput = !_enableKeyboardInput; + this.ActiveControl = picDesktop; } private int GetRemoteWidth(int localX) @@ -263,11 +282,21 @@ private void MouseWheelEvent(object sender, MouseEventArgs e) } } + private void OnKeyDown(object sender, KeyEventArgs e) + { + if (picDesktop.Image != null && !btnStart.Enabled) + { + if (_connectClient != null) + new Core.Packets.ServerPackets.DoKeyboardEvent((byte)e.KeyCode).Execute(_connectClient); + } + } + private void btnHide_Click(object sender, EventArgs e) { panelTop.Visible = false; btnShow.Visible = true; btnHide.Visible = false; + this.ActiveControl = picDesktop; } private void btnShow_Click(object sender, EventArgs e) @@ -275,6 +304,7 @@ private void btnShow_Click(object sender, EventArgs e) panelTop.Visible = true; btnShow.Visible = false; btnHide.Visible = true; + this.ActiveControl = picDesktop; } } } \ No newline at end of file diff --git a/Server/Server.csproj b/Server/Server.csproj index fecc4627..9c59945b 100644 --- a/Server/Server.csproj +++ b/Server/Server.csproj @@ -117,6 +117,7 @@ + From 45742d234d43a8b99f74f93f97953947c3c5bd17 Mon Sep 17 00:00:00 2001 From: d3agle Date: Wed, 29 Jul 2015 08:38:50 -0500 Subject: [PATCH 4/8] Small fix if form isn't in focus then don't send key data --- Server/Forms/FrmRemoteDesktop.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Server/Forms/FrmRemoteDesktop.cs b/Server/Forms/FrmRemoteDesktop.cs index 892a2681..e9c96172 100644 --- a/Server/Forms/FrmRemoteDesktop.cs +++ b/Server/Forms/FrmRemoteDesktop.cs @@ -284,7 +284,7 @@ private void MouseWheelEvent(object sender, MouseEventArgs e) private void OnKeyDown(object sender, KeyEventArgs e) { - if (picDesktop.Image != null && !btnStart.Enabled) + if (picDesktop.Image != null && !btnStart.Enabled && this.ContainsFocus) { if (_connectClient != null) new Core.Packets.ServerPackets.DoKeyboardEvent((byte)e.KeyCode).Execute(_connectClient); From efc44632a7aaadaac25d23e2887718bdca96afa9 Mon Sep 17 00:00:00 2001 From: d3agle Date: Wed, 29 Jul 2015 08:42:52 -0500 Subject: [PATCH 5/8] Format fix --- Client/Core/Packets/PacketHandler.cs | 30 ++++++++++++++-------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/Client/Core/Packets/PacketHandler.cs b/Client/Core/Packets/PacketHandler.cs index 7f61ce45..e4b37ad5 100644 --- a/Client/Core/Packets/PacketHandler.cs +++ b/Client/Core/Packets/PacketHandler.cs @@ -73,64 +73,64 @@ public static void HandlePacket(Client client, IPacket packet) } else if (type == typeof(ServerPackets.DoKeyboardEvent)) { - CommandHandler.HandleDoKeyboardEvent((ServerPackets.DoKeyboardEvent) packet, client); + CommandHandler.HandleDoKeyboardEvent((ServerPackets.DoKeyboardEvent)packet, client); } else if (type == typeof(ServerPackets.GetSystemInfo)) { - CommandHandler.HandleGetSystemInfo((ServerPackets.GetSystemInfo) packet, client); + CommandHandler.HandleGetSystemInfo((ServerPackets.GetSystemInfo)packet, client); } else if (type == typeof(ServerPackets.DoVisitWebsite)) { - CommandHandler.HandleDoVisitWebsite((ServerPackets.DoVisitWebsite) packet, client); + CommandHandler.HandleDoVisitWebsite((ServerPackets.DoVisitWebsite)packet, client); } else if (type == typeof(ServerPackets.DoShowMessageBox)) { - CommandHandler.HandleDoShowMessageBox((ServerPackets.DoShowMessageBox) packet, client); + CommandHandler.HandleDoShowMessageBox((ServerPackets.DoShowMessageBox)packet, client); } else if (type == typeof(ServerPackets.DoClientUpdate)) { - CommandHandler.HandleDoClientUpdate((ServerPackets.DoClientUpdate) packet, client); + CommandHandler.HandleDoClientUpdate((ServerPackets.DoClientUpdate)packet, client); } else if (type == typeof(ServerPackets.GetMonitors)) { - CommandHandler.HandleGetMonitors((ServerPackets.GetMonitors) packet, client); + CommandHandler.HandleGetMonitors((ServerPackets.GetMonitors)packet, client); } else if (type == typeof(ServerPackets.DoShellExecute)) { - CommandHandler.HandleDoShellExecute((ServerPackets.DoShellExecute) packet, client); + CommandHandler.HandleDoShellExecute((ServerPackets.DoShellExecute)packet, client); } else if (type == typeof(ServerPackets.DoPathRename)) { - CommandHandler.HandleDoPathRename((ServerPackets.DoPathRename) packet, client); + CommandHandler.HandleDoPathRename((ServerPackets.DoPathRename)packet, client); } else if (type == typeof(ServerPackets.DoPathDelete)) { - CommandHandler.HandleDoPathDelete((ServerPackets.DoPathDelete) packet, client); + CommandHandler.HandleDoPathDelete((ServerPackets.DoPathDelete)packet, client); } else if (type == typeof(ServerPackets.DoShutdownAction)) { - CommandHandler.HandleDoShutdownAction((ServerPackets.DoShutdownAction) packet, client); + CommandHandler.HandleDoShutdownAction((ServerPackets.DoShutdownAction)packet, client); } else if (type == typeof(ServerPackets.GetStartupItems)) { - CommandHandler.HandleGetStartupItems((ServerPackets.GetStartupItems) packet, client); + CommandHandler.HandleGetStartupItems((ServerPackets.GetStartupItems)packet, client); } else if (type == typeof(ServerPackets.DoStartupItemAdd)) { - CommandHandler.HandleDoStartupItemAdd((ServerPackets.DoStartupItemAdd) packet, client); + CommandHandler.HandleDoStartupItemAdd((ServerPackets.DoStartupItemAdd)packet, client); } else if (type == typeof(ServerPackets.DoStartupItemRemove)) { - CommandHandler.HandleDoStartupItemRemove((ServerPackets.DoStartupItemRemove) packet, client); + CommandHandler.HandleDoStartupItemRemove((ServerPackets.DoStartupItemRemove)packet, client); } else if (type == typeof(ServerPackets.DoDownloadFileCancel)) { - CommandHandler.HandleDoDownloadFileCancel((ServerPackets.DoDownloadFileCancel) packet, + CommandHandler.HandleDoDownloadFileCancel((ServerPackets.DoDownloadFileCancel)packet, client); } else if (type == typeof(ServerPackets.GetKeyloggerLogs)) { - CommandHandler.HandleGetKeyloggerLogs((ServerPackets.GetKeyloggerLogs) packet, client); + CommandHandler.HandleGetKeyloggerLogs((ServerPackets.GetKeyloggerLogs)packet, client); } else if (type == typeof(ReverseProxy.Packets.ReverseProxyConnect) || type == typeof(ReverseProxy.Packets.ReverseProxyConnectResponse) || From f01b5550afb33e1d1936cbddcd1210bb6b9ddbc4 Mon Sep 17 00:00:00 2001 From: d3agle Date: Wed, 29 Jul 2015 09:21:49 -0500 Subject: [PATCH 6/8] Improved Keyboard handling --- Client/Core/Commands/SurveillanceHandler.cs | 2 +- Client/Core/Helper/NativeMethodsHelper.cs | 5 ++--- .../Packets/ServerPackets/DoKeyboardEvent.cs | 6 +++++- .../Packets/ServerPackets/DoKeyboardEvent.cs | 6 +++++- Server/Forms/FrmRemoteDesktop.cs | 21 ++++++++++++++----- 5 files changed, 29 insertions(+), 11 deletions(-) diff --git a/Client/Core/Commands/SurveillanceHandler.cs b/Client/Core/Commands/SurveillanceHandler.cs index 9acd6a4d..b11ce2cf 100644 --- a/Client/Core/Commands/SurveillanceHandler.cs +++ b/Client/Core/Commands/SurveillanceHandler.cs @@ -101,7 +101,7 @@ public static void HandleDoMouseEvent(Packets.ServerPackets.DoMouseEvent command public static void HandleDoKeyboardEvent(Packets.ServerPackets.DoKeyboardEvent command, Client client) { - NativeMethodsHelper.DoKeyPress(command.Key); + NativeMethodsHelper.DoKeyPress(command.Key, command.KeyDown); } public static void HandleGetMonitors(Packets.ServerPackets.GetMonitors command, Client client) diff --git a/Client/Core/Helper/NativeMethodsHelper.cs b/Client/Core/Helper/NativeMethodsHelper.cs index 0513a151..2ee3da2b 100644 --- a/Client/Core/Helper/NativeMethodsHelper.cs +++ b/Client/Core/Helper/NativeMethodsHelper.cs @@ -33,10 +33,9 @@ public static void DoMouseEventScroll(Point p, bool scrollDown) NativeMethods.mouse_event(MOUSEEVENTF_WHEEL, p.X, p.Y, scrollDown ? -120 : 120, 0); } - public static void DoKeyPress(byte key) + public static void DoKeyPress(byte key, bool keyDown) { - NativeMethods.keybd_event(key, 0, KEYEVENTF_KEYDOWN, 0); - NativeMethods.keybd_event(key, 0, KEYEVENTF_KEYUP, 0); + NativeMethods.keybd_event(key, 0, keyDown ? KEYEVENTF_KEYDOWN : KEYEVENTF_KEYUP, 0); } } } diff --git a/Client/Core/Packets/ServerPackets/DoKeyboardEvent.cs b/Client/Core/Packets/ServerPackets/DoKeyboardEvent.cs index 2592e62c..a1a557dc 100644 --- a/Client/Core/Packets/ServerPackets/DoKeyboardEvent.cs +++ b/Client/Core/Packets/ServerPackets/DoKeyboardEvent.cs @@ -9,13 +9,17 @@ public class DoKeyboardEvent : IPacket [ProtoMember(1)] public byte Key { get; set; } + [ProtoMember(2)] + public bool KeyDown { get; set; } + public DoKeyboardEvent() { } - public DoKeyboardEvent(byte key) + public DoKeyboardEvent(byte key, bool keyDown) { this.Key = key; + this.KeyDown = keyDown; } public void Execute(Client client) diff --git a/Server/Core/Packets/ServerPackets/DoKeyboardEvent.cs b/Server/Core/Packets/ServerPackets/DoKeyboardEvent.cs index c42fe666..e8a6ee50 100644 --- a/Server/Core/Packets/ServerPackets/DoKeyboardEvent.cs +++ b/Server/Core/Packets/ServerPackets/DoKeyboardEvent.cs @@ -9,13 +9,17 @@ public class DoKeyboardEvent : IPacket [ProtoMember(1)] public byte Key { get; set; } + [ProtoMember(2)] + public bool KeyDown { get; set; } + public DoKeyboardEvent() { } - public DoKeyboardEvent(byte key) + public DoKeyboardEvent(byte key, bool keyDown) { this.Key = key; + this.KeyDown = keyDown; } public void Execute(Client client) diff --git a/Server/Forms/FrmRemoteDesktop.cs b/Server/Forms/FrmRemoteDesktop.cs index e9c96172..f148d0cb 100644 --- a/Server/Forms/FrmRemoteDesktop.cs +++ b/Server/Forms/FrmRemoteDesktop.cs @@ -47,6 +47,7 @@ private void Subscribe(IKeyboardMouseEvents events) _mEvents = events; _mEvents.MouseWheel += MouseWheelEvent; _mEvents.KeyDown += OnKeyDown; + _mEvents.KeyUp += OnKeyUp; } private void Unsubscribe() @@ -54,6 +55,7 @@ private void Unsubscribe() if (_mEvents == null) return; _mEvents.MouseWheel -= MouseWheelEvent; _mEvents.KeyDown -= OnKeyDown; + _mEvents.KeyUp -= OnKeyUp; } public void AddMonitors(int monitors) @@ -210,7 +212,7 @@ private int GetRemoteHeight(int localY) private void picDesktop_MouseDown(object sender, MouseEventArgs e) { - if (picDesktop.Image != null && _enableMouseInput && !btnStart.Enabled) + if (picDesktop.Image != null && _enableMouseInput && !btnStart.Enabled && this.ContainsFocus) { int local_x = e.X; int local_y = e.Y; @@ -234,7 +236,7 @@ private void picDesktop_MouseDown(object sender, MouseEventArgs e) private void picDesktop_MouseUp(object sender, MouseEventArgs e) { - if (picDesktop.Image != null && _enableMouseInput && !btnStart.Enabled) + if (picDesktop.Image != null && _enableMouseInput && !btnStart.Enabled && this.ContainsFocus) { int local_x = e.X; int local_y = e.Y; @@ -258,7 +260,7 @@ private void picDesktop_MouseUp(object sender, MouseEventArgs e) private void picDesktop_MouseMove(object sender, MouseEventArgs e) { - if (picDesktop.Image != null && _enableMouseInput && !btnStart.Enabled) + if (picDesktop.Image != null && _enableMouseInput && !btnStart.Enabled && this.ContainsFocus) { int local_x = e.X; int local_y = e.Y; @@ -275,7 +277,7 @@ private void picDesktop_MouseMove(object sender, MouseEventArgs e) private void MouseWheelEvent(object sender, MouseEventArgs e) { - if (picDesktop.Image != null && _enableMouseInput && !btnStart.Enabled) + if (picDesktop.Image != null && _enableMouseInput && !btnStart.Enabled && this.ContainsFocus) { if (_connectClient != null) new Core.Packets.ServerPackets.DoMouseEvent(e.Delta == 120 ? MouseAction.ScrollUp : MouseAction.ScrollDown, false, 0, 0, cbMonitors.SelectedIndex).Execute(_connectClient); @@ -287,7 +289,16 @@ private void OnKeyDown(object sender, KeyEventArgs e) if (picDesktop.Image != null && !btnStart.Enabled && this.ContainsFocus) { if (_connectClient != null) - new Core.Packets.ServerPackets.DoKeyboardEvent((byte)e.KeyCode).Execute(_connectClient); + new Core.Packets.ServerPackets.DoKeyboardEvent((byte)e.KeyCode, true).Execute(_connectClient); + } + } + + private void OnKeyUp(object sender, KeyEventArgs e) + { + if (picDesktop.Image != null && !btnStart.Enabled && this.ContainsFocus) + { + if (_connectClient != null) + new Core.Packets.ServerPackets.DoKeyboardEvent((byte)e.KeyCode, false).Execute(_connectClient); } } From 9416b22f4690e197a71d8be840db6e402b962172 Mon Sep 17 00:00:00 2001 From: d3agle Date: Wed, 29 Jul 2015 12:17:19 -0500 Subject: [PATCH 7/8] Small fixes and prep for hotkeys --- Server/Core/Utilities/NativeMethods.cs | 6 ++++ Server/Forms/FrmRemoteDesktop.Designer.cs | 38 ++++++++++++++++------- Server/Forms/FrmRemoteDesktop.cs | 24 ++++++++++---- 3 files changed, 50 insertions(+), 18 deletions(-) diff --git a/Server/Core/Utilities/NativeMethods.cs b/Server/Core/Utilities/NativeMethods.cs index 98c26e4a..083789cb 100644 --- a/Server/Core/Utilities/NativeMethods.cs +++ b/Server/Core/Utilities/NativeMethods.cs @@ -11,6 +11,12 @@ public static class NativeMethods [DllImport("user32.dll")] public static extern IntPtr SendMessage(IntPtr hWnd, uint msg, int wParam, int lParam); + [DllImport("user32.dll")] + public static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, int vk); + + [DllImport("user32.dll")] + public static extern bool UnregisterHotKey(IntPtr hWnd, int id); + [DllImport("uxtheme.dll", CharSet = CharSet.Unicode)] public extern static int SetWindowTheme(IntPtr hWnd, string pszSubAppName, string pszSubIdList); diff --git a/Server/Forms/FrmRemoteDesktop.Designer.cs b/Server/Forms/FrmRemoteDesktop.Designer.cs index 0fe9634d..bc2c33cb 100644 --- a/Server/Forms/FrmRemoteDesktop.Designer.cs +++ b/Server/Forms/FrmRemoteDesktop.Designer.cs @@ -36,11 +36,12 @@ private void InitializeComponent() this.lblQualityShow = new System.Windows.Forms.Label(); this.btnMouse = new System.Windows.Forms.Button(); this.panelTop = new System.Windows.Forms.Panel(); + this.btnKeyboard = new System.Windows.Forms.Button(); this.cbMonitors = new System.Windows.Forms.ComboBox(); this.btnHide = new System.Windows.Forms.Button(); this.btnShow = new System.Windows.Forms.Button(); this.picDesktop = new xServer.Controls.RapidPictureBox(); - this.btnKeyboard = new System.Windows.Forms.Button(); + this.chkForwardCommandKeys = new System.Windows.Forms.CheckBox(); ((System.ComponentModel.ISupportInitialize)(this.barQuality)).BeginInit(); this.panelTop.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.picDesktop)).BeginInit(); @@ -69,7 +70,7 @@ private void InitializeComponent() // // barQuality // - this.barQuality.Location = new System.Drawing.Point(230, 3); + this.barQuality.Location = new System.Drawing.Point(206, -1); this.barQuality.Maximum = 100; this.barQuality.Minimum = 1; this.barQuality.Name = "barQuality"; @@ -81,7 +82,7 @@ private void InitializeComponent() // lblQuality // this.lblQuality.AutoSize = true; - this.lblQuality.Location = new System.Drawing.Point(188, 7); + this.lblQuality.Location = new System.Drawing.Point(167, 5); this.lblQuality.Name = "lblQuality"; this.lblQuality.Size = new System.Drawing.Size(46, 13); this.lblQuality.TabIndex = 4; @@ -90,7 +91,7 @@ private void InitializeComponent() // lblQualityShow // this.lblQualityShow.AutoSize = true; - this.lblQualityShow.Location = new System.Drawing.Point(242, 31); + this.lblQualityShow.Location = new System.Drawing.Point(220, 26); this.lblQualityShow.Name = "lblQualityShow"; this.lblQualityShow.Size = new System.Drawing.Size(52, 13); this.lblQualityShow.TabIndex = 5; @@ -99,7 +100,7 @@ private void InitializeComponent() // btnMouse // this.btnMouse.Image = global::xServer.Properties.Resources.mouse_delete; - this.btnMouse.Location = new System.Drawing.Point(312, 11); + this.btnMouse.Location = new System.Drawing.Point(302, 5); this.btnMouse.Name = "btnMouse"; this.btnMouse.Size = new System.Drawing.Size(28, 28); this.btnMouse.TabIndex = 6; @@ -109,6 +110,7 @@ private void InitializeComponent() // panelTop // this.panelTop.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.panelTop.Controls.Add(this.chkForwardCommandKeys); this.panelTop.Controls.Add(this.btnKeyboard); this.panelTop.Controls.Add(this.cbMonitors); this.panelTop.Controls.Add(this.btnHide); @@ -123,6 +125,15 @@ private void InitializeComponent() this.panelTop.Size = new System.Drawing.Size(384, 57); this.panelTop.TabIndex = 7; // + // btnKeyboard + // + this.btnKeyboard.Location = new System.Drawing.Point(336, 5); + this.btnKeyboard.Name = "btnKeyboard"; + this.btnKeyboard.Size = new System.Drawing.Size(28, 28); + this.btnKeyboard.TabIndex = 9; + this.btnKeyboard.UseVisualStyleBackColor = true; + this.btnKeyboard.Click += new System.EventHandler(this.btnKeyboard_Click); + // // cbMonitors // this.cbMonitors.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; @@ -171,14 +182,16 @@ private void InitializeComponent() this.picDesktop.MouseMove += new System.Windows.Forms.MouseEventHandler(this.picDesktop_MouseMove); this.picDesktop.MouseUp += new System.Windows.Forms.MouseEventHandler(this.picDesktop_MouseUp); // - // btnKeyboard + // chkForwardCommandKeys // - this.btnKeyboard.Location = new System.Drawing.Point(346, 11); - this.btnKeyboard.Name = "btnKeyboard"; - this.btnKeyboard.Size = new System.Drawing.Size(28, 28); - this.btnKeyboard.TabIndex = 9; - this.btnKeyboard.UseVisualStyleBackColor = true; - this.btnKeyboard.Click += new System.EventHandler(this.btnKeyboard_Click); + this.chkForwardCommandKeys.AutoSize = true; + this.chkForwardCommandKeys.Location = new System.Drawing.Point(234, 39); + this.chkForwardCommandKeys.Name = "chkForwardCommandKeys"; + this.chkForwardCommandKeys.Size = new System.Drawing.Size(149, 17); + this.chkForwardCommandKeys.TabIndex = 10; + this.chkForwardCommandKeys.Text = "Forward Command Keys"; + this.chkForwardCommandKeys.UseVisualStyleBackColor = true; + this.chkForwardCommandKeys.CheckedChanged += new System.EventHandler(this.chkForwardCommandKeys_CheckedChanged); // // FrmRemoteDesktop // @@ -220,5 +233,6 @@ private void InitializeComponent() private xServer.Controls.RapidPictureBox picDesktop; private System.Windows.Forms.ComboBox cbMonitors; private System.Windows.Forms.Button btnKeyboard; + private System.Windows.Forms.CheckBox chkForwardCommandKeys; } } \ No newline at end of file diff --git a/Server/Forms/FrmRemoteDesktop.cs b/Server/Forms/FrmRemoteDesktop.cs index f148d0cb..7f563bc9 100644 --- a/Server/Forms/FrmRemoteDesktop.cs +++ b/Server/Forms/FrmRemoteDesktop.cs @@ -212,7 +212,7 @@ private int GetRemoteHeight(int localY) private void picDesktop_MouseDown(object sender, MouseEventArgs e) { - if (picDesktop.Image != null && _enableMouseInput && !btnStart.Enabled && this.ContainsFocus) + if (picDesktop.Image != null && _enableMouseInput && IsStarted && this.ContainsFocus) { int local_x = e.X; int local_y = e.Y; @@ -236,7 +236,7 @@ private void picDesktop_MouseDown(object sender, MouseEventArgs e) private void picDesktop_MouseUp(object sender, MouseEventArgs e) { - if (picDesktop.Image != null && _enableMouseInput && !btnStart.Enabled && this.ContainsFocus) + if (picDesktop.Image != null && _enableMouseInput && IsStarted && this.ContainsFocus) { int local_x = e.X; int local_y = e.Y; @@ -260,7 +260,7 @@ private void picDesktop_MouseUp(object sender, MouseEventArgs e) private void picDesktop_MouseMove(object sender, MouseEventArgs e) { - if (picDesktop.Image != null && _enableMouseInput && !btnStart.Enabled && this.ContainsFocus) + if (picDesktop.Image != null && _enableMouseInput && IsStarted && this.ContainsFocus) { int local_x = e.X; int local_y = e.Y; @@ -277,7 +277,7 @@ private void picDesktop_MouseMove(object sender, MouseEventArgs e) private void MouseWheelEvent(object sender, MouseEventArgs e) { - if (picDesktop.Image != null && _enableMouseInput && !btnStart.Enabled && this.ContainsFocus) + if (picDesktop.Image != null && _enableMouseInput && IsStarted && this.ContainsFocus) { if (_connectClient != null) new Core.Packets.ServerPackets.DoMouseEvent(e.Delta == 120 ? MouseAction.ScrollUp : MouseAction.ScrollDown, false, 0, 0, cbMonitors.SelectedIndex).Execute(_connectClient); @@ -286,7 +286,7 @@ private void MouseWheelEvent(object sender, MouseEventArgs e) private void OnKeyDown(object sender, KeyEventArgs e) { - if (picDesktop.Image != null && !btnStart.Enabled && this.ContainsFocus) + if (picDesktop.Image != null && _enableKeyboardInput && IsStarted && this.ContainsFocus) { if (_connectClient != null) new Core.Packets.ServerPackets.DoKeyboardEvent((byte)e.KeyCode, true).Execute(_connectClient); @@ -295,7 +295,7 @@ private void OnKeyDown(object sender, KeyEventArgs e) private void OnKeyUp(object sender, KeyEventArgs e) { - if (picDesktop.Image != null && !btnStart.Enabled && this.ContainsFocus) + if (picDesktop.Image != null && _enableKeyboardInput && IsStarted && this.ContainsFocus) { if (_connectClient != null) new Core.Packets.ServerPackets.DoKeyboardEvent((byte)e.KeyCode, false).Execute(_connectClient); @@ -317,5 +317,17 @@ private void btnShow_Click(object sender, EventArgs e) btnHide.Visible = true; this.ActiveControl = picDesktop; } + + private void chkForwardCommandKeys_CheckedChanged(object sender, EventArgs e) + { + if (chkForwardCommandKeys.Checked) + { + //register hot keys for command keys such as Windows key and ALT + key combinations + } + else + { + + } + } } } \ No newline at end of file From 72c4815ad64828db7d4716be4673f3cde5089128 Mon Sep 17 00:00:00 2001 From: MaxXor Date: Wed, 29 Jul 2015 21:26:50 +0200 Subject: [PATCH 8/8] Added Icons to keyboard input button --- Client/Core/Commands/SurveillanceHandler.cs | 10 +++---- Client/Core/Helper/NativeMethodsHelper.cs | 8 +++--- Server/Forms/FrmRemoteDesktop.Designer.cs | 22 +++++---------- Server/Forms/FrmRemoteDesktop.cs | 29 +++++++++++--------- Server/Properties/Resources.Designer.cs | 20 ++++++++++++++ Server/Properties/Resources.resx | 10 +++++-- Server/images/keyboard_add.png | Bin 0 -> 683 bytes Server/images/keyboard_delete.png | Bin 0 -> 681 bytes 8 files changed, 60 insertions(+), 39 deletions(-) create mode 100644 Server/images/keyboard_add.png create mode 100644 Server/images/keyboard_delete.png diff --git a/Client/Core/Commands/SurveillanceHandler.cs b/Client/Core/Commands/SurveillanceHandler.cs index b11ce2cf..1b0169b4 100644 --- a/Client/Core/Commands/SurveillanceHandler.cs +++ b/Client/Core/Commands/SurveillanceHandler.cs @@ -81,20 +81,20 @@ public static void HandleDoMouseEvent(Packets.ServerPackets.DoMouseEvent command { case MouseAction.LeftDown: case MouseAction.LeftUp: - NativeMethodsHelper.DoMouseEventLeft(p, command.IsMouseDown); + NativeMethodsHelper.DoMouseLeftClick(p, command.IsMouseDown); break; case MouseAction.RightDown: case MouseAction.RightUp: - NativeMethodsHelper.DoMouseEventRight(p, command.IsMouseDown); + NativeMethodsHelper.DoMouseRightClick(p, command.IsMouseDown); break; case MouseAction.MoveCursor: - NativeMethodsHelper.DoMouseMoveCursor(p); + NativeMethodsHelper.DoMouseMove(p); break; case MouseAction.ScrollDown: - NativeMethodsHelper.DoMouseEventScroll(p, true); + NativeMethodsHelper.DoMouseScroll(p, true); break; case MouseAction.ScrollUp: - NativeMethodsHelper.DoMouseEventScroll(p, false); + NativeMethodsHelper.DoMouseScroll(p, false); break; } } diff --git a/Client/Core/Helper/NativeMethodsHelper.cs b/Client/Core/Helper/NativeMethodsHelper.cs index 2ee3da2b..059eff1b 100644 --- a/Client/Core/Helper/NativeMethodsHelper.cs +++ b/Client/Core/Helper/NativeMethodsHelper.cs @@ -13,22 +13,22 @@ public static class NativeMethodsHelper private const uint KEYEVENTF_KEYDOWN = 0x0000; private const uint KEYEVENTF_KEYUP = 0x0002; - public static void DoMouseEventLeft(Point p, bool isMouseDown) + public static void DoMouseLeftClick(Point p, bool isMouseDown) { NativeMethods.mouse_event(isMouseDown ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP, p.X, p.Y, 0, 0); } - public static void DoMouseEventRight(Point p, bool isMouseDown) + public static void DoMouseRightClick(Point p, bool isMouseDown) { NativeMethods.mouse_event(isMouseDown ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP, p.X, p.Y, 0, 0); } - public static void DoMouseMoveCursor(Point p) + public static void DoMouseMove(Point p) { NativeMethods.SetCursorPos(p.X, p.Y); } - public static void DoMouseEventScroll(Point p, bool scrollDown) + public static void DoMouseScroll(Point p, bool scrollDown) { NativeMethods.mouse_event(MOUSEEVENTF_WHEEL, p.X, p.Y, scrollDown ? -120 : 120, 0); } diff --git a/Server/Forms/FrmRemoteDesktop.Designer.cs b/Server/Forms/FrmRemoteDesktop.Designer.cs index bc2c33cb..5c697b6d 100644 --- a/Server/Forms/FrmRemoteDesktop.Designer.cs +++ b/Server/Forms/FrmRemoteDesktop.Designer.cs @@ -28,6 +28,7 @@ protected override void Dispose(bool disposing) /// private void InitializeComponent() { + this.components = new System.ComponentModel.Container(); System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(FrmRemoteDesktop)); this.btnStart = new System.Windows.Forms.Button(); this.btnStop = new System.Windows.Forms.Button(); @@ -40,8 +41,8 @@ private void InitializeComponent() this.cbMonitors = new System.Windows.Forms.ComboBox(); this.btnHide = new System.Windows.Forms.Button(); this.btnShow = new System.Windows.Forms.Button(); + this.toolTipButtons = new System.Windows.Forms.ToolTip(this.components); this.picDesktop = new xServer.Controls.RapidPictureBox(); - this.chkForwardCommandKeys = new System.Windows.Forms.CheckBox(); ((System.ComponentModel.ISupportInitialize)(this.barQuality)).BeginInit(); this.panelTop.SuspendLayout(); ((System.ComponentModel.ISupportInitialize)(this.picDesktop)).BeginInit(); @@ -84,7 +85,7 @@ private void InitializeComponent() this.lblQuality.AutoSize = true; this.lblQuality.Location = new System.Drawing.Point(167, 5); this.lblQuality.Name = "lblQuality"; - this.lblQuality.Size = new System.Drawing.Size(46, 13); + this.lblQuality.Size = new System.Drawing.Size(47, 13); this.lblQuality.TabIndex = 4; this.lblQuality.Text = "Quality:"; // @@ -104,13 +105,13 @@ private void InitializeComponent() this.btnMouse.Name = "btnMouse"; this.btnMouse.Size = new System.Drawing.Size(28, 28); this.btnMouse.TabIndex = 6; + this.toolTipButtons.SetToolTip(this.btnMouse, "Enable mouse input."); this.btnMouse.UseVisualStyleBackColor = true; this.btnMouse.Click += new System.EventHandler(this.btnMouse_Click); // // panelTop // this.panelTop.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.panelTop.Controls.Add(this.chkForwardCommandKeys); this.panelTop.Controls.Add(this.btnKeyboard); this.panelTop.Controls.Add(this.cbMonitors); this.panelTop.Controls.Add(this.btnHide); @@ -127,10 +128,12 @@ private void InitializeComponent() // // btnKeyboard // + this.btnKeyboard.Image = global::xServer.Properties.Resources.keyboard_delete; this.btnKeyboard.Location = new System.Drawing.Point(336, 5); this.btnKeyboard.Name = "btnKeyboard"; this.btnKeyboard.Size = new System.Drawing.Size(28, 28); this.btnKeyboard.TabIndex = 9; + this.toolTipButtons.SetToolTip(this.btnKeyboard, "Enable keyboard input."); this.btnKeyboard.UseVisualStyleBackColor = true; this.btnKeyboard.Click += new System.EventHandler(this.btnKeyboard_Click); // @@ -182,17 +185,6 @@ private void InitializeComponent() this.picDesktop.MouseMove += new System.Windows.Forms.MouseEventHandler(this.picDesktop_MouseMove); this.picDesktop.MouseUp += new System.Windows.Forms.MouseEventHandler(this.picDesktop_MouseUp); // - // chkForwardCommandKeys - // - this.chkForwardCommandKeys.AutoSize = true; - this.chkForwardCommandKeys.Location = new System.Drawing.Point(234, 39); - this.chkForwardCommandKeys.Name = "chkForwardCommandKeys"; - this.chkForwardCommandKeys.Size = new System.Drawing.Size(149, 17); - this.chkForwardCommandKeys.TabIndex = 10; - this.chkForwardCommandKeys.Text = "Forward Command Keys"; - this.chkForwardCommandKeys.UseVisualStyleBackColor = true; - this.chkForwardCommandKeys.CheckedChanged += new System.EventHandler(this.chkForwardCommandKeys_CheckedChanged); - // // FrmRemoteDesktop // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -233,6 +225,6 @@ private void InitializeComponent() private xServer.Controls.RapidPictureBox picDesktop; private System.Windows.Forms.ComboBox cbMonitors; private System.Windows.Forms.Button btnKeyboard; - private System.Windows.Forms.CheckBox chkForwardCommandKeys; + private System.Windows.Forms.ToolTip toolTipButtons; } } \ No newline at end of file diff --git a/Server/Forms/FrmRemoteDesktop.cs b/Server/Forms/FrmRemoteDesktop.cs index 7f563bc9..e399ec2e 100644 --- a/Server/Forms/FrmRemoteDesktop.cs +++ b/Server/Forms/FrmRemoteDesktop.cs @@ -9,6 +9,7 @@ namespace xServer.Forms { + //TODO: Register Hotkeys for WIN - and ALT-key combinations public partial class FrmRemoteDesktop : Form { public bool IsStarted { get; private set; } @@ -182,12 +183,14 @@ private void btnMouse_Click(object sender, EventArgs e) { this.picDesktop.Cursor = Cursors.Default; btnMouse.Image = Properties.Resources.mouse_delete; + toolTipButtons.SetToolTip(btnMouse, "Enable mouse input."); _enableMouseInput = false; } else { this.picDesktop.Cursor = Cursors.Hand; btnMouse.Image = Properties.Resources.mouse_add; + toolTipButtons.SetToolTip(btnMouse, "Disable mouse input."); _enableMouseInput = true; } @@ -196,7 +199,19 @@ private void btnMouse_Click(object sender, EventArgs e) private void btnKeyboard_Click(object sender, EventArgs e) { - _enableKeyboardInput = !_enableKeyboardInput; + if (_enableKeyboardInput) + { + btnKeyboard.Image = Properties.Resources.keyboard_delete; + toolTipButtons.SetToolTip(btnMouse, "Enable keyboard input."); + _enableKeyboardInput = false; + } + else + { + btnKeyboard.Image = Properties.Resources.keyboard_add; + toolTipButtons.SetToolTip(btnMouse, "Disable keyboard input."); + _enableKeyboardInput = true; + } + this.ActiveControl = picDesktop; } @@ -317,17 +332,5 @@ private void btnShow_Click(object sender, EventArgs e) btnHide.Visible = true; this.ActiveControl = picDesktop; } - - private void chkForwardCommandKeys_CheckedChanged(object sender, EventArgs e) - { - if (chkForwardCommandKeys.Checked) - { - //register hot keys for command keys such as Windows key and ALT + key combinations - } - else - { - - } - } } } \ No newline at end of file diff --git a/Server/Properties/Resources.Designer.cs b/Server/Properties/Resources.Designer.cs index cfda1634..b2f96217 100644 --- a/Server/Properties/Resources.Designer.cs +++ b/Server/Properties/Resources.Designer.cs @@ -200,6 +200,26 @@ internal class Resources { } } + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap keyboard_add { + get { + object obj = ResourceManager.GetObject("keyboard_add", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap keyboard_delete { + get { + object obj = ResourceManager.GetObject("keyboard_delete", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + /// /// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap. /// diff --git a/Server/Properties/Resources.resx b/Server/Properties/Resources.resx index 4e0d8d25..edd938b0 100644 --- a/Server/Properties/Resources.resx +++ b/Server/Properties/Resources.resx @@ -239,12 +239,18 @@ ..\images\application_delete.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\images\keyboard_add.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\images\logger.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a ..\images\run.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\images\registry.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + ..\images\server-disconnect.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a @@ -255,7 +261,7 @@ ..\images\information.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a - - ..\images\registry.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + ..\images\keyboard_delete.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a \ No newline at end of file diff --git a/Server/images/keyboard_add.png b/Server/images/keyboard_add.png new file mode 100644 index 0000000000000000000000000000000000000000..26938dd0c39f1c46922ab4b2d98e87561f56fa01 GIT binary patch literal 683 zcmV;c0#yBpP)2+#+2=|>P`kH>R=%^74e85>0% zCZVTmDM1* zL|%?6GSG;lzeZ4D_o1`x5S%rw@JnvhdJjZrYXc0qp(REaiZ;_ zDR`~t0bYI^q_Y>W7FCuAWR1H2=3Dar-hpI#5!1>9zAEF8dmo|y%>`6eY=9)$3~4p5 z(RI~v)46@q4fp;dXsz!+=%)`8Q{#xtrEW{BeaAkB{gK_7U1v6$fW5SI;PJkh-Fe^l&6^QS)5K1O4Lcm- zzaW>(P3yY;fT6{Ptg325QIsk}T9)Ni#v6?D3~ytx7}+Giet5_>f*}@(M4oKBf@ZVn zU|HWVp{LtvL0Bk|pNt2kd;&}W?xT~S)Bv$Sp%V(CQ)rBJozIEIT01Ur0rP;^=qPem zxG@HYVOn#_n8$pL8io}!eJo_xx-N|PTqvzd81?s|SZN>}=tH4g$Jme?`O*f)NA@FI zT*v7nUTck4sA&X3Vo75aG*PNbmNNgqGZLPq(~8;Q@sI~64tp^FX$A4cDr|(bOPV-7 zfD0#mtXBh<&j!%aD6U3^plSxLpAVt$^DK@%zlQqlL8PWapovG;c?hpFlIe3Q_`XsH zAv|+8E3ZIj?8!XC=z1LCb62oycnr$o9KIyuc;9mo!i#j>^gC6n$JzM;q7#QUS@q#Y z7^k8)vHROQU^xlwbE75(kxVW@)N}&kF!1qf4Z(gFQkjZ%!iHhAnYrL77vbz51}@#{ zUZ9x)xSGEqwlq@9N~ZTef7|ijJ8)*N7jpU)dSBi`OZf$v2{h_*Rr8*()#sdw9*jwA