草庐IT

c# - 异常处理循环拼图

coder 2024-05-21 原文

我最近遇到了一种我以前从未见过的行为。由于缺乏有关内部工作异常处理的基本知识,我无法完全理解最有可能发生的事情 - 或者我可能只是遗漏了一些明显的东西。

我最近向应用程序添加了异常处理,作为未处理异常的一种后备。我基本上处理 ThreadException 和 UnhandledException 如下所示:

// Add the event handler for handling UI thread exceptions to the event.
Application.ThreadException += new ThreadExceptionEventHandler(ExceptionHandler.OnUIThreadException);

// Set the unhandled exception mode to force all Windows Forms errors to go through
// our handler.
Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException);

// Add the event handler for handling non-UI thread exceptions to the event. 
AppDomain.CurrentDomain.UnhandledException +=
    new UnhandledExceptionEventHandler(ExceptionHandler.OnUnhandledException);

// Runs the application.
Application.Run(new ErrorHandlerForm());

我在应用程序中的其他一些代码已经捕获异常 - 由于我没有适当的异常处理,我只是重新抛出异常以确保它没有被吞下:
//code in some method of the Program
try
{
   foo.SomeFooCall();
}
catch(Exception ex)
{
  logger.Log(ex.Message);
  // I don't swallow!
  throw;
}

一旦我有了异常处理(这也是日志记录),我应该删除上面的 try catch 块 - 但我忘了这样做,我遇到了一个奇怪的行为,这是这个问题的主题。

当 foo 调用中的某处抛出异常时,显然被上面的代码捕获,记录然后再次抛出。此时 ExceptionHandling 开始,做一些日志记录和通知(一个简单的消息框)然后去 Application.Exit() .接下来发生的事情是该应用程序将返回相同的 throw这将触发错误处理并产生相同的结果,并且这将持续多次直到它崩溃可能是因为堆栈跟踪已满或它以某种方式检测到无限循环。

编辑:以上处于 Debug模式 - 如果我只是运行它,它将处理一次异常(显示消息框、日志等),然后它就会崩溃(我猜测是堆栈溢出)。

我预计这个问题的答案可能是微不足道的(或者我可能会遗漏一些明显的东西) - 但任何指示/解释都将受到高度赞赏。

编辑:
异常处理程序方法将两个调用都调用到 OnException 方法,类似于:
private void OnUIThreadException(object sender, ThreadExceptionEventArgs e)
{
   OnException(e.Exception);
}

private void OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
   OnException((Exception)e.ExceptionObject);
}

private void OnException(Exception exception)
{
   MessageBox.Show("Fatal Exception: " + exception.Message);

   logger.Log(LoggingLevel.FATAL, "myLousyApp", exception.Message);

   Application.Exit();
}

我实际上做的不仅仅是 smt - 例如询问用户他们是否要重新启动应用程序,如果是这样,则使用进程 ID 作为 cmd arg 重新启动它,这样当它重新启动时,它会等待旧进程退出(它是通过互斥锁保护免受重复实例的影响)。但是对于这个问题,这无关紧要,因为当我遇到所描述的行为时,我不会重新启动应用程序。

编辑:我创建了另一个简单的应用程序来重现这种情况 - 我有一个抛出异常的简单组件(我在循环中抛出任意数量的异常),但是在我对 Application.Exit 的所有测试中,该应用程序很好地关闭了,我可以不要复制它。对我应该寻找什么感到困惑!

最佳答案

tl;dr:这是调试器 .分离,你不会得到这种奇怪的行为。

好吧。我用 Brand Spankin' New Project 做了一些实验,并得出了一个结果。我将首先发布代码,以便您也可以参与其中并亲眼目睹。

编码器

plz email them to me (无关)

表格1.cs


您将需要表单上的两个按钮。适本地给它们加上标题,这样你在做什么就很明显了。

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void button1_Click(object sender, EventArgs e)
    {
        throw new InvalidOperationException("Exception thrown from UI thread");
    }

    private void button2_Click(object sender, EventArgs e)
    {
        new Thread(new ThreadStart(ThrowThreadStart)).Start();
    }

    private static void ThrowThreadStart()
    {
        throw new InvalidOperationException("Exception thrown from background thread");
    }
}

