【问题标题】:.Net application crashes despite being inside try catch block C#尽管在 try catch 块 C# 中,.Net 应用程序还是崩溃了
【发布时间】:2021-02-11 21:44:34
【问题描述】:

我有一个带有大量用户控件、页面等的 WPF 应用程序。 当我部署我的应用程序并运行它时,有时它会崩溃并关闭。很断断续续。我必须转到事件查看器并查看 Windows 日志以查看发生了什么。我将把发现的错误的精简版放在日志中:

 Application: MyApp.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
  Exception Info: System.InvalidOperationException
   at System.ThrowHelper.ThrowInvalidOperationException(System.ExceptionResource)
   at System.Nullable`1[[System.TimeSpan, mscorlib, Version=4.0.0.0, Culture=neutral, 
  PublicKeyToken=b77a5c561934e089]].get_Value()
  at MyApp.UserControls.UserCountdown+<>c__DisplayClass13_0.<StartAnimation>b__0(

我看到我的函数 StartAnimation 是它的炸弹,但我在 try catch 块中有该代码:

   public void StartAnimation()
    {
        try
        {
          //my code
        }
        catch (Exception ex){
           //gracefully handle this
        }
     }

Windows 日志错误不显示行号或任何内容。我不是在寻找编码,我想知道如何阻止我的应用程序完全崩溃并真正进入我的 try catch 块而不是事件查看器?

附:这是我的全部功能。不确定这是否会有所帮助:

public void StartAnimation()
    {
        try
        {

        double from = -90;
        double to = 270;
        int seconds = Seconds;
        int newSecs = seconds;
        if (Seconds > 60)
            newSecs = newSecs / 2;

        TimeSpan duration = TimeSpan.FromSeconds(Seconds);
        storyboard = new Storyboard();
        DoubleAnimation animation = new DoubleAnimation(from, to, new Duration(duration));

        Storyboard.SetTarget(animation, Arc);
        Storyboard.SetTargetProperty(animation, new PropertyPath("EndAngle"));

        storyboard.CurrentTimeInvalidated += (s, e) =>
        {
            int diff = (int)((s as ClockGroup).CurrentTime.Value.TotalSeconds);
            Seconds = seconds - diff;
            TimeSpan t = new TimeSpan(0, 0, Seconds);
            if (t.Hours > 0)
            {
                timeLabel.FontSize = 16;
                TimeDisplay = $"{t.Hours.ToString("D2")}:{t.Minutes.ToString("D2")}:{t.Seconds.ToString("D2")}";
            }
            else if (t.Minutes > 0)
            {
                timeLabel.FontSize = 25;
                TimeDisplay = $"{t.Minutes.ToString("D2")}:{t.Seconds.ToString("D2")}";
            }
            else
            {
                timeLabel.FontSize = 30;
                TimeDisplay = $"{t.Seconds.ToString("D2")}";
            }
        };

        storyboard.Children.Add(animation);
        storyboard.Begin();
        }
        catch (Exception ex)
        {
            Emailer.SendEmail($"Error calling StartAnimation - {ex.StackTrace}", ex.Message, "", "", "");
        }

    }

【问题讨论】:

  • 当你调试电离发生前的最后一行是什么?
  • Red Herring 也许,检查你的“秒”和“秒” - TimeSpan 持续时间 = TimeSpan.FromSeconds(Seconds); ???
  • 我假设两个代码摘录中的Exception 指的是System.Exception 而不是其他类型?我知道这通常是这种情况,但由于它们似乎没有捕获所有(通常是可捕获的)异常,也许他们实际上正在捕获其他东西?
  • 问题所在的行可能是int diff = (int)((s as ClockGroup).CurrentTime.Value.TotalSeconds);CurrentTime 可能是null

标签: c#


【解决方案1】:

您遇到此问题的原因是 C# 事件处理程序无法安全地引发异常。有一个很棒的 StackOverflow 答案here,其中更详细地说明了原因,但要解释一下:

从事件处理程序中抛出异常会使调用者(触发事件)在保证其他事件处理程序也接收事件的同时安全地继续进行的选择很少。

在这种情况下,听起来触发事件的类 (Storyboard) 不会捕获事件处理程序抛出的异常,而是允许程序崩溃(在某些方面,这是最安全的方法)。

回到您的代码,您可以大致简化为以下结构:

try
{
  var storyboard = new Storyboard();

  storyboard.Event += (o, e) => {
    throw new Exception("Example");
  };

  storyboard.Begin();
}
catch (Exception)
{
  // Don't crash
}

问题在于try-catch 块只会捕获在该 try-catch 的调用堆栈中执行的代码所引发的异常。事件处理程序本身可能正在另一个线程的调用堆栈上执行(在这种情况下,很可能是ThreadPool 中的一个线程)并且不在此 try-catch 块中。这意味着除非 Storyboard 类从事件处理程序中捕获异常,然后在其 Begin() 方法的范围内重新抛出它们(它没有这样做),否则您将遇到未处理的异常。

要解决这个问题,您只需将事件处理程序中的代码包装在它自己的 try-catch 块中,如下所示:

storyboard.CurrentTimeInvalidated += (s, e) =>
{
  try
  {
    // TODO: Add your event handler code here
  }
  catch (Exception ex)
  {
    // TODO: Handle exceptions triggered within your animation here
  }
};

【讨论】:

    猜你喜欢
    • 2020-09-15
    • 2019-11-01
    • 2019-07-25
    • 2021-10-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多