VirtualMouse: Allow mouse actions using global keybindings (WIP)

This commit is contained in:
2022-04-10 03:39:51 +02:00
parent 807fa0ee54
commit 6575eb3139
7 changed files with 456 additions and 2 deletions

View File

@@ -0,0 +1,121 @@
using System;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Windows.Forms;
using VAR.Toolbox.Code.Windows;
namespace VAR.Toolbox.Code
{
public class GlobalKeyboardHook
{
#region Declarations
private bool _capturing = false;
private bool _captureAll = false;
/// <summary>
/// The collections of keys to watch for
/// </summary>
private readonly List<Keys> _hookedKeys = new List<Keys>();
/// <summary>
/// Handle to the hook, need this to unhook and call the next hook
/// </summary>
private IntPtr _hHook = IntPtr.Zero;
#endregion Declarations
#region Private methods
/// <summary>
/// The callback for the keyboard hook
/// </summary>
/// <param name="code">The hook code, if it isn't >= 0, the function shouldn't do anyting</param>
/// <param name="wParam">The event type</param>
/// <param name="lParam">The keyhook event information</param>
/// <returns></returns>
private int HookProc(int code, int wParam, ref User32.keyboardHookStruct lParam) {
try
{
if (code >= 0)
{
Keys key = (Keys)lParam.vkCode;
if (_hookedKeys.Contains(key) || _captureAll)
{
KeyEventArgs kea = new KeyEventArgs(key);
if ((wParam == User32.WM_KEYDOWN || wParam == User32.WM_SYSKEYDOWN) && (KeyDown != null))
{
KeyDown(this, kea);
}
else if ((wParam == User32.WM_KEYUP || wParam == User32.WM_SYSKEYUP) && (KeyUp != null))
{
KeyUp(this, kea);
}
if (kea.Handled)
return 1;
}
}
}
catch (Exception)
{
// ignored
}
return User32.CallNextHookEx(_hHook, code, wParam, ref lParam);
}
#endregion Private methods
#region Public events
/// <summary>
/// Occurs when one of the hooked keys is pressed
/// </summary>
public event KeyEventHandler KeyDown;
/// <summary>
/// Occurs when one of the hooked keys is released
/// </summary>
public event KeyEventHandler KeyUp;
#endregion Public events
#region Public methods
public void Start(bool all = false)
{
if (_capturing) { return; }
_captureAll = all;
//IntPtr hInstance = User32.LoadLibrary("User32");
//_hHook = User32.SetWindowsHookEx(User32.WH_KEYBOARD_LL, HookProc, hInstance, 0);
IntPtr iModule = System.Runtime.InteropServices.Marshal.GetHINSTANCE(
System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0]);
_hHook = User32.SetWindowsHookEx(User32.WH_KEYBOARD_LL, HookProc, iModule, 0);
_capturing = true;
}
public void Stop()
{
if (_capturing == false) { return; }
User32.UnhookWindowsHookEx(_hHook);
_capturing = false;
}
public bool IsCapturing()
{
return _capturing;
}
public void AddHook(Keys key)
{
_hookedKeys.Add(key);
}
#endregion Public methods
}
}

View File

@@ -15,7 +15,7 @@ namespace VAR.Toolbox.Code
input.Data.Mouse.X = dx; input.Data.Mouse.X = dx;
input.Data.Mouse.Y = dy; input.Data.Mouse.Y = dy;
input.Data.Mouse.Flags = User32.MOUSEEVENTF_MOVE; input.Data.Mouse.Flags = User32.MOUSEEVENTF_MOVE;
User32.INPUT[] inputs = new User32.INPUT[] { input }; User32.INPUT[] inputs = new[] { input };
if (User32.SendInput(1, inputs, Marshal.SizeOf(typeof(User32.INPUT))) == 0) if (User32.SendInput(1, inputs, Marshal.SizeOf(typeof(User32.INPUT))) == 0)
throw new Exception(); throw new Exception();
} }
@@ -50,7 +50,7 @@ namespace VAR.Toolbox.Code
input.Data.Mouse.Flags = down ? User32.MOUSEEVENTF_RIGHTDOWN : User32.MOUSEEVENTF_RIGHTUP; input.Data.Mouse.Flags = down ? User32.MOUSEEVENTF_RIGHTDOWN : User32.MOUSEEVENTF_RIGHTUP;
} }
User32.INPUT[] inputs = new User32.INPUT[] { input }; User32.INPUT[] inputs = new[] { input };
if (User32.SendInput(1, inputs, Marshal.SizeOf(typeof(User32.INPUT))) == 0) if (User32.SendInput(1, inputs, Marshal.SizeOf(typeof(User32.INPUT))) == 0)
throw new Exception(); throw new Exception();
} }