程序.cs
static class Program
{
    [STAThread]
    static void Main()
    {
        AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
        Application.ThreadException += Application_ThreadException;
        Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException, false);

        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }

    static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
    {
        if (e.Exception != null)
        {
            MessageBox.Show(string.Format("+++ Application.ThreadException: {0}", e.Exception.Message));
        }
        else
        {
            MessageBox.Show("Thread exception event fired, but object was not an exception");
        }
    }

    static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        Exception ex = e.ExceptionObject as Exception;

        if (ex != null)
        {
            MessageBox.Show(string.Format("*** AppDomain.UnhandledException: {0}", ex.Message));
        }
        else
        {
            MessageBox.Show("Unhandled exception event fired, but object was not an exception");
        }
    }
}

项目文件

禁用托管进程,否则 AppDomain(和 Forms 本身)不会在调试 session 之间卸载,这将使行 Application.SetUnhandledExceptionMode(UnhandledExceptionMode.CatchException, false);扔一个 InvalidOperationException如果您更改 UnhandledExceptionMode运行之间的争论。编辑:或根本,如果设置为 CatchException

对于这项调查来说,这不是绝对必要的,但如果您要玩弄并更改 UnhandledExceptionMode -- 如果您自己运行此代码,我希望您可能会这样做- 此设置将使您免于心痛。


测试

调试器内部

