MS Excel和COM托pipe的C#2委托中exception的行为

大家早,

这里的语言理论问题的位…我已经在网上发现了一些参考文献,提示C#中的exception处理和委托在某些情况下有一些不同的行为,但是我找不到关于这个问题的具体文档。

我们最近有一些大的问题,例如在一个Microsoft Excel插件的委托内,导致MSVC运行时崩溃。 删除代表解决了这个,但我现在好奇,找出血淋淋的细节。

作为核心代码的简要例子:

Delegate del; // initialized elsewhere try { del.DynamicInvoke(); } catch(Exception e) { /* Parsing of exception to generate user-friendly message here */ } 

上面的构造允许一个集中的error handlingforms,从纯粹的代码angular度来看是简洁明了的。 每个公开公开的函数都被声明为一个委托并通过上面的片段执行。

在一个简单的控制台应用程序中,从委托抛出一个exception或只是一个普通的意外错误(例如,“意外地”调用空指针上的ToString())按预期方式工作,并按照需要处理错误。

抛出MS Excel,你会遇到硬崩溃。 单步执行代码会显示错误发生的位置,但是没有任何堆栈展开的情况发生在一个破坏的大火球中的任何事情发生之前。

我的假设是,承载.NET运行时(以及我们的代码)的COM正在做一些与正常的.NET代码执行不同的事情。 这终止了终点,而Excel并不知道这一点,而这又反过来试图通过COM来访问终点,只是发现它已经消失了,而Excel却不知所措。

这只会发生在Excel + COM +代表的组合中,但是我并不真诚地知道哪一个在这个行为中更有影响力……任何想法?

我认为你可能在这里find的是,你没有发布MS Excel的COM对象,当你抛出exception时隐式创build。 根据我的经验,MS Office应用程序对其资源非常敏感(尽pipe我的大部分经验都是在Outlook中)。

如果可能,我倾向于不尝试以这种方式处理基于COM的exception。 如果你想中断日志,请看Application.ThreadException(对于WinForms应用程序)和AppDomain.CurrentDomain.UnhandledException。

在函数内部,您可能需要在方法的finally {}块中调用Marshal.ReleaseComObject(),以确保在抛出exception时Excel不会被洗掉。

注意:exception捕捉的整个想法不是“吞噬”它们,而是显示错误信息,但是如果可能的话从状况中恢复,并清理资源。

更好地使用try / catch无论你需要(即围绕可能抛出的代码),然后调用一个通用函数来显示/logging问题。

此外,如果您使用不同的线程来访问Excel COM对象,可能会发生很多奇怪的事情。

要正确回答您的问题,需要更多信息:

  1. 它总是发生吗?
  2. 如果没有,当你看到问题是一个托pipe的exception,或从Excel对象抛出一个?
  3. 你可以创build一个简单的方法,只是抛出一个exception(抛出新的ApplicationException();),看看你是否仍然有问题?

干杯

我觉得一个原因是因为委托调用在另一个线程中抛出exception的实际代码。 因此另一个被称为asynchronous的线程中的exception将不会被当前线程捕获,因为当前的线程函数在asynchronous调用执行之前大部分会退出。

对迟到的答复道歉 – 繁忙的一周!

整个捕捉exception的想法并不是“吞噬”它们,而是显示错误信息,但是如果可能的话从情况中恢复,以及清理资源。

是的,这是主意,但实际上并不总是那么干净。 这里所讨论的代码本质上是一个中间人,通常不知道是否会发生错误,如果发生错误,则无法知道用户实际正在尝试做什么,从而自动恢复。

它总是发生吗?

是的,如果它是一个意外的exception,并且它始终performance为一个源于C#/。NET代码的.NET

创build一个简单的方法,它只是抛出一个exception(抛出新的ApplicationException();),看看你是否仍然有问题?

不,抛出我们自己的例外,每次都很好。 只有运行时本身产生exception,我们才会遇到硬崩溃。

您不释放在引发exception时隐式创build的MS Excel COM对象

这听起来似乎合理。 VBA层将数据从Excel传递到COM,然后传递到.NET,所以它可能会在资源所有权方面做一些事情。

如果你想中断日志,请看Application.ThreadException(对于WinForms应用程序)和AppDomain.CurrentDomain.UnhandledException。

我没有看到前者,但后者永远不会被调用。 我的第一个尝试就是在AppDomain层次上进行尝试,假设无论出于何种原因,基于委托的exception正在通过networking滑落。

你可能想调用Marshal.ReleaseComObject()

有趣的是,我不知道这个function。 我们大多忽略了任何显式的COM代码,并且抛弃了最低限度的希望(!),即.NET CLR的魔力。

我已经解决了它,现在通过删除代表,一切似乎都很高兴。 只需要记住在将来的Office + VBA + COM + CLR代码中谨慎(或彻底避免)委托和例外:-)