fix: resolve ArgumentException and lock screen trigger on WinUI 3 InputInjector#210
Draft
agneszitte wants to merge 16 commits intomainfrom
Draft
fix: resolve ArgumentException and lock screen trigger on WinUI 3 InputInjector#210agneszitte wants to merge 16 commits intomainfrom
agneszitte wants to merge 16 commits intomainfrom
Conversation
…utInjector - Remove XUp from ReleaseAny() on #else path: Windows InputInjector API requires MouseData to specify which X button; sending XUp without it throws ArgumentException - Add ResetTrackedPosition() to MouseHelper for #else path: resets internal tracked position without injecting real mouse movement - Update CleanupPointers() with #if HAS_UNO conditional: on Uno keeps MoveTo(0,0), on WinUI 3 uses ResetTrackedPosition() to avoid physically moving the OS cursor to top-left corner which triggers Windows hot corners and lock screen - Add regression tests for ReleaseAny and CleanupPointers
…s pre-existing warnings - Wrap EnableConfigPersistence and StoreConfig with try/catch to handle InvalidOperationException when ApplicationData.Current is unavailable (unpackaged WinUI 3 apps have no package identity) - Suppress pre-existing CA1873 and CS8619 warnings in Directory.Build.props
- Copy Log: copies human-readable test log (status, counts, all test results with icons, error messages, and console output) - Copy XML: copies NUnit XML results (previous behavior) - Groups results by test class for readability
…EAD on WinUI 3 IsSecondaryApp is a DependencyProperty read from a background thread in ExecuteTestsForInstance. On WinUI 3, this throws COMException (0x8001010E). Cache the value in UnitTestEngineConfig from the UI thread via BuildConfig() before dispatching to Task.Run.
MoveNoCoalesce (0x2000) alone is not a valid mouse input flag on Windows. The OS InputInjector API requires MOUSEEVENTF_MOVE (0x1) to recognize the input as a movement event. Without it, InjectMouseInput throws ArgumentException on WinUI 3.
- Remove MoveNoCoalesce flag from MoveBy (not in any MSDN example, may be
rejected by the WinRT InputInjector API on desktop apps)
- Remove TimeOffsetInMilliseconds from all mouse input constructors (official
MSDN examples never set it, defaulting to 0)
- Materialize IEnumerable to array (.ToArray()) before passing to the Windows
InputInjector.InjectMouseInput API (all official examples use new[] { ... },
lazy LINQ chains may not marshal correctly via CsWinRT projection)
On real Windows, passing large arrays of InjectedInputMouseInfo to InputInjector.InjectMouseInput through the WinRT interop layer causes ArgumentException. Injecting events individually avoids the issue while maintaining the same behavior on Uno (HAS_UNO path).
On WinUI 3, InputInjector.InjectMouseInput with Move flag uses relative deltas from the physical OS cursor position. The tracked position (which resets to 0,0) does not correspond to the actual cursor position, so MoveTo computes wrong deltas and the cursor misses the target element. Fix: In Tap(UIElement), use Win32 SetCursorPos + ClientToScreen to position the cursor at the exact screen coordinates of the element center, then inject Press/Release at that location. DIP-to-pixel conversion uses XamlRoot.RasterizationScale for DPI awareness. Also adds MouseHelper.SetTrackedPosition() to keep the tracked position in sync after direct cursor positioning.
…n WinUI 3 - GetActiveWindow/GetForegroundWindow return null (0x0) for XAML-island child HWNDs when thread focus changes during test execution - Use Process.GetCurrentProcess().MainWindowHandle to reliably find the top-level window for SetWindowPos repositioning - Ensures EnsureClientAreaOnScreen actually moves the root window when test UI elements extend off-screen (Y=-280 clamped to Y=0 by SetCursorPos) - Add cursor-verification guard in Tap() that throws descriptive InvalidOperationException when SetCursorPos cannot reach the target - Remove unused GetParent P/Invoke declaration
e286b78 to
b37bbeb
Compare
SecondaryApp.IsSupported returns true on Windows via OperatingSystem.IsWindows(), but DevServer is intentionally excluded on Windows/WinUI 3 (VS provides native Hot Reload instead). The original #if __SKIA__ check was correct in spirit — SecondaryApp cannot actually run tests on WinUI 3 due to missing DevServer — but the assertion failed because IsSupported checks the OS, not DevServer availability. Changed the test to use #if HAS_UNO_DEVSERVER / __SKIA__ / else branches that match the actual runtime capability instead of making a hard true/false assertion that contradicts the property value.
… errors - SecondaryApp.IsSupported now checks HAS_UNO_DEVSERVER in addition to OS check, so it returns false on WinUI 3 where DevServer is not available - HotReloadTests now uses ignoreIfNotSupported: true on RunsInSecondaryApp - Is_SecondaryApp_Supported test simplified to match corrected property
…esilience - Use Process.MainWindowHandle instead of GetActiveWindow() for ClientToScreen to avoid XAML island child HWND mismatch - Add EnsureClientAreaOnScreen fallback when target coordinates are outside the virtual screen area - Add BringAppToForeground via SetForegroundWindow before each Tap to ensure injected input reaches the test app window - Add GetForegroundWindow as last-resort HWND fallback - Wrap PointersInjectionTests Setup/Cleanup in try-catch to prevent cascading COMException failures across subsequent tests - Update No_Longer_Sane expected failure message
- Mouse: use Absolute|VirtualDesk normalized coordinates through InputInjector instead of SetCursorPos (which only moves OS cursor, not InputInjector's internal position) - Touch: use Win32 Touch Injection API (user32.dll InitializeTouchInjection + InjectTouchInput) instead of WinRT InputInjector.InjectTouchInput (which targets UWP CoreWindow and silently fails on WinUI 3's Win32 windowing) - Add comprehensive diagnostics for mouse and touch injection paths - Add PointerPressed/PointerReleased tracking in pointer injection tests
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes two bugs in the Windows/WinUI 3 (
#else) input injection path that causedSystem.ArgumentExceptionand unintended lock screen activation when usingInputInjectorHelperon real Windows hardware.Fixes #203
Root Causes
Bug 1:
ReleaseAny()throwsArgumentExceptionReleaseAny()in the#else(non-HAS_UNO) path combinedInjectedInputMouseOptions.XUpwithLeftUp | MiddleUp | RightUp. The WindowsInputInjectorAPI requires theMouseDatafield to specify which X button (X1 or X2) whenXUpis set. Without it, the API rejects the call withArgumentException: 'value does not fall within the expected range.'.Fix: Removed
XUpfrom the#elseReleaseAny()path. TheHAS_UNOpath is unchanged (it checks actual button states).Bug 2:
CleanupPointers()triggers Windows hot corners / lock screenCleanupPointers()calledMouse.MoveTo(0, 0), which on the#elsepath generates large negative relative deltas that physically move the real OS cursor to the upper-left corner of the screen. This triggers Windows hot corners, which can activate the lock screen.Note: Bug 2 was masked by Bug 1 in practice — the
ArgumentExceptionwould fire first duringReleaseAny(), preventing execution from reaching theMoveTocall.Fix: Added
#if HAS_UNOconditional toCleanupPointers(). On Uno/Skia, behavior is unchanged (MoveTo(0,0)). On WinUI 3, a newResetTrackedPosition()method resets the internal_trackedPositionfield without injecting any real mouse movement.Changes
InputInjectorHelper.MouseHelper.csXUpfromReleaseAny()#elsepath; addedResetTrackedPosition()methodInputInjectorHelper.cs#if HAS_UNOconditional inCleanupPointers()PointersInjectionTests.csWhen_ReleaseAny_DoesNotThrowandWhen_CleanupPointers_DoesNotThrowregression testsValidation
net10.0-windows10.0.22621andnet10.0-desktoptargetsNo_Longer_Saneintentional,HotReloadTests/SecondaryAppTestsrequire DevServer,TapCoordinatesfailures are pre-existing on Skia Desktop)inputInjectionBrokeredcapability — these are the exact code paths the reporting client exercises