none
How to Marshal an C++ Struct Array using IntPtr RRS feed

  • Question

  • I have a following code in a C++ DLL
    typedef struct _MYPOINT
    {
        int nX;
        int nY;
    } MYPOINT;
     
    extern "C"  __declspec(dllexport) void ModifyPointArray (int nSize, MYPOINT *ptArr[]) 
    { 
        for ( int nI = 0; nI < nSize; nI++ )
        {
    	ptArr[nI]->nX +=5;
    	ptArr[nI]->nY +=5;
        }
    }



    ======================================

    I have defined a Wrapper Class for the DLL something like this.
    namespace MarshalDemo
    {
        [StructLayout(LayoutKind.Sequential)]
        public class MyPoint
        {
            public int nX;
            public int nY;
            
            public MyPoint()
            {
                this.nX = 0;
                this.nY = 0;
            }
            public MyPoint(int x, int y)
            {
                this.nX = x;
                this.nY = y;
            }
        }
       
        public static class MarshalDllWrapper
        {
         
          [DllImport("MarshalDll.dll")]
          public static extern void ModifyPointArray(int nSize, IntPtr [] arrPts);
        }
    }
    What I have tried:

    Now i am trying to call this function using Marshalling using IntPtr, my .NET client code (C#) is something like this.
    MyPoint[] pointArr = { new MyPoint(5, 6), new MyPoint(7, 8), new MyPoint(9, 10) };
    int nCount = pointArr.Length;
    IntPtr[] ptrArr = new IntPtr[nCount];
    for (int nI=0; nI< nCount; nI++)
    {
         ptrArr[nI] = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(MyPoint)));
         Marshal.StructureToPtr(pointArr[nI], ptrArr[nI], true);
    }
    
    MarshalDllWrapper.DisplayPointArray(nCount, ptrArr);
    MyPoint[] pointArr2 = new MyPoint[nCount];
    for (int nI = 0; nI < nCount; nI++)
    {
         Marshal.PtrToStructure(ptrArr[nI], pointArr2[nI]);
         Marshal.FreeCoTaskMem(ptrArr[nI]);
    }

    I am getting an Error on line
    //F:\VS2010\VCS2010\Marshalling\MarshalDll\MarshalDllClient\frmMarshalDemo.cs:line XXX
    Marshal.PtrToStructure(ptrArr[nI], pointArr2[nI]);

    with Error Message
    System.ArgumentNullException: Value cannot be null.
    Parameter name: structure
       at System.Runtime.InteropServices.Marshal.PtrToStructureHelper(IntPtr ptr, Object structure, Boolean allowValueClasses)
       at System.Runtime.InteropServices.Marshal.PtrToStructure(IntPtr ptr, Object structure)
       at MarshalDemo.frmMarshalDemo.btnPointArray_Click(Object sender, 
    EventArgs e) in 
    F:\VS2010\VCS2010\Marshalling\MarshalDll\MarshalDllClient\frmMarshalDemo.cs:line XXX 

    PS : I know if i write a Wrapper something like this.

    public static extern void ModifyPointStructArray(int nSize, [In, Out] MyPoint[] arrStruc);

    it works like a breeze, but i want to do with IntPtr, am i doing something conceptually wrong ?

    What I am doing wrong ?
    Any Help will will apreciated.

    Thanks.

    Praveen.


    Praveen K.

    Tuesday, December 3, 2019 1:47 PM

Answers

  • From https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/arrays/single-dimensional-arrays

    Value Type and Reference Type Arrays

    Consider the following array declaration:

    C#
    SomeType[] array4 = new SomeType[10];
    

    The result of this statement depends on whether SomeType is a value type or a reference type. If it is a value type, the statement creates an array of 10 elements, each of which has the type SomeType. If SomeType is a reference type, the statement creates an array of 10 elements, each of which is initialized to a null reference.

    Try this -

    MyPoint[] pointArr2 = { new MyPoint(), new MyPoint(), new MyPoint() };

    instead of

    MyPoint[] pointArr2 = new MyPoint[nCount];
    Also, you should specify CallingConvention = CallingConvention.Cdecl in [DllImport].


    • Edited by RLWA32 Tuesday, December 3, 2019 3:03 PM
    • Marked as answer by Praveen K. Katiyar Tuesday, December 3, 2019 3:33 PM
    Tuesday, December 3, 2019 3:01 PM

All replies