【发布时间】:2010-10-24 10:13:57
【问题描述】:
我即将向使用 COM 的旧版 C++ 应用程序公开一个用 C# 编写的服务。向非托管客户端报告错误的最佳方法是什么?抛出异常还是简单地返回错误值?
谢谢, 斯特凡诺
【问题讨论】:
我即将向使用 COM 的旧版 C++ 应用程序公开一个用 C# 编写的服务。向非托管客户端报告错误的最佳方法是什么?抛出异常还是简单地返回错误值?
谢谢, 斯特凡诺
【问题讨论】:
你应该抛出异常。异常由框架映射到 HRESULTS,HRESULT 是向 COM 客户端返回错误的标准方式,所以这是要走的路。
每个异常类型都有一个 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
}
}
}
【讨论】:
我同意其他人的观点,即在不熟悉您的项目的情况下,这不是“是或否”的答案。
这取决于许多因素,例如:
Here's a good blog post that discusses a number of subtle points about exception processing.
作者推荐两种方法之一:
要么:
或:
就个人而言,我认为您应该避免将 C# 服务与 C++ 应用程序紧密耦合。换句话说,编写您的 C# 服务,以便理论上任何消费者都可以使用它。同样,您的 C++ 代码应编写为不依赖于 C# 服务的内部工作,因此对异常(或错误代码)的更改或添加不会破坏使用者。
【讨论】:
如果您的 COM 应用程序在调用 C# 服务时支持 IErrorInfo 接口,并且它是一个完全内部的项目,那么抛出异常可能是最好的选择,因为它可以捕获最多的信息。然而,COM 传统上依赖 HR 结果来传达状态结果,如果要将服务发布到其他来源,可能会更好。
编辑:我更喜欢乔的回答。
【讨论】:
我认为这取决于遗留应用程序将如何反应。如果它理解错误返回值,那么就采用这种方法。如果没有,那么您将不得不抛出异常并希望它能够适当地处理它们。
另外,如果它是错误的引用(例如空引用)或其他严重错误,我总是会抛出异常。但是,对于消费者无法事先检查的事情,应避免异常(例如,空的搜索不应引发异常)。
【讨论】:
最好的判断是你是使用异常还是返回值。 “抛出异常”在几个方面胜过“返回值”。但在某些情况下,返回值就足够了。
【讨论】: