dotnet / wpf

WPF is a .NET Core UI framework for building Windows desktop applications.
MIT License
7.04k stars 1.17k forks source link

Mouse.GetPosition throws 'Invalid window handle' when called on UI element loading right after a child window was closed #7381

Open AndreyMarten opened 1 year ago

AndreyMarten commented 1 year ago

Problem description:

An attempt to call the Mouse.GetPosition method for an UI element in its Loaded event handler right after a child window was closed lead to the "System.ComponentModel.Win32Exception: 'Invalid window handle.'" error. This issue occurs only if the mouse pointer is over the UI element that is loaded.

NOTE: You need to enable Common Language Runtime Exceptions to see this error.

Please refer to the Minimal repro and Screencast sections that illustrate the problematic behavior.


Actual behavior:

System.ComponentModel.Win32Exception: 'Invalid window handle.

    WindowsBase.dll!MS.Win32.UnsafeNativeMethods.ClientToScreen(System.Runtime.InteropServices.HandleRef hWnd, ref MS.Win32.NativeMethods.POINT pt) Unknown
    PresentationCore.dll!MS.Internal.PointUtil.ClientToScreen(System.Windows.Point pointClient, System.Windows.PresentationSource presentationSource)   Unknown
    PresentationCore.dll!System.Windows.Input.MouseDevice.GetScreenPositionFromSystem() Unknown
    PresentationCore.dll!System.Windows.Input.MouseDevice.GetScreenPosition()   Unknown
    PresentationCore.dll!System.Windows.Input.MouseDevice.GetPosition(System.Windows.IInputElement relativeTo)  Unknown
    PresentationCore.dll!System.Windows.Input.Mouse.GetPosition(System.Windows.IInputElement relativeTo)    Unknown
>   WpfCore.dll!WpfCore.MainWindow.Border_Loaded(object sender, System.Windows.RoutedEventArgs e) Line 50   C#
    PresentationCore.dll!System.Windows.RoutedEventHandlerInfo.InvokeHandler(object target, System.Windows.RoutedEventArgs routedEventArgs) Unknown
    PresentationCore.dll!System.Windows.EventRoute.InvokeHandlersImpl(object source, System.Windows.RoutedEventArgs args, bool reRaised)    Unknown
    PresentationCore.dll!System.Windows.UIElement.RaiseEventImpl(System.Windows.DependencyObject sender, System.Windows.RoutedEventArgs args)   Unknown
    PresentationCore.dll!System.Windows.UIElement.RaiseEvent(System.Windows.RoutedEventArgs e)  Unknown
    PresentationFramework.dll!System.Windows.BroadcastEventHelper.BroadcastEvent(System.Windows.DependencyObject root, System.Windows.RoutedEvent routedEvent)  Unknown
    PresentationFramework.dll!System.Windows.BroadcastEventHelper.BroadcastLoadedEvent(object root) Unknown
    PresentationCore.dll!System.Windows.Media.MediaContext.FireLoadedPendingCallbacks() Unknown
    PresentationCore.dll!System.Windows.Media.MediaContext.FireInvokeOnRenderCallbacks()    Unknown
    PresentationCore.dll!System.Windows.Media.MediaContext.RenderMessageHandlerCore(object resizedCompositionTarget)    Unknown
    PresentationCore.dll!System.Windows.Media.MediaContext.RenderMessageHandler(object resizedCompositionTarget)    Unknown
    WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs)  Unknown
    WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source, System.Delegate callback, object args, int numArgs, System.Delegate catchHandler) Unknown
    WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeImpl()   Unknown
    WindowsBase.dll!System.Windows.Threading.DispatcherOperation.InvokeInSecurityContext(object state)  Unknown
    WindowsBase.dll!MS.Internal.CulturePreservingExecutionContext.CallbackWrapper(object obj)   Unknown
    System.Private.CoreLib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state)   Unknown
    System.Private.CoreLib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state)   Unknown
    WindowsBase.dll!MS.Internal.CulturePreservingExecutionContext.Run(MS.Internal.CulturePreservingExecutionContext executionContext, System.Threading.ContextCallback callback, object state)  Unknown
    WindowsBase.dll!System.Windows.Threading.DispatcherOperation.Invoke()   Unknown
    WindowsBase.dll!System.Windows.Threading.Dispatcher.ProcessQueue()  Unknown
    WindowsBase.dll!System.Windows.Threading.Dispatcher.WndProcHook(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled)  Unknown
    WindowsBase.dll!MS.Win32.HwndWrapper.WndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) Unknown
    WindowsBase.dll!MS.Win32.HwndSubclass.DispatcherCallbackOperation(object o) Unknown
    WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs)  Unknown
    WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.TryCatchWhen(object source, System.Delegate callback, object args, int numArgs, System.Delegate catchHandler) Unknown
    WindowsBase.dll!System.Windows.Threading.Dispatcher.LegacyInvokeImpl(System.Windows.Threading.DispatcherPriority priority, System.TimeSpan timeout, System.Delegate method, object args, int numArgs)   Unknown
    WindowsBase.dll!MS.Win32.HwndSubclass.SubclassWndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam)  Unknown
    [Native to Managed Transition]  
    [Managed to Native Transition]  
    WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame frame)   Unknown
    WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame frame)   Unknown
    WindowsBase.dll!System.Windows.Threading.Dispatcher.Run()   Unknown
    PresentationFramework.dll!System.Windows.Application.RunDispatcher(object ignore)   Unknown
    PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window window)  Unknown
    PresentationFramework.dll!System.Windows.Application.Run()  Unknown
    WpfCore.dll!WpfCore.App.Main()  Unknown

Expected behavior:

The Mouse.GetPosition method should return a correct Point value.


Minimal repro: dxSample.zip Screencast: screencast.mp4


Steps to reproduce:

  1. Enable showing Common Language Runtime Exceptions in your Visual Studio;
  2. Run any of projects in the attached project;
  3. Click the Test button in the MainWindow and do not move the mouse pointer anywhere;
  4. Click in the button from a child window;

At this moment, an error should occur in the Loaded event handler of the Border element. Please refer to the screencast to see the issue.

miloush commented 1 year ago

So you are trying to get mouse position on a window that you closed and are getting a first chance exception, basically hitting this?

https://github.com/dotnet/wpf/blob/cfec0cc3f3ded32c4d6dc409e41af516afe64073/src/Microsoft.DotNet.Wpf/src/PresentationCore/System/Windows/Input/MouseDevice.cs#L138-L142

Looks working as designed to me.

AndreyMarten commented 1 year ago

Hello,

I'm afraid that's not quite so.

We call the Mouse.GetPosition method for the Border element declared inside TabItem. This TabItem belongs to MainWindow which remains visible during the application's life. The method, in turn, is called for the MainWindow object. Please refer to the corresponding code in MainWindow.xaml.cs:

private void Border_Loaded(object sender, RoutedEventArgs e)
{
    var pt = Mouse.GetPosition(this);
}

Here, this is MainWindow, not a child Window.

We do not call GetPosition for the child window at all. And we close only this child window.

So, even though we call the GetPosition for MainWindow which is still available, the Invalid window handle. exception is thrown in this case.

Thanks, Andrey

miloush commented 1 year ago

Ah I see. It's because that child window is the active source at the time, which is why not moving the mouse is critical.

One option to fix this would be for the

https://github.com/dotnet/wpf/blob/cfec0cc3f3ded32c4d6dc409e41af516afe64073/src/Microsoft.DotNet.Wpf/src/Shared/MS/Internal/PointUtil.cs#L172-L172

to also check inputSource.IsDisposed.