UWP WinUI3 傳入 AddHandler 的 RoutedEventHandler 類型與事件所需不匹配將拋出參數異常

来源:https://www.cnblogs.com/lindexi/p/18049575
-Advertisement-
Play Games

在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...


本文記錄一個 UWP 或 WinUI3 的開發過程中的問題,當開發者調用 AddHandler 時,所需的 Handler 參數類型為 RoutedEventHandler 類型,然而實際上正確類型是需要與所監聽事件匹配才能符合預期工作,否則將拋出缺乏信息的參數異常

開始之前先慣例吐槽一下,我從 2015 開始開發 UWP 應用,然而到 2024 的時候,依然沒有看到開發體驗上的優化。且在 WinUI3 的技術底層設計上就存在無解問題,那就是許多錯誤只依靠 COM 的 HR 錯誤號信息,開發者難以瞭解真正意義上的調錯信息和具體的錯誤原因。比如說本文所記錄的問題

以下是最短復現問題的代碼

    public MainPage()
    {
        this.InitializeComponent();
        RoutedEventHandler handler = (_, _) =>
        {
            System.Diagnostics.Debug.WriteLine("PointerPressed");
        };

        AddHandler(PointerPressedEvent, handler, true);
    }

以上代碼是能夠通過構建的,原因是 AddHandler 裡面的 Handler 參數就是 object 類型的。然而在運行中將會拋出參數異常,異常信息如下

System.ArgumentException: Value does not fall within the expected range.
   at WinRT.ExceptionHelpers.<ThrowExceptionForHR>g__Throw|39_0(Int32 hr)

異常裡面還有 HResult 是 -2147024809 的值。其實這個 -2147024809 需要使用 16 進位去看,結果是有名的 0x80070057 錯誤號。通過 Error 工具可以看到這表示的是 COM 的通用錯誤信息,名為 E_INVALIDARG 的錯誤,意思就是參數錯誤

# for hex 0x80070057 / decimal -2147024809
  COR_E_ARGUMENT                                                 corerror.h
# An argument does not meet the contract of the method.
  DDERR_INVALIDPARAMS                                            ddraw.h
  DIERR_INVALIDPARAM                                             dinput.h
  DSERR_INVALIDPARAM                                             dsound.h
  STIERR_INVALID_PARAM                                           stierr.h
  DRM_E_INVALIDARG                                               windowsplayready.h
  E_INVALIDARG                                                   winerror.h
# One or more arguments are invalid
# as an HRESULT: Severity: FAILURE (1), FACILITY_WIN32 (0x7), Code 0x57
# for hex 0x57 / decimal 87
  ERROR_INVALID_PARAMETER                                        winerror.h
# The parameter is incorrect.
# 8 matches found for "0x80070057"

這就是 WinUI3 的一個無解設計問題,通過 HResult 返回錯誤信息,所包含的信息量太少了,且很多時候距離實際錯誤點又十分遠。應用開發者又不知道 WinUI3 底層投了哪些毒,難以知道所說的參數錯誤具體指的是什麼錯誤。這一點也是制約了 WinUI 3 的生態,但這一點又是屬於 WinUI 3 的基礎設計的問題,預估難以更改

這一次的錯誤信息裡面在 Data 裡面還包含幾條看似沒有用,實際也沒有用的信息,分別如下

+		[0]	{[Description, 不支持此介面
]}	object {System.Collections.DictionaryEntry}
+		[1]	{[RestrictedDescription, 不支持此介面
]}	object {System.Collections.DictionaryEntry}
+		[2]	{[RestrictedErrorReference, ]}	object {System.Collections.DictionaryEntry}
+		[3]	{[RestrictedCapabilitySid, ]}	object {System.Collections.DictionaryEntry}
+		[4]	{[__RestrictedErrorObjectReference, WinRT.ExceptionHelpers+__RestrictedErrorObject]}	object {System.Collections.DictionaryEntry}
+		[5]	{[__HasRestrictedLanguageErrorObject, False]}	object {System.Collections.DictionaryEntry}

也就是描述信息裡面說的是 不支持此介面 的描述信息,合起來就是:遇到參數錯誤了,因為底層不支持參數傳進來的此介面

但是就是不告訴大家,具體錯誤的是哪個參數,且錯在哪裡了。要是能夠明白說明 handler 參數的類型不符合預期之類的,那開發者的調試效率將會高出許多

本文記錄的錯誤問題原因是 PointerPressedEvent 所對應的是 PointerEventHandler 類型,而不是 RoutedEventHandler 類型,修複的代碼如下

        PointerEventHandler handler = (_, _) =>
        {
            System.Diagnostics.Debug.WriteLine("PointerPressed");
        };

        AddHandler(PointerPressedEvent, handler, true);

那日常開發過程中,如何知道 AddHandler 裡面的 handler 參數應該傳入什麼類型的委托呢?其實方法很簡單,只需要使用對應的事件,看看對應的事件定義是什麼。比如 PointerPressedEvent 對應的就是 PointerPressed 事件,按照通用命名法就是對應的事件就是對應路由事件定義去掉 Event 尾碼。通過查閱文檔或者是在 VisualStudio 裡面點點看,就可以看到對應的事件的定義,如下麵代碼就是 PointerPressed 的定義,可以看到事件是 PointerEventHandler 類型的委托

public event PointerEventHandler PointerPressed { add; remove; }

通過此方式即可知道傳入 AddHandler 的 handler 應該使用什麼樣的類型,解決運行時失敗的原因。常見的錯誤都在於更改代碼的時候,忘記同步更改對應的委托類型

