托管代码是否应该返回错误或将异常抛给非托管代码?

时间:2009-05-07 08:36:42

标签: c# c++ exception com

我即将使用COM将使用C#编写的服务公开给传统的C ++应用程序。向非托管客户端报告错误的最佳方法是什么?抛出异常或只返回错误值?

谢谢,  斯特凡诺

5 个答案:

答案 0 :(得分:10)

你应该抛出异常。框架将异常映射到HRESULTS,而HRESULT是将错误返回给COM客户端的标准方法,因此这是可行的方法。

每个Exception类型都有一个HResult属性。当从COM客户端调用的托管代码抛出异常时,运行时将HResult传递给COM客户端。如果需要特定于应用程序的HRESULT代码,可以创建自己的自定义异常类型并设置Exception.HResult属性。

需要注意的一点是,当向COM客户端抛出异常时,调用堆栈信息将丢失。因此,在传播到COM客户端之前记录异常是个好主意。

我有时使用的一种技术如下:为COM客户端显式实现一个ComVisible接口,用于记录和重新抛出异常。 COM客户端使用ComVisible接口在传播异常之前记录异常。 .NET客户端使用具体类,并期望自己安排异常处理。编写时有点啰嗦,但在您随后进行故障排除时可能会有所帮助。

此方法的另一个优点是,您可以根据COM客户端的COM限制量身定制API,并为标准.NET客户端提供更标准的API。例如,COM客户端仅限于通过引用传递数组,而不支持通过引用传递.NET客户端。

示例:

[
ComVisible(true),
GuidAttribute("..."),
Description("...")
]
public interface IMyComVisibleClass
{
    // Text from the Description attribute will be exported to the COM type library.

    [Description("...")]
    MyResult MyMethod(...);

    [Description("...")]
    MyOtherResult MyArrayMethod([In] ref int[] ids,...);
}
...
[
ComVisible(true),
GuidAttribute("..."),
ProgId("..."),
ClassInterface(ClassInterfaceType.None),
Description("...")
]
public class MyComVisibleClass : IMyComVisibleClass
{
    public MyResult MyMethod(...)
    {
        ... implementation without exception handling ...
    }

    public MyOtherResult MyArrayMethod(int[] ids,...)
    {
        ... input parameter does not use ref keyword for .NET clients ...
        ... implementation without exception handling ...
    }

    MyResult IMyComVisibleClass.MyMethod(...)
    {
        // intended for COM clients only
        try
        {
            return this.MyMethod(...);
        }
        catch(Exception ex)
        {
            ... log exception ...
            throw;   // Optionally wrap in a custom exception type
        }
    }

    MyOtherResult IMyComVisibleClass.MyArrayMethod(ref int[] ids, ...)
    {
        // intended for COM clients only
        try
        {
            // Array is passed without ref keyword
            return this.MyArrayMethod(ids, ...);
        }
        catch(Exception ex)
        {
            ... log exception ...
            throw;   // Optionally wrap in a custom exception type
        }
    }

}

答案 1 :(得分:3)

如果您的COM应用程序在调用C#服务时支持IErrorInfo接口,并且它是一个完全内部项目,那么抛出异常可能是最好的选择,因为它捕获了大部分信息。但是,COM传统上依靠人力资源结果来传达状态结果,如果要将服务发布到其他来源可能会更好。

编辑:我更喜欢Joe的回答。

答案 2 :(得分:3)

我同意其他人的意见,如果不亲密地了解你的项目,这不是一个“是或否”的答案。

这取决于许多因素,例如:

  • 安全性(即您的客户应该知道您的例外情况)
  • 效率(即处理时间至关重要)
  • 可维护性(即您可以更改旧版C ++代码以解析异常情况)

Here's a good blog post that discusses a number of subtle points about exception processing.

作者建议采用以下两种方法之一:

或者:

  • 返回错误文档。

或:

  • 记录有关的所有信息 服务器上的异常。
  • 创建一个新的 引用已记录的异常 信息。
  • 将新例外发送给 客户端进行客户端处理 和报告。

就个人而言,我认为您应该避免将C#服务与C ++应用程序紧密耦合。换句话说,编写C#服务,以便理论上可以被任何消费者使用。同样,应该编写C ++代码,使其不依赖于C#服务的内部工作,因此对异常(或错误代码)的更改或添加不会破坏使用者。

答案 3 :(得分:1)

我认为这取决于遗留应用程序的反应方式。如果它理解错误返回值,那么请使用该方法。如果没有,那么你将不得不抛出异常并希望它适当地处理它们。

另外,如果它是一个错误的引用(例如null引用)或其他严重错误,我总是会抛出异常。但是,对于消费者无法事先检查的事情(例如,空的搜索不应该抛出异常),应该避免例外情况。

答案 4 :(得分:1)

最好判断您是使用例外还是返回值将是您。 “抛出异常”在几个方面超过了“回归价值”。但在某些情况下,返回值就足够了。