Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does my WPF application crash when I bump my mousewheel?

Tags:

c#

wpf

When I bump my mousewheel, my WPF application crashes sometimes, with an OverflowException. Here's the start of the stack trace:

at System.Windows.Shell.WindowChromeWorker._HandleNCHitTest(WM uMsg, IntPtr wParam, IntPtr lParam, Boolean& handled)

From that, I've traced it down to the WindowChrome - I can even reproduce it with just the WindowChrome. But it seems like it has to be fullscreen. What's going on here? Is there a workaround?

like image 995
Zarenor Avatar asked May 10 '18 22:05

Zarenor


2 Answers

This is actually an issue in the class the stack trace points to. Microsoft has a bug in the WindowChromeWorker that manifests in an OverflowException.

I've only observed this with Logitech mice, but from what I've seen elsewhere, it may happen with other mice.

The only workarounds available are disallowing full-screen, and preventing the user from sending a message containing side-scroll information.

What I think is going on is this:

//Overflow gets inlined to here. (In System.Windows.Shell.WindowChromeWorker.cs)
var mousePosScreen = new Point(Utility.GET_X_LPARAM(lParam), Utility.GET_Y_LPARAM(lParam));

//Which calls this (In Standard.Utility)
public static int GET_X_LPARAM(IntPtr lParam)
{
    return LOWORD(lParam.ToInt32());
}

//Unsafe cast and overflow happens here (In System.IntPtr)
public unsafe int ToInt32() {
    #if WIN32
        return (int)m_value;
    #else
        long l = (long)m_value;
        return checked((int)l); //Overflow actually occurs and is thrown from here.
    #endif
}

It seems like a bad assumption is made in Standard.Utility that lParams always fit in 32 bits, and some mouse drivers violate that on 64-bit systems by writing there.

The reference source for WindowChromeWorker is here.

like image 186
Zarenor Avatar answered Nov 20 '22 07:11

Zarenor


The workaround that Ivan Golović included in a comment above needs to be in an answer so it can be spotted easily and in case the link goes dead.

Preprocess the message in a HookProc and mark it as handled if the conversion from lParam to int32 overflows.

protected override void OnSourceInitialized( EventArgs e )
  {
     base.OnSourceInitialized( e );
     ( (HwndSource)PresentationSource.FromVisual( this ) ).AddHook( HookProc );
  }
  private IntPtr HookProc( IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled )
  {
     if ( msg == 0x0084 /*WM_NCHITTEST*/ )
     {
        // This prevents a crash in WindowChromeWorker._HandleNCHitTest
        try
        {
           lParam.ToInt32();
        }
        catch ( OverflowException )
        {
           handled = true;
        }
     }
     return IntPtr.Zero;
  }
like image 23
RobinG Avatar answered Nov 20 '22 06:11

RobinG