Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

VB.net ApplicationFramework plus SplashScreen: InvalidOperationException

I recently changed my app from using a custom SplashScreen (it was just a Form with a Timer loaded the main form and closed itself) to the Application Framework.

Here is what I did:

  • Created a new SplashScreenForm that shows the app version etc.
  • Selected that Form at: My Project -> Application -> SplashScreen
  • Moved long running initialisation code from the constructor of the main form to the ApplicationEvents Startup Event

That totally does what I want. The SplashScreen shows up first, than the Startup Event fires and does it's work. The SplashScreen closes and the actual main forms shows up.

So far so good. But our customers sometimes get this nasty exception during Startup:

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)

There seems to be an error during HideSplashScreen(), but the thing is that the whole stack is out of my control so I can't just catch this exception.

Any suggestions?

like image 983
Jürgen Steinblock Avatar asked Dec 28 '10 08:12

Jürgen Steinblock


3 Answers

Put this in your splash from. If there are some components, this sub might be already declared in Designer.vb file. Simply move it's content to your source code and insert first line.

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

I've run throught deep analyse including decompilation of framework assemblies and this should do the trick. Detailed explanation would be longer story.

I'm unable to reproduce this issue on my own computer, but error occurs on various client machines. It seems impossible to reproduce it even with malicious calls. There's no HW nor SW related condition to simulate the issue, but it usually occurs on slow or high CPU used PCs when event loop messages got delayed and working threads are switched in wrong moment.

like image 99
PavlinII Avatar answered Nov 12 '22 18:11

PavlinII


  1. Are you able to reproduce the problem (i.e. distinguish certain scenarios where the problem does and does not occur
  2. What events are you handling in the MyApplication class?
  3. Try to add the following handler in that class, maybe the exception will be caught here, and it will supply you more info about stack
  4. When you reach the handler, check if there are InnerExceptions recursively and determine if the error you see is indeed the source error, or just a rethrow.
  5. Disable "Just My Code" (read this for details) and you might be able to trace the source of the issue

HTH


Private Sub MyApplication_UnhandledException(
    ByVal sender As Object, 
    ByVal e As ApplicationServices.UnhandledExceptionEventArgs) _
       Handles Me.UnhandledException
    Stop
End Sub
like image 40
Shimmy Weitzhandler Avatar answered Nov 12 '22 18:11

Shimmy Weitzhandler


This is my solution. I ended with just swallowing any InvalidOperationException in the UnhandledException Event until the SplashScreen is gone.

To do that I added a Property MainFormLoadingComplete to my MyApplication class that is set to true in the Shown Event of my main form (the splashscreen stays until the Form_Load event is processed).

I also figured out that I have to set MinimumSplashScreenDisplayTime before OnInitialize() to be effective. Hopefully that helps avoiding the exception in the first place. But since this exception is totally random I

SplashScreen code:

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 code:

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 code:

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
like image 22
Jürgen Steinblock Avatar answered Nov 12 '22 19:11

Jürgen Steinblock