.NET 外部非托管库函数引发无法捕获的异常
Posted
技术标签:
【中文标题】.NET 外部非托管库函数引发无法捕获的异常【英文标题】:.NET external unmanaged library function throws uncatchable exception 【发布时间】:2011-02-09 10:47:54 【问题描述】:我在我的 WindowsCE .NET Compact Framework 3.5 应用程序中使用 OpenNetCF 库。 问题是当我调用它的一个函数(NetworkInterface.GetAllNetworkInterfaces() 具体来说)时,它会随机抛出无法捕获的异常并使我的应用程序崩溃:
异常代码:0x80000002 异常地址:0x03F928C4 在 NativeMethods.WZCDeleteIntfObj(INTF_ENTRY& Intf) 在 INTF_ENTRY.Dispose() 在 GetAllNetworkInterfaces()我已经在网络上搜索过,OpenNetCF 社区的错误跟踪器中报告了这个错误。不幸的是,还没有人修复它,我很确定没有人会修复它。 我有源代码,所以可能我必须自己做。如果我能设法捕获异常,而不会使应用程序崩溃,我会很高兴。
抛出异常的方法实际上是通过以下方式导入的native方法:
//---------------------------------------- // WZCDeleteIntfObj: 清除一个 INTF_ENTRY 对象,它是 // 在任何 RPC 调用中分配。 // // 参数 // pIntf // [in] 指向要删除的 INTF_ENTRY 对象的指针 [DllImport("wzcsapi.dll")] 公共静态外部无效 WZCDeleteIntfObj( 参考 INTF_ENTRY Intf);如何从这个方法中捕获异常?
【问题讨论】:
0x80000002 等同于“内存不足”,这是一个非常奇怪的错误。发送到 delete 的 intf 对象此时是否真的有效(即尚未删除)? 这是第三方代码,我得看看。它可能会尝试删除不存在的对象。但是问题是这个异常怎么可能不能被捕获呢? 【参考方案1】:我在 Windows 紧凑框架中使用 OpenNetCF
库时遇到了类似的问题。无法捕获的本机异常是随机抛出的,它使整个应用程序崩溃。在调查此问题的可能原因时,我可以知道这可能是由于内存泄漏导致系统内存不足或数据未对齐。
在调用OpenNETCF.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()
方法之前,我调用了GC.Collect()
来回收所有不可访问的内存。
这解决了这个问题,之后我没有得到任何本机异常。
GC.Collect()
方法强制系统尝试回收最大数量的可用内存。所有对象,无论它们在内存中存在多长时间,都将被考虑收集;但是,不会收集托管代码中引用的对象。
【讨论】:
【参考方案2】:我倾向于将所有 PInvoke 调用设为私有,并将它们包装在一个执行异常捕获的公共方法中:
class MyWrappingClass
[DllImport("wzcsapi.dll")]
private static extern void WZCDeleteIntfObj(ref INTF_ENTRY Intf);
public void useWZCDeleteIntfObj(ref INTF_ENTRY Intf) //you may wish to use a better method name
//you may wish to put some guards here, to check that Intf is valid before passing it to the native library.
try
WZCDeleteIntfObj( Intf );
catch(ExternalException e) //try to catch more specific exception types, such as SEHException.
//handle the exception
然后任何托管代码使用:
MyWrappingClass wrapper = new MyWrappingClass();
wrapper.useWZCDeleteIntfObj( Intf );
MyWrappingClass 还可以方便地放置帮助方法和抽象出一些底层 PInvoke 调用的方法,从而更轻松地处理原生库。
【讨论】:
在这种情况下它不会有帮助。使用 WZCDeleteIntfObj 方法的代码尝试使用空的 catch 语句捕获异常:try catch 正如我所说,异常没有被捕获。 啊,好的。我建议您在调用函数之前检查输入变量和软件其他相关部分的状态,以确保它们处于有效状态。关于您收到的具体错误,恐怕我不熟悉。【参考方案3】:尝试使用托管 C++ 将异常捕获为 C++/C 异常,然后作为 CLR 异常重新抛出。当 pInvoke 变得太难时,托管 C++ 通常是一个不错的选择。
【讨论】:
【参考方案4】:我遇到了同样的问题。 NativeException 不能在 c# try/catch 中被捕获。至少在 .NET CF 3.5 上没有。
抛出的错误是数据类型未对齐异常。这是一个可能发生的错误,例如ARM 处理器,因为 ARM 架构要求数据在内存中正确对齐。 我查看了 OpenNetCF.Net 代码,但找不到未对齐问题,也许我是 Windows WZZ API 中的错误导致错误。
我最终试图在 C 中捕获异常。本机异常是 SEH 异常。这是在 C 中实现异常的 Windows 方式。 这意味着 C++ try/catch 不会捕获它。 捕获它的方法是使用 __try/__except 块。
所以我最终创建了一个 C++ 项目(代码如下)。在 wzcsapi.dll 中创建一个调用 WZCDeleteIntfObj 的本机 DLL。代码只是捕获并忽略异常。 然后我从 codeplex 检索 OpenNet 代码,并将其修改为使用我的本机 dll 作为 wzcsapi 的 WZCDeleteIntfObj 方法的包装器。 结果看起来很有希望,但我不知道这是否会导致内存泄漏。
WZCWrapper.h
#include "stdafx.h"
#define WZCWrapper_API __declspec(dllexport)
typedef struct
DWORD dwDataLen;
LPBYTE pData;
RAW_DATA, *PRAW_DATA;
typedef struct
LPWSTR wszGuid;
LPWSTR wszDescr;
ULONG ulMediaState;
ULONG ulMediaType;
ULONG ulPhysicalMediaType;
INT nInfraMode;
INT nAuthMode;
INT nWepStatus;
DWORD dwCtlFlags;
DWORD dwCapabilities;
RAW_DATA rdSSID;
RAW_DATA rdBSSID;
RAW_DATA rdBSSIDList;
RAW_DATA rdStSSIDList;
BOOL bInitialized;
INTF_ENTRY, *PINTF_ENTRY;
WZCWrapper.dll
#include "stdafx.h"
#include "WZCWrapper.h"
typedef void (__cdecl *MYPROC)(PINTF_ENTRY);
static HINSTANCE wzcApiDll;
static BOOL initialized;
static MYPROC WZCDeleteIntfObj;
WZCWrapper_API VOID DLL_WZCDeleteIntfObj(PINTF_ENTRY pIntf)
if (!initialized)
wzcApiDll = LoadLibrary(TEXT("wzcsapi.dll"));
WZCDeleteIntfObj = (MYPROC) GetProcAddress(wzcApiDll, TEXT("WZCDeleteIntfObj"));
initialized = TRUE;
__try
WZCDeleteIntfObj(pIntf);
__except (GetExceptionCode() == STATUS_DATATYPE_MISALIGNMENT)
// FreeLibrary(wzcApiDll); // will release dll (we should not use this, keeping the dll open, as we need it again
WZCWrapper.def
LIBRARY "WZCWrapper"
EXPORTS
WZCDeleteIntfObj=DLL_WZCDeleteIntfObj
来自 OpenNetCF.Net 的 WZC.C 的修改部分
//---------------------------------------
// WZCDeleteIntfObj: cleans an INTF_ENTRY object that is
// allocated within any RPC call.
//
// Parameters
// pIntf
// [in] pointer to the INTF_ENTRY object to delete
[DllImport("WZCWrapper.dll")]
internal static extern void
WZCDeleteIntfObj(
ref INTF_ENTRY Intf);
【讨论】:
以上是关于.NET 外部非托管库函数引发无法捕获的异常的主要内容,如果未能解决你的问题,请参考以下文章