View File

@@ -203,5 +203,63 @@ namespace VAR.Toolbox.Code.Windows
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy, public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int x, int y, int cx, int cy,
uint uFlags); uint uFlags);
/// <summary>
/// defines the callback type for the hook
/// </summary>
public delegate int keyboardHookProc(int code, int wParam, ref keyboardHookStruct lParam);
public struct keyboardHookStruct {
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
public const int WH_KEYBOARD_LL = 13;
public const int WM_KEYDOWN = 0x100;
public const int WM_KEYUP = 0x101;
public const int WM_SYSKEYDOWN = 0x104;
public const int WM_SYSKEYUP = 0x105;
/// <summary>
/// Sets the windows hook, do the desired event, one of hInstance or threadId must be non-null
/// </summary>
/// <param name="idHook">The id of the event you want to hook</param>
/// <param name="callback">The callback.</param>
/// <param name="hInstance">The handle you want to attach the event to, can be null</param>
/// <param name="threadId">The thread you want to attach the event to, can be null</param>
/// <returns>a handle to the desired hook</returns>
[DllImport("user32.dll")]
public static extern IntPtr SetWindowsHookEx(int idHook, keyboardHookProc callback, IntPtr hInstance, uint threadId);
/// <summary>
/// Unhooks the windows hook.
/// </summary>
/// <param name="hInstance">The hook handle that was returned from SetWindowsHookEx</param>
/// <returns>True if successful, false otherwise</returns>
[DllImport("user32.dll")]
public static extern bool UnhookWindowsHookEx(IntPtr hInstance);
/// <summary>
/// Calls the next hook.
/// </summary>
/// <param name="idHook">The hook id</param>
/// <param name="nCode">The hook code</param>
/// <param name="wParam">The wparam.</param>
/// <param name="lParam">The lparam.</param>
/// <returns></returns>
[DllImport("user32.dll")]
public static extern int CallNextHookEx(IntPtr idHook, int nCode, int wParam, ref keyboardHookStruct lParam);
/// <summary>
/// Loads the library.
/// </summary>
/// <param name="lpFileName">Name of the library</param>
/// <returns>A handle to the library</returns>
[DllImport("kernel32.dll")]
public static extern IntPtr LoadLibrary(string lpFileName);
} }
} }

View File

@@ -0,0 +1,81 @@
using System.ComponentModel;
namespace VAR.Toolbox.UI.Tools
{
partial class FrmVirtualMouse
{
/// <summary>
/// Required designer variable.
/// </summary>
private IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this.btnStartStop = new VAR.Toolbox.Controls.CButton();
this.lsbInputs = new VAR.Toolbox.Controls.ListBoxMonospace();
this.SuspendLayout();
//
// btnStartStop
//
this.btnStartStop.Location = new System.Drawing.Point(335, 331);
this.btnStartStop.Name = "btnStartStop";
this.btnStartStop.Size = new System.Drawing.Size(92, 58);
this.btnStartStop.TabIndex = 0;
this.btnStartStop.Text = "Start";
this.btnStartStop.UseVisualStyleBackColor = true;
this.btnStartStop.Click += new System.EventHandler(this.btnStartStop_Click);
//
// lsbInputs
//
this.lsbInputs.BackColor = System.Drawing.Color.Black;
this.lsbInputs.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this.lsbInputs.Font = new System.Drawing.Font("Consolas", 9F);
this.lsbInputs.ForeColor = System.Drawing.Color.Gray;
this.lsbInputs.FormattingEnabled = true;
this.lsbInputs.ItemHeight = 14;
this.lsbInputs.Location = new System.Drawing.Point(12, 23);
this.lsbInputs.Name = "lsbInputs";
this.lsbInputs.SelectionMode = System.Windows.Forms.SelectionMode.MultiExtended;
this.lsbInputs.Size = new System.Drawing.Size(224, 282);
this.lsbInputs.TabIndex = 1;
//
// FrmVirtualMouse
//
this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(800, 450);
this.Controls.Add(this.lsbInputs);
this.Controls.Add(this.btnStartStop);
this.Location = new System.Drawing.Point(0, 0);
this.Name = "FrmVirtualMouse";
this.Text = "FrmVirtualMouse";
this.ResumeLayout(false);
}
private VAR.Toolbox.Controls.ListBoxMonospace lsbInputs;
private VAR.Toolbox.Controls.CButton btnStartStop;
#endregion
}
}

