dotnet 读 WPF 源代码笔记 启动欢迎界面 SplashScreen 的原理

本文详细解析了WPF中SplashScreen的实现,包括如何在主机启动前显示启动图,以及核心逻辑。讨论了WPF源代码中加载图片资源的方法,如GetResourceStream和Assembly.GetManifestResourceStream的性能差异。还介绍了第三方库kkwpsv/SplashImage提供的高性能解决方案,并概述了WPF启动图界面的三个关键步骤:图片解码、转换为BGRA格式及使用GDI显示。最后提到了WPF与UWP在启动速度上的差异以及WPF源代码的开源情况。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文是我在读 WPF 源代码做的笔记。在 WPF 中的启动界面,为了能让 WPF 的启动界面显示足够快,需要在应用的 WPF 主机还没有启动完成之前就显示出启动图,此时的启动图需要自己解析图片同时也需要自己创建显示窗口

从 WPF 的 src\Microsoft.DotNet.Wpf\src\WindowsBase\System\Windows\SplashScreen.cs 文件可以看到 WPF 的 SplashScreen 的核心逻辑

在 SplashScreen 的构造函数会传入资源名,也就是启动图的资源名,或者加上指定程序集和图片资源名

        public SplashScreen(string resourceName) : this(Assembly.GetEntryAssembly(), resourceName)
        {
        }

        public SplashScreen(Assembly resourceAssembly, string resourceName)
        {
        	// 忽略代码
        }

当然了,这个设计扩展性不够好哈,不支持指定任意的图片。如果想要指定本地路径的任意图片作为启动图的,可以使用 lsj 提供的 kkwpsv/SplashImage: Fast splash Image with GDI+ in C# 库,当然了,这个库代码量特别少,我推荐大家可以抄抄代码。这个库提供的是高性能的版本,可以在另一个线程中执行,换句话说,就是使用 kkwpsv/SplashImage 作为欢迎界面,是可以做到不占用 WPF 主线程时间的,性能比 WPF 提供的好

在 WPF 的 SplashScreen 的 Show 方法,就是启动图的核心逻辑

先调用 GetResourceStream 从自己的程序集里面读取图片资源的原始 Stream 对象,通过此方式的读取性能特别强,因此不是真的读取到内存里面,而是获取一个指针而已。但是有趣的是在这个方法上面有注释说比 Assembly.GetManifestResourceStream 慢 200-300 毫秒,也许是当年的设备才需要这么长的时间

        // This is 200-300 ms slower than Assembly.GetManifestResourceStream() but works with localization.
        private UnmanagedMemoryStream GetResourceStream()

在获取到启动图片的 UnmanagedMemoryStream 之后,将使用下面代码转换为指针,用于后续传入给 WIC 层

IntPtr pImageSrcBuffer;
unsafe
{
    pImageSrcBuffer = new IntPtr(umemStream.PositionPointer);
}

接下来就是调用 CreateLayeredWindowFromImgBuffer 创建一个窗口然后这个窗口显示图片内容

if (CreateLayeredWindowFromImgBuffer(pImageSrcBuffer, umemStream.Length, topMost) && autoClose == true)
{
    Dispatcher.CurrentDispatcher.BeginInvoke(
        DispatcherPriority.Loaded,
        (DispatcherOperationCallback)ShowCallback,
        this);
}

可以看到在调用 CreateLayeredWindowFromImgBuffer 方法成功之后,就会调用 Dispatcher 插入 ShowCallback 函数,在 ShowCallback 里面用来自动关闭启动界面,如下面代码

        private static object ShowCallback(object arg)
        {
            SplashScreen splashScreen = (SplashScreen)arg;
            splashScreen.Close(TimeSpan.FromSeconds(0.3));
            return null;
        }

从上面代码可以看到,在 WPF 中默认的启动图界面将会在 Loaded 完成之后延迟 0.3 秒执行,而具体是什么 Loaded 就不需要关注了。因为通过 BeginInvoke 插入的优先级是 DispatcherPriority.Loaded 优先级,也就是启动过程如果再没有什么比 DispatcherPriority.Loaded 更高的优先级,那就是启动完成了

在 WPF 里面的 SplashScreen 的核心逻辑里面包含以下三步

第一步是通过 WIC 层解码咱传入的图片,这样就支持不做任何优

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值