.NET外部非托管库函数抛出无法捕获的异常

时间:2011-02-09 10:47:54

标签: c# .net windows-ce

我在WindowsCE .NET Compact Framework 3.5应用程序中使用OpenNetCF库。 问题是当我调用其中一个函数( NetworkInterface.GetAllNetworkInterfaces()是特定的)时,它会随机抛出无法捕获的异常并导致我的应用程序崩溃:

ExceptionCode: 0x80000002
exceptionAddress: 0x03F928C4
at NativeMethods.WZCDeleteIntfObj(INTF_ENTRY& Intf)
at INTF_ENTRY.Dispose()
at GetAllNetworkInterfaces()

我正在搜索网络,并在OpenNetCF社区的错误跟踪器中报告此错误。不幸的是,还没有人修复它,我很确定没有人会修复它。 我有源代码,所以我可能必须自己做。如果我能成功捕获异常而不会崩溃应用程序,我将很高兴。

抛出异常的方法实际上是以下列方式导入的本机方法:

        //---------------------------------------
        // 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("wzcsapi.dll")]
        public static extern void
            WZCDeleteIntfObj(
            ref INTF_ENTRY Intf);

如何从此方法中捕获异常?

4 个答案:

答案 0 :(得分:1)

我在Windows紧凑框架中使用OpenNetCF库时遇到了类似的问题。无法捕获的本机异常是随机抛出的,它使整个应用程序崩溃。在调查此问题的可能原因时,我可以了解它可能是由于内存泄漏导致系统内存不足或数据错位。

我调用GC.Collect()来回收在调用OpenNETCF.Net.NetworkInformation.NetworkInterface.GetAllNetworkInterfaces()方法之前无法访问的所有内存。 这解决了这个问题,之后我没有得到任何本机异常。

GC.Collect()方法强制系统尝试回收最大可用内存量。所有对象,无论它们在记忆中存在多长时间,都被考虑收集;但是,不会收集托管代码中引用的对象。

答案 1 :(得分:0)

我倾向于将所有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调用,使得处理本机库变得更容易。

答案 2 :(得分:0)

尝试使用托管C ++将异常捕获为C ++ / C异常,然后重新抛出为CLR异常。当pInvoke变得太难时,托管C ++通常是一个不错的选择。

答案 3 :(得分:0)

我遇到了同样的问题。无法在c#try / catch中捕获NativeException。至少不在.NET CF 3.5上。

抛出的错误是DataType未对齐异常。这是一个可能发生在例如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);