From 4ca905fa6c718f42cc9c8131d73090a984907239 Mon Sep 17 00:00:00 2001
From: Allen Wolf <63997543+aaw3@users.noreply.github.com>
Date: Wed, 10 Jan 2024 15:49:49 -0600
Subject: [PATCH] Add project
---
src/App.config | 6 +
src/Form1.Designer.cs | 122 ++
src/Form1.cs | 413 ++++++
src/Form1.resx | 120 ++
src/KeyboardHook.cs | 335 +++++
src/KeyboardInputSwitch.csproj | 85 ++
src/KeyboardInputSwitch.sln | 25 +
src/KeyboardLock.cs | 1733 ++++++++++++++++++++++++++
src/Program.cs | 22 +
src/Properties/AssemblyInfo.cs | 36 +
src/Properties/Resources.Designer.cs | 71 ++
src/Properties/Resources.resx | 117 ++
src/Properties/Settings.Designer.cs | 30 +
src/Properties/Settings.settings | 7 +
14 files changed, 3122 insertions(+)
create mode 100755 src/App.config
create mode 100755 src/Form1.Designer.cs
create mode 100755 src/Form1.cs
create mode 100755 src/Form1.resx
create mode 100755 src/KeyboardHook.cs
create mode 100755 src/KeyboardInputSwitch.csproj
create mode 100755 src/KeyboardInputSwitch.sln
create mode 100755 src/KeyboardLock.cs
create mode 100755 src/Program.cs
create mode 100755 src/Properties/AssemblyInfo.cs
create mode 100755 src/Properties/Resources.Designer.cs
create mode 100755 src/Properties/Resources.resx
create mode 100755 src/Properties/Settings.Designer.cs
create mode 100755 src/Properties/Settings.settings
diff --git a/src/App.config b/src/App.config
new file mode 100755
index 0000000..5754728
--- /dev/null
+++ b/src/App.config
@@ -0,0 +1,6 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/Form1.Designer.cs b/src/Form1.Designer.cs
new file mode 100755
index 0000000..79e105a
--- /dev/null
+++ b/src/Form1.Designer.cs
@@ -0,0 +1,122 @@
+namespace KeyboardInputSwitch
+{
+ partial class Form1
+ {
+ ///
+ /// Required designer variable.
+ ///
+ private System.ComponentModel.IContainer components = null;
+
+ ///
+ /// Clean up any resources being used.
+ ///
+ /// true if managed resources should be disposed; otherwise, false.
+ protected override void Dispose(bool disposing)
+ {
+ if (disposing && (components != null))
+ {
+ components.Dispose();
+ }
+ base.Dispose(disposing);
+ }
+
+ #region Windows Form Designer generated code
+
+ ///
+ /// Required method for Designer support - do not modify
+ /// the contents of this method with the code editor.
+ ///
+ private void InitializeComponent()
+ {
+ this.btnRefresh = new System.Windows.Forms.Button();
+ this.fakePictureBox = new System.Windows.Forms.PictureBox();
+ this.image = new System.Windows.Forms.PictureBox();
+ this.lstWindows = new System.Windows.Forms.ComboBox();
+ this.btnTargetWindow = new System.Windows.Forms.Button();
+ ((System.ComponentModel.ISupportInitialize)(this.fakePictureBox)).BeginInit();
+ ((System.ComponentModel.ISupportInitialize)(this.image)).BeginInit();
+ this.SuspendLayout();
+ //
+ // btnRefresh
+ //
+ this.btnRefresh.Anchor = System.Windows.Forms.AnchorStyles.Top;
+ this.btnRefresh.Location = new System.Drawing.Point(498, 93);
+ this.btnRefresh.Name = "btnRefresh";
+ this.btnRefresh.Size = new System.Drawing.Size(75, 23);
+ this.btnRefresh.TabIndex = 11;
+ this.btnRefresh.Text = "Refresh";
+ this.btnRefresh.UseVisualStyleBackColor = true;
+ //
+ // fakePictureBox
+ //
+ this.fakePictureBox.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.fakePictureBox.BackColor = System.Drawing.SystemColors.ButtonFace;
+ this.fakePictureBox.Location = new System.Drawing.Point(228, 122);
+ this.fakePictureBox.Name = "fakePictureBox";
+ this.fakePictureBox.Size = new System.Drawing.Size(345, 205);
+ this.fakePictureBox.TabIndex = 9;
+ this.fakePictureBox.TabStop = false;
+ //
+ // image
+ //
+ this.image.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom)
+ | System.Windows.Forms.AnchorStyles.Left)
+ | System.Windows.Forms.AnchorStyles.Right)));
+ this.image.BackColor = System.Drawing.SystemColors.ButtonFace;
+ this.image.Location = new System.Drawing.Point(228, 122);
+ this.image.Name = "image";
+ this.image.Size = new System.Drawing.Size(345, 205);
+ this.image.TabIndex = 10;
+ this.image.TabStop = false;
+ //
+ // lstWindows
+ //
+ this.lstWindows.Anchor = System.Windows.Forms.AnchorStyles.Top;
+ this.lstWindows.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
+ this.lstWindows.FormattingEnabled = true;
+ this.lstWindows.Location = new System.Drawing.Point(228, 95);
+ this.lstWindows.Name = "lstWindows";
+ this.lstWindows.Size = new System.Drawing.Size(260, 21);
+ this.lstWindows.TabIndex = 8;
+ //
+ // btnTargetWindow
+ //
+ this.btnTargetWindow.Anchor = System.Windows.Forms.AnchorStyles.Top;
+ this.btnTargetWindow.Location = new System.Drawing.Point(579, 144);
+ this.btnTargetWindow.Name = "btnTargetWindow";
+ this.btnTargetWindow.Size = new System.Drawing.Size(83, 34);
+ this.btnTargetWindow.TabIndex = 12;
+ this.btnTargetWindow.Text = "Selected Window";
+ this.btnTargetWindow.UseVisualStyleBackColor = true;
+ //
+ // Form1
+ //
+ 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.btnTargetWindow);
+ this.Controls.Add(this.btnRefresh);
+ this.Controls.Add(this.fakePictureBox);
+ this.Controls.Add(this.image);
+ this.Controls.Add(this.lstWindows);
+ this.Name = "Form1";
+ this.Text = "Form1";
+ this.Load += new System.EventHandler(this.Form1_Load);
+ ((System.ComponentModel.ISupportInitialize)(this.fakePictureBox)).EndInit();
+ ((System.ComponentModel.ISupportInitialize)(this.image)).EndInit();
+ this.ResumeLayout(false);
+
+ }
+
+ #endregion
+
+ private System.Windows.Forms.Button btnRefresh;
+ private System.Windows.Forms.PictureBox fakePictureBox;
+ private System.Windows.Forms.PictureBox image;
+ private System.Windows.Forms.ComboBox lstWindows;
+ private System.Windows.Forms.Button btnTargetWindow;
+ }
+}
+
diff --git a/src/Form1.cs b/src/Form1.cs
new file mode 100755
index 0000000..cfde189
--- /dev/null
+++ b/src/Form1.cs
@@ -0,0 +1,413 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Diagnostics;
+using System.Drawing;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+using KeyboardHooker;
+
+namespace KeyboardInputSwitch
+{
+ public partial class Form1 : Form
+ {
+ //constants
+ byte opacity = 255; //0-255
+
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct PSIZE
+ {
+ public int x;
+ public int y;
+ }
+
+ [DllImport("dwmapi.dll")]
+ static extern int DwmRegisterThumbnail(IntPtr dest, IntPtr src, out IntPtr thumb);
+
+ [DllImport("dwmapi.dll")]
+ static extern int DwmUnregisterThumbnail(IntPtr thumb);
+
+ [DllImport("dwmapi.dll")]
+ static extern int DwmQueryThumbnailSourceSize(IntPtr thumb, out PSIZE size);
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct DWM_THUMBNAIL_PROPERTIES
+ {
+ public int dwFlags;
+ public Rect rcDestination;
+ public Rect rcSource;
+ public byte opacity;
+ public bool fVisible;
+ public bool fSourceClientAreaOnly;
+ }
+
+ [DllImport("dwmapi.dll")]
+ static extern int DwmUpdateThumbnailProperties(IntPtr hThumb, ref DWM_THUMBNAIL_PROPERTIES props);
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct Rect
+ {
+ internal Rect(int left, int top, int right, int bottom)
+ {
+ Left = left;
+ Top = top;
+ Right = right;
+ Bottom = bottom;
+ }
+
+ public int Left;
+ public int Top;
+ public int Right;
+ public int Bottom;
+ }
+
+ private List windows;
+
+ private void GetWindows()
+ {
+ windows = new List();
+
+ EnumWindows(Callback, 0);
+
+ lstWindows.Items.Clear();
+ foreach (Window w in windows)
+ lstWindows.Items.Add(w);
+ }
+
+ private bool Callback(IntPtr hwnd, int lParam)
+ {
+ if (this.Handle != hwnd && (GetWindowLongA(hwnd, GWL_STYLE) & TARGETWINDOW) == TARGETWINDOW)
+ {
+ StringBuilder sb = new StringBuilder(200);
+ GetWindowText(hwnd, sb, sb.Capacity);
+ Window t = new Window();
+ t.Handle = hwnd;
+ t.Title = sb.ToString();
+ windows.Add(t);
+ }
+
+ return true; //continue enumeration
+ }
+
+ delegate bool EnumWindowsCallback(IntPtr hwnd, int lParam);
+
+ [DllImport("user32.dll")]
+ static extern int EnumWindows(EnumWindowsCallback lpEnumFunc, int lParam);
+
+ [DllImport("user32.dll")]
+ public static extern void GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
+
+ [DllImport("user32.dll")]
+ static extern ulong GetWindowLongA(IntPtr hWnd, int nIndex);
+
+ static readonly int GWL_STYLE = -16;
+
+ static readonly ulong WS_VISIBLE = 0x10000000L;
+ static readonly ulong WS_BORDER = 0x00800000L;
+ static readonly ulong TARGETWINDOW = WS_BORDER | WS_VISIBLE;
+
+ internal class Window
+ {
+ public string Title;
+ public IntPtr Handle;
+
+ public override string ToString()
+ {
+ return Title;
+ }
+ }
+
+ private IntPtr thumb;
+ private void btnRefresh_Click(object sender, EventArgs e)
+ {
+ if (thumb != IntPtr.Zero)
+ DwmUnregisterThumbnail(thumb);
+
+ GetWindows();
+ }
+ private void lstWindows_SelectedIndexChanged(object sender, EventArgs e)
+ {
+ if (lstWindows.SelectedIndex > -1)
+ {
+
+ Window w = (Window)lstWindows.SelectedItem;
+ if (thumb != IntPtr.Zero)
+ DwmUnregisterThumbnail(thumb);
+
+ int i = DwmRegisterThumbnail(this.Handle, w.Handle, out thumb);
+ if (i == 0)
+ UpdateThumb();
+
+ CurrentWindow = w;
+ }
+ }
+
+ static readonly int DWM_TNP_VISIBLE = 0x8;
+ static readonly int DWM_TNP_OPACITY = 0x4;
+ static readonly int DWM_TNP_RECTDESTINATION = 0x1;
+
+ private void UpdateThumb()
+ {
+ if (thumb != IntPtr.Zero)
+ {
+ PSIZE size;
+ DwmQueryThumbnailSourceSize(thumb, out size);
+
+ DWM_THUMBNAIL_PROPERTIES props = new DWM_THUMBNAIL_PROPERTIES();
+ props.dwFlags = DWM_TNP_VISIBLE | DWM_TNP_RECTDESTINATION | DWM_TNP_OPACITY;
+
+ props.fVisible = true;
+ props.opacity = (byte)opacity; //opacity.Value;
+
+ props.rcDestination = new Rect(image.Left, image.Top, image.Right, image.Bottom);
+ if (size.x < image.Width)
+ props.rcDestination.Right = props.rcDestination.Left + size.x;
+ if (size.y < image.Height)
+ props.rcDestination.Bottom = props.rcDestination.Top + size.y;
+
+ DwmUpdateThumbnailProperties(thumb, ref props);
+ }
+ }
+
+
+ [DllImport("user32.dll")]
+ static extern bool SetForegroundWindow(IntPtr hWnd);
+
+ [DllImport("user32.dll")]
+ private static extern IntPtr GetForegroundWindow();
+
+
+ // A Win32 constant
+ const int WM_SETTEXT = 0x000C;
+ const int WM_KEYDOWN = 0x0100;
+ const int VK_RETURN = 0x0D;
+
+ // An overload of the SendMessage function, this time taking in a string as the lParam.
+ [DllImport("User32.dll")]
+ public static extern Int32 SendMessage(int hWnd, int Msg, int wParam, string lParam);
+ [DllImport("User32.Dll")]
+ public static extern Int32 PostMessage(int hWnd, int msg, int wParam, int lParam);
+
+ [DllImport("user32.dll")]
+ static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
+ const int SW_MAXIMIZE = 3;
+ const int SW_SHOW = 5;
+ const int SW_HIDE = 0;
+ const int SW_SHOWNORMAL = 1;
+ const int SW_RESTORE = 9;
+
+ [DllImport("user32.dll", EntryPoint = "SetWindowPos")]
+ public static extern IntPtr SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int Y, int cx, int cy, int wFlags);
+ const int HWND_BOTTOM = 1;
+ const int HWND_NOTOPMOST = -2;
+ const int HWND_TOP = 0;
+ const int HWND_TOPMOST = -1;
+
+ const int SWP_ASYNCWINDOWPOS = 0x4000;
+ const int SWP_DEFERERASE = 0x2000;
+ const int SWP_DRAWFRAME = 0x0020;
+ const int SWP_FRAMECHANGED = 0x0020;
+ const int SWP_HIDEWINDOW = 0x0080;
+ const int SWP_NOACTIVATE = 0x0010;
+ const int SWP_NOCOPYBITS = 0x0100;
+ const int SWP_NOMOVE = 0x0002;
+ const int SWP_NOOWNERZORDER = 0x0200;
+ const int SWP_NOREDRAW = 0x0008;
+ const int SWP_NOREPOSITION = 0x0200;
+ const int SWP_NOSENDCHANGING = 0x0400;
+ const int SWP_NOSIZE = 0x0001;
+ const int SWP_NOZORDER = 0x0004;
+ const int SWP_SHOWWINDOW = 0x0040;
+
+ [DllImport("user32.dll")]
+ static extern bool SetCursorPos(int X, int Y);
+
+ [DllImport("user32.dll")]
+ private static extern IntPtr GetWindow(IntPtr hWnd, uint uCmd);
+
+
+ KeyboardHook Hook = new KeyboardHook();
+
+ public Form1()
+ {
+ InitializeComponent();
+ }
+
+ Window CurrentWindow;
+ Window SelectedWindow;
+ private void Form1_Load(object sender, EventArgs e)
+ {
+ #region initialization
+ GetWindows();
+
+ Hook.Install();
+ this.FormClosing += (s, eargs) => {
+ Hook.Uninstall();
+ Hook.KeyDown -= Event_KeyDown;
+ btnRefresh.Click -= btnRefresh_Click;
+ lstWindows.SelectedIndexChanged -= btnRefresh_Click;
+ };
+
+ Hook.KeyDown += Event_KeyDown;
+ this.Resize += (s, eargs) => UpdateThumb();
+
+ btnRefresh.Click += btnRefresh_Click;
+ lstWindows.SelectedIndexChanged += lstWindows_SelectedIndexChanged;
+ #endregion
+
+ btnTargetWindow.Click += (s, eargs) => {
+ if (lstWindows.SelectedIndex < 0)
+ return;
+
+ SelectedWindow = CurrentWindow;
+ switchActivated = false;
+ MessageBox.Show($"Successfully set target window to:\n\"{SelectedWindow.Title}\"", "Target Window Set", MessageBoxButtons.OK, MessageBoxIcon.Information);
+ };
+ }
+
+ IntPtr previousSelectedWindow;
+
+ const KeyboardHook.VKeys OnKey = KeyboardHook.VKeys.DIVIDE;
+ const KeyboardHook.VKeys OffKey = KeyboardHook.VKeys.MULTIPLY;
+
+ bool switchActivated = false;
+ private void Event_KeyDown(KeyboardHook.VKeys key)
+ {
+ if (SelectedWindow == null)
+ return;
+
+ if (key == OnKey)
+ {
+ if (switchActivated)
+ return;
+
+ switchActivated = true;
+ previousSelectedWindow = GetForegroundWindow();
+
+
+ SetForegroundWindow(SelectedWindow.Handle);
+ SetCursorMidOfWindow(CenterScreenFromWindow(SelectedWindow.Handle));
+
+ IntPtr topmost = GetTopmostWindowOnScreen(Screen.FromHandle(SelectedWindow.Handle));
+ StringBuilder sb = new StringBuilder(200);
+ GetWindowText(topmost, sb, sb.Capacity);
+ Console.WriteLine("Topmost: " + sb.ToString());
+
+ }
+ else if (key == OffKey)
+ {
+ if (!switchActivated)
+ return;
+
+ switchActivated = false;
+ SetForegroundWindow(previousSelectedWindow);
+
+ SetCursorMidOfWindow(CenterScreenFromWindow(previousSelectedWindow));
+
+ }
+ }
+
+
+ public class WindowZOrder
+ {
+ public IntPtr hWnd { get; set; }
+ public int zOrder { get; set; }
+ public WindowZOrder(IntPtr hWnd, int zOrder)
+ {
+ this.hWnd = hWnd;
+ this.zOrder = zOrder;
+ }
+ }
+
+ public IntPtr GetTopmostWindowOnScreen(Screen scrn)
+ {
+ List windowsOnScreen = GetWindowsOnScreen(scrn);
+
+
+ List zOrderList = new List();
+
+ for (int i = 0; i < windowsOnScreen.Count; i++)
+ {
+ int zOrder = 0;
+ if (GetWindowZOrder(windowsOnScreen[i], out zOrder))
+ {
+ zOrderList.Add(new WindowZOrder(windowsOnScreen[i], zOrder));
+ }
+ }
+
+ zOrderList.Sort((x, y) => y.zOrder.CompareTo(x.zOrder)); //x Compared to y for LOW to HIGH or y Compared to x for HIGH to LOW
+
+ Console.WriteLine(zOrderList[0].zOrder);
+
+ return zOrderList[0].hWnd;
+ }
+
+ List callbackHandles = new List();
+ public List GetWindowsOnScreen(Screen scrn)
+ {
+ callbackHandles.Clear();
+ EnumWindows(WindowsScreenCallback, 0);
+
+ List windowsOnScreen = new List();//
+
+ for (int i = 0; i < callbackHandles.Count; i++)
+ {
+ if (scrn.Equals(Screen.FromHandle(callbackHandles[i])))
+ {
+ windowsOnScreen.Add(callbackHandles[i]);
+ }
+ }
+
+ return windowsOnScreen;
+ }
+
+ public bool GetWindowZOrder(IntPtr hwnd, out int zOrder)
+ {
+ const uint GW_HWNDPREV = 3;
+ const uint GW_HWNDLAST = 1;
+
+ var lowestHwnd = GetWindow(hwnd, GW_HWNDLAST);
+
+ var z = 0;
+ var hwndTmp = lowestHwnd;
+ while (hwndTmp != IntPtr.Zero)
+ {
+ if (hwnd == hwndTmp)
+ {
+ zOrder = z;
+ return true;
+ }
+
+ hwndTmp = GetWindow(hwndTmp, GW_HWNDPREV);
+ z++;
+ }
+
+ zOrder = int.MinValue;
+ return false;
+ }
+
+ public bool WindowsScreenCallback(IntPtr hwnd, int lParam)
+ {
+ if (this.Handle != hwnd && (GetWindowLongA(hwnd, GWL_STYLE) & TARGETWINDOW) == TARGETWINDOW)
+ callbackHandles.Add(hwnd);
+ return true;
+ }
+
+ public Rectangle CenterScreenFromWindow(IntPtr hWnd)
+ {
+ Screen screen = Screen.FromHandle(hWnd);
+ return new Rectangle(new Point(screen.Bounds.X + (screen.Bounds.Width / 2), screen.Bounds.Y + (screen.Bounds.Height / 2)), new Size(0, 0));
+ }
+
+ public void SetCursorMidOfWindow(Rectangle rect)
+ {
+ SetCursorPos(rect.X, rect.Y);
+ }
+ }
+}
diff --git a/src/Form1.resx b/src/Form1.resx
new file mode 100755
index 0000000..29dcb1b
--- /dev/null
+++ b/src/Form1.resx
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/src/KeyboardHook.cs b/src/KeyboardHook.cs
new file mode 100755
index 0000000..0717966
--- /dev/null
+++ b/src/KeyboardHook.cs
@@ -0,0 +1,335 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Data;
+using System.Drawing;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+using System.Runtime.InteropServices;
+using System.Diagnostics;
+using System.Windows.Input;
+using System.Security.Permissions;
+
+namespace KeyboardHooker
+{
+
+ ///
+ /// Class for intercepting low level keyboard hooks
+ ///
+ public class KeyboardHook
+ {
+
+ ///
+ /// Virtual Keys
+ ///
+ public enum VKeys
+ {
+ // Losely based on http://www.pinvoke.net/default.aspx/Enums/VK.html
+ //NULL = 0x00, //I ADDED THIS ONE MYSELF <-----------------------------------
+ LBUTTON = 0x01, // Left mouse button
+ RBUTTON = 0x02, // Right mouse button
+ CANCEL = 0x03, // Control-break processing
+ MBUTTON = 0x04, // Middle mouse button (three-button mouse)
+ XBUTTON1 = 0x05, // Windows 2000/XP: X1 mouse button
+ XBUTTON2 = 0x06, // Windows 2000/XP: X2 mouse button
+ // 0x07 // Undefined
+ BACK = 0x08, // BACKSPACE key
+ TAB = 0x09, // TAB key
+ // 0x0A-0x0B, // Reserved
+ CLEAR = 0x0C, // CLEAR key
+ RETURN = 0x0D, // ENTER key
+ // 0x0E-0x0F, // Undefined
+ SHIFT = 0x10, // SHIFT key
+ CONTROL = 0x11, // CTRL key
+ MENU = 0x12, // ALT key
+ PAUSE = 0x13, // PAUSE key
+ CAPITAL = 0x14, // CAPS LOCK key
+ KANA = 0x15, // Input Method Editor (IME) Kana mode
+ HANGUL = 0x15, // IME Hangul mode
+ // 0x16, // Undefined
+ JUNJA = 0x17, // IME Junja mode
+ FINAL = 0x18, // IME final mode
+ HANJA = 0x19, // IME Hanja mode
+ KANJI = 0x19, // IME Kanji mode
+ // 0x1A, // Undefined
+ ESCAPE = 0x1B, // ESC key
+ CONVERT = 0x1C, // IME convert
+ NONCONVERT = 0x1D, // IME nonconvert
+ ACCEPT = 0x1E, // IME accept
+ MODECHANGE = 0x1F, // IME mode change request
+ SPACE = 0x20, // SPACEBAR
+ PRIOR = 0x21, // PAGE UP key
+ NEXT = 0x22, // PAGE DOWN key
+ END = 0x23, // END key
+ HOME = 0x24, // HOME key
+ LEFT = 0x25, // LEFT ARROW key
+ UP = 0x26, // UP ARROW key
+ RIGHT = 0x27, // RIGHT ARROW key
+ DOWN = 0x28, // DOWN ARROW key
+ SELECT = 0x29, // SELECT key
+ PRINT = 0x2A, // PRINT key
+ EXECUTE = 0x2B, // EXECUTE key
+ SNAPSHOT = 0x2C, // PRINT SCREEN key
+ INSERT = 0x2D, // INS key
+ DELETE = 0x2E, // DEL key
+ HELP = 0x2F, // HELP key
+ KEY_0 = 0x30, // 0 key
+ KEY_1 = 0x31, // 1 key
+ KEY_2 = 0x32, // 2 key
+ KEY_3 = 0x33, // 3 key
+ KEY_4 = 0x34, // 4 key
+ KEY_5 = 0x35, // 5 key
+ KEY_6 = 0x36, // 6 key
+ KEY_7 = 0x37, // 7 key
+ KEY_8 = 0x38, // 8 key
+ KEY_9 = 0x39, // 9 key
+ // 0x3A-0x40, // Undefined
+ KEY_A = 0x41, // A key
+ KEY_B = 0x42, // B key
+ KEY_C = 0x43, // C key
+ KEY_D = 0x44, // D key
+ KEY_E = 0x45, // E key
+ KEY_F = 0x46, // F key
+ KEY_G = 0x47, // G key
+ KEY_H = 0x48, // H key
+ KEY_I = 0x49, // I key
+ KEY_J = 0x4A, // J key
+ KEY_K = 0x4B, // K key
+ KEY_L = 0x4C, // L key
+ KEY_M = 0x4D, // M key
+ KEY_N = 0x4E, // N key
+ KEY_O = 0x4F, // O key
+ KEY_P = 0x50, // P key
+ KEY_Q = 0x51, // Q key
+ KEY_R = 0x52, // R key
+ KEY_S = 0x53, // S key
+ KEY_T = 0x54, // T key
+ KEY_U = 0x55, // U key
+ KEY_V = 0x56, // V key
+ KEY_W = 0x57, // W key
+ KEY_X = 0x58, // X key
+ KEY_Y = 0x59, // Y key
+ KEY_Z = 0x5A, // Z key
+ LWIN = 0x5B, // Left Windows key (Microsoft Natural keyboard)
+ RWIN = 0x5C, // Right Windows key (Natural keyboard)
+ APPS = 0x5D, // Applications key (Natural keyboard)
+ // 0x5E, // Reserved
+ SLEEP = 0x5F, // Computer Sleep key
+ NUMPAD0 = 0x60, // Numeric keypad 0 key
+ NUMPAD1 = 0x61, // Numeric keypad 1 key
+ NUMPAD2 = 0x62, // Numeric keypad 2 key
+ NUMPAD3 = 0x63, // Numeric keypad 3 key
+ NUMPAD4 = 0x64, // Numeric keypad 4 key
+ NUMPAD5 = 0x65, // Numeric keypad 5 key
+ NUMPAD6 = 0x66, // Numeric keypad 6 key
+ NUMPAD7 = 0x67, // Numeric keypad 7 key
+ NUMPAD8 = 0x68, // Numeric keypad 8 key
+ NUMPAD9 = 0x69, // Numeric keypad 9 key
+ MULTIPLY = 0x6A, // Multiply key
+ ADD = 0x6B, // Add key
+ SEPARATOR = 0x6C, // Separator key
+ SUBTRACT = 0x6D, // Subtract key
+ DECIMAL = 0x6E, // Decimal key
+ DIVIDE = 0x6F, // Divide key
+ F1 = 0x70, // F1 key
+ F2 = 0x71, // F2 key
+ F3 = 0x72, // F3 key
+ F4 = 0x73, // F4 key
+ F5 = 0x74, // F5 key
+ F6 = 0x75, // F6 key
+ F7 = 0x76, // F7 key
+ F8 = 0x77, // F8 key
+ F9 = 0x78, // F9 key
+ F10 = 0x79, // F10 key
+ F11 = 0x7A, // F11 key
+ F12 = 0x7B, // F12 key
+ F13 = 0x7C, // F13 key
+ F14 = 0x7D, // F14 key
+ F15 = 0x7E, // F15 key
+ F16 = 0x7F, // F16 key
+ F17 = 0x80, // F17 key
+ F18 = 0x81, // F18 key
+ F19 = 0x82, // F19 key
+ F20 = 0x83, // F20 key
+ F21 = 0x84, // F21 key
+ F22 = 0x85, // F22 key, (PPC only) Key used to lock device.
+ F23 = 0x86, // F23 key
+ F24 = 0x87, // F24 key
+ // 0x88-0X8F, // Unassigned
+ NUMLOCK = 0x90, // NUM LOCK key
+ SCROLL = 0x91, // SCROLL LOCK key
+ // 0x92-0x96, // OEM specific
+ // 0x97-0x9F, // Unassigned
+ LSHIFT = 0xA0, // Left SHIFT key
+ RSHIFT = 0xA1, // Right SHIFT key
+ LCONTROL = 0xA2, // Left CONTROL key
+ RCONTROL = 0xA3, // Right CONTROL key
+ LMENU = 0xA4, // Left MENU key
+ RMENU = 0xA5, // Right MENU key
+ BROWSER_BACK = 0xA6, // Windows 2000/XP: Browser Back key
+ BROWSER_FORWARD = 0xA7, // Windows 2000/XP: Browser Forward key
+ BROWSER_REFRESH = 0xA8, // Windows 2000/XP: Browser Refresh key
+ BROWSER_STOP = 0xA9, // Windows 2000/XP: Browser Stop key
+ BROWSER_SEARCH = 0xAA, // Windows 2000/XP: Browser Search key
+ BROWSER_FAVORITES = 0xAB, // Windows 2000/XP: Browser Favorites key
+ BROWSER_HOME = 0xAC, // Windows 2000/XP: Browser Start and Home key
+ VOLUME_MUTE = 0xAD, // Windows 2000/XP: Volume Mute key
+ VOLUME_DOWN = 0xAE, // Windows 2000/XP: Volume Down key
+ VOLUME_UP = 0xAF, // Windows 2000/XP: Volume Up key
+ MEDIA_NEXT_TRACK = 0xB0,// Windows 2000/XP: Next Track key
+ MEDIA_PREV_TRACK = 0xB1,// Windows 2000/XP: Previous Track key
+ MEDIA_STOP = 0xB2, // Windows 2000/XP: Stop Media key
+ MEDIA_PLAY_PAUSE = 0xB3,// Windows 2000/XP: Play/Pause Media key
+ LAUNCH_MAIL = 0xB4, // Windows 2000/XP: Start Mail key
+ LAUNCH_MEDIA_SELECT = 0xB5, // Windows 2000/XP: Select Media key
+ LAUNCH_APP1 = 0xB6, // Windows 2000/XP: Start Application 1 key
+ LAUNCH_APP2 = 0xB7, // Windows 2000/XP: Start Application 2 key
+ // 0xB8-0xB9, // Reserved
+ OEM_1 = 0xBA, // Used for miscellaneous characters; it can vary by keyboard.
+ // Windows 2000/XP: For the US standard keyboard, the ';:' key
+ OEM_PLUS = 0xBB, // Windows 2000/XP: For any country/region, the '+' key
+ OEM_COMMA = 0xBC, // Windows 2000/XP: For any country/region, the ',' key
+ OEM_MINUS = 0xBD, // Windows 2000/XP: For any country/region, the '-' key
+ OEM_PERIOD = 0xBE, // Windows 2000/XP: For any country/region, the '.' key
+ OEM_2 = 0xBF, // Used for miscellaneous characters; it can vary by keyboard.
+ // Windows 2000/XP: For the US standard keyboard, the '/?' key
+ OEM_3 = 0xC0, // Used for miscellaneous characters; it can vary by keyboard.
+ // Windows 2000/XP: For the US standard keyboard, the '`~' key
+ // 0xC1-0xD7, // Reserved
+ // 0xD8-0xDA, // Unassigned
+ OEM_4 = 0xDB, // Used for miscellaneous characters; it can vary by keyboard.
+ // Windows 2000/XP: For the US standard keyboard, the '[{' key
+ OEM_5 = 0xDC, // Used for miscellaneous characters; it can vary by keyboard.
+ // Windows 2000/XP: For the US standard keyboard, the '\|' key
+ OEM_6 = 0xDD, // Used for miscellaneous characters; it can vary by keyboard.
+ // Windows 2000/XP: For the US standard keyboard, the ']}' key
+ OEM_7 = 0xDE, // Used for miscellaneous characters; it can vary by keyboard.
+ // Windows 2000/XP: For the US standard keyboard, the 'single-quote/double-quote' key
+ OEM_8 = 0xDF, // Used for miscellaneous characters; it can vary by keyboard.
+ // 0xE0, // Reserved
+ // 0xE1, // OEM specific
+ OEM_102 = 0xE2, // Windows 2000/XP: Either the angle bracket key or the backslash key on the RT 102-key keyboard
+ // 0xE3-E4, // OEM specific
+ PROCESSKEY = 0xE5, // Windows 95/98/Me, Windows NT 4.0, Windows 2000/XP: IME PROCESS key
+ // 0xE6, // OEM specific
+ PACKET = 0xE7, // Windows 2000/XP: Used to pass Unicode characters as if they were keystrokes. The VK_PACKET key is the low word of a 32-bit Virtual Key value used for non-keyboard input methods. For more information, see Remark in KEYBDINPUT, SendInput, WM_KEYDOWN, and WM_KEYUP
+ // 0xE8, // Unassigned
+ // 0xE9-F5, // OEM specific
+ ATTN = 0xF6, // Attn key
+ CRSEL = 0xF7, // CrSel key
+ EXSEL = 0xF8, // ExSel key
+ EREOF = 0xF9, // Erase EOF key
+ PLAY = 0xFA, // Play key
+ ZOOM = 0xFB, // Zoom key
+ NONAME = 0xFC, // Reserved
+ PA1 = 0xFD, // PA1 key
+ OEM_CLEAR = 0xFE // Clear key
+ }
+
+ ///
+ /// Internal callback processing function
+ ///
+ private delegate IntPtr KeyboardHookHandler(int nCode, IntPtr wParam, IntPtr lParam);
+ private KeyboardHookHandler hookHandler;
+
+ ///
+ /// Function that will be called when defined events occur
+ ///
+ /// VKeys
+ public delegate void KeyboardHookCallback(VKeys key);
+
+ #region Events
+ public event KeyboardHookCallback KeyDown;
+ public event KeyboardHookCallback KeyUp;
+ #endregion
+
+ ///
+ /// Hook ID
+ ///
+ private IntPtr hookID = IntPtr.Zero;
+
+ ///
+ /// Install low level keyboard hook
+ ///
+ public void Install()
+ {
+ int a = 0;
+ int b = 0;
+ a ^= b;
+ hookHandler = HookFunc;
+ hookID = SetHook(hookHandler);
+ }
+
+ ///
+ /// Remove low level keyboard hook
+ ///
+ public void Uninstall()
+ {
+ UnhookWindowsHookEx(hookID);
+ }
+
+ ///
+ /// Registers hook with Windows API
+ ///
+ /// Callback function
+ /// Hook ID
+ private IntPtr SetHook(KeyboardHookHandler proc)
+ {
+ using (ProcessModule module = Process.GetCurrentProcess().MainModule)
+ return SetWindowsHookEx(13, proc, GetModuleHandle(module.ModuleName), 0);
+ }
+
+ ///
+ /// Default hook call, which analyses pressed keys
+ ///
+ private IntPtr HookFunc(int nCode, IntPtr wParam, IntPtr lParam)
+ {
+ if (nCode >= 0)
+ {
+ int iwParam = wParam.ToInt32();
+
+ if ((iwParam == WM_KEYDOWN || iwParam == WM_SYSKEYDOWN))
+ if (KeyDown != null)
+ KeyDown((VKeys)Marshal.ReadInt32(lParam));
+ if ((iwParam == WM_KEYUP || iwParam == WM_SYSKEYUP))
+ if (KeyUp != null)
+ KeyUp((VKeys)Marshal.ReadInt32(lParam));
+ }
+
+ return CallNextHookEx(hookID, nCode, wParam, lParam);
+ }
+
+ ///
+ /// Destructor. Unhook current hook
+ ///
+ ~KeyboardHook()
+ {
+ Uninstall();
+ }
+
+ ///
+ /// Low-Level function declarations
+ ///
+ #region WinAPI
+ private const int WM_KEYDOWN = 0x100;
+ private const int WM_SYSKEYDOWN = 0x104;
+ private const int WM_KEYUP = 0x101;
+ private const int WM_SYSKEYUP = 0x105;
+
+ [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ private static extern IntPtr SetWindowsHookEx(int idHook, KeyboardHookHandler lpfn, IntPtr hMod, uint dwThreadId);
+
+ [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ private static extern bool UnhookWindowsHookEx(IntPtr hhk);
+
+ [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
+
+ [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ private static extern IntPtr GetModuleHandle(string lpModuleName);
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/src/KeyboardInputSwitch.csproj b/src/KeyboardInputSwitch.csproj
new file mode 100755
index 0000000..0c765ba
--- /dev/null
+++ b/src/KeyboardInputSwitch.csproj
@@ -0,0 +1,85 @@
+
+
+
+
+ Debug
+ AnyCPU
+ {1160BD58-B82D-4D9A-A88F-4D465B81AB57}
+ WinExe
+ KeyboardInputSwitch
+ KeyboardInputSwitch
+ v4.7.2
+ 512
+ true
+ true
+
+
+ AnyCPU
+ true
+ full
+ false
+ bin\Debug\
+ DEBUG;TRACE
+ prompt
+ 4
+
+
+ AnyCPU
+ pdbonly
+ true
+ bin\Release\
+ TRACE
+ prompt
+ 4
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Form
+
+
+ Form1.cs
+
+
+
+
+
+
+ Form1.cs
+
+
+ ResXFileCodeGenerator
+ Resources.Designer.cs
+ Designer
+
+
+ True
+ Resources.resx
+
+
+ SettingsSingleFileGenerator
+ Settings.Designer.cs
+
+
+ True
+ Settings.settings
+ True
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/KeyboardInputSwitch.sln b/src/KeyboardInputSwitch.sln
new file mode 100755
index 0000000..80f3a18
--- /dev/null
+++ b/src/KeyboardInputSwitch.sln
@@ -0,0 +1,25 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 16
+VisualStudioVersion = 16.0.29519.87
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KeyboardInputSwitch", "KeyboardInputSwitch.csproj", "{1160BD58-B82D-4D9A-A88F-4D465B81AB57}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {1160BD58-B82D-4D9A-A88F-4D465B81AB57}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1160BD58-B82D-4D9A-A88F-4D465B81AB57}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1160BD58-B82D-4D9A-A88F-4D465B81AB57}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1160BD58-B82D-4D9A-A88F-4D465B81AB57}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {FE7AF3AA-D193-46DB-8F3E-A2C55BE952DE}
+ EndGlobalSection
+EndGlobal
diff --git a/src/KeyboardLock.cs b/src/KeyboardLock.cs
new file mode 100755
index 0000000..3ee2db4
--- /dev/null
+++ b/src/KeyboardLock.cs
@@ -0,0 +1,1733 @@
+using System;
+using System.Collections.Generic;
+using System.ComponentModel;
+using System.Globalization;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Security.Permissions;
+using System.Text;
+using System.Windows.Forms;
+
+
+namespace KeyboardHookMain //Was first created by refactorsaurusrex
+{
+ ///
+ /// Equivalent of the Keys enumerations, but includes a value allowing Windows Logo keys to act as a modifer key.
+ ///
+ [Flags]
+ public enum KeysEx
+ {
+ ///
+ /// The bitmask to extract modifiers from a key value.
+ ///
+ Modifiers = -65536,
+ ///
+ /// No key pressed.
+ ///
+ None = 0,
+ //
+ // Summary:
+ // The left mouse button.
+ LButton = 1,
+ //
+ // Summary:
+ // The right mouse button.
+ RButton = 2,
+ //
+ // Summary:
+ // The CANCEL key.
+ Cancel = 3,
+ //
+ // Summary:
+ // The middle mouse button (three-button mouse).
+ MButton = 4,
+ //
+ // Summary:
+ // The first x mouse button (five-button mouse).
+ XButton1 = 5,
+ //
+ // Summary:
+ // The second x mouse button (five-button mouse).
+ XButton2 = 6,
+ //
+ // Summary:
+ // The BACKSPACE key.
+ Back = 8,
+ //
+ // Summary:
+ // The TAB key.
+ Tab = 9,
+ //
+ // Summary:
+ // The LINEFEED key.
+ LineFeed = 10,
+ //
+ // Summary:
+ // The CLEAR key.
+ Clear = 12,
+ //
+ // Summary:
+ // The ENTER key.
+ Enter = 13,
+ //
+ // Summary:
+ // The RETURN key.
+ Return = 13,
+ //
+ // Summary:
+ // The SHIFT key.
+ ShiftKey = 16,
+ //
+ // Summary:
+ // The CTRL key.
+ ControlKey = 17,
+ //
+ // Summary:
+ // The ALT key.
+ Menu = 18,
+ //
+ // Summary:
+ // The PAUSE key.
+ Pause = 19,
+ //
+ // Summary:
+ // The CAPS LOCK key.
+ CapsLock = 20,
+ //
+ // Summary:
+ // The CAPS LOCK key.
+ Capital = 20,
+ //
+ // Summary:
+ // The IME Kana mode key.
+ KanaMode = 21,
+ //
+ // Summary:
+ // The IME Hanguel mode key. (maintained for compatibility; use HangulMode)
+ HanguelMode = 21,
+ //
+ // Summary:
+ // The IME Hangul mode key.
+ HangulMode = 21,
+ //
+ // Summary:
+ // The IME Junja mode key.
+ JunjaMode = 23,
+ //
+ // Summary:
+ // The IME final mode key.
+ FinalMode = 24,
+ //
+ // Summary:
+ // The IME Kanji mode key.
+ KanjiMode = 25,
+ //
+ // Summary:
+ // The IME Hanja mode key.
+ HanjaMode = 25,
+ //
+ // Summary:
+ // The ESC key.
+ Escape = 27,
+ //
+ // Summary:
+ // The IME convert key.
+ IMEConvert = 28,
+ //
+ // Summary:
+ // The IME nonconvert key.
+ IMENonconvert = 29,
+ //
+ // Summary:
+ // The IME accept key. Obsolete, use System.Windows.Forms.Keys.IMEAccept instead.
+ IMEAceept = 30,
+ //
+ // Summary:
+ // The IME accept key, replaces System.Windows.Forms.Keys.IMEAceept.
+ IMEAccept = 30,
+ //
+ // Summary:
+ // The IME mode change key.
+ IMEModeChange = 31,
+ //
+ // Summary:
+ // The SPACEBAR key.
+ Space = 32,
+ //
+ // Summary:
+ // The PAGE UP key.
+ Prior = 33,
+ //
+ // Summary:
+ // The PAGE UP key.
+ PageUp = 33,
+ //
+ // Summary:
+ // The PAGE DOWN key.
+ Next = 34,
+ //
+ // Summary:
+ // The PAGE DOWN key.
+ PageDown = 34,
+ //
+ // Summary:
+ // The END key.
+ End = 35,
+ //
+ // Summary:
+ // The HOME key.
+ Home = 36,
+ //
+ // Summary:
+ // The LEFT ARROW key.
+ Left = 37,
+ //
+ // Summary:
+ // The UP ARROW key.
+ Up = 38,
+ //
+ // Summary:
+ // The RIGHT ARROW key.
+ Right = 39,
+ //
+ // Summary:
+ // The DOWN ARROW key.
+ Down = 40,
+ //
+ // Summary:
+ // The SELECT key.
+ Select = 41,
+ //
+ // Summary:
+ // The PRINT key.
+ Print = 42,
+ //
+ // Summary:
+ // The EXECUTE key.
+ Execute = 43,
+ //
+ // Summary:
+ // The PRINT SCREEN key.
+ PrintScreen = 44,
+ //
+ // Summary:
+ // The PRINT SCREEN key.
+ Snapshot = 44,
+ //
+ // Summary:
+ // The INS key.
+ Insert = 45,
+ //
+ // Summary:
+ // The DEL key.
+ Delete = 46,
+ //
+ // Summary:
+ // The HELP key.
+ Help = 47,
+ //
+ // Summary:
+ // The 0 key.
+ D0 = 48,
+ //
+ // Summary:
+ // The 1 key.
+ D1 = 49,
+ //
+ // Summary:
+ // The 2 key.
+ D2 = 50,
+ //
+ // Summary:
+ // The 3 key.
+ D3 = 51,
+ //
+ // Summary:
+ // The 4 key.
+ D4 = 52,
+ //
+ // Summary:
+ // The 5 key.
+ D5 = 53,
+ //
+ // Summary:
+ // The 6 key.
+ D6 = 54,
+ //
+ // Summary:
+ // The 7 key.
+ D7 = 55,
+ //
+ // Summary:
+ // The 8 key.
+ D8 = 56,
+ //
+ // Summary:
+ // The 9 key.
+ D9 = 57,
+ //
+ // Summary:
+ // The A key.
+ A = 65,
+ //
+ // Summary:
+ // The B key.
+ B = 66,
+ //
+ // Summary:
+ // The C key.
+ C = 67,
+ //
+ // Summary:
+ // The D key.
+ D = 68,
+ //
+ // Summary:
+ // The E key.
+ E = 69,
+ //
+ // Summary:
+ // The F key.
+ F = 70,
+ //
+ // Summary:
+ // The G key.
+ G = 71,
+ //
+ // Summary:
+ // The H key.
+ H = 72,
+ //
+ // Summary:
+ // The I key.
+ I = 73,
+ //
+ // Summary:
+ // The J key.
+ J = 74,
+ //
+ // Summary:
+ // The K key.
+ K = 75,
+ //
+ // Summary:
+ // The L key.
+ L = 76,
+ //
+ // Summary:
+ // The M key.
+ M = 77,
+ //
+ // Summary:
+ // The N key.
+ N = 78,
+ //
+ // Summary:
+ // The O key.
+ O = 79,
+ //
+ // Summary:
+ // The P key.
+ P = 80,
+ //
+ // Summary:
+ // The Q key.
+ Q = 81,
+ //
+ // Summary:
+ // The R key.
+ R = 82,
+ //
+ // Summary:
+ // The S key.
+ S = 83,
+ //
+ // Summary:
+ // The T key.
+ T = 84,
+ //
+ // Summary:
+ // The U key.
+ U = 85,
+ //
+ // Summary:
+ // The V key.
+ V = 86,
+ //
+ // Summary:
+ // The W key.
+ W = 87,
+ //
+ // Summary:
+ // The X key.
+ X = 88,
+ //
+ // Summary:
+ // The Y key.
+ Y = 89,
+ //
+ // Summary:
+ // The Z key.
+ Z = 90,
+ //
+ // Summary:
+ // The left Windows logo key (Microsoft Natural Keyboard).
+ LWin = 91,
+ //
+ // Summary:
+ // The right Windows logo key (Microsoft Natural Keyboard).
+ RWin = 92,
+ //
+ // Summary:
+ // The application key (Microsoft Natural Keyboard).
+ Apps = 93,
+ //
+ // Summary:
+ // The computer sleep key.
+ Sleep = 95,
+ //
+ // Summary:
+ // The 0 key on the numeric keypad.
+ NumPad0 = 96,
+ //
+ // Summary:
+ // The 1 key on the numeric keypad.
+ NumPad1 = 97,
+ //
+ // Summary:
+ // The 2 key on the numeric keypad.
+ NumPad2 = 98,
+ //
+ // Summary:
+ // The 3 key on the numeric keypad.
+ NumPad3 = 99,
+ //
+ // Summary:
+ // The 4 key on the numeric keypad.
+ NumPad4 = 100,
+ //
+ // Summary:
+ // The 5 key on the numeric keypad.
+ NumPad5 = 101,
+ //
+ // Summary:
+ // The 6 key on the numeric keypad.
+ NumPad6 = 102,
+ //
+ // Summary:
+ // The 7 key on the numeric keypad.
+ NumPad7 = 103,
+ //
+ // Summary:
+ // The 8 key on the numeric keypad.
+ NumPad8 = 104,
+ //
+ // Summary:
+ // The 9 key on the numeric keypad.
+ NumPad9 = 105,
+ //
+ // Summary:
+ // The multiply key.
+ Multiply = 106,
+ //
+ // Summary:
+ // The add key.
+ Add = 107,
+ //
+ // Summary:
+ // The separator key.
+ Separator = 108,
+ //
+ // Summary:
+ // The subtract key.
+ Subtract = 109,
+ //
+ // Summary:
+ // The decimal key.
+ Decimal = 110,
+ //
+ // Summary:
+ // The divide key.
+ Divide = 111,
+ //
+ // Summary:
+ // The F1 key.
+ F1 = 112,
+ //
+ // Summary:
+ // The F2 key.
+ F2 = 113,
+ //
+ // Summary:
+ // The F3 key.
+ F3 = 114,
+ //
+ // Summary:
+ // The F4 key.
+ F4 = 115,
+ //
+ // Summary:
+ // The F5 key.
+ F5 = 116,
+ //
+ // Summary:
+ // The F6 key.
+ F6 = 117,
+ //
+ // Summary:
+ // The F7 key.
+ F7 = 118,
+ //
+ // Summary:
+ // The F8 key.
+ F8 = 119,
+ //
+ // Summary:
+ // The F9 key.
+ F9 = 120,
+ //
+ // Summary:
+ // The F10 key.
+ F10 = 121,
+ //
+ // Summary:
+ // The F11 key.
+ F11 = 122,
+ //
+ // Summary:
+ // The F12 key.
+ F12 = 123,
+ //
+ // Summary:
+ // The F13 key.
+ F13 = 124,
+ //
+ // Summary:
+ // The F14 key.
+ F14 = 125,
+ //
+ // Summary:
+ // The F15 key.
+ F15 = 126,
+ //
+ // Summary:
+ // The F16 key.
+ F16 = 127,
+ //
+ // Summary:
+ // The F17 key.
+ F17 = 128,
+ //
+ // Summary:
+ // The F18 key.
+ F18 = 129,
+ //
+ // Summary:
+ // The F19 key.
+ F19 = 130,
+ //
+ // Summary:
+ // The F20 key.
+ F20 = 131,
+ //
+ // Summary:
+ // The F21 key.
+ F21 = 132,
+ //
+ // Summary:
+ // The F22 key.
+ F22 = 133,
+ //
+ // Summary:
+ // The F23 key.
+ F23 = 134,
+ //
+ // Summary:
+ // The F24 key.
+ F24 = 135,
+ //
+ // Summary:
+ // The NUM LOCK key.
+ NumLock = 144,
+ //
+ // Summary:
+ // The SCROLL LOCK key.
+ Scroll = 145,
+ //
+ // Summary:
+ // The left SHIFT key.
+ LShiftKey = 160,
+ //
+ // Summary:
+ // The right SHIFT key.
+ RShiftKey = 161,
+ //
+ // Summary:
+ // The left CTRL key.
+ LControlKey = 162,
+ //
+ // Summary:
+ // The right CTRL key.
+ RControlKey = 163,
+ //
+ // Summary:
+ // The left ALT key.
+ LMenu = 164,
+ //
+ // Summary:
+ // The right ALT key.
+ RMenu = 165,
+ //
+ // Summary:
+ // The browser back key (Windows 2000 or later).
+ BrowserBack = 166,
+ //
+ // Summary:
+ // The browser forward key (Windows 2000 or later).
+ BrowserForward = 167,
+ //
+ // Summary:
+ // The browser refresh key (Windows 2000 or later).
+ BrowserRefresh = 168,
+ //
+ // Summary:
+ // The browser stop key (Windows 2000 or later).
+ BrowserStop = 169,
+ //
+ // Summary:
+ // The browser search key (Windows 2000 or later).
+ BrowserSearch = 170,
+ //
+ // Summary:
+ // The browser favorites key (Windows 2000 or later).
+ BrowserFavorites = 171,
+ //
+ // Summary:
+ // The browser home key (Windows 2000 or later).
+ BrowserHome = 172,
+ //
+ // Summary:
+ // The volume mute key (Windows 2000 or later).
+ VolumeMute = 173,
+ //
+ // Summary:
+ // The volume down key (Windows 2000 or later).
+ VolumeDown = 174,
+ //
+ // Summary:
+ // The volume up key (Windows 2000 or later).
+ VolumeUp = 175,
+ ///
+ /// The media next track key (Windows 2000 or later).
+ ///
+ MediaNextTrack = 176,
+ ///
+ /// The media previous track key (Windows 2000 or later).
+ ///
+ MediaPreviousTrack = 177,
+ ///
+ /// The media Stop key (Windows 2000 or later).
+ ///
+ MediaStop = 178,
+ ///
+ /// The media play pause key (Windows 2000 or later).
+ ///
+ MediaPlayPause = 179,
+ ///
+ /// The launch mail key (Windows 2000 or later).
+ ///
+ LaunchMail = 180,
+ ///
+ /// The select media key (Windows 2000 or later).
+ ///
+ SelectMedia = 181,
+ ///
+ /// The start application one key (Windows 2000 or later).
+ ///
+ LaunchApplication1 = 182,
+ ///
+ /// The start application two key (Windows 2000 or later).
+ ///
+ LaunchApplication2 = 183,
+ ///
+ /// The OEM 1 key.
+ ///
+ Oem1 = 186,
+ ///
+ /// The OEM Semicolon key on a US standard keyboard (Windows 2000 or later).
+ ///
+ OemSemicolon = 186,
+ ///
+ /// The OEM plus key on any country/region keyboard (Windows 2000 or later).
+ ///
+ Oemplus = 187,
+ ///
+ /// The OEM comma key on any country/region keyboard (Windows 2000 or later).
+ ///
+ Oemcomma = 188,
+ ///
+ /// The OEM minus key on any country/region keyboard (Windows 2000 or later).
+ ///
+ OemMinus = 189,
+ ///
+ /// The OEM period key on any country/region keyboard (Windows 2000 or later).
+ ///
+ OemPeriod = 190,
+ ///
+ /// The OEM question mark key on a US standard keyboard (Windows 2000 or later).
+ ///
+ OemQuestion = 191,
+ ///
+ /// The OEM 2 key.
+ ///
+ Oem2 = 191,
+ ///
+ /// The OEM tilde key on a US standard keyboard (Windows 2000 or later).
+ ///
+ Oemtilde = 192,
+ ///
+ /// The OEM 3 key.
+ ///
+ Oem3 = 192,
+ ///
+ /// The OEM 4 key.
+ ///
+ Oem4 = 219,
+ ///
+ /// The OEM open bracket key on a US standard keyboard (Windows 2000 or later).
+ ///
+ OemOpenBrackets = 219,
+ ///
+ /// The OEM pipe key on a US standard keyboard (Windows 2000 or later).
+ ///
+ OemPipe = 220,
+ ///
+ /// The OEM 5 key.
+ ///
+ Oem5 = 220,
+ ///
+ /// The OEM 6 key.
+ ///
+ Oem6 = 221,
+ ///
+ /// The OEM close bracket key on a US standard keyboard (Windows 2000 or later).
+ ///
+ OemCloseBrackets = 221,
+ ///
+ /// The OEM 7 key.
+ ///
+ Oem7 = 222,
+ ///
+ /// The OEM singled/double quote key on a US standard keyboard (Windows 2000
+ /// or later).
+ ///
+ OemQuotes = 222,
+ ///
+ /// The OEM 8 key.
+ ///
+ Oem8 = 223,
+ ///
+ /// The OEM 102 key.
+ ///
+ Oem102 = 226,
+ ///
+ /// The OEM angle bracket or backslash key on the RT 102 key keyboard (Windows
+ /// 2000 or later).
+ ///
+ OemBackslash = 226,
+ ///
+ /// The PROCESS KEY key.
+ ///
+ ProcessKey = 229,
+ ///
+ /// Used to pass Unicode characters as if they were keystrokes. The Packet key
+ /// value is the low word of a 32-bit virtual-key value used for non-keyboard
+ /// input methods.
+ ///
+ Packet = 231,
+ ///
+ /// The ATTN key.
+ ///
+ Attn = 246,
+ ///
+ /// The CRSEL key.
+ ///
+ Crsel = 247,
+ ///
+ /// The EXSEL key.
+ ///
+ Exsel = 248,
+ ///
+ /// The ERASE EOF key.
+ ///
+ EraseEof = 249,
+ ///
+ /// The PLAY key.
+ ///
+ Play = 250,
+ ///
+ /// The ZOOM key.
+ ///
+ Zoom = 251,
+ ///
+ /// A constant reserved for future use.
+ ///
+ NoName = 252,
+ ///
+ /// The PA1 key.
+ ///
+ Pa1 = 253,
+ ///
+ /// The CLEAR key.
+ ///
+ OemClear = 254,
+ ///
+ /// The bitmask to extract a key code from a key value.
+ ///
+ KeyCode = 65535,
+ ///
+ /// The SHIFT modifier key.
+ ///
+ Shift = 65536,
+ ///
+ /// The CTRL modifier key.
+ ///
+ Control = 131072,
+ ///
+ /// The ALT modifier key.
+ ///
+ Alt = 262144,
+ ///
+ /// The WINLOGO modifier key.
+ ///
+ WinLogo = 524288
+ }
+
+ public enum KeyState
+ {
+ ///
+ /// Indicates that the key is pressed down.
+ ///
+ Down,
+ ///
+ /// Indicates that the key has been released.
+ ///
+ Up
+ }
+
+ static class NativeMethods
+ {
+ internal delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
+
+ [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ internal static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
+
+ [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ [return: MarshalAs(UnmanagedType.Bool)]
+ internal static extern bool UnhookWindowsHookEx(IntPtr hhk);
+
+ [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ internal static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
+
+ [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ internal static extern short GetKeyState(int nVirtKey);
+
+ [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
+ internal static extern uint MapVirtualKey(uint uCode, uint uMapType);
+
+ internal static class KeyStateConstants
+ {
+ internal const int KEY_PRESSED = 0x8000;
+ internal const int WM_KEYDOWN = 0x0100;
+ internal const int WM_KEYUP = 0x0101;
+ internal const int WM_SYSKEYDOWN = 0x0104;
+ internal const int WM_SYSKEYUP = 0x0105;
+ internal const int WH_KEYBOARD_LL = 13;
+ }
+ }
+
+ public class KeyCombination : IEquatable
+ {
+ ///
+ /// The backing keys for this combination.
+ ///
+ KeysEx keyChain;
+
+ ///
+ /// Creates a new KeyCombination based on the provided archetype. The new combination is a deep copy
+ /// of the original.
+ ///
+ /// The combination to copy from.
+ /// A new KeyCombination object.
+ public static KeyCombination CopyFrom(KeyCombination combo)
+ {
+ return new KeyCombination(combo.Keys);
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The keys that constitute a keyboard combination.
+ public KeyCombination(Keys keys)
+ {
+ keyChain = keys.ToKeysEx();
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The keys that constitute a keyboard combination.
+ public KeyCombination(KeysEx keys)
+ {
+ keyChain = keys;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The keys that constitute a keyboard combination, express in the following
+ /// format: "Modifer + Modifier + Key".
+ public KeyCombination(string keys)
+ {
+ if (!string.IsNullOrEmpty(keys))
+ FromStringToKeys(keys);
+ }
+
+ ///
+ /// Returns true if the values of its operands are equal, otherwise false.
+ ///
+ public static bool operator ==(KeyCombination x, KeyCombination y)
+ {
+ if (ReferenceEquals(null, x))
+ return ReferenceEquals(null, y);
+
+ return x.Equals(y);
+ }
+
+ ///
+ /// returns false if its operands are equal, otherwise true.
+ ///
+ public static bool operator !=(KeyCombination x, KeyCombination y)
+ {
+ return !(x == y);
+ }
+
+ ///
+ /// Adds the specified keys to this combination. Note that a combination can only contain a single
+ /// non-modifier key. If one already exists, it will be overwritten.
+ ///
+ /// The keys to add.
+ public void Add(KeysEx keys)
+ {
+ KeysEx keyCode = keys & KeysEx.KeyCode;
+
+ if (keyCode != KeysEx.None)
+ keyChain = keyCode | keyChain & KeysEx.Modifiers;
+
+ KeysEx modifiers = keys & KeysEx.Modifiers;
+
+ if (modifiers != KeysEx.None)
+ keyChain = modifiers | keyChain & KeysEx.KeyCode | keyChain & KeysEx.Modifiers;
+ }
+
+ ///
+ /// Removes the specified key from this combination. Must be called once for each key to be removed.
+ ///
+ /// The key to remove.
+ public void Remove(KeysEx key)
+ {
+ if (Contains(key))
+ keyChain ^= key;
+
+ if (key == KeysEx.LWin || key == KeysEx.RWin)
+ {
+ if (Contains(KeysEx.WinLogo))
+ keyChain ^= KeysEx.WinLogo;
+ }
+ }
+
+ ///
+ /// Determines whether the specified key exists in this combination.
+ ///
+ /// The key.
+ /// True if the specified key exists; otherwise, false.
+ public bool Contains(KeysEx key)
+ {
+ switch (key)
+ {
+ case KeysEx.Control:
+ case KeysEx.Shift:
+ case KeysEx.Alt:
+ case KeysEx.WinLogo:
+ return (keyChain & KeysEx.Modifiers) == key;
+
+ default:
+ return (keyChain & KeysEx.KeyCode) == key;
+ }
+ }
+
+ ///
+ /// Gets the keys that constitute this combination.
+ ///
+ public KeysEx Keys
+ {
+ get { return keyChain; }
+ }
+
+ ///
+ /// Gets a value indicating whether this instance is empty.
+ ///
+ public bool IsEmpty
+ {
+ get { return Keys == KeysEx.None; }
+ }
+
+ ///
+ /// Gets the unmodified key for this combination.
+ ///
+ public KeysEx UnmodifiedKey
+ {
+ get { return keyChain & KeysEx.KeyCode; }
+ }
+
+ ///
+ /// Returns a that represents this instance.
+ ///
+ /// A that represents this instance.
+ public override string ToString()
+ {
+ return keyChain.ToFriendlyString();
+ }
+
+ ///
+ /// Gets a value indicating whether this combination is valid.
+ ///
+ /// A valid combination is one which can be used as a keyboard shortcut. Certain
+ /// keys such as Enter or CapsLock cannot be used for a shortcut. A combination also cannot
+ /// consist solely of modifier keys.
+ public bool IsValid
+ {
+ get
+ {
+ KeysEx keyCode = keyChain & KeysEx.KeyCode;
+ switch (keyCode)
+ {
+ case KeysEx.Enter:
+ case KeysEx.CapsLock:
+ case KeysEx.NumLock:
+ case KeysEx.Tab:
+ case KeysEx.None:
+ case KeysEx.ShiftKey:
+ case KeysEx.LShiftKey:
+ case KeysEx.RShiftKey:
+ case KeysEx.ControlKey:
+ case KeysEx.LControlKey:
+ case KeysEx.RControlKey:
+ case KeysEx.Menu:
+ case KeysEx.LMenu:
+ case KeysEx.RMenu:
+ case KeysEx.LWin:
+ case KeysEx.RWin:
+ return false;
+
+ case KeysEx.Delete:
+ if ((keyChain & KeysEx.Modifiers) == (KeysEx.Control | KeysEx.Alt))
+ return false;
+ break;
+ }
+
+ return true;
+ }
+ }
+
+ ///
+ /// Determines whether the specified is equal to this instance.
+ ///
+ /// The to compare with the current .
+ /// true if the specified is equal to this instance; otherwise, false.
+ public override bool Equals(object obj)
+ {
+ return Equals(obj as KeyCombination);
+ }
+
+ ///
+ /// Returns a hash code for this instance.
+ ///
+ /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table.
+ public override int GetHashCode()
+ {
+ unchecked
+ {
+ return 17 * 23 + ToString().GetHashCode();
+ }
+ }
+
+ ///
+ /// Indicates whether the current object is equal to another object of the same type.
+ ///
+ /// An object to compare with this object.
+ /// true if the current object is equal to the parameter; otherwise, false.
+ public bool Equals(KeyCombination other)
+ {
+ if (ReferenceEquals(null, other))
+ return false;
+
+ return ToString() == other.ToString();
+ }
+
+ ///
+ /// Froms the string to keys.
+ ///
+ /// The keys.
+ void FromStringToKeys(string keys)
+ {
+ IEnumerable segments = keys.Split(new[] { '+' });
+
+ foreach (string segment in segments)
+ {
+ string modifiedSegment = segment.ToLowerInvariant().Trim() == "ctrl" ? "Control" : segment;
+ keyChain |= EnumParser.Parse(modifiedSegment.Trim());
+ }
+ }
+ }
+
+ public static class EnumParser
+ {
+ ///
+ /// Converts the string representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.
+ ///
+ /// The enum type.
+ /// A string containing the name or value to convert.
+ public static T Parse(string value) where T : struct
+ {
+ return (T)Enum.Parse(typeof(T), value);
+ }
+
+ ///
+ /// Converts the string representation of the name or numeric value of one or more enumerated constants to an equivalent enumerated object.
+ ///
+ /// The enum type.
+ /// A string containing the name or value to convert.
+ /// If true, ignore case; otherwise, regard case.
+ public static T Parse(string value, bool ignoreCase) where T : struct
+ {
+ return (T)Enum.Parse(typeof(T), value, ignoreCase);
+ }
+ }
+
+ public static class Extensions
+ {
+ ///
+ /// Converts the specified Keys to a KeysEx enumeration.
+ ///
+ /// The keys to convert.
+ public static KeysEx ToKeysEx(this Keys keys)
+ {
+ return EnumParser.Parse(keys.ToString());
+ }
+ }
+
+ public static class KeysExtensions
+ {
+ ///
+ /// Returns a friendly string representation of this KeysEx instance.
+ ///
+ /// The keys to convert to a friendly string.
+ public static string ToFriendlyString(this KeysEx keys)
+ {
+ var friendlyString = new StringBuilder();
+
+ if (IsWinLogoModified(keys))
+ friendlyString.Append("WinLogo + ");
+
+ if (IsControlModified(keys))
+ friendlyString.Append("Ctrl + ");
+
+ if (IsShiftModified(keys))
+ friendlyString.Append("Shift + ");
+
+ if (IsAltModified(keys))
+ friendlyString.Append("Alt + ");
+
+ string unmodifiedKey = UnmodifiedKey(keys);
+
+ if (string.IsNullOrEmpty(unmodifiedKey) && friendlyString.Length >= 3)
+ friendlyString.Remove(friendlyString.Length - 3, 3);
+ else
+ friendlyString.Append(unmodifiedKey);
+
+ return friendlyString.ToString();
+ }
+
+ ///
+ /// Returns KeyCode portion of this instance. That is, the key stripped of modifiers.
+ ///
+ /// The keys to remove modifier keys from.
+ static string UnmodifiedKey(KeysEx keys)
+ {
+ KeysEx keyCode = keys & KeysEx.KeyCode;
+
+ switch (keyCode)
+ {
+ case KeysEx.Menu:
+ case KeysEx.LMenu:
+ case KeysEx.RMenu:
+ case KeysEx.ShiftKey:
+ case KeysEx.LShiftKey:
+ case KeysEx.RShiftKey:
+ case KeysEx.ControlKey:
+ case KeysEx.LControlKey:
+ case KeysEx.RControlKey:
+ case KeysEx.LWin:
+ case KeysEx.RWin:
+ return string.Empty;
+ }
+
+ switch (keyCode)
+ {
+ case KeysEx.D0:
+ case KeysEx.D1:
+ case KeysEx.D2:
+ case KeysEx.D3:
+ case KeysEx.D4:
+ case KeysEx.D5:
+ case KeysEx.D6:
+ case KeysEx.D7:
+ case KeysEx.D8:
+ case KeysEx.D9:
+ return keyCode.ToString().Remove(0, 1);
+ }
+
+ if (keyCode.ToString().ToUpperInvariant().StartsWith("OEM"))
+ {
+ const uint convertToChar = 2;
+ uint keyLiteral = NativeMethods.MapVirtualKey((uint)keyCode, convertToChar);
+ return Convert.ToChar(keyLiteral).ToString(CultureInfo.InvariantCulture);
+ }
+
+ return keyCode.ToString();
+ }
+
+ ///
+ /// Determines whether the specified keys contains the WinLogo modifier, the LWin key, or the RWin key.
+ ///
+ /// The keys to check for logo key modifiers.
+ /// True if this instance contains a logo key modifier; otherwise, false.
+ static bool IsWinLogoModified(KeysEx keys)
+ {
+ if ((keys & KeysEx.WinLogo) == KeysEx.WinLogo)
+ return true;
+
+ KeysEx keyCode = keys & KeysEx.KeyCode;
+ switch (keyCode)
+ {
+ case KeysEx.LWin:
+ case KeysEx.RWin:
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Determines whether this enumeration contains the Alt modifier.
+ ///
+ /// The keys to check for the Alt modifier.
+ /// True if this enumeration contains the Alt modifier; otherwise false.
+ static bool IsAltModified(KeysEx keys)
+ {
+ if ((keys & KeysEx.Alt) == KeysEx.Alt)
+ return true;
+
+ KeysEx keyCode = keys & KeysEx.KeyCode;
+ switch (keyCode)
+ {
+ case KeysEx.Menu:
+ case KeysEx.LMenu:
+ case KeysEx.RMenu:
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Determines whether this enumeration contains the Shift modifier.
+ ///
+ /// The keys to check for the Shift modifier.
+ /// True if this enumeration contains the Shift modifier; otherwise false.
+ static bool IsShiftModified(KeysEx keys)
+ {
+ if ((keys & KeysEx.Shift) == KeysEx.Shift)
+ return true;
+
+ KeysEx keyCode = keys & KeysEx.KeyCode;
+ switch (keyCode)
+ {
+ case KeysEx.ShiftKey:
+ case KeysEx.LShiftKey:
+ case KeysEx.RShiftKey:
+ return true;
+ }
+
+ return false;
+ }
+
+ ///
+ /// Determines whether this enumeration contains the Control modifier.
+ ///
+ /// The keys to check for the Control modifier.
+ /// True if this enumeration contains the Control modifier; otherwise false.
+ static bool IsControlModified(KeysEx keys)
+ {
+ if ((keys & KeysEx.Control) == KeysEx.Control)
+ return true;
+
+ KeysEx keyCode = keys & KeysEx.KeyCode;
+ switch (keyCode)
+ {
+ case KeysEx.ControlKey:
+ case KeysEx.LControlKey:
+ case KeysEx.RControlKey:
+ return true;
+ }
+
+ return false;
+ }
+ }
+
+ public class KeyboardHook : IDisposable
+ {
+ const int keyUp = NativeMethods.KeyStateConstants.WM_KEYUP;
+ const int systemKeyUp = NativeMethods.KeyStateConstants.WM_SYSKEYUP;
+ const int keyDown = NativeMethods.KeyStateConstants.WM_KEYDOWN;
+ const int systemKeyDown = NativeMethods.KeyStateConstants.WM_SYSKEYDOWN;
+
+ static readonly HashSet currentHookKeys = new HashSet();
+ static readonly NativeMethods.LowLevelKeyboardProc lockdownHookCallBack = LockdownHookCallBack;
+ static bool isKeyboardLockedDown;
+ static IntPtr lockdownHookId;
+ static KeysEx lockedDownKeys;
+
+ IntPtr hookId;
+ bool isDisposed;
+ bool keyReleased;
+ readonly NativeMethods.LowLevelKeyboardProc hookCallback;
+
+ ///
+ /// Occurs when a key is pressed while keyboard lockdown is engaged.
+ ///
+ public static event EventHandler LockedDownKeyboardKeyPressed = delegate { };
+
+ ///
+ /// Engages a full keyboard lockdown, which disables all keyboard processing beyond the LockedDownKeyboardKeyPressed event.
+ ///
+ public static void EngageFullKeyboardLockdown()
+ {
+ lockdownHookId = NativeMethods.SetWindowsHookEx(
+ NativeMethods.KeyStateConstants.WH_KEYBOARD_LL,
+ lockdownHookCallBack,
+ IntPtr.Zero,
+ 0);
+
+ if (lockdownHookId == IntPtr.Zero)
+ throw new Win32Exception(Marshal.GetLastWin32Error());
+
+ isKeyboardLockedDown = true;
+ }
+
+ ///
+ /// Releases keyboard lockdown and allows keyboard processing as normal.
+ ///
+ public static void ReleaseFullKeyboardLockdown()
+ {
+ if (lockdownHookId != IntPtr.Zero)
+ {
+ NativeMethods.UnhookWindowsHookEx(lockdownHookId);
+ lockdownHookId = IntPtr.Zero;
+ }
+
+ isKeyboardLockedDown = false;
+ }
+
+ ///
+ /// Determines whether the specified key combination is already in use.
+ ///
+ /// The combination to check.
+ /// True if the key combination is already in use; otherwise, false.
+ public static bool IsKeyCombinationTaken(KeyCombination combination)
+ {
+ return currentHookKeys.Contains(combination);
+ }
+
+ ///
+ /// Raises the KeyboardLockDownKeyPressed event by invoking each subscribed delegate asynchronously.
+ ///
+ /// The keys that are pressed.
+ /// The state of the keys.
+ static void OnLockedDownKeyboardKeyPressed(KeysEx pressedKeys, KeyState state)
+ {
+ foreach (EventHandler pressedEvent in LockedDownKeyboardKeyPressed.GetInvocationList())
+ {
+ var args = new KeyboardLockDownKeyPressedEventArgs(pressedKeys, state);
+ AsyncCallback callback = ar => ((EventHandler)ar.AsyncState).EndInvoke(ar);
+ pressedEvent.BeginInvoke(null, args, callback, pressedEvent);
+ }
+ }
+
+ ///
+ /// Returns a value indicating whether a non-modifier key is currently pressed.
+ ///
+ /// The key to check. Key must not be a modifier bit mask.
+ /// True if the key is currently pressed; otherwise false.
+ /// Occurs if key is a modifier bit mask.
+ static bool IsKeyPressed(KeysEx key)
+ {
+ if ((key & KeysEx.Modifiers) == KeysEx.Modifiers)
+ {
+ throw new ArgumentException("Key cannot contain any modifiers.", "key");
+ }
+
+ const int keyPressed = NativeMethods.KeyStateConstants.KEY_PRESSED;
+ return (NativeMethods.GetKeyState((int)key) & keyPressed) == keyPressed;
+ }
+
+ ///
+ /// Callback handler for keyboard lockdowns.
+ ///
+ static IntPtr LockdownHookCallBack(int nCode, IntPtr wParam, IntPtr lParam)
+ {
+ int keyStateParam = (int)wParam;
+ KeysEx pressedKey = (KeysEx)Marshal.ReadInt32(lParam);
+
+ switch (pressedKey)
+ {
+ case KeysEx.LControlKey:
+ case KeysEx.RControlKey:
+ pressedKey = KeysEx.Control;
+ break;
+
+ case KeysEx.RMenu:
+ case KeysEx.LMenu:
+ pressedKey = KeysEx.Alt;
+ break;
+
+ case KeysEx.LShiftKey:
+ case KeysEx.RShiftKey:
+ pressedKey = KeysEx.Shift;
+ break;
+
+ case KeysEx.LWin:
+ case KeysEx.RWin:
+ pressedKey = KeysEx.WinLogo;
+ break;
+ }
+
+ KeyState keyState;
+ if (keyStateParam == keyUp || keyStateParam == systemKeyUp)
+ {
+ keyState = KeyState.Up;
+ lockedDownKeys ^= pressedKey;
+ }
+ else if (keyStateParam == keyDown || keyStateParam == systemKeyDown)
+ {
+ keyState = KeyState.Down;
+ lockedDownKeys |= pressedKey;
+ }
+ else
+ {
+ throw new ArgumentOutOfRangeException("wParam", "Invalid key state detected.");
+ }
+
+ OnLockedDownKeyboardKeyPressed(lockedDownKeys, keyState);
+ return new IntPtr(1);
+ }
+
+ ///
+ /// Initializes a new instance of the KeyboardHook class.
+ ///
+ public KeyboardHook()
+ {
+ hookCallback = HookCallBack;
+ Combination = new KeyCombination(KeysEx.None);
+ }
+
+ ///
+ /// Finalizes an instance of the KeyboardHook class.
+ ///
+ ~KeyboardHook()
+ {
+ Dispose(false);
+ }
+
+ ///
+ /// Occurs when the KeyboardHook's assigned key combination is pressed.
+ ///
+ public event EventHandler Pressed = delegate { };
+
+ ///
+ /// Gets the key combination for this hook.
+ ///
+ public KeyCombination Combination { get; private set; }
+
+ ///
+ /// Gets or sets a value indicating whether to allow normal processing of key strokes after
+ /// the hook has finished processing it.
+ ///
+ public bool AllowPassThrough { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether the KeyboardHook Pressed event is fired repeatedly for the duration
+ /// of the key press or only once per key press.
+ ///
+ public bool AutoRepeat { get; set; }
+
+ ///
+ /// Gets a value indicating whether the KeyboardHook is currently active.
+ ///
+ public bool IsEngaged
+ {
+ get { return hookId != IntPtr.Zero; }
+ }
+
+ ///
+ /// Removes the keys associated with this hook. This can only be performed when the hook is not active.
+ ///
+ /// Occurs if the hook is currently engaged.
+ public void RemoveKeys() //----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------Was only public void before
+ {
+ if (Combination.Keys == KeysEx.None)
+ return;
+
+ if (IsEngaged)
+ throw new InvalidOperationException("Cannot remove keys while hook is engaged.");
+
+ if (Combination != null && currentHookKeys.Contains(Combination))
+ currentHookKeys.Remove(Combination);
+
+ Combination = new KeyCombination(KeysEx.None);
+ }
+
+ ///
+ /// Associates the specified key combination with this hook. This can only be performed when the hook is not active.
+ ///
+ /// The key combination.
+ /// Occurs if this hook is currently engaged -OR- if the key combination is invalid
+ /// -OR- if the key combination is already in use by another hook.
+ public void SetKeys(KeyCombination combination)
+ {
+ if (Combination == combination)
+ return;
+
+ if (IsEngaged)
+ throw new InvalidOperationException("Cannot set keys while hook is engaged.");
+
+ if (!combination.IsValid)
+ throw new InvalidOperationException("Key combination is not valid.");
+
+ if (currentHookKeys.Contains(combination))
+ throw new InvalidOperationException(string.Format("The combination '{0}' is already in use.", combination));
+
+ if (Combination != null && currentHookKeys.Contains(Combination))
+ currentHookKeys.Remove(Combination);
+
+ currentHookKeys.Add(combination);
+ Combination = combination;
+ }
+
+ ///
+ /// Activates the KeyboardHook.
+ ///
+ /// Occurs if the KeyboardHook is empty OR if the KeyboardHook is already engaged.
+ /// Occurs if the KeyboardHook has been disposed.
+ [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.UnmanagedCode)]
+ public void Engage()
+ {
+ if (isDisposed)
+ throw new ObjectDisposedException("KeyboardHook");
+
+ if (Combination == null)
+ throw new InvalidOperationException("Cannot engage hook when Combination is null.");
+
+ if (IsEngaged)
+ return;
+
+ hookId = NativeMethods.SetWindowsHookEx(
+ NativeMethods.KeyStateConstants.WH_KEYBOARD_LL,
+ hookCallback,
+ IntPtr.Zero,
+ 0);
+
+ if (hookId == IntPtr.Zero)
+ throw new Win32Exception(Marshal.GetLastWin32Error());
+ }
+
+ ///
+ /// Removes the KeyboardHook from the system.
+ ///
+ /// Disengage removes the hook from the system, but maintains all its data. Use Engage to re-install the hook. To
+ /// discard the hook and all its data, use Dispose. It is not necessary to call Disengage prior to Dispose.
+ public void Disengage()
+ {
+ if (hookId != IntPtr.Zero)
+ {
+ NativeMethods.UnhookWindowsHookEx(hookId);
+ hookId = IntPtr.Zero;
+ }
+ }
+
+ ///
+ /// Returns the string representation of the KeyboardHook.
+ ///
+ /// A string representing the KeyboardHook.
+ public override string ToString()
+ {
+ return Combination == null ? string.Empty : Combination.ToString();
+ }
+
+ ///
+ /// Disengages the KeyboardHook and releases all associated resources.
+ ///
+ public void Dispose()
+ {
+ Dispose(true);
+ GC.SuppressFinalize(this);
+ }
+
+ ///
+ /// Disengages the KeyboardHook, releases all unmanaged resources, and optionally releases
+ /// all managed resources.
+ ///
+ /// True to release both managed and unmanaged resources; false to
+ /// release only unmanaged resources.
+ protected virtual void Dispose(bool isDisposing)
+ {
+ if (isDisposed)
+ return;
+
+ if (isDisposing)
+ {
+ // No managed resources to dispose.
+ }
+
+ Disengage();
+
+ if (Combination != null)
+ currentHookKeys.Remove(Combination);
+
+ isDisposed = true;
+ }
+
+ ///
+ /// Raises the Pressed event.
+ ///
+ /// The Pressed event is executed asynchronously.
+ protected virtual void OnPressed()
+ {
+ // Invoke these asychronously in case they're slow. (Testing revealed that Windows will reassert
+ // responsibility for the key stroke(s) if the hookcallback doesn't immediately return.)
+ foreach (EventHandler pressedEvent in Pressed.GetInvocationList())
+ pressedEvent.BeginInvoke(this, EventArgs.Empty, ar => ((EventHandler)ar.AsyncState).EndInvoke(ar), pressedEvent);
+ }
+
+ ///
+ /// Determines whether the specified key, combined with all currently pressed modifier keys, matches this hook's key combination.
+ ///
+ /// The unmodified key to combine with all currently pressed keyboard modifier keys.
+ /// True if unmodifiedKey matches Combination.UnmodifiedKey and all modifier keys contained in the
+ /// Combination property are currently pressed; otherwise, false.
+ protected bool CheckIsHookKeyCombination(KeysEx unmodifiedKey)
+ {
+ KeyCombination pressedKeyCombo = new KeyCombination(unmodifiedKey);
+
+ if (IsKeyPressed(KeysEx.ControlKey))
+ pressedKeyCombo.Add(KeysEx.Control);
+
+ if (IsKeyPressed(KeysEx.ShiftKey))
+ pressedKeyCombo.Add(KeysEx.Shift);
+
+ if (IsKeyPressed(KeysEx.LMenu) || IsKeyPressed(KeysEx.RMenu))
+ pressedKeyCombo.Add(KeysEx.Alt);
+
+ if ((IsKeyPressed(KeysEx.LWin) || IsKeyPressed(KeysEx.RWin)))
+ pressedKeyCombo.Add(KeysEx.WinLogo);
+
+ return Combination == pressedKeyCombo;
+ }
+
+ ///
+ /// The callback proceedure for the installed KeyboardHook.
+ ///
+ /// A code the KeyboardHook procedure uses to determine how to process the message.
+ /// The key state.
+ /// The key pressed.
+ /// A value indicating whether or not to process additional hooks in the current hook chain.
+ IntPtr HookCallBack(int nCode, IntPtr wParam, IntPtr lParam)
+ {
+ if (isKeyboardLockedDown)
+ return new IntPtr(1); // A non-zero return value blocks additional processing of key strokes.
+
+ // MSDN documentation indicates that nCodes less than 0 should always only invoke CallNextHookEx.
+ if (nCode < 0)
+ return NativeMethods.CallNextHookEx(hookId, nCode, wParam, lParam);
+
+ int keyStateParam = (int)wParam;
+ KeyState keyState;
+
+ if (keyStateParam == keyUp || keyStateParam == systemKeyUp)
+ keyState = KeyState.Up;
+ else if (keyStateParam == keyDown || keyStateParam == systemKeyDown)
+ keyState = KeyState.Down;
+ else
+ throw new ArgumentOutOfRangeException("wParam", "Invalid key state detected.");
+
+ var pressedKey = (KeysEx)Marshal.ReadInt32(lParam);
+
+ if (!CheckIsHookKeyCombination(pressedKey))
+ return NativeMethods.CallNextHookEx(hookId, nCode, wParam, lParam);
+
+ if (keyState == KeyState.Up)
+ {
+ keyReleased = true;
+ return NativeMethods.CallNextHookEx(hookId, nCode, wParam, lParam);
+ }
+
+ // If AutoRepeat is on, always process hook; otherwise, only process hook if they key has been released and re-pressed.
+ if (AutoRepeat || keyReleased)
+ {
+ keyReleased = false;
+ OnPressed();
+ }
+
+ if (AllowPassThrough)
+ return NativeMethods.CallNextHookEx(hookId, nCode, wParam, lParam);
+
+ return new IntPtr(1);
+ }
+ }
+
+ public class KeyboardLockDownKeyPressedEventArgs : EventArgs
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The keys that were pressed.
+ /// The state of the pressed keys.
+ ///
+ public KeyboardLockDownKeyPressedEventArgs(KeysEx keys, KeyState state)
+ {
+ Keys = keys;
+ State = state;
+ }
+
+ ///
+ /// Gets the keys that were pressed.
+ ///
+ public KeysEx Keys { get; private set; }
+
+ ///
+ /// Gets the state of the pressed keys.
+ ///
+ public KeyState State { get; private set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Program.cs b/src/Program.cs
new file mode 100755
index 0000000..6e0cc04
--- /dev/null
+++ b/src/Program.cs
@@ -0,0 +1,22 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using System.Windows.Forms;
+
+namespace KeyboardInputSwitch
+{
+ static class Program
+ {
+ ///
+ /// The main entry point for the application.
+ ///
+ [STAThread]
+ static void Main()
+ {
+ Application.EnableVisualStyles();
+ Application.SetCompatibleTextRenderingDefault(false);
+ Application.Run(new Form1());
+ }
+ }
+}
diff --git a/src/Properties/AssemblyInfo.cs b/src/Properties/AssemblyInfo.cs
new file mode 100755
index 0000000..b911141
--- /dev/null
+++ b/src/Properties/AssemblyInfo.cs
@@ -0,0 +1,36 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// General Information about an assembly is controlled through the following
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+[assembly: AssemblyTitle("KeyboardInputSwitch")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("KeyboardInputSwitch")]
+[assembly: AssemblyCopyright("Copyright © 2021")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Setting ComVisible to false makes the types in this assembly not visible
+// to COM components. If you need to access a type in this assembly from
+// COM, set the ComVisible attribute to true on that type.
+[assembly: ComVisible(false)]
+
+// The following GUID is for the ID of the typelib if this project is exposed to COM
+[assembly: Guid("1160bd58-b82d-4d9a-a88f-4d465b81ab57")]
+
+// Version information for an assembly consists of the following four values:
+//
+// Major Version
+// Minor Version
+// Build Number
+// Revision
+//
+// You can specify all the values or you can default the Build and Revision Numbers
+// by using the '*' as shown below:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
diff --git a/src/Properties/Resources.Designer.cs b/src/Properties/Resources.Designer.cs
new file mode 100755
index 0000000..37ef0ca
--- /dev/null
+++ b/src/Properties/Resources.Designer.cs
@@ -0,0 +1,71 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace KeyboardInputSwitch.Properties
+{
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class Resources
+ {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal Resources()
+ {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager
+ {
+ get
+ {
+ if ((resourceMan == null))
+ {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("KeyboardInputSwitch.Properties.Resources", typeof(Resources).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture
+ {
+ get
+ {
+ return resourceCulture;
+ }
+ set
+ {
+ resourceCulture = value;
+ }
+ }
+ }
+}
diff --git a/src/Properties/Resources.resx b/src/Properties/Resources.resx
new file mode 100755
index 0000000..ffecec8
--- /dev/null
+++ b/src/Properties/Resources.resx
@@ -0,0 +1,117 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
\ No newline at end of file
diff --git a/src/Properties/Settings.Designer.cs b/src/Properties/Settings.Designer.cs
new file mode 100755
index 0000000..afff720
--- /dev/null
+++ b/src/Properties/Settings.Designer.cs
@@ -0,0 +1,30 @@
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace KeyboardInputSwitch.Properties
+{
+
+
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
+ internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
+ {
+
+ private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
+
+ public static Settings Default
+ {
+ get
+ {
+ return defaultInstance;
+ }
+ }
+ }
+}
diff --git a/src/Properties/Settings.settings b/src/Properties/Settings.settings
new file mode 100755
index 0000000..abf36c5
--- /dev/null
+++ b/src/Properties/Settings.settings
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+