6

我最近将我的应用程序从使用自定义 SplashScreen(它只是一个带有 Timer 的表单加载了主表单并自行关闭)更改为 Application Framework。

这是我所做的:

  • 创建了一个新的 SplashScreenForm 来显示应用程序版本等。
  • 在以下位置选择该表单:我的项目 -> 应用程序 -> SplashScreen
  • 将长时间运行的初始化代码从主窗体的构造函数移至 ApplicationEvents 启动事件

这完全符合我的要求。SplashScreen 首先出现,然后 Startup Event 触发并起作用。SplashScreen 关闭并显示实际的主窗体。

到现在为止还挺好。但是我们的客户有时会在启动过程中遇到这个讨厌的异常:

System.InvalidOperationException: Invoke oder BeginInvoke kann für ein Steuerelement erst aufgerufen werden, wenn das Fensterhandle erstellt wurde.
   bei System.Windows.Forms.Control.WaitForWaitHandle(WaitHandle waitHandle)
   bei System.Windows.Forms.Control.MarshaledInvoke(Control caller, Delegate method, Object[] args, Boolean synchronous)
   bei System.Windows.Forms.Control.Invoke(Delegate method, Object[] args)
   bei System.Windows.Forms.Control.Invoke(Delegate method)
   bei Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.HideSplashScreen()
   bei Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase.MainFormLoadingDone(Object sender, EventArgs e)
   bei System.EventHandler.Invoke(Object sender, EventArgs e)
   bei System.Windows.Forms.Form.OnLoad(EventArgs e)
   bei System.Windows.Forms.Form.OnCreateControl()
   bei System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
   bei System.Windows.Forms.Control.CreateControl()
   bei System.Windows.Forms.Control.WmShowWindow(Message& m)
   bei System.Windows.Forms.Control.WndProc(Message& m)
   bei System.Windows.Forms.ScrollableControl.WndProc(Message& m)
   bei System.Windows.Forms.Form.WmShowWindow(Message& m)
   bei System.Windows.Forms.Form.WndProc(Message& m)
   bei System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
   bei System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
   bei System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

在 HideSplashScreen() 期间似乎有一个错误,但问题是整个堆栈超出了我的控制范围,所以我不能只捕获这个异常。

有什么建议么?

4

4 回答 4

5

把这个放在你的飞溅中。如果有一些组件,这个 sub 可能已经在 Designer.vb 文件中声明。只需将其内容移动到您的源代码并插入第一行。

Protected Overrides Sub Dispose(ByVal disposing As Boolean)
    If disposing Then My.Application.SplashScreen = Nothing
    MyBase.Dispose(disposing)
End Sub

我已经进行了深入分析,包括反编译框架程序集,这应该可以解决问题。详细的解释将是更长的故事。

我无法在我自己的计算机上重现此问题,但在各种客户端计算机上都会出现错误。即使有恶意调用,似乎也无法重现它。没有硬件或软件相关的条件来模拟问题,但它通常发生在低速或高 CPU 使用的 PC 上,当事件循环消息被延迟并且工作线程在错误的时刻切换时。

于 2012-06-27T15:40:26.697 回答
1
  1. 您是否能够重现问题(即区分问题发生和未发生的某些场景
  2. 你在处理什么事件MyApplication class
  3. 尝试在该类中添加以下处理程序,可能会在此处捕获异常,它将为您提供有关堆栈的更多信息
  4. 当您到达处理程序时,递归检查是否存在InnerExceptions 并确定您看到的错误确实是源错误,还是只是重新抛出。
  5. 禁用“只是我的代码”(阅读内容了解详细信息),您也许可以追踪问题的根源

高温高压


Private Sub MyApplication_UnhandledException(
    ByVal sender As Object, 
    ByVal e As ApplicationServices.UnhandledExceptionEventArgs) _
       Handles Me.UnhandledException
    Stop
End Sub
于 2010-12-28T08:29:26.133 回答
1

这是我的解决方案。最后我只是吞下了 UnhandledException 事件中的任何 InvalidOperationException,直到 SplashScreen 消失。

为此,我在MainFormLoadingCompleteMyApplication 类中添加了一个属性,该属性在我的主窗体的 Shown Event 中设置为 true(启动画面一直存在,直到 Form_Load 事件被处理)。

我还发现我必须先设置MinimumSplashScreenDisplayTime才能OnInitialize()有效。希望这有助于首先避免异常。但由于这个例外是完全随机的,我

闪屏代码:

Public Class SplashScreen

    Private Sub SplashScreen_Disposed(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Disposed
        ' Simulate InvalidOperationException
        Throw New InvalidOperationException
    End Sub

End Class

表格1代码:

Public Class Form1

    Private Sub Form1_Shown(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Shown
        My.MyApplication.MainFormLoadingComplete = True
    End Sub

End Class

我的应用程序代码:

Partial Friend Class MyApplication

    Public Shared Property MainFormLoadingComplete As Boolean

    Protected Overrides Function OnInitialize(ByVal commandLineArgs As System.Collections.ObjectModel.ReadOnlyCollection(Of String)) As Boolean
        ' MinimumSplashScreenDisplayTime has to be set before OnInitialize to be effective
        MinimumSplashScreenDisplayTime = 3000 
        Return MyBase.OnInitialize(commandLineArgs)
    End Function

    Private Sub MyApplication_Startup( _
        ByVal sender As Object, ByVal e As Microsoft.VisualBasic.ApplicationServices.StartupEventArgs) Handles Me.Startup
        ' Simulate application init process
        System.Threading.Thread.Sleep(5000)
    End Sub

    Private Sub MyApplication_UnhandledException(ByVal sender As Object, ByVal e As Microsoft.VisualBasic.ApplicationServices.UnhandledExceptionEventArgs) Handles Me.UnhandledException

        ' Swallow InvalidOperationException if the MainForm has not been shown
        If MainFormLoadingComplete = False AndAlso IsCausedByHideSplashScreen(e.Exception) Then
            ' Logging stuff...

            ' Prevent application exit
            e.ExitApplication = False
        End If

    End Sub

    Private Function IsCausedByHideSplashScreen(ByVal ex As Exception) As Boolean
        If ex.GetType Is GetType(InvalidOperationException) Then
            Return New StackTrace(ex).GetFrames().Count(Function(x) x.GetMethod().Name = "HideSplashScreen") > 0
        Else
            Return False
        End If
    End Function

End Class
于 2011-01-29T14:20:06.307 回答
-1

刚刚遇到这个,我们使用相同的技术遇到了完全相同的问题。

我将尝试您的解决方案,但我只是想让您知道如何重现它:

我们的支持人员为此花费了大量时间,最终将其范围缩小到仅在 Windows 7 中发生,并且仅在 Windows 从引导启动后才发生。

如果您重新启动 Windows,然后立即启动使用此启动屏幕技术的应用程序,该错误几乎会一直发生。

如果您重新启动 Windows,请等待几分钟然后启动您的应用程序,您将永远不会看到错误。

我通过在主窗体的加载事件底部放置一个 sleep(400) 来解决它,但是有些人仍然看到错误,我将尝试你的解决方案。

于 2011-04-20T16:02:08.477 回答