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