From 9ea2ceba4e6768fb37d85a1245f191686b2cff11 Mon Sep 17 00:00:00 2001 From: FuPeiJiang <42662615+FuPeiJiang@users.noreply.github.com> Date: Wed, 17 Dec 2025 05:13:13 -0500 Subject: [PATCH] Switch to dotnet9.0 --- AHK v1/Combined Example.ahk | 4 +- AHK v1/Context Example.ahk | 4 +- .../Development Tools/AhiScanCodeTester.ahk | 12 +- AHK v1/Lib/AutoHotInterception.ahk | 12 +- AHK v1/Monitor.ahk | 4 +- AHK v1/RollMouse.ahk | 40 +-- AHK v1/SubscribeAbsolute dragging example.ahk | 2 +- AHK v1/SubscribeAll Example.ahk | 2 +- AHK v1/TabletLib/JSON.ahk | 22 +- AHK v1/TabletLib/TabletLib.ahk | 2 +- AHK v2/Context Example.ahk | 2 +- AHK v2/Lib/CLR.ahk | 14 +- AHK v2/Monitor.ahk | 4 +- C#/AutoHotInterception.sln | 4 +- .../AutoHotInterception.csproj | 80 +---- .../DeviceHandlers/KeyboardHandler.cs | 4 +- .../Helpers/ScanCodeHelper.cs | 2 +- C#/AutoHotInterception/Manager.cs | 10 +- .../Properties/AssemblyInfo.cs | 36 -- C#/AutoHotInterception/ScanCodeChecker.cs | 21 +- C#/AutoHotInterception/ToDo.md | 16 +- C#/Dependencies/Readme.md | 4 +- C#/Dependencies/x64/Readme.md | 2 +- C#/Dependencies/x86/Readme.md | 2 +- C#/TestApp/App.config | 6 - C#/TestApp/Helpers/TestDevices.cs | 7 +- C#/TestApp/KeyboardAndMouseTester.cs | 3 - C#/TestApp/Program.cs | 1 - C#/TestApp/Properties/AssemblyInfo.cs | 36 -- C#/TestApp/ScanCodeTester.cs | 3 +- C#/TestApp/TabletTester.cs | 5 - C#/TestApp/TestApp.csproj | 80 +---- C#/UnitTests/TranslateAhkCodeTests.cs | 11 +- C#/UnitTests/TranslateScanCodesTests.cs | 5 +- C#/UnitTests/UnitTests.csproj | 71 +--- CHANGELOG.md | 44 +-- README.md | 326 +++++++++--------- 37 files changed, 323 insertions(+), 580 deletions(-) delete mode 100644 C#/AutoHotInterception/Properties/AssemblyInfo.cs delete mode 100644 C#/TestApp/App.config delete mode 100644 C#/TestApp/Properties/AssemblyInfo.cs diff --git a/AHK v1/Combined Example.ahk b/AHK v1/Combined Example.ahk index a774e2e..ffd8f31 100644 --- a/AHK v1/Combined Example.ahk +++ b/AHK v1/Combined Example.ahk @@ -20,10 +20,10 @@ KeyEvent(state){ #if cm1.IsActive ::aaa::JACKPOT -1:: +1:: ToolTip % "KEY DOWN EVENT @ " A_TickCount return - + 1 up:: ToolTip % "KEY UP EVENT @ " A_TickCount return diff --git a/AHK v1/Context Example.ahk b/AHK v1/Context Example.ahk index 6e6b6b3..53ff85c 100644 --- a/AHK v1/Context Example.ahk +++ b/AHK v1/Context Example.ahk @@ -9,10 +9,10 @@ return #if cm1.IsActive ::aaa::JACKPOT -1:: +1:: ToolTip % "KEY DOWN EVENT @ " A_TickCount return - + 1 up:: ToolTip % "KEY UP EVENT @ " A_TickCount return diff --git a/AHK v1/Development Tools/AhiScanCodeTester.ahk b/AHK v1/Development Tools/AhiScanCodeTester.ahk index 2b81cff..c8192dd 100644 --- a/AHK v1/Development Tools/AhiScanCodeTester.ahk +++ b/AHK v1/Development Tools/AhiScanCodeTester.ahk @@ -4,7 +4,7 @@ ; REQUIRES AHK >= 1.1.32 /* -The purpose of this tool is to compare the keyboard events that AHK sees to the keyboard events that AHI sees +The purpose of this tool is to compare the keyboard events that AHK sees to the keyboard events that AHI sees To use it, set the vid and pid variables below to the VID and PID of a keyboard... ... then run this script and press keys ONLY ON THAT KEYBOARD Pressing keys on another keyboard will break the script! @@ -69,26 +69,26 @@ AhiKeyEvent(keyEvents){ msgbox % "Expecting 1 or 2 AHI key events, but got " numEvents ExitApp } - + ; Note that keyEvents is a ZERO-BASED array! ahiSc1 := keyEvents[0].Code ahiState1 := keyEvents[0].state - + if (numEvents == 2){ ahiSc2 := keyEvents[1].Code ahiState2 := keyEvents[1].state } - + ahkSc1 := ahkKeyEvent.Code if (ahkSc1 > 256){ ahkSc1 .= " (Ext " ahkSc1 - 256 ")" } ahkState1 := ahkKeyEvent.State - + row := LV_Add(, GetKeyName("SC" DecToHex(ahkKeyEvent.Code)), ahkSc1, ahkState1, ahiSc1, ahiState1, ahiSc2, ahiState2) LV_Modify(row, "Vis") - + AhkKeyBuffer := [] } diff --git a/AHK v1/Lib/AutoHotInterception.ahk b/AHK v1/Lib/AutoHotInterception.ahk index 9bde5ba..f067dca 100644 --- a/AHK v1/Lib/AutoHotInterception.ahk +++ b/AHK v1/Lib/AutoHotInterception.ahk @@ -92,7 +92,7 @@ class AutoHotInterception { SetState(state){ this.Instance.SetState(state) } - + MoveCursor(x, y, cm := "Screen", mouseId := -1){ if (mouseId == -1) mouseId := 11 ; Use 1st found mouse @@ -108,7 +108,7 @@ class AutoHotInterception { } CoordMode, Mouse, % oldMode } - + GetDirection(cp, dp){ d := dp - cp if (d > 0) @@ -176,7 +176,7 @@ class AutoHotInterception { SubscribeKeyboard(id, block, callback, concurrent := false) { this.Instance.SubscribeKeyboard(id, block, callback, concurrent) } - + UnsubscribeKeyboard(id){ this.Instance.UnsubscribeKeyboard(id) } @@ -192,7 +192,7 @@ class AutoHotInterception { SubscribeMouseButtons(id, block, callback, concurrent := false) { this.Instance.SubscribeMouseButtons(id, block, callback, concurrent) } - + UnsubscribeMouseButtons(id){ this.Instance.UnsubscribeMouseButtons(id) } @@ -251,12 +251,12 @@ class AutoHotInterception { this.id := id result := this.parent.Instance.SetContextCallback(id, this.OnContextCallback.Bind(this)) } - + OnContextCallback(state) { Sleep 0 this.IsActive := state } - + Remove(){ this.parent.Instance.RemoveContextCallback(this.id) } diff --git a/AHK v1/Monitor.ahk b/AHK v1/Monitor.ahk index 905ae6c..7a37695 100644 --- a/AHK v1/Monitor.ahk +++ b/AHK v1/Monitor.ahk @@ -39,7 +39,7 @@ Loop 2 { start := starts[devType] UpdateWidth(0, 1) ; Reset max width - + ; Add device entries Loop 10 { i := start + A_Index @@ -71,7 +71,7 @@ Loop 2 { xpos := columnX[devType] + idW + maxWidths[devType] Gui, Add, Button, % "x" xpos " y" rowY - vhOff " h14 w" copyW " hwndhwnd", Copy GuiControl, +g, % hwnd, % fn - + fn := Func("CopyClipboard").Bind(strings[A_index].handle) Gui, Add, Button, % "x" xpos " y" rowY + vhOff " h14 w" copyW " hwndhwnd", Copy GuiControl, +g, % hwnd, % fn diff --git a/AHK v1/RollMouse.ahk b/AHK v1/RollMouse.ahk index 7cb9e37..a1b070c 100644 --- a/AHK v1/RollMouse.ahk +++ b/AHK v1/RollMouse.ahk @@ -12,22 +12,22 @@ class RollMouse { MoveThreshold := {x: 4, y: 4} ; Good value for my mouse with FPS games: 4 ; Good value for my laptop trackpad: 3 - + ; The speed at which to move the mouse, can be decimals (eg 0.5) ; X and Y do not need to be equal ; Good value for my mouse with FPS games: x:2, y: 1 (don't need vertical roll so much) ;~ MoveFactor := {x: 1, y: 1} MoveFactor := {x: 0.5, y: 0.25} ; Good value for my laptop trackpad: 0.2 - + ; How fast (in ms) to send moves when rolling. ; High values for this will cause rolls to appear jerky instead of smooth ; if you halved this, double MoveFactor to get the same move amount, but at a faster frequency. RollFreq := 1 - + ; How long to wait after each move to decide whether a roll has taken place. TimeOutRate := 50 - + ; The amount that we are currently rolling by LastMove := {x: 0, y: 0} @@ -40,27 +40,27 @@ class RollMouse { STATE_OVER_THRESH := 2 STATE_ROLLING := 3 StateNames := ["UNDER THRESHOLD", "OVER THRESHOLD", "ROLLING"] - + State := 1 - + TimeOutFunc := 0 History := {} ; Movement history. The most recent item is first (Index 1), and old (high index) items get pruned off the end ; Was an option in old RollMouse Friction := 0 - + __New(mouseId){ this.TimeOutFunc := this.DoRoll.Bind(this) this.AHI := new AutoHotInterception() this.mouseId := mouseId this.AHI.SubscribeMouseMove(this.mouseId, false, this.MouseMove.Bind(this)) } - + MouseMove(x, y){ static axes := {x: 1, y: 2} ;~ ToolTip % x ", " y moved := {x: 0, y: 0} - + for axis, index in axes { obj := {} obj.delta_move := %axis% @@ -70,7 +70,7 @@ class RollMouse { if (obj.abs_delta_move >= this.MoveThreshold[axis]){ moved[axis] := 1 } - + this.UpdateHistory(axis, obj) } @@ -81,7 +81,7 @@ class RollMouse { this.ChangeState(this.STATE_UNDER_THRESH) } } - + UpdateHistory(axis, obj){ this.History[axis].InsertAt(1, obj) ; Enforce max number of entries @@ -90,16 +90,16 @@ class RollMouse { this.History[axis].RemoveAt(max, max - this.MOVE_BUFFER_SIZE) } } - + DoRoll(){ static axes := {x: 1, y: 2} - + ;s := "" - + if (this.State != this.STATE_ROLLING){ ; If roll has just started, calculate roll vector from movement history this.LastMove := {x: 0, y: 0} - + for axis in axes { ;s .= axis ": " trend := 0 @@ -127,7 +127,7 @@ class RollMouse { this.LastMove[axis] := round(this.LastMove[axis] * this.MoveFactor[axis]) } } - + if (this.LastMove.x = 0 && this.LastMove.y = 0){ return } @@ -157,10 +157,10 @@ class RollMouse { this.Debug("Changing State to : " this.StateNames[newstate]) this.State := newstate } - + ; DO NOT return if this.State == newstate! ; We need to reset the timer! - + if (this.State = this.STATE_UNDER_THRESH){ ; Kill the timer SetTimer % fn, Off @@ -175,11 +175,11 @@ class RollMouse { } */ } - + InitHistory(){ this.History := {x: [], y: []} } - + Debug(text){ OutputDebug % "AHK| " text } diff --git a/AHK v1/SubscribeAbsolute dragging example.ahk b/AHK v1/SubscribeAbsolute dragging example.ahk index 44a7230..0b216a8 100644 --- a/AHK v1/SubscribeAbsolute dragging example.ahk +++ b/AHK v1/SubscribeAbsolute dragging example.ahk @@ -33,7 +33,7 @@ ProcessInput(newState, nx := "", ny := ""){ ; x and y hold current x and current y ; state is the current state of the button ; stateChanged indicates whether state just changed or not - + ; ---- Start of your code ---- static dragStartX, dragStartY tooltip % "Current Coords: " x ", " y, 0, 0, 1 diff --git a/AHK v1/SubscribeAll Example.ahk b/AHK v1/SubscribeAll Example.ahk index d93227a..325675d 100644 --- a/AHK v1/SubscribeAll Example.ahk +++ b/AHK v1/SubscribeAll Example.ahk @@ -16,7 +16,7 @@ KeyEvent(code, state){ } MouseButtonEvent(code, state){ - ToolTip % "Mouse Button - Code: " code ", State: " state + ToolTip % "Mouse Button - Code: " code ", State: " state } ^Esc:: diff --git a/AHK v1/TabletLib/JSON.ahk b/AHK v1/TabletLib/JSON.ahk index c43980f..e5c8e5f 100644 --- a/AHK v1/TabletLib/JSON.ahk +++ b/AHK v1/TabletLib/JSON.ahk @@ -80,7 +80,7 @@ class JSON ; the 'IsArray' property. If so, Array() will be called normally, ; otherwise, use a custom base object for arrays static json_array := Func("Array").IsBuiltIn || ![].IsArray ? {IsArray: true} : 0 - + ; sacrifice readability for minor(actually negligible) performance gain (ch == "{") ? ( is_key := true @@ -89,12 +89,12 @@ class JSON ; ch == "[" : ( value := json_array ? new json_array : [] , next := json_value_or_array_closing ) - + ObjInsertAt(stack, 1, value) if (this.keys) this.keys[value] := [] - + } else { if (ch == q) { i := pos @@ -118,7 +118,7 @@ class JSON , value := StrReplace(value, "\t", "`t") pos := i ; update pos - + i := 0 while (i := InStr(value, "\",, i+1)) { if !(SubStr(value, i+1, 1) == "u") @@ -133,7 +133,7 @@ class JSON key := value, next := ":" continue } - + } else { value := SubStr(text, pos, i := RegExMatch(text, "[\]\},\s]|$",, pos)-pos) @@ -160,7 +160,7 @@ class JSON if (this.keys && this.keys.HasKey(holder)) this.keys[holder].Push(key) } - + } ; while ( ... ) return this.rev ? this.Walk(root, "") : root[""] @@ -169,7 +169,7 @@ class JSON ParseError(expect, text, pos, len:=1) { static q := Chr(34) - + line := StrSplit(SubStr(text, 1, pos), "`n", "`r").Length() col := pos - InStr(text, "`n",, -(StrLen(text)-pos+1)) msg := Format("{1}`n`nLine:`t{2}`nCol:`t{3}`nChar:`t{4}" @@ -195,7 +195,7 @@ class JSON if IsObject(value) for i, k in this.keys[value] value[k] := this.Walk.Call(this, value, k) ; bypass __Call - + return this.rev.Call(holder, key, value) } } @@ -254,7 +254,7 @@ class JSON is_array := value.IsArray ; Array() is not overridden, rollback to old method of ; identifying array-like objects. Due to the use of a for-loop - ; sparse arrays such as '[1,,3]' are detected as objects({}). + ; sparse arrays such as '[1,,3]' are detected as objects({}). if (!is_array) { for i in value is_array := i == A_Index @@ -266,7 +266,7 @@ class JSON Loop, % value.Length() { if (this.gap) str .= this.indent - + v := this.Str(value, A_Index) str .= (v != "") && value.HasKey(A_Index) ? v . "," : "null," } @@ -294,7 +294,7 @@ class JSON return is_array ? "[" . str . "]" : "{" . str . "}" } - + } else ; is_number ? value : "value" return ObjGetCapacity([value], 1)=="" ? value : this.Quote(value) } diff --git a/AHK v1/TabletLib/TabletLib.ahk b/AHK v1/TabletLib/TabletLib.ahk index 32ff3d7..e22613a 100644 --- a/AHK v1/TabletLib/TabletLib.ahk +++ b/AHK v1/TabletLib/TabletLib.ahk @@ -35,7 +35,7 @@ Class Box { StartY := 0 EndX := 0 EndY := 0 - + __New(name){ this.BoxName := name } diff --git a/AHK v2/Context Example.ahk b/AHK v2/Context Example.ahk index ffd77ed..343fde8 100644 --- a/AHK v2/Context Example.ahk +++ b/AHK v2/Context Example.ahk @@ -14,7 +14,7 @@ return ToolTip("KEY DOWN EVENT @ " A_TickCount) return } - + 1 up:: { ToolTip("KEY UP EVENT @ " A_TickCount) diff --git a/AHK v2/Lib/CLR.ahk b/AHK v2/Lib/CLR.ahk index 57fe49b..bdf37be 100644 --- a/AHK v2/Lib/CLR.ahk +++ b/AHK v2/Lib/CLR.ahk @@ -27,13 +27,13 @@ CLR_CreateObject(Assembly, TypeName, Args*) { if !(argCount := Args.Length) return Assembly.CreateInstance_2(TypeName, true) - + vargs := ComObjArray(0xC, argCount) Loop argCount vargs[A_Index-1] := Args[A_Index] - + static Array_Empty := ComObjArray(0xC,0), null := ComValue(13,0) - + return Assembly.CreateInstance_3(TypeName, true, 0, null, vargs, null, Array_Empty) } @@ -96,7 +96,7 @@ CLR_CompileAssembly(Code, References, ProviderAssembly, ProviderType, AppDomain: { if !AppDomain AppDomain := CLR_GetDefaultDomain() - + asmProvider := CLR_LoadLibrary(ProviderAssembly, AppDomain) codeProvider := asmProvider.CreateInstance(ProviderType) codeCompiler := codeProvider.CreateCompiler() @@ -108,7 +108,7 @@ CLR_CompileAssembly(Code, References, ProviderAssembly, ProviderType, AppDomain: aRefs := ComObjArray(8, Refs.Length) Loop Refs.Length aRefs[A_Index-1] := Refs[A_Index] - + ; Set parameters for compiler. prms := CLR_CreateObject(asmSystem, "System.CodeDom.Compiler.CompilerParameters", aRefs) , prms.OutputAssembly := FileName @@ -116,10 +116,10 @@ CLR_CompileAssembly(Code, References, ProviderAssembly, ProviderType, AppDomain: , prms.GenerateExecutable := SubStr(FileName,-4)=".exe" , prms.CompilerOptions := CompilerOptions , prms.IncludeDebugInformation := true - + ; Compile! compilerRes := codeCompiler.CompileAssemblyFromSource(prms, Code) - + if error_count := (errors := compilerRes.Errors).Count { error_text := "" diff --git a/AHK v2/Monitor.ahk b/AHK v2/Monitor.ahk index a5c48bd..e199a36 100644 --- a/AHK v2/Monitor.ahk +++ b/AHK v2/Monitor.ahk @@ -45,7 +45,7 @@ Loop 2 { start := starts[devType] UpdateWidth(0, 1) ; Reset max width - + ; Add device entries Loop 10 { i := start + A_Index @@ -80,7 +80,7 @@ Loop 2 { btnCopyVidPid := monitorGui.Add("Button", "x" xpos " y" rowY - vhOff " h14 w" copyW, "Copy") btnCopyVidPid.OnEvent("Click", CopyClipboard.Bind("0x" strings[A_index].vid ", 0x" strings[A_index].pid)) - + btnCopyHandle := monitorGui.Add("Button", "x" xpos " y" rowY + vhOff " h14 w" copyW, "Copy") btnCopyHandle.OnEvent("Click", CopyClipboard.Bind(strings[A_index].handle)) } diff --git a/C#/AutoHotInterception.sln b/C#/AutoHotInterception.sln index 5bbe268..895dd68 100644 --- a/C#/AutoHotInterception.sln +++ b/C#/AutoHotInterception.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2027 +# Visual Studio Version 18 +VisualStudioVersion = 18.0.11217.181 d18.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AutoHotInterception", "AutoHotInterception\AutoHotInterception.csproj", "{68FA4BC3-C277-44D0-8333-18D51DC3CA19}" EndProject diff --git a/C#/AutoHotInterception/AutoHotInterception.csproj b/C#/AutoHotInterception/AutoHotInterception.csproj index 8a2a4b5..4b2c64a 100644 --- a/C#/AutoHotInterception/AutoHotInterception.csproj +++ b/C#/AutoHotInterception/AutoHotInterception.csproj @@ -1,70 +1,20 @@  - - + - Debug - AnyCPU - {68FA4BC3-C277-44D0-8333-18D51DC3CA19} - Library - Properties - AutoHotInterception - AutoHotInterception - v4.6.1 - 512 + + net9.0 + enable + - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - if not exist "$(TargetDir)x86" mkdir "$(TargetDir)x86" -if not exist "$(TargetDir)x64" mkdir "$(TargetDir)x64" -if not exist "$(TargetDir)x86\interception.dll" xcopy /Q /Y "$(SolutionDir)dependencies\x86\interception.dll" "$(TargetDir)x86" -if not exist "$(TargetDir)x64\interception.dll" xcopy /Q /Y "$(SolutionDir)dependencies\x64\interception.dll" "$(TargetDir)x64" - - - xcopy /Q /Y "$(TargetPath)" "$(SolutionDir)..\Lib" - + + + + + + + + + + \ No newline at end of file diff --git a/C#/AutoHotInterception/DeviceHandlers/KeyboardHandler.cs b/C#/AutoHotInterception/DeviceHandlers/KeyboardHandler.cs index 106610c..259057b 100644 --- a/C#/AutoHotInterception/DeviceHandlers/KeyboardHandler.cs +++ b/C#/AutoHotInterception/DeviceHandlers/KeyboardHandler.cs @@ -10,7 +10,7 @@ class KeyboardHandler : DeviceHandler { public KeyboardHandler(IntPtr deviceContext, int deviceId) : base (deviceContext, deviceId) { - + } /// @@ -19,7 +19,7 @@ public KeyboardHandler(IntPtr deviceContext, int deviceId) : base (deviceContext /// public override void DisableFilterIfNeeded() { - if (AllButtonsMapping == null + if (AllButtonsMapping == null && SingleButtonMappings.Count == 0 && ContextCallback == null) { diff --git a/C#/AutoHotInterception/Helpers/ScanCodeHelper.cs b/C#/AutoHotInterception/Helpers/ScanCodeHelper.cs index 45803e4..d3116aa 100644 --- a/C#/AutoHotInterception/Helpers/ScanCodeHelper.cs +++ b/C#/AutoHotInterception/Helpers/ScanCodeHelper.cs @@ -159,7 +159,7 @@ public static List TranslateAhkCode(ushort code, int ahkState) { state += (ushort)((ushort)order * 2); } - + if (order == Order.Normal) { strokes.Add(new Stroke() { key = { code = code, state = state } }); diff --git a/C#/AutoHotInterception/Manager.cs b/C#/AutoHotInterception/Manager.cs index 3a6a500..c67206b 100644 --- a/C#/AutoHotInterception/Manager.cs +++ b/C#/AutoHotInterception/Manager.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; -using System.Threading; +using System.Collections.Concurrent; using AutoHotInterception.DeviceHandlers; using AutoHotInterception.Helpers; @@ -556,8 +552,8 @@ private static void PollThread(object obj) //Debug.WriteLine($"Stroke {strokes.Count}: {RenderStroke(stroke)}"); } - // Loop through the list checking the first 2 indexes for valid "two-code" key combinations. - // If no combo is found, send index 0 on its way, remove it off the top of the list, repeat + // Loop through the list checking the first 2 indexes for valid "two-code" key combinations. + // If no combo is found, send index 0 on its way, remove it off the top of the list, repeat while (strokes.Count > 0) { if (strokes.Count >= 2 && ScanCodeHelper.IsDoubleScanCode(new List { strokes[0], strokes[1] })) diff --git a/C#/AutoHotInterception/Properties/AssemblyInfo.cs b/C#/AutoHotInterception/Properties/AssemblyInfo.cs deleted file mode 100644 index c1be75e..0000000 --- a/C#/AutoHotInterception/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -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("AutoHotInterception")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("AutoHotInterception")] -[assembly: AssemblyCopyright("Copyright © 2018")] -[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("68fa4bc3-c277-44d0-8333-18d51dc3ca19")] - -// 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/C#/AutoHotInterception/ScanCodeChecker.cs b/C#/AutoHotInterception/ScanCodeChecker.cs index 48f0a02..7fc91c8 100644 --- a/C#/AutoHotInterception/ScanCodeChecker.cs +++ b/C#/AutoHotInterception/ScanCodeChecker.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Threading; -using AutoHotInterception.Helpers; +using AutoHotInterception.Helpers; namespace AutoHotInterception { @@ -17,6 +13,7 @@ public class ScanCodeChecker : IDisposable private int _deviceId; private bool _block; private Thread _pollThread; + private readonly CancellationTokenSource _cts = new(); public ScanCodeChecker() { @@ -40,7 +37,7 @@ private void PollThread() int deviceId2; var stroke1 = new ManagedWrapper.Stroke(); var stroke2 = new ManagedWrapper.Stroke(); - while (true) + while (!_cts.IsCancellationRequested) { var strokes = new List(); if (ManagedWrapper.Receive(_deviceContext, deviceId1 = ManagedWrapper.WaitWithTimeout(_deviceContext, 10), ref stroke1, 1) > 0) @@ -80,13 +77,19 @@ public string OkCheck() private int IsMonitoredDevice(int device) { - return (Convert.ToInt32(_deviceId == device) ); + return Convert.ToInt32(_deviceId == device) ; } public void Dispose() { - _pollThread.Abort(); - _pollThread.Join(); + _cts.Cancel(); + + if (_pollThread != null && _pollThread.IsAlive) + { + _pollThread.Join(); + } + + _cts.Dispose(); } } diff --git a/C#/AutoHotInterception/ToDo.md b/C#/AutoHotInterception/ToDo.md index 5c20510..eab44b3 100644 --- a/C#/AutoHotInterception/ToDo.md +++ b/C#/AutoHotInterception/ToDo.md @@ -1,15 +1,15 @@ -# Fix issues with Numlock/Pause +# Fix issues with Numlock/Pause BlueChipps in AHI Discord has a proposed fix [here](https://github.com/bluechipps/AutoHotInterception/tree/pause_numlock_support) # Send before firing callback in Subscription Mode? -If trying to write a system like AHK's hotstrings, but using AHI, -if you had subscriptions with block set to false, you would want the key to be sent before firing the callback. -That way, if you intended to replace `btw` with `By the way`, when the callback fired for `w`, -the `w` would already be in the text editor. -As it stands, when the callback for `w` fired, the `w` would not bein the text editor, and after AHI sent the `w` +If trying to write a system like AHK's hotstrings, but using AHI, +if you had subscriptions with block set to false, you would want the key to be sent before firing the callback. +That way, if you intended to replace `btw` with `By the way`, when the callback fired for `w`, +the `w` would already be in the text editor.
+As it stands, when the callback for `w` fired, the `w` would not bein the text editor, and after AHI sent the `w` (After firing the callback), you would have no chance to delete the `w` (So you would end up with `by the wayw` in the text editor) # Disablling filter whilst sub or unsub is happening -When a Subscribe or Unsubscribe call is made, the filter is turned off before the sub/unsub, and then turned on again afterwards. -This seems un-necesarry, and could result in some blocked input "leaking" through. +When a Subscribe or Unsubscribe call is made, the filter is turned off before the sub/unsub, and then turned on again afterwards.
+This seems un-necesarry, and could result in some blocked input "leaking" through.
Consider removing this \ No newline at end of file diff --git a/C#/Dependencies/Readme.md b/C#/Dependencies/Readme.md index 342bce2..8470e12 100644 --- a/C#/Dependencies/Readme.md +++ b/C#/Dependencies/Readme.md @@ -1,3 +1,3 @@ -In order to build, the Interception DLLs need to be placed in this folder . -It should contain `x86` and `x64` folders, as extracted from the `library` folder in the interception zip +In order to build, the Interception DLLs need to be placed in this folder.
+It should contain `x86` and `x64` folders, as extracted from the `library` folder in the interception zip
YOU MAY ALSO NEED TO RUN UNBLOCKER.PS1 AS ADMIN!! diff --git a/C#/Dependencies/x64/Readme.md b/C#/Dependencies/x64/Readme.md index 4dd0bf5..5908929 100644 --- a/C#/Dependencies/x64/Readme.md +++ b/C#/Dependencies/x64/Readme.md @@ -1,2 +1,2 @@ -In order to build, you should copy `interception.dll` into this folder from `library\x64` in the Interception zip +In order to build, you should copy `interception.dll` into this folder from `library\x64` in the Interception zip
YOU MAY ALSO NEED TO RUN UNBLOCKER.PS1 AS ADMIN!! diff --git a/C#/Dependencies/x86/Readme.md b/C#/Dependencies/x86/Readme.md index dad5200..eeace30 100644 --- a/C#/Dependencies/x86/Readme.md +++ b/C#/Dependencies/x86/Readme.md @@ -1,2 +1,2 @@ -In order to build, you should copy `interception.dll` into this folder from `library\x86` in the Interception zip +In order to build, you should copy `interception.dll` into this folder from `library\x86` in the Interception zip
YOU MAY ALSO NEED TO RUN UNBLOCKER.PS1 AS ADMIN!! diff --git a/C#/TestApp/App.config b/C#/TestApp/App.config deleted file mode 100644 index 731f6de..0000000 --- a/C#/TestApp/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/C#/TestApp/Helpers/TestDevices.cs b/C#/TestApp/Helpers/TestDevices.cs index 2b369b5..4e6d0e0 100644 --- a/C#/TestApp/Helpers/TestDevices.cs +++ b/C#/TestApp/Helpers/TestDevices.cs @@ -1,10 +1,5 @@ -using AutoHotInterception; -using AutoHotInterception.Helpers; +using AutoHotInterception.Helpers; using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace TestApp { diff --git a/C#/TestApp/KeyboardAndMouseTester.cs b/C#/TestApp/KeyboardAndMouseTester.cs index 3280e34..6ab41f0 100644 --- a/C#/TestApp/KeyboardAndMouseTester.cs +++ b/C#/TestApp/KeyboardAndMouseTester.cs @@ -1,9 +1,6 @@ using AutoHotInterception; using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using TestApp.Helpers; namespace TestApp diff --git a/C#/TestApp/Program.cs b/C#/TestApp/Program.cs index ac4a1ef..55bef06 100644 --- a/C#/TestApp/Program.cs +++ b/C#/TestApp/Program.cs @@ -1,5 +1,4 @@ using System; -using TestApp.Helpers; namespace TestApp { diff --git a/C#/TestApp/Properties/AssemblyInfo.cs b/C#/TestApp/Properties/AssemblyInfo.cs deleted file mode 100644 index f20af2c..0000000 --- a/C#/TestApp/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,36 +0,0 @@ -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("TestApp")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("TestApp")] -[assembly: AssemblyCopyright("Copyright © 2018")] -[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("02cbcbb9-c17f-4c6a-8f93-d7eaf038caed")] - -// 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/C#/TestApp/ScanCodeTester.cs b/C#/TestApp/ScanCodeTester.cs index a2af3af..11505e0 100644 --- a/C#/TestApp/ScanCodeTester.cs +++ b/C#/TestApp/ScanCodeTester.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Diagnostics; using AutoHotInterception; @@ -26,7 +25,7 @@ public void OnKeyEvent(KeyEvent[] keyEvents) var str = ""; foreach (var keyEvent in keyEvents) { - str += $"Code: {keyEvent.Code} (0x{keyEvent.Code.ToString("X")}) - {keyEvent.Code + 256}, State: {keyEvent.State} | "; + str += $"Code: {keyEvent.Code} (0x{keyEvent.Code:X}) - {keyEvent.Code + 256}, State: {keyEvent.State} | "; } Debug.WriteLine(str); } diff --git a/C#/TestApp/TabletTester.cs b/C#/TestApp/TabletTester.cs index b4edf66..bb85f64 100644 --- a/C#/TestApp/TabletTester.cs +++ b/C#/TestApp/TabletTester.cs @@ -1,9 +1,4 @@ using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using AutoHotInterception; namespace TestApp diff --git a/C#/TestApp/TestApp.csproj b/C#/TestApp/TestApp.csproj index 3698f00..e5797fe 100644 --- a/C#/TestApp/TestApp.csproj +++ b/C#/TestApp/TestApp.csproj @@ -1,77 +1,17 @@  - - + - Debug - AnyCPU - {02CBCBB9-C17F-4C6A-8F93-D7EAF038CAED} Exe - TestApp - TestApp - v4.6.1 - 512 - true + net9.0 - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - {68fa4bc3-c277-44d0-8333-18d51dc3ca19} - AutoHotInterception - + - - - - if not exist "$(TargetDir)\x86" mkdir "$(TargetDir)\x86" -if not exist "$(TargetDir)\x64" mkdir "$(TargetDir)\x64" -if not exist "$(TargetDir)\x86\interception.dll" xcopy "$(SolutionDir)\dependencies\x86\interception.dll" "$(TargetDir)\x86" -if not exist "$(TargetDir)\x64\interception.dll" xcopy "$(SolutionDir)\dependencies\x64\interception.dll" "$(TargetDir)\x64" - + + + + + + \ No newline at end of file diff --git a/C#/UnitTests/TranslateAhkCodeTests.cs b/C#/UnitTests/TranslateAhkCodeTests.cs index 5ad7604..26fd52b 100644 --- a/C#/UnitTests/TranslateAhkCodeTests.cs +++ b/C#/UnitTests/TranslateAhkCodeTests.cs @@ -1,18 +1,17 @@ using AutoHotInterception.Helpers; using NUnit.Framework; -using System.Collections.Generic; namespace UnitTests { [TestFixture] class TranslateAhkCodeTests { - [Test, TestCaseSource("TestKeyProvider")] + [Test, TestCaseSource(nameof(TestKeyProvider))] public void PressRelease(string name, int code, List pressResult, List releaseResult) { var actualResult = ScanCodeHelper.TranslateAhkCode((ushort)code, 1); AssertResults(pressResult, actualResult); - + actualResult = ScanCodeHelper.TranslateAhkCode((ushort)code, 0); AssertResults(releaseResult, actualResult); } @@ -32,8 +31,10 @@ private void AssertResults(List expectedResult, List Result(ushort code1, ushort state1, ushort? code2 = null, ushort? state2 = null) { - var strokes = new List(); - strokes.Add(new ManagedWrapper.Stroke() { key = { code = code1, state = state1 } }); + var strokes = new List + { + new ManagedWrapper.Stroke() { key = { code = code1, state = state1 } } + }; if (code2 != null) { strokes.Add(new ManagedWrapper.Stroke() { key = { code = (ushort)code2, state = (ushort)state2 } }); diff --git a/C#/UnitTests/TranslateScanCodesTests.cs b/C#/UnitTests/TranslateScanCodesTests.cs index 0d715ca..c578870 100644 --- a/C#/UnitTests/TranslateScanCodesTests.cs +++ b/C#/UnitTests/TranslateScanCodesTests.cs @@ -1,5 +1,4 @@ -using System.Collections.Generic; -using System.Diagnostics; +using System.Diagnostics; using AutoHotInterception.Helpers; using NUnit.Framework; using static AutoHotInterception.Helpers.ManagedWrapper; @@ -57,7 +56,7 @@ private static ExpectedResult Result(ushort code, ushort state) return results; } - [Test, TestCaseSource("TestKeyProvider")] + [Test, TestCaseSource(nameof(TestKeyProvider))] public void PressRelease(string name, List pressStrokes, List releaseStrokes, ExpectedResult pressResult, ExpectedResult releaseResult ) { Debug.WriteLine($"\nTesting key {name}..."); diff --git a/C#/UnitTests/UnitTests.csproj b/C#/UnitTests/UnitTests.csproj index 6c21cb1..62345e3 100644 --- a/C#/UnitTests/UnitTests.csproj +++ b/C#/UnitTests/UnitTests.csproj @@ -1,70 +1,17 @@  - - - - + - Debug - AnyCPU - {8EDF4429-251A-416D-BB68-93F227191BCF} - Library - Properties - UnitTests - UnitTests - v4.8 - 512 - - + net9.0 + enable - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - - ..\packages\NUnit.3.13.2\lib\net45\nunit.framework.dll - - - - - - - + - + + + + - - {68fa4bc3-c277-44d0-8333-18d51dc3ca19} - AutoHotInterception - + - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index cef4953..96b4bbf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,45 +22,45 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), ### Added - Add two SubscribeAbsolute example scripts which show how to process movement data coming from a tablet ### Changed -- Input is now processed even faster -Since 0.6.0, there could have been a 10ms delay between processing one piece of input and the next +- Input is now processed even faster
+Since 0.6.0, there could have been a 10ms delay between processing one piece of input and the next
Now it should be effectively instant ### Deprecated ### Removed ### Fixed -- Subscription / Context mode Extended keycodes fixed -Previously, some keys were not properly subscribe-able (Pause / NumLock) -Also, if any key was subscribed to with block enabled, and that key generated Extended Modifier keys when pressed -(eg when you press Home with NumLock off, keyboards send LShift with a state of 2, then Home with a state of 2) +- Subscription / Context mode Extended keycodes fixed
+Previously, some keys were not properly subscribe-able (Pause / NumLock)
+Also, if any key was subscribed to with block enabled, and that key generated Extended Modifier keys when pressed
+(eg when you press Home with NumLock off, keyboards send LShift with a state of 2, then Home with a state of 2)
then the Extended Modifier key (LShift with a state of 2 in the above example) was NOT blocked. -- SendKeyEvent() now sends exactly the same ScanCodes that would be sent if you really pressed it -Similar to the above example, if you sent Home, previously, only Home would be sent with a state of 2 -LShift would not have been sent with a state of 2 as it should -Also, Pause should send a state of 4, whereas before it sent a state of 2 +- SendKeyEvent() now sends exactly the same ScanCodes that would be sent if you really pressed it
+Similar to the above example, if you sent Home, previously, only Home would be sent with a state of 2
+LShift would not have been sent with a state of 2 as it should
+Also, Pause should send a state of 4, whereas before it sent a state of 2
- AhiScanCodeTester.ahk in Development Tools now works again -- Bug introduced in 0.7.0 whereby unsubscribing whilst a callback is still running would lock up the script is now fixed +- Bug introduced in 0.7.0 whereby unsubscribing whilst a callback is still running would lock up the script is now fixed
(WorkerThreads now use Tasks and CancellationTokens) ## [0.7.0] - 2022-01-17 ### Added - Add RemoveContextManager() to remove a Context Manager ### Changed -- MAJOR changes to the code behind the scenes - the code is now organized way better, and key / button handling has been consolidated into the same code. +- MAJOR changes to the code behind the scenes - the code is now organized way better, and key / button handling has been consolidated into the same code.
Nothing should have changed apart from what is listed in this changelog -- If you SubscribeKey to subscribe to a specific key on a device, and use SubscribeKeyboard to subscribe to all keys on the same device +- If you SubscribeKey to subscribe to a specific key on a device, and use SubscribeKeyboard to subscribe to all keys on the same device Then SubscribeKey now takes precedence (SubscribeKey callback fires, and SubscribeKeyboard does not) - AhiScanCodeTester.ahk in Development Tools is currently broken due to changes in AHI - it will be fixed in the next release ### Deprecated ### Removed ### Fixed -- If you had Context Mode enabled for a keyboard, and a SubscribeKey or SubscribeKeyboard subscription for the same keyboard +- If you had Context Mode enabled for a keyboard, and a SubscribeKey or SubscribeKeyboard subscription for the same keyboard then using UnsubscribeKey or UnsubscribeKeyboard would disable Context Mode - CreateContextManager now correctly throws an error if one already exists - When unsubscribing from a mouse movement which was not subscribed in Concurrent mode, the WorkerThread is now properly Disposed ## [0.6.0] - 2022-01-14 ### Changed -- Input is now processed faster. Rather than checking for input every 1ms, we now use Interception's `WaitWithTimeout` function +- Input is now processed faster. Rather than checking for input every 1ms, we now use Interception's `WaitWithTimeout` function to process input as soon as it happens ## [0.5.3] - 2020-05-14 @@ -109,7 +109,7 @@ You will still need the DLLs from the Lib folder, but not the AHK scripts ### Added - Added TabletButtons demo for converting a graphics tablet into a button box ### Fixed -- Absolute Mode mouse movement subscriptions now work again +- Absolute Mode mouse movement subscriptions now work again
## [0.4.4] - 2019-07-09 ### Added @@ -118,17 +118,17 @@ You will still need the DLLs from the Lib folder, but not the AHK scripts ## [0.4.3] - 2019-06-10 **EXPERIMENTAL TEST RELEASE** ### Fixed - Fixed issue #39 -Almost complete rewrite of mouse polling code -Multiple event types (Movement, mouse button events) supported per update ("stroke") of the mouse -It is now possible to block a button or movement, but leave unblocked events unblocked -Previously, a stroke was either blocked or not - if any one part of the stroke was blocked, it was all blocked +Almost complete rewrite of mouse polling code
+Multiple event types (Movement, mouse button events) supported per update ("stroke") of the mouse
+It is now possible to block a button or movement, but leave unblocked events unblocked
+Previously, a stroke was either blocked or not - if any one part of the stroke was blocked, it was all blocked
### Changed - [Monitor script] GUI layout made more robust - [Monitor script] Add option to filter key presses and only show key releases ## [0.4.2] - 2019-06-08 ### Fixed -- Fixed issue #37 +- Fixed issue #37
When multiple mouse buttons changed state in one update of the mouse, only one event would be fired for one of the buttons ## [0.4.1] - 2019-05-15 @@ -156,7 +156,7 @@ When multiple mouse buttons changed state in one update of the mouse, only one e ### Added - UnsubscribeKey, UnsubscribeMouseButton, UnsubscribeMouseMove, UnsubscribeMouseMoveRelative, UnsubscribeMouseMoveAbsolute methods added to Subscription Mode - Added "Unsubscription Example.ahk" to demo Subscribe / Unsubscribe -### Changed +### Changed - Fixed Build Event to copy Interception dll ### Fixed - SubscribeMouseMove endpoint fixed to not return bool (Fix "Can not implicitly convert type Void to object" error) diff --git a/README.md b/README.md index e2d0f15..6328f59 100644 --- a/README.md +++ b/README.md @@ -6,56 +6,56 @@ # AutoHotInterception -AutoHotInterception (AHI) allows you to execute AutoHotkey code in response to events from a *specific* keyboard or mouse, whilst (optionally) blocking the native functionality (i.e. stopping Windows from seeing that keyboard or mouse event). -In other words, you can use a key on a second (or third, or fourth...) keyboard to trigger AHK code, and that key will not be seen by applications. You can use the *same key* on multiple keyboards for individual actions. -For example, you could have 3 keyboards connected, and on the 1st (Main keyboard), no changes are applied, but on keyboard #2, when you press `F1`, it replaces it with `a`, and on keyboard #3, when you press `F1`, it replaces it with `b`. -Keyboard Keys, Mouse Buttons and Mouse movement (Both Relative and Absolute modes) are supported. -Both AHK v1 and AHK v2 are supported. +AutoHotInterception (AHI) allows you to execute AutoHotkey code in response to events from a *specific* keyboard or mouse, whilst (optionally) blocking the native functionality (i.e. stopping Windows from seeing that keyboard or mouse event).
+In other words, you can use a key on a second (or third, or fourth...) keyboard to trigger AHK code, and that key will not be seen by applications. You can use the *same key* on multiple keyboards for individual actions.
+For example, you could have 3 keyboards connected, and on the 1st (Main keyboard), no changes are applied, but on keyboard #2, when you press `F1`, it replaces it with `a`, and on keyboard #3, when you press `F1`, it replaces it with `b`.
+Keyboard Keys, Mouse Buttons and Mouse movement (Both Relative and Absolute modes) are supported.
+Both AHK v1 and AHK v2 are supported.
AHI uses the Interception driver by Francisco Lopez -# Getting Help -## [AHI Discussion Thread on the AHK forums](https://autohotkey.com/boards/viewtopic.php?f=6&t=45307) +# Getting Help
+## [AHI Discussion Thread on the AHK forums](https://autohotkey.com/boards/viewtopic.php?f=6&t=45307)
## [Discord Channel](https://discord.gg/sFPMv86) ------ # WARNING -**TAKE CARE** when using this code. Because Interception is a driver, and sits below windows proper, blocking with Interception goes so deep that it can even block CTRL+ALT+DEL etc. As such, it is entirely possible to lock up all input, or at least make life a little difficult. -In general, worst-case scenario would require use of the reset button. +**TAKE CARE** when using this code. Because Interception is a driver, and sits below windows proper, blocking with Interception goes so deep that it can even block CTRL+ALT+DEL etc. As such, it is entirely possible to lock up all input, or at least make life a little difficult.
+In general, worst-case scenario would require use of the reset button.
For example, using Subscription Mode with `block` enabled will **totally** block that key from working on that keyboard. -So if you block `Ctrl` on your only keyboard, you just blocked CTRL+ALT+DEL. -The best insurance policy is to have another keyboard or mouse handy, one that you don't block. -Be wary of making scripts using this code run on startup. Know how to enter "Safe Mode" in windows and disable startup of the scripts. Know mouse alternatives to emergency keyboard actions (Right click on clock for Task Manager!) -As they say - ***With great power comes great responsibility***. -If this all scares you and you don't really understand it, then TL/DR is you should probably stick to "Context Mode", it's safer. +So if you block `Ctrl` on your only keyboard, you just blocked CTRL+ALT+DEL.
+The best insurance policy is to have another keyboard or mouse handy, one that you don't block.
+Be wary of making scripts using this code run on startup. Know how to enter "Safe Mode" in windows and disable startup of the scripts. Know mouse alternatives to emergency keyboard actions (Right click on clock for Task Manager!)
+As they say - ***With great power comes great responsibility***.
+If this all scares you and you don't really understand it, then TL/DR is you should probably stick to "Context Mode", it's safer.
------ # Device IDs / VIDs PIDs etc -Interception identifies unique devices by an ID. This is a number from 1..20. -Devices 1-10 are always keyboards -Devices 11-20 are always mice -This ID scheme is totally unique to Interception, and IDs may change as you plug / unplug devices etc. -On PC, devices are often identified by VendorID (VID) and ProductID (PID). These are identifiers baked into the hardware at time of manufacture, and are identical for all devices of the same make / model. -Most AHI functions (eg to Subscribe to a key etc) use an Interception ID, so some handy functions are provided to allow you to find the (current) Interception ID of your device, given a VID / PID. -If you are unsure of what the VID / PID of your device is (or even if Interception can see it), you can use the included Monitor script to find it. +Interception identifies unique devices by an ID. This is a number from 1..20.
+Devices 1-10 are always keyboards
+Devices 11-20 are always mice
+This ID scheme is totally unique to Interception, and IDs may change as you plug / unplug devices etc.
+On PC, devices are often identified by VendorID (VID) and ProductID (PID). These are identifiers baked into the hardware at time of manufacture, and are identical for all devices of the same make / model.
+Most AHI functions (eg to Subscribe to a key etc) use an Interception ID, so some handy functions are provided to allow you to find the (current) Interception ID of your device, given a VID / PID.
+If you are unsure of what the VID / PID of your device is (or even if Interception can see it), you can use the included Monitor script to find it.
-You will need to know the VID / PID of at least one of your devices in order to do anything with AHI. +You will need to know the VID / PID of at least one of your devices in order to do anything with AHI.
## Monitor App -This handy tool allows you to check if AHI is working, and also to find the VID/PID or DeviceHandle of your devices. -You can use the handy "Copy" buttons to copy the VID/PID or DeviceHandle of the device to the clipboard. +This handy tool allows you to check if AHI is working, and also to find the VID/PID or DeviceHandle of your devices.
+You can use the handy "Copy" buttons to copy the VID/PID or DeviceHandle of the device to the clipboard.
When using the monitor app, **DO NOT** tick all devices at once, as if it crashes, it will lock up all devices. -Instead, tick one at a time and see if it your device. -![](https://github.com/evilC/AutoHotInterception/blob/master/Monitor.png) +Instead, tick one at a time and see if it your device.
+![](https://github.com/evilC/AutoHotInterception/blob/master/Monitor.png)
------ # Known Issues -If you unplug / replug a device, or go into hibernate and resume, the Interception ID of a device will increase by 1. -If the ID of a device goes above 10 (For keyboards) or 20 (For Mice), **The device will completely cease to function until the next reboot** - not only in AutoHotInterception, but in Windows also. +If you unplug / replug a device, or go into hibernate and resume, the Interception ID of a device will increase by 1.
+If the ID of a device goes above 10 (For keyboards) or 20 (For Mice), **The device will completely cease to function until the next reboot** - not only in AutoHotInterception, but in Windows also.
There is nothing I can do to fix this issue, it is a limitation of the Interception driver ------ @@ -63,25 +63,25 @@ There is nothing I can do to fix this issue, it is a limitation of the Intercept # Setup ## Install the Intereception driver -Download and install the [Interception Driver](https://github.com/oblitum/Interception/releases) -Note that you **must** run `install-interception.exe` at an admin command prompt (**Not double-click it**) - once you do so, it will instruct you to execute `install-interception.exe /install` to actually perform the install. -Here is a GIF showing the process: +Download and install the [Interception Driver](https://github.com/oblitum/Interception/releases)
+Note that you **must** run `install-interception.exe` at an admin command prompt (**Not double-click it**) - once you do so, it will instruct you to execute `install-interception.exe /install` to actually perform the install.
+Here is a GIF showing the process:
![](https://github.com/evilC/AutoHotInterception/blob/master/InterceptionInstall.gif) ## Build your AutoHotInterception folder -1. Download an AHI release from the [releases page](https://github.com/evilC/AutoHotInterception/releases) and extract it to a folder. -DO NOT use the "Clone or Download" link on the main page. -2. From the AHI release zip, extract **EITHER** the `AHK v1` folder **OR** the `AHK v2` folder to somewhere on your disk. -This is the "working folder" where (at least initially) you will be running scripts from. -It contains a number of sample `.ahk` scripts and a `lib` folder. +1. Download an AHI release from the [releases page](https://github.com/evilC/AutoHotInterception/releases) and extract it to a folder.
+DO NOT use the "Clone or Download" link on the main page.
+2. From the AHI release zip, extract **EITHER** the `AHK v1` folder **OR** the `AHK v2` folder to somewhere on your disk.
+This is the "working folder" where (at least initially) you will be running scripts from.
+It contains a number of sample `.ahk` scripts and a `lib` folder.
3. From the AHI release zip, extract `AutoHotInterception.dll` from the `Common\lib` folder and place it into `lib` in your "working folder" -4. In the Interception installer zip, there is a `library` folder containing `x86` and `x64` folders. -Copy both of these folders into the `lib` folder in your "working folder". +4. In the Interception installer zip, there is a `library` folder containing `x86` and `x64` folders.
+Copy both of these folders into the `lib` folder in your "working folder".
-Example for AHK v1 - the "working folder" is on the left, top right is the AutoHotInterception zip, bottom right is the Interception zip. +Example for AHK v1 - the "working folder" is on the left, top right is the AutoHotInterception zip, bottom right is the Interception zip.
![](https://github.com/evilC/AutoHotInterception/blob/master/FolderSetup.gif) -The folder structure should end up looking like: +The folder structure should end up looking like:
``` AHI Root Folder Monitor.ahk @@ -97,13 +97,13 @@ AHI Root Folder x64 interception.dll ``` -4. Right-click `Unblocker.ps1` in the lib folder and select `Run as Admin`. -This is because downloaded DLLs are often blocked and will not work. +4. Right-click `Unblocker.ps1` in the lib folder and select `Run as Admin`.
+This is because downloaded DLLs are often blocked and will not work.
Alternatively, this can be done manually in one of two ways: - 1. By right clicking the DLLs, selecting Properties, and checking a "Block" box if it exists. - 1. Before you open any zip (ie the AutoHotInterception zip or the Interception zip), right click it and select "Unblock". + 1. By right clicking the DLLs, selecting Properties, and checking a "Block" box if it exists.
+ 1. Before you open any zip (ie the AutoHotInterception zip or the Interception zip), right click it and select "Unblock".
5. Run `Monitor.ahk` from your working folder to check that everything works -6. (Optional) The contents of the `lib` folder can actually be placed in one of the AutoHotkey lib folders (eg `My Documents\AutoHotkey\lib` - make it if it does not exist), and the `#include` lines of the sample scripts changed to `#include `, to enable your AHI scripts to be in any folder, without each needing it's own copy of the library files. +6. (Optional) The contents of the `lib` folder can actually be placed in one of the AutoHotkey lib folders (eg `My Documents\AutoHotkey\lib` - make it if it does not exist), and the `#include` lines of the sample scripts changed to `#include `, to enable your AHI scripts to be in any folder, without each needing it's own copy of the library files.
------ @@ -134,9 +134,9 @@ Initialize the library global AHI := AutoHotInterception() ``` -*Note* -The `AHI` variable is an AHK class that makes it easy to interact with the AutoHotInterception DLL (Which itself then interacts with the Interception dll). For example, it wraps `GetDeviceList()` to make it return a normal AHK array. Most of the time you will not need it. -For advanced users, if you wish to directly communicate with the AHI DLL (eg for best possible performance), you can call `AHI.Instance` instead of `AHI` for most functions (eg when sending of synthesized input using `SendMouseMove`). +*Note*
+The `AHI` variable is an AHK class that makes it easy to interact with the AutoHotInterception DLL (Which itself then interacts with the Interception dll). For example, it wraps `GetDeviceList()` to make it return a normal AHK array. Most of the time you will not need it.
+For advanced users, if you wish to directly communicate with the AHI DLL (eg for best possible performance), you can call `AHI.Instance` instead of `AHI` for most functions (eg when sending of synthesized input using `SendMouseMove`).
``` AHI := new AutoHotInterception() AHI.Instance.SendMouseMove(...) @@ -144,44 +144,44 @@ AHI.Instance.SendMouseMove(...) ## Misc Commands ### SetState -`SetState(true|false)` -Turns on or off all subscriptions (Starts on) -Where `true` is on, `false` is off. -eg `AHI.SetState(false)` +`SetState(true|false)`
+Turns on or off all subscriptions (Starts on)
+Where `true` is on, `false` is off.
+eg `AHI.SetState(false)`
-## Finding Device IDs +## Finding Device IDs
### USB Devices -In most cases, you will want to hard-wire a script to a specific VID/PID - in this instance, use one of the following methods. -For all these methods, if you have multiple identical VID/PID devices, you can specify an `instance` (Starts from 1). +In most cases, you will want to hard-wire a script to a specific VID/PID - in this instance, use one of the following methods.
+For all these methods, if you have multiple identical VID/PID devices, you can specify an `instance` (Starts from 1).
#### GetDeviceId -`AHI.GetDeviceId(, , [,] )` -Where `isMouse` is `true` if you wish to find a mouse, or `false` if you wish to find a keyboard. -eg `AHI.GetDeviceId(false, 0x04F2, 0x0112)` to find a keyboard with VID 0x04F2 and PID 0x0112 +`AHI.GetDeviceId(, , [,] )`
+Where `isMouse` is `true` if you wish to find a mouse, or `false` if you wish to find a keyboard.
+eg `AHI.GetDeviceId(false, 0x04F2, 0x0112)` to find a keyboard with VID 0x04F2 and PID 0x0112
#### GetKeyboardId -`AHI.GetKeyboardId(, [,] )` +`AHI.GetKeyboardId(, [,] )`
#### GetMouseId -`AHI.GetMouseId(, [,] )` +`AHI.GetMouseId(, [,] )`
-### PS/2 and other Legacy devices (Can also apply to Laptops) -Some devices (eg older machines with PS/2 interfaces, or some laptops) may not use USB, so these will not have a VID and PID. +### PS/2 and other Legacy devices (Can also apply to Laptops)
+Some devices (eg older machines with PS/2 interfaces, or some laptops) may not use USB, so these will not have a VID and PID.
In this case, use the monitor app (Or `GetDeviceList()`) to findle out the "Handle" of your device, and get it's ID from that. -#### GetDeviceIdFromHandle -`AHI.GetDeviceIdFromHandle(, [,] )` -This works in the same way as `GetDeviceId` above, except you pass a string containing the handle. -eg `AHI.GetDeviceIdFromHandle(false, "ACPI\PNP0303")` to find a keyboard with the handle `ACPI\PNP0303` +#### GetDeviceIdFromHandle
+`AHI.GetDeviceIdFromHandle(, [,] )`
+This works in the same way as `GetDeviceId` above, except you pass a string containing the handle.
+eg `AHI.GetDeviceIdFromHandle(false, "ACPI\PNP0303")` to find a keyboard with the handle `ACPI\PNP0303`
-#### GetKeyboardIdFromHandle -`AHI.GetKeyboardIdFromHandle( [,] )` +#### GetKeyboardIdFromHandle
+`AHI.GetKeyboardIdFromHandle( [,] )`
-#### GetMouseIdFromHandle -`AHI.GetMouseIdFromHandle( [,] )` +#### GetMouseIdFromHandle
+`AHI.GetMouseIdFromHandle( [,] )`
### Getting a list of devices -If you wish to get a list of all available devices, you can call `AHI.GetDeviceList()`, which will return an array of `DeviceInfo` objects, each of which has the following properties: +If you wish to get a list of all available devices, you can call `AHI.GetDeviceList()`, which will return an array of `DeviceInfo` objects, each of which has the following properties:
``` Id isMouse @@ -191,19 +191,19 @@ Handle ``` ## Input Detection -AHI has two input detection modes - *Context Mode* and *Subscription Mode*, and both can be used simultaneously. +AHI has two input detection modes - *Context Mode* and *Subscription Mode*, and both can be used simultaneously.
### Context mode -Context mode is so named as it takes advantage of AutoHotkey's [Context Sensitive Hotkeys](https://autohotkey.com/docs/Hotkeys.htm#Context). -As such, only Keyboard Keys and Mouse Buttons are supported in this mode. Mouse Movement is not supported. +Context mode is so named as it takes advantage of AutoHotkey's [Context Sensitive Hotkeys](https://autohotkey.com/docs/Hotkeys.htm#Context).
+As such, only Keyboard Keys and Mouse Buttons are supported in this mode. Mouse Movement is not supported.
-In context mode, you create a *Context Manager* object which turns on/off a set of AHK hotkeys for you. -You wrap your hotkeys in an #if block which is controlled by the manager. +In context mode, you create a *Context Manager* object which turns on/off a set of AHK hotkeys for you.
+You wrap your hotkeys in an #if block which is controlled by the manager.
-Create a Context Manager for the keyboard or mouse, pass it the Interception ID of the device. -Then Create your hotkeys, wrapped in an `#if` block that checks the `.IsActive` property of your Context Manager +Create a Context Manager for the keyboard or mouse, pass it the Interception ID of the device.
+Then Create your hotkeys, wrapped in an `#if` block that checks the `.IsActive` property of your Context Manager
-(Complete, working script) +(Complete, working script)
#### AHK v1 ``` #include Lib\AutoHotInterception.ahk @@ -214,10 +214,10 @@ cm1 := AHI.CreateContextManager(keyboard1Id) #if cm1.IsActive ; Start the #if block ::aaa::JACKPOT -1:: +1:: ToolTip % "KEY DOWN EVENT @ " A_TickCount return - + 1 up:: ToolTip % "KEY UP EVENT @ " A_TickCount return @@ -239,7 +239,7 @@ cm1 := AHI.CreateContextManager(keyboard1Id) ToolTip("KEY DOWN EVENT @ " A_TickCount) return } - + 1 up:: { ToolTip("KEY UP EVENT @ " A_TickCount) @@ -251,20 +251,20 @@ cm1 := AHI.CreateContextManager(keyboard1Id) You can remove a Context Manager using `AHI.RemoveContextManager(keyboard1Id)` ### Subscription mode -In Subscription mode, you bypass AHK's hotkey system completely, and Interception notifies you of key events via callbacks. -All forms of input are supported in Subscription Mode. -Subscription Mode overrides Context Mode - that is, if a key on a keyboard has been subscribed to with Subscription Mode, then Context Mode will not fire for that key on that keyboard. -SubscribeKey overrides SubscribeKeyboard - that is, if you have subscribed to all keys and a specific key on the same keyboard, then if you press the specific key, it's callback will fire and the callback for SubscribeKeyboard will not. -Each Subscribe endpoint also has a corresponding Unsubscribe endpoint, which removes the subscription and any block associated with it. -Both keyboard and mouse subscription functions have an optional `concurrent` parameter. This controls whether callbacks are fired sequentially or not. -False (Default) means that a new callback will not be fired until the last one has completed. This is especially useful for subscriptions involving mouse movement. -True means that a new thread will be used for each callback. If your callback has a long-running loop in it, this could mean that a callback could interrupt the previous callback, resulting in a steady buildup of callbacks (Read memory leak). Use at own risk! +In Subscription mode, you bypass AHK's hotkey system completely, and Interception notifies you of key events via callbacks.
+All forms of input are supported in Subscription Mode.
+Subscription Mode overrides Context Mode - that is, if a key on a keyboard has been subscribed to with Subscription Mode, then Context Mode will not fire for that key on that keyboard.
+SubscribeKey overrides SubscribeKeyboard - that is, if you have subscribed to all keys and a specific key on the same keyboard, then if you press the specific key, it's callback will fire and the callback for SubscribeKeyboard will not.
+Each Subscribe endpoint also has a corresponding Unsubscribe endpoint, which removes the subscription and any block associated with it.
+Both keyboard and mouse subscription functions have an optional `concurrent` parameter. This controls whether callbacks are fired sequentially or not.
+False (Default) means that a new callback will not be fired until the last one has completed. This is especially useful for subscriptions involving mouse movement.
+True means that a new thread will be used for each callback. If your callback has a long-running loop in it, this could mean that a callback could interrupt the previous callback, resulting in a steady buildup of callbacks (Read memory leak). Use at own risk!
#### Subscribing to Keyboard keys -##### Subscribe to a specific key on a specific keyboard -`SubscribeKey(, , , , )` -`UnsubscribeKey(, )` -eg +##### Subscribe to a specific key on a specific keyboard
+`SubscribeKey(, , , , )`
+`UnsubscribeKey(, )`
+eg
###### AHK v1 `AHI.SubscribeKey(keyboardId, GetKeySC("1"), true, Func("KeyEvent"))` @@ -285,13 +285,13 @@ KeyEvent(state){ } ``` -##### Subscribe to all keys on a specific keyboard -`SubscribeKeyboard(, , , )` -eg +##### Subscribe to all keys on a specific keyboard
+`SubscribeKeyboard(, , , )`
+eg
###### AHK v1 -`AHI.SubscribeKeyboard(keyboardId, true, Func("KeyEvent"))` +`AHI.SubscribeKeyboard(keyboardId, true, Func("KeyEvent"))`
-Callback function is passed scancode of pressed key and state +Callback function is passed scancode of pressed key and state
``` KeyEvent(code, state){ ToolTip % "Keyboard Key - Code: " code ", State: " state @@ -299,9 +299,9 @@ KeyEvent(code, state){ ``` ###### AHK v2 -`AHI.SubscribeKeyboard(keyboardId, true, KeyEvent)` +`AHI.SubscribeKeyboard(keyboardId, true, KeyEvent)`
-Callback function is passed scancode of pressed key and state +Callback function is passed scancode of pressed key and state
``` KeyEvent(code, state){ ToolTip("Keyboard Key - Code: " code ", State: " state) @@ -310,9 +310,9 @@ KeyEvent(code, state){ #### Subscribing to Mouse Buttons ##### Subscribing to a specific button on a specific mouse -`SubscribeMouseButton(,