VB.net ApplicationFramework 加 SplashScreen:InvalidOperationException

发布于 2024-10-09 17:28:26 字数 2063 浏览 6 评论 0原文

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

这就是我所做的:

  • 创建一个新的 SplashScreenForm,显示应用程序版本等。
  • 在以下位置选择该表单:我的项目 ->应用-> SplashScreen
  • 将长时间运行的初始化代码从主窗体的构造函数移动到 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() 过程中似乎出现错误,但问题是整个堆栈超出了我的控制范围,因此我无法捕获此异常。

有什么建议吗?

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

你又不是我 2024-10-16 17:28:26

将其放入您的启动画面中。如果有一些组件,该子组件可能已在 Designer.vb 文件中声明。只需将其内容移至源代码并插入第一行即可。

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

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

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

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.

永不分离 2024-10-16 17:28:26
  1. 您是否能够重现该问题(即区分发生和不发生问题的某些场景
  2. 您在 MyApplication 类中处理什么事件?
  3. 尝试在该类中添加以下处理程序,也许是异常将在这里捕获,它将为您提供有关堆栈的更多信息
  4. 当您到达处理程序时,递归检查是否有 InnerException 并确定您看到的错误是否确实是源错误,或者只是一个错误重新抛出。
  5. 禁用“仅我的代码”(阅读this 了解详细信息),您也许能够追踪问题的根源

HTH


Private Sub MyApplication_UnhandledException(
    ByVal sender As Object, 
    ByVal e As ApplicationServices.UnhandledExceptionEventArgs) _
       Handles Me.UnhandledException
    Stop
End Sub
  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
何以笙箫默 2024-10-16 17:28:26

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

为此,我向 MyApplication 类添加了一个属性 MainFormLoadingComplete,该属性在主窗体的 Shown 事件中设置为 true(闪屏一直保留到处理 Form_Load 事件为止)。

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

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

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
南巷近海 2024-10-16 17:28:26

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

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

我们的支持人员在这方面花费了大量时间,最终将其范围缩小到仅在 Windows 7 中发生,并且仅在 Windows 之后发生已经从开机启动了。

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

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

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

just came across this, we have the exact same problem using the same technique.

I'm going to try your solution that works but I just wanted to let you know how to reproduce it:

Our support people spent a lot of time on this and finally narrowed it down to happening only in windows 7 and only happening just after windows has been started from boot.

If you reboot windows then immediately launch your app that uses this splash screen technique the error will happen almost all the time.

If you reboot windows, wait a few minutes then start your app you will never see the error.

I worked around it by putting in a sleep(400) at the bottom of my main form's load event, however some were still seeing the error and I'm going to try your solution instead.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文