View File

@@ -0,0 +1,59 @@
using System;
using System.Windows.Forms;
using VAR.Toolbox.Code;
using VAR.Toolbox.Controls;
namespace VAR.Toolbox.UI.Tools
{
public partial class FrmVirtualMouse : Frame, IToolForm
{
public string ToolName => "VirtualMouse";
public bool HasIcon => false;
private readonly GlobalKeyboardHook _globalKeyboard = new GlobalKeyboardHook();
public FrmVirtualMouse()
{
InitializeComponent();
PostInitializeComponent();
}
private void PostInitializeComponent()
{
_globalKeyboard.KeyDown += GlobalKeyboard_OnKeyDown;
}
private void GlobalKeyboard_OnKeyDown(object sender, KeyEventArgs keyEvent)
{
string key = keyEvent.KeyCode.ToString();
lsbInputs.Items.Add(key);
if (key == "F1")
{
Mouse.SetButton(Mouse.MouseButtons.Left, true);
}
if (key == "F2")
{
Mouse.Move(0, -10000);
}
if (key == "F3")
{
Mouse.SetButton(Mouse.MouseButtons.Left, false);
}
}
private void btnStartStop_Click(object sender, EventArgs e)
{
if (_globalKeyboard.IsCapturing())
{
_globalKeyboard.Stop();
btnStartStop.Text = "Start";
return;
}
_globalKeyboard.Start(true);
btnStartStop.Text = "Stop";
}
}
}

View File

@@ -0,0 +1,120 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -48,6 +48,9 @@
<Reference Include="System.Drawing" /> <Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" /> <Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" /> <Reference Include="System.Xml" />
<Reference Include="VAR.Toolbox">
<HintPath>bin\Debug\VAR.Toolbox.exe</HintPath>
</Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="..\VAR.HttpServer\VAR.HttpServer\HttpProcessor.cs"> <Compile Include="..\VAR.HttpServer\VAR.HttpServer\HttpProcessor.cs">
@@ -83,6 +86,7 @@
<Compile Include="Code\Configuration\IConfiguration.cs" /> <Compile Include="Code\Configuration\IConfiguration.cs" />
<Compile Include="Code\Configuration\MemoryBackedConfiguration.cs" /> <Compile Include="Code\Configuration\MemoryBackedConfiguration.cs" />
<Compile Include="Code\EventDispatcher.cs" /> <Compile Include="Code\EventDispatcher.cs" />
<Compile Include="Code\GlobalKeyboardHook.cs" />
<Compile Include="Code\HexUtils.cs" /> <Compile Include="Code\HexUtils.cs" />
<Compile Include="Code\IEventListener.cs" /> <Compile Include="Code\IEventListener.cs" />
<Compile Include="Code\ProxyCmdExecutors\ProxyCmdExecutorWMIC.cs" /> <Compile Include="Code\ProxyCmdExecutors\ProxyCmdExecutorWMIC.cs" />
@@ -245,6 +249,12 @@
</Compile> </Compile>
<Compile Include="Program.cs" /> <Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="UI\Tools\FrmVirtualMouse.cs">
<SubType>Form</SubType>
</Compile>
<Compile Include="UI\Tools\FrmVirtualMouse.Designer.cs">
<DependentUpon>FrmVirtualMouse.cs</DependentUpon>
</Compile>
<Compile Include="UI\Tools\FrmWebcam.cs"> <Compile Include="UI\Tools\FrmWebcam.cs">
<SubType>Form</SubType> <SubType>Form</SubType>
</Compile> </Compile>
@@ -313,6 +323,11 @@
<Content Include="Images\toolbox.svg" /> <Content Include="Images\toolbox.svg" />
<Content Include="Toolbox.ico" /> <Content Include="Toolbox.ico" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<EmbeddedResource Include="UI\Tools\FrmVirtualMouse.resx">
<DependentUpon>FrmVirtualMouse.cs</DependentUpon>
</EmbeddedResource>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets. Other similar extension points exist, see Microsoft.Common.targets.