抛出 UI 线程
  • 单击 UI 中的 throw
  • 在调试器中获取“未处理的异常”帮助程序
  • F5继续执行
  • 将显示对话框,表明应用程序处理程序收到来自 UI 线程的异常事件
  • 点击确定
  • 应用程序不会崩溃,因此请随时冲洗并重复。

  • 抛出一个后台线程
  • 点击背景 throw
  • 将显示对话框,表明 AppDomain 处理程序收到来自后台线程的异常事件
  • 在调试器中获取“未处理的异常”帮助程序
  • F5继续执行
  • goto 2 .真的。

  • 在这里,无论出于何种原因,AppDomain 处理程序似乎都胜过调试器。但是,在 AppDomain 完成之后,调试器确实设法获取未处理的异常。但接下来发生的事情令人费解:AppDomain 处理程序再次运行。然后再次。再一次,无穷无尽。在处理程序中放置一个断点表明它不是递归的(至少在 .NET 中不是),因此这可能不会以堆栈溢出结束。


    现在,让我们尝试一下......

    在调试器之外

    界面线程

    相同的过程,相同的结果——当然,调试器助手显然不存在。程序仍然没有崩溃,因为 UnhandledExceptionMode.CatchException按照它所说的去做,并在“内部”(至少在 Forms 内)处理异常,而不是将其升级到 Feds AppDomain。

    后台线程

    现在这很有趣。
  • 点击背景 throw
  • 将显示对话框,表明 AppDomain 处理程序收到来自后台线程的异常事件
  • 获取 Windows 崩溃对话框
  • 点击Debug使用 JIT 调试捕获异常
  • 获取“未处理的异常”助手
  • F5继续
  • 程序退出

  • 首先,AppDomain 不会像附加调试器那样进入循环,其次,从 Windows 错误对话框中及时附加调试器 触发这种奇怪的行为。

    结论

    对于进入 AppDomain 的未处理异常,调试器似乎做了一些奇怪的事情。我不太了解调试器是如何发挥其魔力的,所以我不会推测,但是循环 只有当连接调试器时发生,所以如果循环是问题,分离是您可以使用的一种解决方法(也许使用 Debugger.Launch() 让自己有时间在需要时重新连接)。

    <>

    关于c# - 异常处理循环拼图,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2072459/

    有关c# - 异常处理循环拼图的更多相关文章

    1. ruby - 树顶语法无限循环 - 2

      我脑子里浮现出一些关于一种新编程语言的想法,所以我想我会尝试实现它。一位friend建议我尝试使用Treetop(Rubygem)来创建一个解析器。Treetop的文档很少,我以前从未做过这种事情。我的解析器表现得好像有一个无限循环,但没有堆栈跟踪;事实证明很难追踪到。有人可以指出入门级解析/AST指南的方向吗?我真的需要一些列出规则、常见用法等的东西来使用像Treetop这样的工具。我的语法分析器在GitHub上,以防有人希望帮助我改进它。class{initialize=lambda(name){receiver.name=name}greet=lambda{IO.puts("He

    2. ruby-on-rails - 在 Ruby 中循环遍历多个数组 - 2

      我有多个ActiveRecord子类Item的实例数组,我需要根据最早的事件循环打印。在这种情况下,我需要打印付款和维护日期,如下所示:ItemAmaintenancerequiredin5daysItemBpaymentrequiredin6daysItemApaymentrequiredin7daysItemBmaintenancerequiredin8days我目前有两个查询,用于查找maintenance和payment项目(非排他性查询),并输出如下内容:paymentrequiredin...maintenancerequiredin...有什么方法可以改善上述(丑陋的)代

    3. ruby - 如何指定 Rack 处理程序 - 2

      Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

    4. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

      我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

    5. ruby-on-rails - Rails - 乐观锁定总是触发 StaleObjectError 异常 - 2

      我正在学习Rails,并阅读了关于乐观锁的内容。我已将类型为integer的lock_version列添加到我的articles表中。但现在每当我第一次尝试更新记录时,我都会收到StaleObjectError异常。这是我的迁移:classAddLockVersionToArticle当我尝试通过Rails控制台更新文章时:article=Article.first=>#我这样做:article.title="newtitle"article.save我明白了:(0.3ms)begintransaction(0.3ms)UPDATE"articles"SET"title"='dwdwd

    6. ruby - #之间? Cooper 的 *Beginning Ruby* 中的错误或异常 - 2

      在Cooper的书BeginningRuby中,第166页有一个我无法重现的示例。classSongincludeComparableattr_accessor:lengthdef(other)@lengthother.lengthenddefinitialize(song_name,length)@song_name=song_name@length=lengthendenda=Song.new('Rockaroundtheclock',143)b=Song.new('BohemianRhapsody',544)c=Song.new('MinuteWaltz',60)a.betwee

    7. c# - 如何在 ruby​​ 中调用 C# dll? - 2

      如何在ruby​​中调用C#dll? 最佳答案 我能想到几种可能性:为您的DLL编写(或找人编写)一个COM包装器,如果它还没有,则使用Ruby的WIN32OLE库来调用它;看看RubyCLR,其中一位作者是JohnLam,他继续在Microsoft从事IronRuby方面的工作。(估计不会再维护了,可能不支持.Net2.0以上的版本);正如其他地方已经提到的,看看使用IronRuby,如果这是您的技术选择。有一个主题是here.请注意,最后一篇文章实际上来自JohnLam(看起来像是2009年3月),他似乎很自在地断言RubyCL

    8. ruby - 在 Ruby 中重新分配常量时抛出异常? - 2

      我早就知道Ruby中的“常量”(即大写的变量名)不是真正常量。与其他编程语言一样,对对象的引用是唯一存储在变量/常量中的东西。(侧边栏:Ruby确实具有“卡住”引用对象不被修改的功能,据我所知,许多其他语言都没有提供这种功能。)所以这是我的问题:当您将一个值重新分配给常量时,您会收到如下警告:>>FOO='bar'=>"bar">>FOO='baz'(irb):2:warning:alreadyinitializedconstantFOO=>"baz"有没有办法强制Ruby抛出异常而不是打印警告?很难弄清楚为什么有时会发生重新分配。 最佳答案

    9. C# 到 Ruby sha1 base64 编码 - 2

      我正在尝试在Ruby中复制Convert.ToBase64String()行为。这是我的C#代码:varsha1=newSHA1CryptoServiceProvider();varpasswordBytes=Encoding.UTF8.GetBytes("password");varpasswordHash=sha1.ComputeHash(passwordBytes);returnConvert.ToBase64String(passwordHash);//returns"W6ph5Mm5Pz8GgiULbPgzG37mj9g="当我在Ruby中尝试同样的事情时,我得到了相同sha

    10. 基于C#实现简易绘图工具【100010177】 - 2

      C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

    随机推荐