Application.Run 和 Form.Load 之间会发生什么

本文关键字:什么 之间 Load Run Form Application | 更新日期: 2023-09-27 18:34:20

我有一个用 VB.NET 编写的用于Framework 4.5的WinForms应用程序。我注意到应用程序的启动时间异常长(我编写的其他应用程序在启动时几乎立即启动时做更多工作,此应用程序需要>5 秒(多次启动后启动时间不会改变,所以我想这不是应用程序第一次启动期间未缓存 CLR 代码的情况。

我通过在启动期间写下时间进行了一些测试:

Module modMain
    Public MyLog As System.Text.StringBuilder
    <STAThread>
    Public Sub Main()
        MyLog = New System.Text.StringBuilder
        Application.EnableVisualStyles()
        Application.SetCompatibleTextRenderingDefault(False)
        MyLog.AppendLine("Before run: " & Date.Now.ToString & "," & Date.Now.Millisecond.ToString)
        Application.Run(frmMain)
    End Sub
End Module

Sub Main()是应用程序入口点。它运行frmMain,在那里我能控制的第一个真正的东西是Sub InitializeComponent(),由设计师生成:

<System.Diagnostics.DebuggerStepThrough()> _
Private Sub InitializeComponent()
    MyLog.AppendLine("Init Start: " & Date.Now.ToString & "," & Date.Now.Millisecond.ToString)
    'All the control initializations
    MyLog.AppendLine("Init End: " & Date.Now.ToString & "," & Date.Now.Millisecond.ToString)
End Sub

最后我到达了Form.Load活动

Private Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    MyLog.AppendLine("Form_Load Start: " & Date.Now.ToString & "," & Date.Now.Millisecond.ToString)
    '...
    MyLog.AppendLine("Form_Load End: " & Date.Now.ToString & "," & Date.Now.Millisecond.ToString)
End Sub

现在,MyLog 的输出如下:

Before run: 15.12.2014 19:56:47,579
Init Start: 15.12.2014 19:56:51,451
Init End: 15.12.2014 19:56:51,521
Form_Load Start: 15.12.2014 19:56:51,544
Form_Load End: 15.12.2014 19:56:51,547

您可以看到,主要暂停发生在 Application.Run()Sub InitializeComponent() 之间。我从其他问题中知道,GUI 线程启动了一个消息循环,但我不知道为什么这个应用程序应该比其他应用程序慢得多。

所以我的问题是:在Application.Run和我重新获得对代码的控制点之间究竟发生了什么,我可以做些什么来加速它吗?在那里完成的工作是否与表单上的组件有任何关系?

我已经尝试使用 frmMain.ShowDialog() 而不是 Application.Run(frmMain) ,但这导致了相同的结果。我正在使用Visual Studio Express,所以很遗憾我不能使用更深入的性能分析器。

将其标记为 C# 和 VB.NET,因为非常欢迎两种语言的答案。

编辑
我做了更多的测试,包括 SLaks 答案中提出的解决方案。使用 NGEN 预编译程序集似乎没有任何明显的效果。所以我想这不是InitializeComponent代码的 JIT 编译。

然而,我注意到,在其他系统上,程序几乎是即时启动的(>快 10 倍(,即使有问题的计算机在各个方面都慢。计算机之间的区别在于操作系统:

Windows 7: Slow start
Windows 8.1: Fast start
Windows Server 2008: Fast start

这些只是更多的线索,我真的不知道它是否有助于答案。

编辑 2
在启动期间查看 ProcMon,我发现执行在以下行挂起:

"15:56:29.3547260","Electrochemical Calculator.exe","5972","CreateFile","C:'Users'Jens'Desktop'Electrochemical Calculator Barebone'Electrochemical Calculator'bin'Release'de'Electrochemical Calculator.resources.dll","PATH NOT FOUND","Desired Access: Read Attributes, Disposition: Open, Options: Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a"
"15:56:29.3548019","Electrochemical Calculator.exe","5972","CreateFile","C:'Users'Jens'Desktop'Electrochemical Calculator Barebone'Electrochemical Calculator'bin'Release'de'Electrochemical Calculator.resources'Electrochemical Calculator.resources.dll","PATH NOT FOUND","Desired Access: Read Attributes, Disposition: Open, Options: Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a"
"15:56:29.3548612","Electrochemical Calculator.exe","5972","CreateFile","C:'Users'Jens'Desktop'Electrochemical Calculator Barebone'Electrochemical Calculator'bin'Release'de'Electrochemical Calculator.resources.exe","PATH NOT FOUND","Desired Access: Read Attributes, Disposition: Open, Options: Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a"
"15:56:29.3549519","Electrochemical Calculator.exe","5972","CreateFile","C:'Users'Jens'Desktop'Electrochemical Calculator Barebone'Electrochemical Calculator'bin'Release'de'Electrochemical Calculator.resources'Electrochemical Calculator.resources.exe","PATH NOT FOUND","Desired Access: Read Attributes, Disposition: Open, Options: Open Reparse Point, Attributes: n/a, ShareMode: Read, Write, Delete, AllocationSize: n/a"
"15:56:32.8796760","Electrochemical Calculator.exe","5972","CreateFile","C:'Windows'Fonts'StaticCache.dat","SUCCESS","Desired Access: Generic Read, Disposition: Open, Options: Synchronous IO Non-Alert, Non-Directory File, Attributes: n/a, ShareMode: Read, Delete, AllocationSize: n/a, OpenResult: Opened"
"15:56:32.8797088","Electrochemical Calculator.exe","5972","QueryStandardInformationFile","C:'Windows'Fonts'StaticCache.dat","SUCCESS","AllocationSize: 9,633,792, EndOfFile: 9,633,792, NumberOfLinks: 1, DeletePending: False, Directory: False"
"15:56:32.8797218","Electrochemical Calculator.exe","5972","ReadFile","C:'Windows'Fonts'StaticCache.dat","SUCCESS","Offset: 0, Length: 60, Priority: Normal"
"15:56:32.8797429","Electrochemical Calculator.exe","5972","CreateFileMapping","C:'Windows'Fonts'StaticCache.dat","FILE LOCKED WITH ONLY READERS","SyncType: SyncTypeCreateSection, PageProtection: "

该问题仅在发布版本中进一步出现,并且仅在我直接从 Windows 资源管理器启动程序时出现。调试版本会立即启动(0.3 秒,而 5-10 秒(,从 Visual Studio 启动时发布版本也是如此。

Application.Run 和 Form.Load 之间会发生什么

好吧,您消除了启动延迟的所有正常来源。 它绝对与 Application.Run(( 没有任何关系,直到表单的 InitializeComponent(( 方法完成运行才会开始。 而且,通过使用Ngen.exe消除了抖动开销。 一定要区分冷启动和暖启动延迟,如果只是第一次启动程序时很慢,那就是硬件问题,你需要一个更快的磁盘。 它仅在某些机器上很慢的事实强烈表明了环境问题。

由在 InitializeComponent(( 中运行的代码触发。 换句话说,窗体上控件的所有构造函数和属性资源库。 这通常需要很少的时间,但周围肯定有麻烦制造者。 您需要寻找一个重要的控件,尤其是在后台使用 COM(又名 ActiveX(的控件。 比如网络浏览器。 还有更多的可能性,任何类名以"Ax"开头的东西。 此类控件可以加载许多其他 DLL,并且容易对安全软件感兴趣。

一些调试技巧:

  • 确保您有一个良好的备份并开始从窗体中删除控件,从重要的控件开始。
  • 5 秒的延迟足够长,足以在单步执行 InitializeComponent(( 方法时注意到。 立即告诉您哪个特定的控件和语句导致延迟。
  • 将调试器切换到非托管模式会告诉您有关加载到程序中的其他 DLL 的更多信息。 项目+属性,调试选项卡,勾选"启用本机代码调试"选项。 在调试时,请留意"输出"窗口,你将看到加载任何非托管 DLL 的加载通知。 这可以查明导致延迟的特定 DLL。
  • 如果可能,请禁用反恶意软件,以便消除由于不适当的 DLL 扫描而导致的延迟。
  • SysInternals的TcpView实用程序非常适合检测网络延迟。 当您看到程序联系 CRL 服务器时,请注意,证书吊销列表查询可能会很慢。
  • SysInternals的Process Monitor实用程序非常适合查看由于大多数其他原因而导致的延迟。 跟踪可能非常大,如果需要另一双眼睛,请保存它并将其发布到文件共享站点上。

此时间用于加载窗体控件使用的每个程序集,并对 InitializeComponent 方法进行 JIT。

就像汉斯说的,你已经消除了明显的,对我来说,问题似乎是环境......那么,在极端条件下,您如何对此进行故障排除......

由于该过程始终需要 5 秒或更长时间(从资源管理器启动时(,以下是我在这种情况下会想到的一些方法 -

注意 - 由于可能存在计时问题,您可能需要重复实验几次才能获得不错的结果。显然,我仍然很难保证结果......如果我亲自做,我希望如果这些高层次的想法不能100%奏效,我会即兴发挥。但我肯定会给他们一个机会。

  • 使用 Procdump 启动进程,并在进程的性能计数器达到某个值(如 2、3 或 4(后执行n完全转储Elapsed Time。然后,您可以在 windbg(大多数信息,难以使用(或 VS.Net(更易于使用,但可能会也可能不会显示您想要查找的内容,即使它在那里 - 打开转储时请使用混合模式调试(中打开这些转储。

  • 在类似的路线上,但功能不那么强大,更棘手。配置进程资源管理器以在查看进程属性时显示本机堆栈跟踪...启动进程后,立即切换到进程资源管理器,并查看进程中各种线程的堆栈跟踪。这更棘手,因为根据有多少线程,手动通过它们的堆栈跟踪可能根本无法正常工作。可能值得一两次尝试,以防它确实以最少的努力使问题变得明显。

无论哪种情况,请确保将符号配置为Microsoft的公共符号服务器。以便您从本机堆栈跟踪中获取大部分信息。

总而言之,这个想法是......在这种情况下,一些高级函数应该很有可能显示在堆栈跟踪中,如果它下面的事情需要几秒钟才能完成。

注意 - 一旦您知道了高级功能,它可能是故事的结束,或者可能仍然需要将其与其他系统级进程相关联。(再次防病毒是典型的例子(。但你当然可以期待有更多的线索来帮助支持任何这样的假设。

相关文章: