记C#和C++混合开发的坑们

记C#和C++混合开发的坑们

开发网络摄像机的过程中,面对NetSDK.dll的许多的坑,完全看不懂的说啊!

先说一个例子吧。这个例子是调用SDK中的H264_DVR_SearchDevice方法,枚举网络中的摄像机。

这个方法的原型定义是这样的:

H264_DVR_API bool CALL_METHOD H264_DVR_SearchDevice(char* szBuf, int nBufLen, int* pRetLen, int nSearchTime);

这里面有必要说一下的是参数的问题,第一个,szBuf是搜索到的设备列表缓冲区,是一个struct数组的首地址。Struct的名字是SDK_CONFIG_NET_COMMON_V2,内部就不列出来了,很大,也没有意义。第二个参数,nBufLen,是缓冲区的长度,以字节算的。第三个是返回来的有效数据的长度,也是以字节算的。因此计算有多少个设备,看看这些字节数包括多少个结构体大小就行了。最后一个是搜索时间,Demo显示5000,单位应该是ms了。

这里在C#中,我重新定义了这个方法的签名,如下所示:

public static extern bool H264_DVR_SearchDevice(ref IntPtr szBuf, int nBufLen, out int pRetLen, int nSearchTime);

调用的时候,问题发生在托管内存和非托管内存之间的转换上。

1.       H264_DVR_SearchDevice方法调用的问题

1.1    方式一、

 private void SearchDevices()

 {

     SDK_CONFIG_NET_COMMON_V2 dev = new SDK_CONFIG_NET_COMMON_V2();

     int size = Marshal.SizeOf(dev);

     int nBufLen = size * 100;

     byte[] szBuf = new byte[nBufLen];


     GCHandle unmanaged_data_handle = GCHandle.Alloc(szBuf, GCHandleType.Pinned);

     IntPtr fCloud = unmanaged_data_handle.AddrOfPinnedObject();

     int pRetLen;

     bool bRet = RoangNetSDK.H264_DVR_SearchDevice(ref fCloud, nBufLen, out pRetLen, 5000);

     Console.WriteLine("H264_DVR_SearchDevice = " + bRet + ", pRetLen = " + pRetLen);

     Console.WriteLine("szBuf[0]=" + szBuf[0]);

     //unmanaged_data_handle.Free();


 }

这个方法很笨拙,先创造一个结构体对象出来,然后算算有多大,然后开辟一块内存出来,这块内存是托管内存,然后转成非托管内存,然后交给这个方法去执行,然后就执行成功了。但后面打印szBuf[0]的时候报异常,显示“未将对象引用设置到对象的实例。”,基本就是空指针类似的问题。如果打开后面的“unmanaged_data_handle.Free()”而注释掉打印这一句,则在Free的这句上报“"引发类型为“System.ExecutionEngineException”的异常。”,程序崩溃退出。

我知道前面的方式很笨拙,先new出一个struct对象来,然后测量其大小,多了一个对象,其实这是不得已的。

按我的理解,我写成下面这样应该是比较理想的,即时算不上好的话:   

//SDK_CONFIG_NET_COMMON_V2 dev = new SDK_CONFIG_NET_COMMON_V2();

    //int size = Marshal.SizeOf(dev);

    int size = Marshal.SizeOf(typeof(SDK_CONFIG_NET_COMMON_V2));

    int nBufLen = size * 100;

    byte[] szBuf = new byte[nBufLen];

得到的大小都对,但是下面的执行却坏了菜了——H264_DVR_SearchDevice执行不下去了。从鼠标的感觉上来看,5秒之后程序进入了死循环,VS2012不响应了,程序界面显示“不响应”,用鼠标点关闭可以强制关闭之,然后VS2012跳出来了,如下说:

嘿,这就搞笑了,大小的计算还影响后面的执行?莫非HiFier们听音乐真的能听出火电水电还是风电来了?要说我把代码写难看点我可以认了,我的内存怎么就不能释放了??难道叔可忍,婶也得忍吗??

