From 5785b0ac5442dace1564885340b5bc04258ae3cd Mon Sep 17 00:00:00 2001 From: Allen Wolf <63997543+aaw3@users.noreply.github.com> Date: Tue, 9 Jan 2024 20:25:25 -0600 Subject: [PATCH] Add Project --- .gitignore | 8 +- App.xaml | 9 + App.xaml.cs | 17 + AssemblyInfo.cs | 10 + Config.cs | 107 +++++ ConfigClasses.cs | 43 ++ DllInject.cs | 17 + Handler.cs | 141 ++++++ HandlerCommunications.cs | 30 ++ KeyData.cs | 157 +++++++ KeyHandling.cs | 25 ++ KeyboardData.cs | 119 +++++ KeyboardHook.cs | 448 +++++++++++++++++++ MacroBoard.csproj | 47 ++ MacroBoard.csproj.user | 17 + MacroBoard.sln | 31 ++ MainWindow.xaml | 118 +++++ MainWindow.xaml.cs | 686 +++++++++++++++++++++++++++++ Messages.cs | 776 +++++++++++++++++++++++++++++++++ ModData.cs | 23 + NativeMethods.cs | 172 ++++++++ Properties/launchSettings.json | 8 + Tunnel.cs | 37 ++ VolumeControl.cs | 231 ++++++++++ app.manifest | 76 ++++ 25 files changed, 3350 insertions(+), 3 deletions(-) create mode 100755 App.xaml create mode 100755 App.xaml.cs create mode 100755 AssemblyInfo.cs create mode 100755 Config.cs create mode 100755 ConfigClasses.cs create mode 100755 DllInject.cs create mode 100755 Handler.cs create mode 100755 HandlerCommunications.cs create mode 100755 KeyData.cs create mode 100755 KeyHandling.cs create mode 100755 KeyboardData.cs create mode 100755 KeyboardHook.cs create mode 100755 MacroBoard.csproj create mode 100755 MacroBoard.csproj.user create mode 100755 MacroBoard.sln create mode 100755 MainWindow.xaml create mode 100755 MainWindow.xaml.cs create mode 100755 Messages.cs create mode 100755 ModData.cs create mode 100755 NativeMethods.cs create mode 100755 Properties/launchSettings.json create mode 100755 Tunnel.cs create mode 100755 VolumeControl.cs create mode 100755 app.manifest diff --git a/.gitignore b/.gitignore index d9a98a5..896bfac 100644 --- a/.gitignore +++ b/.gitignore @@ -7,10 +7,12 @@ # or wget: # wget --no-check-certificate http://gist.githubusercontent.com/kmorcinek/2710267/raw/.gitignore +RawInput.Sharp/* + # User-specific files -*.suo -*.user -*.sln.docstates +.suo +.user +.sln.docstates # Build results [Dd]ebug/ diff --git a/App.xaml b/App.xaml new file mode 100755 index 0000000..7a61778 --- /dev/null +++ b/App.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/App.xaml.cs b/App.xaml.cs new file mode 100755 index 0000000..ca244cc --- /dev/null +++ b/App.xaml.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; + +namespace MacroBoard +{ + /// + /// Interaction logic for App.xaml + /// + public partial class App : Application + { + } +} diff --git a/AssemblyInfo.cs b/AssemblyInfo.cs new file mode 100755 index 0000000..74087a1 --- /dev/null +++ b/AssemblyInfo.cs @@ -0,0 +1,10 @@ +using System.Windows; + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] diff --git a/Config.cs b/Config.cs new file mode 100755 index 0000000..5a63aeb --- /dev/null +++ b/Config.cs @@ -0,0 +1,107 @@ +using Linearstar.Windows.RawInput; +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Windows; +using System.Xml; +using System.Xml.Serialization; +using System.Linq; + +namespace MacroBoard +{ + class Config + { + public static readonly string ConfigDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Configs"); + public static readonly string AppConfigDir = Path.Combine(ConfigDir, "Application"); + public static readonly string ModConfigDir = Path.Combine(ConfigDir, "Mods"); + + public static readonly string AppConfigDevicesPath = Path.Combine(AppConfigDir, "Devices.cfg"); + + /// + /// Saves and updates the device config if a new device is found + /// + /// The DeviceConfig object being stored + /// Path where the configuration file will be saved + public static void SaveDevices(DeviceConfig deviceConfig, Stream saveStream) + { + XmlSerializer xs = new XmlSerializer(typeof(DeviceConfig)); + + xs.Serialize(saveStream, deviceConfig); + } + + public static DeviceConfig ReadDevices(string savePath) + { + XmlSerializer xs = new XmlSerializer(typeof(DeviceConfig)); + + using (TextReader tw = new StreamReader(savePath)) + { + return (DeviceConfig)xs.Deserialize(tw); + } + } + + //??? + public static DeviceConfig GetDevices() + { + return new DeviceConfig(/*new bool(),*/ new List()); + } + + //??? + public static bool NewDevicesFound() + { + return new bool(); + } + + public static void /*DeviceConfig*/ HandleDeviceConfig(List keyboardList) + { + if (File.Exists(AppConfigDevicesPath)) + { + DeviceConfig dc = ReadDevices(AppConfigDevicesPath); + + //New Keyboard(s) Found + if (dc.Keyboards.Count < keyboardList.Count) + { + List AddedKeyboardsList = new List(); + + AddedKeyboardsList = keyboardList.Where(x => !dc.Keyboards.Any(y => y.KeyboardName == x.KeyboardName)).ToList(); + + + foreach (var keyboard in dc.Keyboards) + System.Diagnostics.Debug.WriteLine("dc.Keyboards: " + keyboard.KeyboardName); + + foreach (var keyboard in keyboardList) + { + System.Diagnostics.Debug.WriteLine("keyboardList: " + keyboard.KeyboardName); + } + + string addedKeyboards = ""; + foreach (var keyboard in AddedKeyboardsList) + addedKeyboards += $"\r\n{keyboard.KeyboardName}\r\n"; + + MessageBox.Show($"New devices have been found.\r\nPress \"Yes\" if you would like to configure these devices.\r\n\r\nAdded Devices:\r\n{addedKeyboards}", "New Devices Found", MessageBoxButton.YesNo, MessageBoxImage.Question); + } + //Old Keyboard(s) Removed + else if (dc.Keyboards.Count > keyboardList.Count) + { + List RemovedKeyboardsList = new List(); + + RemovedKeyboardsList = dc.Keyboards.Where(x => !keyboardList.Any(y => x.KeyboardName == y.KeyboardName)).ToList(); + + string removedKeyboards = ""; + foreach (var keyboard in RemovedKeyboardsList) + removedKeyboards += $"\r\n{keyboard.KeyboardName}\r\n"; + + MessageBox.Show($"Old devices have been removed.\r\nRemoved Devices:\r\n{removedKeyboards}", "Old Devices Removed", MessageBoxButton.YesNo, MessageBoxImage.Question); + } + } + else + { + using (var f = File.Create(AppConfigDevicesPath)) + { + DeviceConfig dc = new DeviceConfig(keyboardList); + SaveDevices(dc, f); + } + } + } + } +} diff --git a/ConfigClasses.cs b/ConfigClasses.cs new file mode 100755 index 0000000..d14f797 --- /dev/null +++ b/ConfigClasses.cs @@ -0,0 +1,43 @@ +using System; +using System.Collections.Generic; + +namespace MacroBoard +{ + public class DeviceConfig + { + public DeviceConfig(List keyboards) + { + Keyboards = keyboards; + } + + //Need a blank constructor to allow for Xml Deserialization + private DeviceConfig() { } + + + public List Keyboards { get; set; } + } + + public class KeyboardConfig + { + public KeyboardConfig(string keyboardAlias, bool isMacroBoard, bool hasAutoNumLock, string keyboardName, string keyboardPath, bool isDefaultMacroBoard) + { + KeyboardAlias = keyboardAlias; + IsMacroBoard = isMacroBoard; + HasAutoNumLock = hasAutoNumLock; + KeyboardName = keyboardName; + KeyboardPath = keyboardPath; + IsDefaultMacroBoard = isDefaultMacroBoard; + } + + //Need a blank constructor to allow for Xml Deserialization + private KeyboardConfig() { } + + public string KeyboardAlias { get; set; } + public bool IsMacroBoard { get; set; } + public bool HasAutoNumLock { get; set; } + public string KeyboardName { get; set; } + public string KeyboardPath { get; set; } + + public bool IsDefaultMacroBoard { get; set; } + } +} diff --git a/DllInject.cs b/DllInject.cs new file mode 100755 index 0000000..c446074 --- /dev/null +++ b/DllInject.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; + +namespace MacroBoard.Inject +{ +#warning Need to get proper unmangled names in the future. + class DllInject + { + [DllImport("KeyboardHook.dll")] + public static extern int InstallHook(IntPtr hwndParent); + + [DllImport("KeyboardHook.dll")] + public static extern int UninstallHook(); + } +} diff --git a/Handler.cs b/Handler.cs new file mode 100755 index 0000000..3162f69 --- /dev/null +++ b/Handler.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Text; + +namespace MacroBoard +{ + class Handler + { + //Contains list of ModKeyCombination objects accessible by the mod's Type + public static Dictionary> ModsKeyCombinations = new Dictionary>(); + + /// + /// This is a boolean mapping a ModType to a boolean saying whether or not it is allowed to set key combinations determined by the HandlerCommunications.HandleType + /// + private static Dictionary SupportedByHandler = new Dictionary(); + + public static void SetHandlerType(dynamic modType, bool b) + { + SupportedByHandler.Add(modType, b); + } + + public static void HandleCombination(List Keys) + { + + var modDataList = ModData.ModDataList; + + for (int i = 0; i < modDataList.Count; i++) + { + //Contains list of KeyCombinations under a specific mod + var modKeyCombinationsList = ModsKeyCombinations[modDataList[i].Type]; + + for (int j = 0; j < modKeyCombinationsList.Count; j++) + { + + int keysMatching = 0; + var ModComboKeys = modKeyCombinationsList[j].Keys; + + for (int k = 0; k < ModComboKeys.Count; k++) + { + + for (int l = 0; l < Keys.Count; l++) + { + + if (KeyData.CompareToModKeybind(ModComboKeys[k], Keys[l], MainWindow.DefaultKeyboardAlias)) + { + keysMatching++; + } + } + + } + + if (keysMatching == ModComboKeys.Count) + { + Debug.WriteLine("Calling Method: " + modKeyCombinationsList[j].CallbackMethod.Method.Name); + modKeyCombinationsList[j].CallbackMethod.Invoke(); + } + } + } + } + } + + public class ModKeyCombination + { + //Convert List<[Mod Name].ModKeyComination> to List Not sure how to cast different types, so copying each invidivual primitive at a time. + public static List Convert(dynamic data) + { + List ComboList = new List(); + Debug.WriteLine(data.Count as object); + for (int i = 0; i < data.Count; i++) + { + ModKeyCombination Combo = new ModKeyCombination(); + foreach (PropertyInfo prop in data[i].GetType().GetProperties()) + { + if (prop.Name == "Keys") + { + List keyListData = new List(); + var modKeys = prop.GetValue(data[i]); + for (int j = 0; j < modKeys.Count; j++) + { + KeyData keyData = new KeyData(); + + foreach (PropertyInfo propKey in modKeys[j].GetType().GetProperties()) + { + + typeof(KeyData) + .GetProperty(propKey.Name, BindingFlags.Instance | BindingFlags.Public) + .SetValue(keyData, propKey.GetValue(modKeys[j])); + } + + keyListData.Add(keyData); + } + + Combo.Keys = keyListData; + } + else + { + typeof(ModKeyCombination) + .GetProperty(prop.Name, BindingFlags.Instance | BindingFlags.Public) + .SetValue(Combo, prop.GetValue(data[i])); + + } + + + } + + ComboList.Add(Combo); + } + + return ComboList; + } + + public static void SetModCombinations(dynamic modType, List Combinations) + { + if (!Handler.ModsKeyCombinations.ContainsKey(modType)) + { + Handler.ModsKeyCombinations.Add(modType, Combinations); + } + else + { + Handler.ModsKeyCombinations[modType] = Combinations; + } + } + + public ModKeyCombination() + { + + } + + public ModKeyCombination(List keys, Action callbackMethod) + { + Keys = keys; + CallbackMethod = callbackMethod; + + } + + + public List Keys { get; set; } + public Action CallbackMethod { get; set; } + } diff --git a/HandlerCommunications.cs b/HandlerCommunications.cs new file mode 100755 index 0000000..6d1ed2a --- /dev/null +++ b/HandlerCommunications.cs @@ -0,0 +1,30 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MacroBoard +{ + class HandlerCommunications + { + /// + /// Tells the Mod Handler how keys should be handled. + /// + public enum HandleType + { + /// + /// Have the mod handler (Main Application) handle all of the keys. This means the mod won't recieve "Call" method calls. + /// + HandlerOnly, + + /// + /// Have the mod handle all of the keys. This means the mod handler (Main Application) won't accept any [List<KeyCombination>, delegate] dicts. + /// + ModOnly, + + /// + /// Have both the mod handler (Main Application) and mod handle the keys. This means the mod will recieve "Call" method calls, and can send [List<KeyCombination>, delegate] dicts to the mod handler. + /// + Both + } + } +} diff --git a/KeyData.cs b/KeyData.cs new file mode 100755 index 0000000..1f53b6e --- /dev/null +++ b/KeyData.cs @@ -0,0 +1,157 @@ +using Linearstar.Windows.RawInput; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using static MacroBoard.Hook.KeyboardHook; + +namespace MacroBoard +{ + + + public class KeyData + { + public KeyData() { } + + private static List Keys = new List(); + + public static void ProcessKey(KeyData data, uint WindowMessage) + { + KeyState state = KeyState.Unknown; + + if ((KeyMessages)WindowMessage == KeyMessages.WM_KEYDOWN || (KeyMessages)WindowMessage == KeyMessages.WM_SYSKEYDOWN) + { + state = KeyState.Down; + } else if ((KeyMessages)WindowMessage == KeyMessages.WM_KEYUP || (KeyMessages)WindowMessage == KeyMessages.WM_SYSKEYUP) + { + state = KeyState.Up; + } + else + { + //state is Unknown, this shouldn't happen + throw new Exception("KeyState Unknown"); + } + + if (state == KeyState.Down) + { + //boolean determining if this key is already down + bool keyAlreadyExists = false; + for (int i = 0; i < Keys.Count; i++) + { + KeyData key = Keys[i]; + if (Compare(data, key)) + { + keyAlreadyExists = true; + } + + if (!keyAlreadyExists) + Keys.Add(data); + + + + } + + Debug.WriteLine("Keys count: " + Keys.Count); + + Keys.ForEach(kbd => Debug.WriteLine(kbd.KeyboardAlias + " : (" + (VKeys)kbd.VirtualKey + ") " + kbd.VirtualKey + " : (" + (RawKeyboardFlags)kbd.Flags + ") " + kbd.Flags + " : " + kbd.ScanCode)); + Debug.WriteLine("\r\n"); + + if (state == KeyState.Up) + { + for (int i = 0; i < Keys.Count; i++) + { + KeyData key = Keys[i]; + + if (Compare(data, key)) + { + Keys.Remove(key); + } + + } + } + else + { + //Check all Key Combinations + Handler.HandleCombination(Keys); + } + } + + public static KeyData Format(string KeyboardAlias, RawInputKeyboardData data) + { + + return new KeyData( + KeyboardAlias, + (int)data.Keyboard.Flags, + data.Keyboard.ScanCode, + data.Keyboard.VirutalKey + ); + } + + public static bool Compare(KeyData key1, KeyData key2) + { + RawKeyboardFlags key1Flags = (RawKeyboardFlags)key1.Flags; + RawKeyboardFlags key2Flags = (RawKeyboardFlags)key2.Flags; + + if (key1.KeyboardAlias == key2.KeyboardAlias && + key1Flags.HasFlag(RawKeyboardFlags.KeyE0) == key2Flags.HasFlag(RawKeyboardFlags.KeyE0) && //could technically just remove the None/KeyUp flag from both and it would solve the problem simpler. + key1Flags.HasFlag(RawKeyboardFlags.KeyE1) == key2Flags.HasFlag(RawKeyboardFlags.KeyE1) && + key1.ScanCode == key2.ScanCode && + key1.VirtualKey == key2.VirtualKey) + { + return true; + } + return false; + } + + //Same as Compare, but is used specifically for comparing Keys and ModKeyCombinations specifically for KeyAlias being [Default] + public static bool CompareToModKeybind(KeyData modData, KeyData keyData, string ReplaceDefault) + { + RawKeyboardFlags key1Flags = (RawKeyboardFlags)modData.Flags; + RawKeyboardFlags key2Flags = (RawKeyboardFlags)keyData.Flags; + + if (((modData.KeyboardAlias == keyData.KeyboardAlias) || (modData.KeyboardAlias == "[Default]" && ReplaceDefault == keyData.KeyboardAlias)) && + key1Flags.HasFlag(RawKeyboardFlags.KeyE0) == key2Flags.HasFlag(RawKeyboardFlags.KeyE0) && //could technically just remove the None/KeyUp flag from both and it would solve the problem simpler. + key1Flags.HasFlag(RawKeyboardFlags.KeyE1) == key2Flags.HasFlag(RawKeyboardFlags.KeyE1) && + modData.ScanCode == keyData.ScanCode && + modData.VirtualKey == keyData.VirtualKey) + { + return true; + } + return false; + } + + public int Flags { get; set; } + public int ScanCode { get; set; } + public int VirtualKey { get; set; } + + //Custom made data + + //The string passed when the key is pressed to tell the mod what keyboard it was sent from. + public /*readonly*/ string KeyboardAlias { get; set; } + + + public KeyData(string keyboardAlias, int flags, int scanCode, int virtualKey) + { + KeyboardAlias = keyboardAlias; + Flags = flags; + ScanCode = scanCode; + VirtualKey = virtualKey; + } + } + + public enum KeyState + { + Unknown, + Down, + Up, + } + + [Flags] + public enum RawKeyboardFlags : ushort + { + None = 0, + Up = 1 << 0, + KeyE0 = 1 << 1, + KeyE1 = 1 << 2, + } +} diff --git a/KeyHandling.cs b/KeyHandling.cs new file mode 100755 index 0000000..dd403d1 --- /dev/null +++ b/KeyHandling.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace MacroBoard +{ + public class KeyHandling + { + [Flags] + public enum KeyModifiers + { + None = 0, + LShift = 1 << 0, + RShift = 1 << 1, + LControl = 1 << 2, + RControl = 1 << 3, + LAlt = 1 << 4, + RAlt = 1 << 5, + LWin = 1 << 6, + RWin = 1 << 7 + } + + + } +} diff --git a/KeyboardData.cs b/KeyboardData.cs new file mode 100755 index 0000000..6cb0e16 --- /dev/null +++ b/KeyboardData.cs @@ -0,0 +1,119 @@ +using Linearstar.Windows.RawInput; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using static MacroBoard.Hook.KeyboardHook; + +namespace MacroBoard +{ + + + class KeyboardData + { + private static List ActiveData = new List(); + + public static void ProcessKey(KeyboardData data) + { + KeyState state = KeyState.Unknown; + + if ((KeyMessages)data.WindowMessage == KeyMessages.WM_KEYDOWN || (KeyMessages)data.WindowMessage == KeyMessages.WM_SYSKEYDOWN) + { + state = KeyState.Down; + } else if ((KeyMessages)data.WindowMessage == KeyMessages.WM_KEYUP || (KeyMessages)data.WindowMessage == KeyMessages.WM_SYSKEYUP) + { + state = KeyState.Up; + } + else + { + //state is Unknown, this shouldn't happen + throw new Exception("KeyState Unknown"); + } + + if (state == KeyState.Down) + { + ActiveData.Add(data); + } + + ActiveData.ForEach(kbd => Debug.WriteLine(kbd.KeyboardAlias + " : " + (VKeys)kbd.VirtualKey + " : " + kbd.Flags + " : " + kbd.ScanCode)); + Debug.WriteLine("\r\n"); + + if (state == KeyState.Up) + { + for (int i = 0; i < ActiveData.Count; i++) + { + KeyboardData kbd = ActiveData[i]; + + + RawKeyboardFlags kbdFlags = (RawKeyboardFlags)kbd.Flags; + RawKeyboardFlags dataFlags = (RawKeyboardFlags)data.Flags; + + if (kbd.KeyboardAlias == data.KeyboardAlias && + kbdFlags.HasFlag(RawKeyboardFlags.KeyE0) == dataFlags.HasFlag(RawKeyboardFlags.KeyE0) && //could technically just remove the None/KeyUp flag from both and it would solve the problem simpler. + kbdFlags.HasFlag(RawKeyboardFlags.KeyE1) == dataFlags.HasFlag(RawKeyboardFlags.KeyE1) && + kbd.ScanCode == data.ScanCode && + kbd.VirtualKey == data.VirtualKey) + { + ActiveData.Remove(kbd); + } + } + } + } + + public static KeyboardData Format(string KeyboardAlias, RawInputKeyboardData data) + { + // Using WindowMessage to determine key state + return new KeyboardData( + KeyboardAlias, + data.Keyboard.ExtraInformation, + (int)data.Keyboard.Flags, + data.Keyboard.ScanCode, + data.Keyboard.VirutalKey, + data.Keyboard.WindowMessage + ); + } + + //Passed data from "RawInputKeyboardData" + + public readonly uint ExtraInformation; + public readonly /*RawKeyboardFlags*/ int Flags; + public readonly int ScanCode; + public readonly int VirtualKey; + public readonly uint WindowMessage; + + //Custom made data + + //The string passed when the key is pressed to tell the mod what keyboard it was sent from. + public readonly string KeyboardAlias; + + //Determine if any key modifiers are presed. + //public readonly int KeyModifiers; + + + public KeyboardData(string keyboardAlias, uint extraInformation, int flags, int scanCode, int virtualKey, uint windowMessage) + { + KeyboardAlias = keyboardAlias; + ExtraInformation = extraInformation; + Flags = flags; + ScanCode = scanCode; + VirtualKey = virtualKey; + WindowMessage = windowMessage; + } + } + + public enum KeyState + { + Unknown, + Down, + Up, + } + + [Flags] + public enum RawKeyboardFlags : ushort + { + None = 0, + Up = 1 << 0, + KeyE0 = 1 << 1, + KeyE1 = 1 << 2, + } +} diff --git a/KeyboardHook.cs b/KeyboardHook.cs new file mode 100755 index 0000000..ea5af97 --- /dev/null +++ b/KeyboardHook.cs @@ -0,0 +1,448 @@ +using MacroBoard.Native; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Windows; +using System.Windows.Input; +using System.Windows.Interop; + +namespace MacroBoard.Hook +{ + /// + /// 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 + + 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 + } + + public enum CustomMessages + { + WM_APP = 0x8000, //32768 + WM_HOOK = WM_APP + 1, //32769 + WM_INPUT_DEVICE_CHANGE = 0x00FE, //254 + } + + + + /// + /// 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; + + /// + /// Window Handle to send message of incoming keys + /// + private IntPtr hWndServer; + + /// + /// Install low level keyboard hook + /// + public void Install(IntPtr hWndParent, Hooks hookType, params dynamic[] data) + { + switch (hookType) + { + case Hooks.WH_KEYBOARD: + IntPtr hInstance = data[0]; + if (hInstance != IntPtr.Zero) + { + var hProc = Libraries.GetProcAddress(hInstance, "InstallHook"); + Debug.WriteLine("HPROC: " + hProc); + hookID = SetWindowsHookEx((int)Hooks.WH_KEYBOARD, hProc, hInstance, 0); + } + break; + + case Hooks.WH_KEYBOARD_LL: + hookHandler = HookFunc; + hookID = SetHook_LL(hookHandler); + break; + } + + hWndServer = hWndParent; + + } + + /// + /// Remove low level keyboard hook + /// + public void Uninstall() + { + UnhookWindowsHookEx(hookID); + } + + /// + /// Registers hook with Windows API + /// + /// Callback function + /// Hook ID + //private IntPtr SetHook(Hooks hookType, ) + //{ + // using (ProcessModule module = Process.GetCurrentProcess().MainModule) + // return SetWindowsHookEx((int)Hooks.WH_KEYBOARD, proc, GetModuleHandle(module.ModuleName), 0); + + //} + + + /// + /// Registers LOW LEVEL hook with Windows API + /// + /// Callback function + /// Hook ID + private IntPtr SetHook_LL(KeyboardHookHandler proc) + { + using (ProcessModule module = Process.GetCurrentProcess().MainModule) + return SetWindowsHookEx((int)Hooks.WH_KEYBOARD_LL, proc, GetModuleHandle(module.ModuleName), 0); + + } + + + /// + /// Default hook call, which analyses pressed keys + /// + private IntPtr HookFunc(int nCode, IntPtr wParam, IntPtr lParam) + { + //new Thread(x => { MessageBox.Show("HOOK FUNC"); }).Start(); + if (nCode >= 0) + { + Debug.WriteLine("WITHOUT SENDMESSAGE"); + + //1 means handled, 0 means unhandled + bool handled = Messages.SendMessage(hWndServer, (int)CustomMessages.WM_HOOK, wParam, lParam) == (IntPtr)1 ? true : false; + Debug.WriteLine("SEND MESSAGE HANDLED? : " + handled); + + if (handled) + { + return (IntPtr)1; + } + + int iwParam = wParam.ToInt32(); + + if ((iwParam == (int)KeyMessages.WM_KEYDOWN || iwParam == (int)KeyMessages.WM_SYSKEYDOWN)) + if (KeyDown != null) + KeyDown((VKeys)Marshal.ReadInt32(lParam)); + if ((iwParam == (int)KeyMessages.WM_KEYUP || iwParam == (int)KeyMessages.WM_SYSKEYUP)) + if (KeyUp != null) + KeyUp((VKeys)Marshal.ReadInt32(lParam)); + } + + return CallNextHookEx(hookID, nCode, wParam, lParam); + } + + /// + /// Destructor. Unhook current hook + /// + ~KeyboardHook() + { + Uninstall(); + } + public enum Hooks + { + WH_KEYBOARD = 2, + WH_KEYBOARD_LL = 13 + } + + /// + /// Low-Level function declarations + /// + #region WinAPI + + + public enum KeyMessages + { + WM_KEYDOWN = 0x100, + WM_SYSKEYDOWN = 0x104, + WM_KEYUP = 0x101, + 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", SetLastError = true)] + static extern IntPtr SetWindowsHookEx(int hookType, IntPtr 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 + } + + /// + /// Doesn't work (only works with WH_KEYBOARD_LL) + /// + [StructLayout(LayoutKind.Sequential)] + public class KBDLLHOOKSTRUCT + { + public uint vkCode; + public uint scanCode; + public KBDLLHOOKSTRUCTFlags flags; + public uint time; + public UIntPtr dwExtraInfo; + } + + /// + /// Doesn't work (only works with WH_KEYBOARD_LL) + /// + [Flags] + public enum KBDLLHOOKSTRUCTFlags : uint + { + LLKHF_EXTENDED = 0x01, + LLKHF_INJECTED = 0x10, + LLKHF_ALTDOWN = 0x20, + LLKHF_UP = 0x80, + } + + //[StructLayout(LayoutKind.Sequential)] + //public class keymsgflags + //{ + + // unsigned int repcnt : 16; + // unsigned int scancode : 8; + // unsigned int extkey : 1; + // unsigned int reserved : 4; + // unsigned int context : 1; + // unsigned int prevstate : 1; + // unsigned int transition : 1; + //}; + + [Flags] + public enum KBDHOOKSTRUCTFlags : uint + { + KHF_EXTENDED = 0x01000000, + KHF_INJECTED = 0x10000000, + //Not sure if the KHF_LOWER_IL_INJECTED is correct, might have to be 0x00000002? but the others were shown in that form but didn't need to be like that + KHF_LOWER_IL_INJECTED = 0x00200000, + KHF_ALTDOWN = 0x20000000, + KHF_UP = 0x80000000 + } +} diff --git a/MacroBoard.csproj b/MacroBoard.csproj new file mode 100755 index 0000000..5760379 --- /dev/null +++ b/MacroBoard.csproj @@ -0,0 +1,47 @@ + + + + WinExe + netcoreapp3.1 + true + app.manifest + + + + true + + + + true + full + true + + + + + + + + + + + + + + + + + + + + + + + + + + C:\Windows\Microsoft.NET\Framework\v2.0.50727\System.Windows.Forms.dll + + + + diff --git a/MacroBoard.csproj.user b/MacroBoard.csproj.user new file mode 100755 index 0000000..72081c1 --- /dev/null +++ b/MacroBoard.csproj.user @@ -0,0 +1,17 @@ + + + + true + + + + Designer + + + + + + Designer + + + \ No newline at end of file diff --git a/MacroBoard.sln b/MacroBoard.sln new file mode 100755 index 0000000..42815e6 --- /dev/null +++ b/MacroBoard.sln @@ -0,0 +1,31 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.31112.23 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MacroBoard", "MacroBoard.csproj", "{685525DF-1CE7-4BFA-94DF-60476F201C1A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Debug|x86 = Debug|x86 + Release|Any CPU = Release|Any CPU + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {685525DF-1CE7-4BFA-94DF-60476F201C1A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {685525DF-1CE7-4BFA-94DF-60476F201C1A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {685525DF-1CE7-4BFA-94DF-60476F201C1A}.Debug|x86.ActiveCfg = Debug|Any CPU + {685525DF-1CE7-4BFA-94DF-60476F201C1A}.Debug|x86.Build.0 = Debug|Any CPU + {685525DF-1CE7-4BFA-94DF-60476F201C1A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {685525DF-1CE7-4BFA-94DF-60476F201C1A}.Release|Any CPU.Build.0 = Release|Any CPU + {685525DF-1CE7-4BFA-94DF-60476F201C1A}.Release|x86.ActiveCfg = Release|Any CPU + {685525DF-1CE7-4BFA-94DF-60476F201C1A}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {30CBE9E0-E653-446E-821A-7D0FF24CF191} + EndGlobalSection +EndGlobal diff --git a/MainWindow.xaml b/MainWindow.xaml new file mode 100755 index 0000000..0b508f8 --- /dev/null +++ b/MainWindow.xaml @@ -0,0 +1,118 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +