首页 > 其他分享 >WinAppSDK / WinUI3 项目无法使用 SystemEvents 的问题

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

时间:2024-03-23 16:56:28浏览次数:245  
标签:dispatcherQueue SynchronizationContext WinUI3 syncContext WinAppSDK 线程 SystemEve

SystemEvents 是一个开发 win32 窗口项目很常用的类,其中封装了一些常用的系统广播消息。在 WinUI3 项目中,SystemEvents 事件经常无法触发,简单排查了一下原因。

SystemEvent 内封装了一个线程和一个窗口,通过窗口消息在内部线程上调用事件,内部使用了 SystemEventInvokeInfo 对象来保存委托,RaiseEvent 方法遍历调用保存的 SystemEventInvokeInfo.Invoke 方法来触发事件。

public SystemEventInvokeInfo(Delegate d)
{
    _delegate = d;
    _syncContext = AsyncOperationManager.SynchronizationContext;
}
// fire the given event with the given params.
public void Invoke(bool checkFinalization, params object[] args)
{
    try
    {
        // If we didn't get call back invoke directly.
        if (_syncContext == null)
        {
            InvokeCallback(args);
        }
        else
        {
            // otherwise tell the context to do it for us.
            _syncContext.Send(new SendOrPostCallback(InvokeCallback), args);
        }
    }
    catch (InvalidAsynchronousStateException)
    {
        // if the synch context is invalid -- do the invoke directly for app compat.
        // If the app's shutting down, don't fire the event (unless it's shutdown).
        if (!checkFinalization || !AppDomain.CurrentDomain.IsFinalizingForUnload())
        {
            InvokeCallback(args);
        }
    }
}

我们可以注意到 SystemEventInvokeInfo.Invoke 判断了 _syncContext 变量,_syncContext 变量在 SystemEventInvokeInfo 构造时捕获,Invoke 时使用 _syncContext.Send 方法调用。

/// <summary>
/// DispatcherQueueSyncContext allows developers to await calls and get back onto the
/// UI thread. Needs to be installed on the UI thread through DispatcherQueueSyncContext.SetForCurrentThread
/// </summary>
public class DispatcherQueueSynchronizationContext : SynchronizationContext
{
    private readonly DispatcherQueue m_dispatcherQueue;

    public DispatcherQueueSynchronizationContext(DispatcherQueue dispatcherQueue)
    {
        m_dispatcherQueue = dispatcherQueue;
    }

    public override void Post(SendOrPostCallback d, object state)
    {
        if (d == null)
            throw new ArgumentNullException(nameof(d));

        m_dispatcherQueue.TryEnqueue(() => d(state));
    }

    public override void Send(SendOrPostCallback d, object state)
    {
        throw new NotSupportedException("Send not supported");
    }

    public override SynchronizationContext CreateCopy()
    {
        return new DispatcherQueueSynchronizationContext(m_dispatcherQueue);
    }
}

而 WinUI3 的 UI 线程的默认 SynchronizationContext 为 DispatcherQueueSynchronizationContext,简单查看源码可以发现 DispatcherQueueSynchronizationContext.Send 并未实现,而是直接抛出了异常,所以从 UI 线程注册的 SystemEvents 事件默认是不会触发的。

解决方案也很简单:

SystemEvents.InvokeOnEventsThread(() =>
{
    // 不需要设置,因为默认就是null
    //SynchronizationContext.SetSynchronizationContext(null);

    SystemEvents.DisplaySettingsChanged += (s, a) =>
    {
        Debug.WriteLine("DisplaySettingsChanged");
    };
});

我们借用一下 SystemEvents 的内部线程,在此线程上注册事件时 SystemEventInvokeInfo 捕获不到 SynchronizationContext,就会在 SystemEvents 内部线程上触发事件,自然就能正常触发了。

标签:dispatcherQueue,SynchronizationContext,WinUI3,syncContext,WinAppSDK,线程,SystemEve
From: https://www.cnblogs.com/blue-fire/p/18091294

相关文章

  • 聊聊MAUI、WinUI3和WPF的优势及劣势
    今天在群里聊到WinUI3的学习及发展,还有他那堪比玩具的使用体验,正好梳理一篇关于WinUI3、MAUI和WPF优劣势,我整理的不是很好,所以又让ChatGPT在生成了一遍,感觉整体还可以。看完可以相互讨论一下;引言:在应用程序开发领域,选择合适的框架对于开发人员和业务来说至关重要。本文将比较并......
  • 四、WinUI3下TitleBar的自定义
    WinUI3下TitleBar的自定义对于Windows软件开发者来说重写标题栏样式是一个很重要的事情,在WPF阶段很多人写出来性能很差的窗口,而且为了适配Win11系统的Snaplayout后性能就......
  • 【WinUI3】Unpackged启动爆找不到Microsoft.xaml.ui.dll错误
    其实官网就有说如何Unpackage启动,但是因为爆的是dll的加载失败问题,所以我还是迷惑了好一阵。解决方法是在启动项目的项目文件里添加:<WindowsPackageType>None</WindowsPa......
  • 【WinUI3】ListView / GridView 学习总结
    简述官方对Listview和Gridview的描述是:Thefeature-richListViewandGridViewcontrolsworkoutofbox.Theyrequirenocustomization,buttheycanbecustom......
  • WinUI3 使用Win32Api 实现窗口停靠常驻桌面功能。
    我们可以通过使用Win32Api来制作一些强大的功能,本文将通过示例代码来介绍使用Win32Api来之做桌面窗口停靠功能;效果图:   一.通过Nuget引入Vanara.PInv......