1.2    方式二、

还是调用这个方法,只不过这次这样写,似乎看起来还是有些神清气爽的。

private void SearchDevices()

{

    SDK_CONFIG_NET_COMMON_V2[] devices = new SDK_CONFIG_NET_COMMON_V2[100];

    int singleSize = Marshal.SizeOf(typeof(SDK_CONFIG_NET_COMMON_V2));

    int nBufLen = singleSize * 100;

    IntPtr pt = Marshal.AllocHGlobal(nBufLen);

    int pRetLen;

    bool bRet = RoangNetSDK.H264_DVR_SearchDevice(ref pt, nBufLen, out pRetLen, 5000);

    Console.WriteLine("H264_DVR_SearchDevice = " + bRet + ", pRetLen = " + pRetLen);

    for (int i = 0; i < 100; i++)

    {

IntPtr ptr = (IntPtr)((UInt32)pt + i * singleSize);

devices[i] = (SDK_CONFIG_NET_COMMON_V2)Marshal.PtrToStructure(ptr, typeof(SDK_CONFIG_NET_COMMON_V2));

//Console.WriteLine("OsVersion:{0} szVersion:{1}", infos[i].osVersion, infos[i].szVersion);

    }

    Marshal.FreeHGlobal(pt);

}

问题是,这样的结果最惨。程序在执行H264_DVR_SearchDevice的时候直接崩溃了。从输出窗口上来看是发生了内存访问的异常了。

程序“[10616] RoangCamTest.vshost.exe: 程序跟踪”已退出,返回值为 0 (0x0)。

程序“[10616] RoangCamTest.vshost.exe: 托管(v2.0.50727)”已退出,返回值为 -1073741819 (0xc0000005) 'Access violation'。

嘿!真服了!哪里有问题?

参照上次,把大小的计算方式换换,这样可行否?这下代码成这样了:

private void SearchDevices()

{

   

    //int singleSize = Marshal.SizeOf(typeof(SDK_CONFIG_NET_COMMON_V2));

    SDK_CONFIG_NET_COMMON_V2 dev = new SDK_CONFIG_NET_COMMON_V2();

    int singleSize = Marshal.SizeOf(dev);

    int nBufLen = singleSize * 100;

    IntPtr pt = Marshal.AllocHGlobal(nBufLen);

    int pRetLen;

    bool bRet = RoangNetSDK.H264_DVR_SearchDevice(ref pt, nBufLen, out pRetLen, 5000);

    Console.WriteLine("H264_DVR_SearchDevice = " + bRet + ", pRetLen = " + pRetLen);


    SDK_CONFIG_NET_COMMON_V2[] devices = new SDK_CONFIG_NET_COMMON_V2[100];

    for (int i = 0; i < 100; i++)

    {

IntPtr ptr = (IntPtr)((UInt32)pt + i * singleSize);

devices[i] = (SDK_CONFIG_NET_COMMON_V2)Marshal.PtrToStructure(ptr, typeof(SDK_CONFIG_NET_COMMON_V2));

//Console.WriteLine("OsVersion:{0} szVersion:{1}", infos[i].osVersion, infos[i].szVersion);

    }

    Marshal.FreeHGlobal(pt);


}

果然执行下去了!可是,在后面执行转换的时候,无法进行,如是说:

嗯嗯,我觉得我该放弃了。

1.3    方式三、

算了,没有方式三了。老子不干了。

2.       .NET 3.5和.NET 4.5的坑

只要项目切换到4.0/4.5,程序运行直接报错:

网络资料都说,加上“CallingConvention = CallingConvention.Cdecl”就好了,可是就真不是好了。好容易在一处网页上说,换成.NET 3.5就好了,我就换过来了,还真好了!可是,现在面对的坑就是前面说的那段啦。

到现在为止,我还没越过这些坑,正在等有牛人来指导一下呢。先谢过各位大牛!请不吝赐教!

猜你喜欢

转载自my.oschina.net/smzd/blog/1786428