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:
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?
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.
MyApplication class
?InnerException
s recursively and determine if the error you see is indeed the source error, or just a rethrow.HTH
Private Sub MyApplication_UnhandledException(
ByVal sender As Object,
ByVal e As ApplicationServices.UnhandledExceptionEventArgs) _
Handles Me.UnhandledException
Stop
End Sub
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With