額外補充一點,以上的代碼的 handler 局部變數是安全的,不會被回收,原因是雖然在以上代碼裡面看起來 handler 局部變數沒被引用,然而在 AddHandler 底層裡面已經做好了引用,不會導致 handler 被回收,從而導致 COM 層訪問被回收的記憶體而炸掉的問題。但是此問題在古老的 UWP 是存在的。一個推薦的優化方法就是將 handler 存放在欄位裡面,手動防止被回收

本文代碼放在 githubgitee 上,可以使用如下命令行拉取代碼

先創建一個空文件夾,接著使用命令行 cd 命令進入此空文件夾,在命令行裡面輸入以下代碼,即可獲取到本文的代碼

git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin d43a62536b449ef337160f9931265a0db482ed12

以上使用的是 gitee 的源,如果 gitee 不能訪問,請替換為 github 的源。請在命令行繼續輸入以下代碼,將 gitee 源換成 github 源進行拉取代碼

git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin d43a62536b449ef337160f9931265a0db482ed12

獲取代碼之後,進入 FelawchechadaGeqedaihallnela 文件夾,即可獲取到源代碼

博客園博客只做備份,博客發佈就不再更新,如果想看最新博客,請訪問 https://blog.lindexi.com/

如圖片看不見,請在瀏覽器開啟不安全http內容相容

知識共用許可協議
本作品採用知識共用署名-非商業性使用-相同方式共用 4.0 國際許可協議進行許可。歡迎轉載、使用、重新發佈,但務必保留文章署名[林德熙](https://www.cnblogs.com/lindexi)(包含鏈接:https://www.cnblogs.com/lindexi ),不得用於商業目的,基於本文修改後的作品務必以相同的許可發佈。如有任何疑問,請與我[聯繫](mailto:[email protected])。
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一:背景 講故事 公司部署在某碟上的項目在9月份壓測50併發時,發現某個容器線程、記憶體非正常的上漲,導致功能出現了異常無法使用。根據所學,自己分析了下線程和記憶體問題,分析時可以使用lldb或者windbg,但是個人比較傾向於界面化的windbg,所以最終使用windbg開乾。 二:WinDbg 分析 ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
一周排行
    -Advertisement-
    Play Games
  • 示例項目結構 在 Visual Studio 中創建一個 WinForms 應用程式後,項目結構如下所示: MyWinFormsApp/ │ ├───Properties/ │ └───Settings.settings │ ├───bin/ │ ├───Debug/ │ └───Release/ ...
  • [STAThread] 特性用於需要與 COM 組件交互的應用程式,尤其是依賴單線程模型(如 Windows Forms 應用程式)的組件。在 STA 模式下,線程擁有自己的消息迴圈,這對於處理用戶界面和某些 COM 組件是必要的。 [STAThread] static void Main(stri ...
  • 在WinForm中使用全局異常捕獲處理 在WinForm應用程式中,全局異常捕獲是確保程式穩定性的關鍵。通過在Program類的Main方法中設置全局異常處理,可以有效地捕獲並處理未預見的異常,從而避免程式崩潰。 註冊全局異常事件 [STAThread] static void Main() { / ...
  • 前言 給大家推薦一款開源的 Winform 控制項庫,可以幫助我們開發更加美觀、漂亮的 WinForm 界面。 項目介紹 SunnyUI.NET 是一個基於 .NET Framework 4.0+、.NET 6、.NET 7 和 .NET 8 的 WinForm 開源控制項庫,同時也提供了工具類庫、擴展 ...
  • 說明 該文章是屬於OverallAuth2.0系列文章,每周更新一篇該系列文章(從0到1完成系統開發)。 該系統文章,我會儘量說的非常詳細,做到不管新手、老手都能看懂。 說明:OverallAuth2.0 是一個簡單、易懂、功能強大的許可權+可視化流程管理系統。 有興趣的朋友,請關註我吧(*^▽^*) ...
  • 一、下載安裝 1.下載git 必須先下載並安裝git,再TortoiseGit下載安裝 git安裝參考教程:https://blog.csdn.net/mukes/article/details/115693833 2.TortoiseGit下載與安裝 TortoiseGit,Git客戶端,32/6 ...
  • 前言 在項目開發過程中,理解數據結構和演算法如同掌握蓋房子的秘訣。演算法不僅能幫助我們編寫高效、優質的代碼,還能解決項目中遇到的各種難題。 給大家推薦一個支持C#的開源免費、新手友好的數據結構與演算法入門教程:Hello演算法。 項目介紹 《Hello Algo》是一本開源免費、新手友好的數據結構與演算法入門 ...
  • 1.生成單個Proto.bat內容 @rem Copyright 2016, Google Inc. @rem All rights reserved. @rem @rem Redistribution and use in source and binary forms, with or with ...
  • 一:背景 1. 講故事 前段時間有位朋友找到我,說他的窗體程式在客戶這邊出現了卡死,讓我幫忙看下怎麼回事?dump也生成了,既然有dump了那就上 windbg 分析吧。 二:WinDbg 分析 1. 為什麼會卡死 窗體程式的卡死,入口門檻很低,後續往下分析就不一定了,不管怎麼說先用 !clrsta ...
  • 前言 人工智慧時代,人臉識別技術已成為安全驗證、身份識別和用戶交互的關鍵工具。 給大家推薦一款.NET 開源提供了強大的人臉識別 API,工具不僅易於集成,還具備高效處理能力。 本文將介紹一款如何利用這些API,為我們的項目添加智能識別的亮點。 項目介紹 GitHub 上擁有 1.2k 星標的 C# ...