准确来说,这个不算是 WPF 的问题,而是系统等的问题。在某些设备上的使用了 WindowChrome 功能的 WPF 应用,将在运行过程,在 WindowChromeWorker 类里面抛出 System.OverflowException 异常。核心原因是这些设备是 x64 设备,运行的 x64 的 WPF 应用程序,在消息循环里面传入的 lParam 是一个 x64 的指针,但在 WPF 里面使用 ToInt32 方法进行转换,刚好此 x64 的指针超过 int 的范围,从而抛出异常
此问题已修复
这是一个上古就存在的问题,有人报告说安装了某些驱动就会存在此异常,但是我没有调查到在符合什么情况下就会抛出此异常。此异常的调用堆栈大概如下
异常的中文描述是 算术运算导致溢出
通过阅读代码可以了解到是在 WindowChromeWorker._WndProc
方法里面接收到 Windows 消息,在 _HandleNCHitTest
转换 lParam
参数读取时抛出异常,以下是此方法的有删减的代码
现在的 WPF 是基于最友好的 MIT 协议开源的,所有的源代码都可以从 https://github.com/dotnet/wpf 下载到。如果对以上的 _HandleNCHitTest
方法感兴趣,想要阅读完全的代码,还请到官方开源仓库获取
通过以上代码可以看到,使用 Utility 的 GET_X_LPARAM 方法从 lParam
参数获取一个点的 x 参数。从业务分析上,实际上 lParam
转换的 x 参数是作为屏幕的坐标点,而屏幕的坐标点在 2022 时还是一个 Int16 范围的值。换句话说,如果 lParam
转换出一个 Int64 的长度,那一定是不符合预期的
在 GET_X_LPARAM
方法里面,将会通过 ToInt32
的方法进行转换,且取其低位。在 GET_Y_LPARAM
里,将会转换 lParam
为 Int32 取高位。而抛出的异常就是 ToInt32 这个方法
如果传入了一个 IntPtr 是一个 long 的值,那将会在 ToInt32 方法抛出 OverflowException 异常
我将此问题报告给 WPF 官方,请看 System.OverflowException in PresentationFramework.dll in System.Windows.Shell.WindowChromeWorker · Issue #6777 · dotnet/wpf
然而有趣的是我就是 WPF 官方开发者,于是我就自己修复了这个问题,请看 Fix System.OverflowException in WindowChromeWorker._HandleNCHitTest by lindexi · Pull Request #6779 · dotnet/wpf
我的修复的方法是转换为 Long 再进行裁剪,这个做法我认为是对的,我也阅读了一些相关的对 NCHitTest 消息处理的博客,例如 当无边框窗口被子窗口遮挡导致难以调节窗口大小时,可通过处理 NCHITTEST 消息重新支持调节窗口大小 - walterlv 也都是如此处理
此问题已经被我修复,修复代码已合入 WPF 主版本,遇到此问题的修复方法是升级 .NET SDK 或框架到最新版本
如果自己在用的是不能更新的旧框架,可以使用以下代码进行规避
更多的相关链接:
System.OverflowException in PresentationFramework.dll in System.Windows.Shell.WindowChromeWorker microsoft/dotnet#689
OverflowException when converting 64-bit IntPtr to Int32 ControlzEx/ControlzEx#30
Arithmetic operation resulted in an overflow. MahApps/MahApps.Metro#3301
Exception:算术运算导致溢出。 HandyOrg/HandyControl#886
https://developercommunity.visualstudio.com/t/overflow-exception-in-windowchrome/167357
https://stackoverflow.com/questions/33287542/what-would-cause-wm-nchittest-lparam-to-overflow-a-32-bit-integer
原文链接: http://blog.lindexi.com/post/WPF-%E5%B7%B2%E7%9F%A5%E9%97%AE%E9%A2%98-%E6%9F%90%E4%BA%9B%E8%AE%BE%E5%A4%87%E4%B8%8A%E7%9A%84%E5%BA%94%E7%94%A8%E5%9C%A8-WindowChromeWorker-%E6%8A%9B%E5%87%BA-System.OverflowException-%E5%BC%82%E5%B8%B8
本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。
欢迎转载、使用、重新发布,但务必保留文章署名 林德熙 (包含链接: https://blog.lindexi.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我 联系。