3

大家早,

这里有点语言理论问题......我在网上找到了一些参考资料,表明 C# 中的异常处理和委托在某些情况下有一些不同的行为,但我找不到任何关于此事的具体文档。

我们最近在 Microsoft Excel 插件的委托中遇到了一些大问题,导致 MSVC 运行时发生硬崩溃。删除代表解决了这个问题,但我现在很想知道血腥的细节。

作为核心代码的一个简洁示例:

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

上述结构允许集中形式的错误处理,并且从纯代码的角度来看是简洁明了的。每个公开公开的函数都被声明为委托并通过上述片段执行。

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

扔进 MS Excel,你会遇到严重的崩溃。单步执行代码将显示错误发生的位置,但在一切都陷入巨大的破坏火球之前,似乎没有发生堆栈展开。

我的假设是,托管 .NET 运行时(以及我们的代码)的 COM 正在做一些不同于正常 .NET 代码执行的事情。这会杀死端点,而 Excel 不知道这一点,而 Excel 又试图通过 COM 访问端点,却发现它不知何故消失了,而 Excel 作为回报也报废了。

这只发生在 Excel+COM+delegates 的组合中,但老实说,我不知道哪个对这种行为影响更大……有什么想法吗?

4

4 回答 4

1

I think what you might find here is that you are not releasing the MS Excel COM objects you are implicitly creating when an exception is thrown. In my experience MS Office apps are very sensitive to their resources not being released (though most of my experience is with Outlook).

I would tend NOT to try to handle COM-based exceptions in this way if at all possible. If you want centalized logging, look at Application.ThreadException (for WinForms app) and AppDomain.CurrentDomain.UnhandledException instead.

Inside of your functions, you may want to call Marshal.ReleaseComObject() in your methods' finally {} blocks in order to make sure Excel doesn't get hosed when your exceptions are thrown.

于 2008-10-19T17:36:09.440 回答
0

我觉得一个原因是由于委托正在调用在另一个线程中引发异常的实际代码。所以另一个线程中的异常被称为 async 不会在当前线程中被捕获,因为当前线程函数大部分会在异步调用执行之前退出。

于 2009-02-16T13:45:55.407 回答
0

为迟到的回复道歉 - 忙碌的一周!

异常捕获的整个想法不是“吞下”它们并显示错误消息,而是尽可能从情况中恢复,以及清理资源。

是的,这就是想法,但在实践中它并不总是那么干净。这里有问题的代码本质上是一个中间人,通常不知道是否会发生错误,如果发生错误,它不知道用户实际尝试做什么,因此会自动恢复。

它总是发生吗?

是的,如果这是一个意外异常并且它始终显示为源自 C#/.NET 代码的 .NET 异常

创建一个简单的方法,它只是抛出一个异常( throw new ApplicationException();),看看你是否还有问题?

不,每次抛出我们自己的异常都可以正常工作。只有当运行时本身产生异常时,我们才会遇到硬崩溃。

您没有释放在引发异常时隐式创建的 MS Excel COM 对象

这听起来很有道理。VBA 层通过 COM 将数据从 Excel 传递到 .NET,因此它很可能在资源所有权方面做一些时髦的事情。

如果您想要集中式日志记录,请查看 Application.ThreadException(对于 WinForms 应用程序)和 AppDomain.CurrentDomain.UnhandledException。

我没有调查前者,但后者永远不会被调用。我的第一个尝试是在 AppDomain 级别捕获它,假设无论出于何种原因,基于委托的异常正在通过网络传播。

你可能想调用 Marshal.ReleaseComObject()

有趣的是,我不知道该功能。我们几乎忽略了任何显式的 COM 代码,只希望(!).NET CLR 发挥它的魔力......

我现在已经通过删除代表来解决它,一切似乎都很开心。只需要记住在未来的 Office+VBA+COM+CLR 代码中要小心(或完全避免)委托和异常 :-)

于 2008-10-21T12:35:36.237 回答
0

旁注:异常捕获的整个想法不是“吞下”它们并显示错误消息,而是尽可能从情况中恢复,以及清理资源。

最好在需要的地方使用 try/catch(即围绕可能抛出的代码),然后调用一个通用函数来显示/记录问题。

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

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

  1. 它总是发生吗?
  2. 如果不是,当您看到问题是托管异常还是从 Excel 对象抛出的异常?
  3. 你能创建一个简单的方法,只抛出一个异常( throw new ApplicationException();),看看你是否还有问题?

干杯

于 2008-10-17T19:34:24.803 回答