VB.net ApplicationFramework加上SplashScreen:InvalidOperationException

时间:2010-12-28 08:08:26

标签: vb.net winforms invoke invalidoperationexception

我最近改变了我的应用程序,使用自定义SplashScreen(它只是一个表格,其中一个Timer加载了主窗体并自行关闭)到应用程序框架。

这是我做的:

  • 创建了一个新的SplashScreenForm,显示了应用版本等。
  • 选择表格:我的项目 - >申请 - >闪屏
  • 将长时间运行的初始化代码从主窗体的构造函数移动到ApplicationEvents启动事件

这完全符合我的要求。 SplashScreen首先显示,而不是启动事件触发并且它是否正常工作。 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 个答案:

答案 0 :(得分:5)

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

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

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

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

答案 1 :(得分:1)

  1. 您是否能够重现问题(即区分问题发生和不发生的某些情况
  2. 您在MyApplication class
  3. 处理了哪些事件?
  4. 尝试在该类中添加以下处理程序,可能会在此处捕获异常,并且它将为您提供有关堆栈的更多信息
  5. 当您到达处理程序时,检查是否递归InnerException并确定您看到的错误是否确实是源错误,或者只是重新抛出。
  6. 禁用“仅我的代码”(详情请参阅this),您可以跟踪问题的根源
  7. HTH


    Private Sub MyApplication_UnhandledException(
        ByVal sender As Object, 
        ByVal e As ApplicationServices.UnhandledExceptionEventArgs) _
           Handles Me.UnhandledException
        Stop
    End Sub
    

答案 2 :(得分:1)

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

为此,我在MyApplication类中添加了一个Property MainFormLoadingComplete,在我的主窗体的Shown Event中设置为true(启动画面一直保持到处理Form_Load事件为止)。

我还想到我必须在MinimumSplashScreenDisplayTime之前设置OnInitialize()才能生效。希望这有助于首先避免异常。但是因为这个例外是完全随机的我

SplashScreen代码:

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

Form1代码:

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

MyApplication代码:

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

答案 3 :(得分:-1)

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

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

我们的支持人员花了很多时间在这上面,最后将其缩小到仅在Windows 7中发生,并且仅在Windows启动后才发生。

如果您重新启动Windows,请立即启动使用此启动画面技术的应用程序,错误几乎会一直发生。

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

我通过在主窗体的加载事件的底部进入睡眠状态(400)来解决它,但有些人仍然看到错误,我将尝试使用您的解决方案。