统一处理非托管API中的错误代码 [英] Uniformly handling error codes in an unmanaged API
问题描述
ErrorCode result = Api.Method();
if(result!= ErrorCode.SUCCESS){
throw Helper.ErrorToException(result);
}
这工作正常。问题是,我有这么多非管理的方法调用,这是非常令人沮丧和重复的。所以,我尝试切换到这个:
public static void ApiCall(Func< ErrorCode> apiMethod){
ErrorCode result = apiMethod();
if(result!= ErrorCode.SUCCESS){
throw Helper.ErrorToException(result);
}
}
哪个可以将所有这些调用一行:
Helper.ApiCall(()=> Api.Method());
然而,有两个立即的问题。首先,如果我的非托管方法使用 out
参数,我必须首先初始化局部变量,因为方法调用实际上在委托中。我想要简单地声明一个 out
目的地,而不进行初始化。
其次,如果一个例外是扔了,我真的不知道它来自哪里。调试器跳入 ApiCall
方法,堆栈跟踪仅显示包含调用 ApiCall
的方法,而不是委托自己因为我可以在一个方法中有很多API调用,所以调试困难。
然后我想到使用PostSharp将所有非托管调用包含错误代码检查,但我不知道如何使用 extern
方法完成。如果最终只是为每个人创建一个包装方法,那么我将会遇到与 ApiCall
方法相同的异常问题,对吧?另外,如果调试器只存在于编译程序集中,那么调试器如何知道如何在我的代码中显示抛出异常的站点?
接下来,我尝试实现自定义封送者将拦截API调用的返回值并检查错误代码。不幸的是,您不能应用自定义封送器来返回值。但是我认为如果已经有效的话,这将是一个非常干净的解决方案。
[return:
MarshalAs(UnmanagedType .CustomMarshaler,MarshalTypeRef = typeof(ApiMethod))]
public static extern ErrorCode Method();
现在我完全没有想法。还有其他一些方法可以解决这个问题?
按照 ErrorHandler
从Visual Studio 2010 SDK它存在于早期版本中,但新版本具有 CallWithCOMConvention(Action)
,根据API与其他托管代码的互动情况,这可能很有价值。
在可用的方法中,我建议您执行以下操作:
-
成功(int)
(Failed()
只是!成功()
,所以你可以 -
ThrowOnFailure(int)
(抛出正确的异常对于您的退货代码) -
CallWith_MyErrorCode_Convention(Action)
和CallWith_MyErrorCode_Convention(Func< ; int>)
(如CallWithCOMConvention
,但对于您的错误代码) -
IsCriticalException(异常)
(由$ code使用CallWith_MyErrorCode_Convention )
I'm writing a wrapper around a fairly large unmanaged API. Almost every imported method returns a common error code when it fails. For now, I'm doing this:
ErrorCode result = Api.Method();
if (result != ErrorCode.SUCCESS) {
throw Helper.ErrorToException(result);
}
This works fine. The problem is, I have so many unmanaged method calls that this gets extremely frustrating and repetitive. So, I tried switching to this:
public static void ApiCall(Func<ErrorCode> apiMethod) {
ErrorCode result = apiMethod();
if (result != ErrorCode.SUCCESS) {
throw Helper.ErrorToException(result);
}
}
Which allows me to cut down all of those calls to one line:
Helper.ApiCall(() => Api.Method());
There are two immediate problems with this, however. First, if my unmanaged method makes use of out
parameters, I have to initialize the local variables first because the method call is actually in a delegate. I would like to be able to simply declare a out
destination without initializing it.
Second, if an exception is thrown, I really have no idea where it came from. The debugger jumps into the ApiCall
method and the stack trace only shows the method that contains the call to ApiCall
rather than the delegate itself. Since I could have many API calls in a single method, this makes debugging difficult.
I then thought about using PostSharp to wrap all of the unmanaged calls with the error code check, but I'm not sure how that would be done with extern
methods. If it ends up simply creating a wrapper method for each of them, then I would have the same exception problem as with the ApiCall
method, right? Plus, how would the debugger know how to show me the site of the thrown exception in my code if it only exists in the compiled assembly?
Next, I tried implementing a custom marshaler that would intercept the return value of the API calls and check the error code there. Unfortunately, you can't apply a custom marshaler to return values. But I think that would have been a really clean solution it if had worked.
[return:
MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef=typeof(ApiMethod))]
public static extern ErrorCode Method();
Now I'm completely out of ideas. What are some other ways that I could handle this?
Follow ErrorHandler
class from the Visual Studio 2010 SDK. It existed in earlier versions, but the new one has CallWithCOMConvention(Action)
, which may prove valuable depending on how your API interacts with other managed code.
Of the available methods, I recommend implementing the following:
Succeeded(int)
(Failed()
is just!Succeeded()
, so you can skip it)ThrowOnFailure(int)
(Throws a proper exception for your return code)CallWith_MyErrorCode_Convention(Action)
andCallWith_MyErrorCode_Convention(Func<int>)
(likeCallWithCOMConvention
, but for your error codes)IsCriticalException(Exception)
(used byCallWith_MyErrorCode_Convention
)
这篇关于统一处理非托管API中的错误代码的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!