WinAppSDK / WinUI3 项目无法使用 SystemEvents 的题目

打印 上一主题 下一主题

主题 846|帖子 846|积分 2538

SystemEvents 是一个开发 win32 窗口项目很常用的类,此中封装了一些常用的体系广播消息。在 WinUI3 项目中,SystemEvents 事件经常无法触发,简朴排查了一下原因。
SystemEvent 内封装了一个线程和一个窗口,通过窗口消息在内部线程上调用事件,内部使用了 SystemEventInvokeInfo 对象来保存委托,RaiseEvent 方法遍历调用保存的 SystemEventInvokeInfo.Invoke 方法来触发事件。
  1. public SystemEventInvokeInfo(Delegate d)
  2. {
  3.     _delegate = d;
  4.     _syncContext = AsyncOperationManager.SynchronizationContext;
  5. }
复制代码
  1. // fire the given event with the given params.
  2. public void Invoke(bool checkFinalization, params object[] args)
  3. {
  4.     try
  5.     {
  6.         // If we didn't get call back invoke directly.
  7.         if (_syncContext == null)
  8.         {
  9.             InvokeCallback(args);
  10.         }
  11.         else
  12.         {
  13.             // otherwise tell the context to do it for us.
  14.             _syncContext.Send(new SendOrPostCallback(InvokeCallback), args);
  15.         }
  16.     }
  17.     catch (InvalidAsynchronousStateException)
  18.     {
  19.         // if the synch context is invalid -- do the invoke directly for app compat.
  20.         // If the app's shutting down, don't fire the event (unless it's shutdown).
  21.         if (!checkFinalization || !AppDomain.CurrentDomain.IsFinalizingForUnload())
  22.         {
  23.             InvokeCallback(args);
  24.         }
  25.     }
  26. }
复制代码
我们可以留意到 SystemEventInvokeInfo.Invoke 判断了 _syncContext 变量,_syncContext 变量在 SystemEventInvokeInfo 构造时捕捉,Invoke 时使用 _syncContext.Send 方法调用。
  1. /// <summary>
  2. /// DispatcherQueueSyncContext allows developers to await calls and get back onto the
  3. /// UI thread. Needs to be installed on the UI thread through DispatcherQueueSyncContext.SetForCurrentThread
  4. /// </summary>
  5. public class DispatcherQueueSynchronizationContext : SynchronizationContext
  6. {
  7.     private readonly DispatcherQueue m_dispatcherQueue;
  8.     public DispatcherQueueSynchronizationContext(DispatcherQueue dispatcherQueue)
  9.     {
  10.         m_dispatcherQueue = dispatcherQueue;
  11.     }
  12.     public override void Post(SendOrPostCallback d, object state)
  13.     {
  14.         if (d == null)
  15.             throw new ArgumentNullException(nameof(d));
  16.         m_dispatcherQueue.TryEnqueue(() => d(state));
  17.     }
  18.     public override void Send(SendOrPostCallback d, object state)
  19.     {
  20.         throw new NotSupportedException("Send not supported");
  21.     }
  22.     public override SynchronizationContext CreateCopy()
  23.     {
  24.         return new DispatcherQueueSynchronizationContext(m_dispatcherQueue);
  25.     }
  26. }
复制代码
而 WinUI3 的 UI 线程的默认 SynchronizationContext 为 DispatcherQueueSynchronizationContext,简朴检察源码可以发现 DispatcherQueueSynchronizationContext.Send 并未实现,而是直接抛出了非常,以是从 UI 线程注册的 SystemEvents 事件默认是不会触发的。
解决方案也很简朴:
  1. SystemEvents.InvokeOnEventsThread(() =>
  2. {
  3.     // 不需要设置,因为默认就是null
  4.     //SynchronizationContext.SetSynchronizationContext(null);
  5.     SystemEvents.DisplaySettingsChanged += (s, a) =>
  6.     {
  7.         Debug.WriteLine("DisplaySettingsChanged");
  8.     };
  9. });
复制代码
我们借用一下 SystemEvents 的内部线程,在此线程上注册事件时 SystemEventInvokeInfo 捕捉不到 SynchronizationContext,就会在 SystemEvents 内部线程上触发事件,自然就能正常触发了。

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

使用道具 举报

0 个回复

正序浏览

快速回复

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

本版积分规则

小小小幸运

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表