Remember the pits of C# and C++ mixed development

Remember the pits of C# and C++ mixed development

 

In the process of developing network cameras, faced with many pits in NetSDK.dll, I couldn't understand it at all!

 

Let me give an example first. This example is to call the H264_DVR_SearchDevice method in the SDK to enumerate the cameras in the network.

The prototype definition of this method is as follows:

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

 

It is necessary to talk about the problem of parameters. First, szBuf is the searched device list buffer, which is the first address of a struct array. The name of Struct is SDK_CONFIG_NET_COMMON_V2, which is not listed internally. It is very large and meaningless. The second parameter, nBufLen, is the length of the buffer, in bytes. The third is the length of the returned valid data, also in bytes. So count how many devices there are and see how many struct sizes are included in those bytes. The last one is the search time, Demo shows 5000, the unit should be ms.

 

Here in C#, I redefine the signature of this method as follows:

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

 

When calling, the problem occurs in the conversion between managed and unmanaged memory.

 

1. The problem of H264_DVR_SearchDevice method call

1.1 Method 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();


 }

This method is very clumsy. First create a structure object, then calculate how big it is, and then open up a piece of memory. This memory is managed memory, then convert it to unmanaged memory, and then hand it over to this method to execute, and then execute It worked. However, when szBuf[0] is printed later, an exception is reported, which shows "The object reference is not set to the instance of the object.", which is basically a problem similar to a null pointer. If you open the following "unmanaged_data_handle.Free()" and comment out the print sentence, then report "" in the Free sentence and throw an exception of type "System.ExecutionEngineException". ", the program crashes and exits.

I know that the previous method is very clumsy. First create a new struct object, then measure its size, and add another object. In fact, this is a last resort.

From what I understand, it should be ideal if I write the following, even if it is not good:   

//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];

The obtained size is correct, but the following execution is broken - H264_DVR_SearchDevice cannot be executed. From the feeling of the mouse, after 5 seconds, the program enters an infinite loop, VS2012 does not respond, and the program interface displays "not responding". Click to close it with the mouse to force it to close, and then VS2012 jumps out, as follows:

Hey, this is funny, the calculation of the size also affects the subsequent execution? Could it be that HiFiers can really hear whether thermal power, hydropower or wind power is coming from listening to music? To say that I wrote the code ugly, I can recognize it, why can't my memory be released? ? Could it be that uncle can bear it, and aunt has to bear it? ?

 

1.2 Method two,

It is still called this method, but this time, it seems to be refreshing.

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);

}

The problem is, this is the worst outcome. The program crashes directly when executing H264_DVR_SearchDevice. From the output window, a memory access exception occurred.

Program "[10616] RoangCamTest.vshost.exe: Program Trace" exited with a return value of 0 (0x0).

Program '[10616] RoangCamTest.vshost.exe: Hosting (v2.0.50727)' exited with -1073741819 (0xc0000005) 'Access violation'.

Hey! Really satisfied! Where is the problem?

Referring to the last time, change the calculation method of size. Is this feasible? The code now looks like this:

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);


}

It really worked! However, when the conversion is performed later, it cannot be performed, such as:

Hmm, I think I should give up.

1.3 Method three,

Forget it, there is no way three. The old man quit.

 

2. The pit of .NET 3.5 and .NET 4.5

As long as the project is switched to 4.0/4.5, the program will report an error directly:

The network information says that adding "CallingConvention = CallingConvention.Cdecl" is good, but it's really not good. It was easy to say on a web page that it would be good to replace it with .NET 3.5, and I changed it, and it was really good! However, the pit we are facing now is the one I mentioned earlier.

So far, I haven't crossed these pits, and I'm waiting for someone to guide me. Thanks in advance everyone! Please do not hesitate to enlighten me!

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324454916&siteId=291194637