UWP WinUI3 传入 AddHandler 的 RoutedEventHandler 类型与事故所需不匹配 ...

打印 上一主题 下一主题

主题 576|帖子 576|积分 1728

本文记录一个 UWP 或 WinUI3 的开发过程中的问题,当开发者调用 AddHandler 时,所需的 Handler 参数类型为 RoutedEventHandler 类型,然而实际上正确类型是需要与所监听事故匹配才能符合预期工作,否则将抛出缺乏信息的参数异常
开始之前先惯例吐槽一下,我从 2015 开始开发 UWP 应用,然而到 2024 的时候,依然没有看到开发体验上的优化。且在 WinUI3 的技术底层设计上就存在无解问题,那就是许多错误只依靠 COM 的 HR 错误号信息,开发者难以了解真正意义上的调错信息和具体的错误缘故原由。比如说本文所记录的问题
以下是最短复现问题的代码
  1.     public MainPage()
  2.     {
  3.         this.InitializeComponent();
  4.         RoutedEventHandler handler = (_, _) =>
  5.         {
  6.             System.Diagnostics.Debug.WriteLine("PointerPressed");
  7.         };
  8.         AddHandler(PointerPressedEvent, handler, true);
  9.     }
复制代码
以上代码是能够通过构建的,缘故原由是 AddHandler 里面的 Handler 参数就是 object 类型的。然而在运行中将会抛出参数异常,异常信息如下
  1. System.ArgumentException: Value does not fall within the expected range.
  2.    at WinRT.ExceptionHelpers.<ThrowExceptionForHR>g__Throw|39_0(Int32 hr)
复制代码
异常里面还有 HResult 是 -2147024809 的值。其实这个 -2147024809 需要使用 16 进制去看,效果是有名的 0x80070057 错误号。通过 Error 工具可以看到这表现的是 COM 的通用错误信息,名为 E_INVALIDARG 的错误,意思就是参数错误
  1. # for hex 0x80070057 / decimal -2147024809
  2.   COR_E_ARGUMENT                                                 corerror.h
  3. # An argument does not meet the contract of the method.
  4.   DDERR_INVALIDPARAMS                                            ddraw.h
  5.   DIERR_INVALIDPARAM                                             dinput.h
  6.   DSERR_INVALIDPARAM                                             dsound.h
  7.   STIERR_INVALID_PARAM                                           stierr.h
  8.   DRM_E_INVALIDARG                                               windowsplayready.h
  9.   E_INVALIDARG                                                   winerror.h
  10. # One or more arguments are invalid
  11. # as an HRESULT: Severity: FAILURE (1), FACILITY_WIN32 (0x7), Code 0x57
  12. # for hex 0x57 / decimal 87
  13.   ERROR_INVALID_PARAMETER                                        winerror.h
  14. # The parameter is incorrect.
  15. # 8 matches found for "0x80070057"
复制代码
这就是 WinUI3 的一个无解设计问题,通过 HResult 返回错误信息,所包罗的信息量太少了,且很多时候隔断实际错误点又十分远。应用开发者又不知道 WinUI3 底层投了哪些毒,难以知道所说的参数错误具体指的是什么错误。这一点也是制约了 WinUI 3 的生态,但这一点又是属于 WinUI 3 的基础设计的问题,预估难以更改
这一次的错误信息里面在 Data 里面还包罗几条看似没有用,实际也没有用的信息,分别如下
  1. +                [0]        {[Description, 不支持此接口
  2. ]}        object {System.Collections.DictionaryEntry}
  3. +                [1]        {[RestrictedDescription, 不支持此接口
  4. ]}        object {System.Collections.DictionaryEntry}
  5. +                [2]        {[RestrictedErrorReference, ]}        object {System.Collections.DictionaryEntry}
  6. +                [3]        {[RestrictedCapabilitySid, ]}        object {System.Collections.DictionaryEntry}
  7. +                [4]        {[__RestrictedErrorObjectReference, WinRT.ExceptionHelpers+__RestrictedErrorObject]}        object {System.Collections.DictionaryEntry}
  8. +                [5]        {[__HasRestrictedLanguageErrorObject, False]}        object {System.Collections.DictionaryEntry}
复制代码
也就是形貌信息里面说的是 不支持此接口 的形貌信息,合起来就是:碰到参数错误了,因为底层不支持参数传进来的此接口
但是就是不告诉各人,具体错误的是哪个参数,且错在哪里了。要是能够明确说明 handler 参数的类型不符合预期之类的,那开发者的调试效率将会高出许多
本文记录的错误问题缘故原由是 PointerPressedEvent 所对应的是 PointerEventHandler 类型,而不是 RoutedEventHandler 类型,修复的代码如下
  1.         PointerEventHandler handler = (_, _) =>
  2.         {
  3.             System.Diagnostics.Debug.WriteLine("PointerPressed");
  4.         };
  5.         AddHandler(PointerPressedEvent, handler, true);
复制代码
那日常开发过程中,如何知道 AddHandler 里面的 handler 参数应该传入什么类型的委托呢?其实方法很简朴,只需要使用对应的事故,看看对应的事故界说是什么。比如 PointerPressedEvent 对应的就是 PointerPressed 事故,按照通用定名法就是对应的事故就是对应路由事故界说去掉 Event 后缀。通过查阅文档或者是在 VisualStudio 里面点点看,就可以看到对应的事故的界说,如下面代码就是 PointerPressed 的界说,可以看到事故是 PointerEventHandler 类型的委托
  1. public event PointerEventHandler PointerPressed { add; remove; }
复制代码
通过此方式即可知道传入 AddHandler 的 handler 应该使用什么样的类型,解决运行时失败的缘故原由。常见的错误都在于更改代码的时候,忘记同步更改对应的委托类型
额外补充一点,以上的代码的 handler 局部变量是安全的,不会被回收,缘故原由是虽然在以上代码里面看起来 handler 局部变量没被引用,然而在 AddHandler 底层里面已经做好了引用,不会导致 handler 被回收,从而导致 COM 层访问被回收的内存而炸掉的问题。但是此问题在古老的 UWP 是存在的。一个推荐的优化方法就是将 handler 存放在字段里面,手动防止被回收
本文代码放在 githubgitee 上,可以使用如下命令行拉取代码
先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码
  1. git init
  2. git remote add origin https://gitee.com/lindexi/lindexi_gd.git
  3. git pull origin d43a62536b449ef337160f9931265a0db482ed12
复制代码
以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继承输入以下代码,将 gitee 源换成 github 源进行拉取代码
  1. git remote remove origin
  2. git remote add origin https://github.com/lindexi/lindexi_gd.git
  3. git pull origin d43a62536b449ef337160f9931265a0db482ed12
复制代码
获取代码之后,进入 FelawchechadaGeqedaihallnela 文件夹,即可获取到源代码

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

梦应逍遥

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表