立即显示启动画面

5
我们正在处理WinForm应用程序的缓慢启动问题(该应用程序非常大,有许多控件程序集)。控件程序集是DevComponents。已应用Ngen以防止JIT编译,但加载时间仅略有减少。
该应用程序有一个启动画面,但只有在应用程序启动后12秒才出现。是否有任何方法可以立即显示启动画面?
我们目前的建议是创建一个轻量级应用程序,并在其中添加启动画面,在单独的进程中运行主应用程序,并在主应用程序初始化完成后关闭轻量级应用程序。

如果NGEN优化不是很重要,我建议放弃它,保留所有JIT的好处。 - R. Martinho Fernandes
1
你是如何显示启动界面的?能给一个代码示例吗? - Evan Mulawski
它是通过使用单独的表单来实现的。 - Andrei Schneider
4个回答

12
你永远无法立即显示.NET应用程序的启动画面。即使您使用NGen消除了JIT编译时间,您仍然需要等待所有.NET Framework DLL加载到内存中。它是一个相当大的框架,在冷启动时需要花费相当长的时间进行加载。这方面真的没有什么可以做的。
微软已经尽可能地减轻了这种痛苦。WindowsFormsApplicationBase(它在Microsoft.VisualBasic命名空间中定义,但不要让它吓到您;它可以从C#应用程序中完美使用)提供了一个内置机制来显示启动画面。您只需将其SplashScreen属性设置为适当的窗体,其他所有内容都将在幕后处理。即使在冷启动情况下,它也经过了大量优化,以实现最快的响应时间,但仍然不会是立即的。
你唯一的选择是在非托管代码中编写一个小包装器,其唯一目的是尽快启动闪屏,然后调用你的.NET应用程序开始启动。当然,这里越轻越好。C++是一种选择,但C可能是更好的选择。你需要最小化你必须链接的外部库的数量,所以像MFC或Qt这样的框架肯定不行:你需要直接针对Windows API。

Visual Studio团队在VS 2010中做了类似的事情。他们有一个相当有趣的解释过程可在他们的博客上找到

即使Visual Studio 2010使用WPF作为主窗口,但使用WPF作为闪屏将需要我们等待CLR和WPF初始化,然后才能在屏幕上绘制单个像素。虽然在.Net 4.0中,我们在CLR和WPF启动速度方面取得了巨大的进步,但它仍然无法完全匹配原始Win32的性能。因此,选择使用本地C++代码和Win32进行闪屏。

但我不会在这里花太多时间。由于通常应该给用户选择打开和关闭闪屏的选项,并且大多数用户将选择关闭它,因此很少有人会首先看到它。任何优化分析器都会告诉你,这不值得优化。


5
对于一个使用C++编写并使用本地Windows API的轻量级、可重用的启动屏幕组件,请参见Stefan Olson's splash screen。它采用了Cody Gray提到的方法,即从一个单独的非CLR进程开始,然后加载主CLR应用程序。
我在上一份工作中不得不实现完全相同的事情,我可以确认这种方法很好用——用户在开始菜单中单击程序图标和启动屏幕出现之间的时间只有几毫秒,所以感觉“瞬间”就出现了。

你可以考虑在帖子中总结链接内容 - FabienAndre

1

我建议您继续采用您目前的建议。

Winforms应用程序只有在完全加载到内存后才会显示,而且由于需要时间,我建议使用一个加载器应用程序来显示闪屏并执行主要的大型应用程序(作为子进程)。

在大型应用程序中,在加载完成后终止父应用程序。顺便说一句,这不会杀死子进程。


一个小的WinForms应用程序不太可能比一个的WinForms应用程序更快地加载同一个窗体。关键是在任何情况下都要首先加载启动屏幕窗体,而不是从显示主窗体开始,并让主窗体显示启动屏幕。 - Cody Gray
什么?一个没有除CLR之外的任何依赖关系的小型WinForms应用程序将比具有大量依赖项的应用程序加载得快得多。你说的“从显示主窗体开始并让主窗体显示闪屏”是什么意思?你能详细说明一下吗? - Dheeraj Kumar
1
阅读我的评论,针对Jason Moore的回答。CLR直到需要它们时才会加载依赖项。除非您实际上使用来自这些外部程序集的代码,无论它们是否被应用程序引用,否则不会有任何惩罚。我的意思是,在您的Main方法开始时显示一个不依赖于任何外部依赖项的简单启动屏幕窗体,它将在空WinForms应用程序中以及在具有一百个依赖项的应用程序中同样快地加载。这些依赖项直到您请求它们才会被加载,这就是JIT编译的魔力。 - Cody Gray

0
过去我通常采用的快速而简单的方法是使用两个应用程序,即主应用程序和启动画面应用程序。主应用程序是正常的,具有所有重量级DLL、控件等。启动画面应用程序只是一个应用程序和一个Windows窗体,其中所有不必要的参考DLL都从项目中剥离了出来——实际上,您可以更进一步地使用轻量级的.NET框架,如紧凑版.NET框架或较早版本的.NET框架,如1.X或2.0。
您需要做的是让启动画面应用程序启动并立即显示单个启动画面。设置一个Windows窗体计时器(设为100毫秒),并在窗体的Load事件的最后一行启用计时器。当计时器触发时,禁用计时器,然后启动您的真实应用程序。请参见this discussion以了解如何执行此操作。现在,主应用程序将开始启动。一旦应用程序完成初始化并可能加载第一个窗体,然后让主应用程序关闭启动画面应用程序。有关终止应用程序的更多信息,请参见here

这样做如何实际改善情况并不清楚。即使您有一个相对较小的.NET应用程序,仍然需要等待加载.NET DLL和CLR。更重要的是,CLR足够聪明,不会加载您未使用的程序集/引用的DLL。如果您的启动屏幕窗体不使用在那些引用DLL中定义的任何控件,并且它是您在屏幕上显示的第一件事情,那么在“大型”应用程序中,它将与“小型”应用程序一样快。 - Cody Gray
此外,我非常建议放弃使用计时器的建议。用户不想要等待更长时间来加载您的应用程序,只是因为您向他们展示了广告牌。我并不反对为加载时间非常长的应用程序添加启动画面,但即使只有100毫秒的延迟也是如此完全无意义,几乎令人反感。 - Cody Gray
我的观点是,启动屏幕应用程序不必引用原帖中提到的任何大型DevComponents dll。这样做并针对较小的.NET框架将使其更快。我假设只有主应用程序需要引用(因此加载)所有似乎使原始程序加载需要12秒或更长时间的DevComponents dll。 - Jason Moore
  1. 除非需要,否则DevComponents DLL不会被加载。如果启动画面窗体不使用它们(也不应该使用),那么它们不会被加载以显示启动画面。只有在主窗体加载后才会被加载,希望这是在启动画面出现之后。
  2. 紧凑框架适用于移动设备,而不是Windows桌面版本,因此这是行不通的。而且4.0版本比2.0版本小得多。不确定1.0版本,但是为了一个启动画面而要求安装两个FW版本没有太大意义。
- Cody Gray

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接