How to read int** 2D Array from c++ .dll to c#

BananaLin 41 Reputation points
2021-09-30T07:34:17.727+00:00

I have a DLL file and need to call functions from the file. From the .h file can see the content as follows:

struct COrganContour_Dll
{
 int** m_nContourList;//輪廓線座標串列,每一點按照XY的順序排列
 int* m_nContourSize;//紀錄每個輪廓線有多少個點的數量
 int m_nContourListSize;//輪廓線的數量

 COrganContour_Dll() :
 m_nContourList(nullptr),
 m_nContourSize(nullptr),
 m_nContourListSize(0)
 {}
};

In the C++, this function is used like this:

 char strSrcTestCTR[] = "Z:\\Test.ctr";
                    auto pCContourManager_Dll = OpenContourToContourManager(strSrcTestCTR);
                    for (int i = 0; i < pCContourManager_Dll->m_nOrganNameListSize; i++)
                    {
                        cout << "OrganID " << i << " ";
                        cout << pCContourManager_Dll->m_strOrganNameList[i] << "\n";
                    }

                    auto nOrganID = 45; //OrganID 45 的器官名稱為 Left femur

                    auto pCOrganContour_Dll = GetContourByOrganName(
                        pCContourManager_Dll->m_pContourManager,
                        pCContourManager_Dll->m_strOrganNameList[nOrganID],
                        150 //找第150頁上的輪廓線
                    );
                    cout << pCContourManager_Dll->m_strOrganNameList[nOrganID];
                    cout << " Contour Number ";
                    cout << pCOrganContour_Dll->m_nContourListSize << "\n";
                    for (int ContourID = 0;
                        ContourID < min(3, pCOrganContour_Dll->m_nContourListSize);
                        ContourID++)
                    {
                        cout << "Contour " << ContourID;
                        cout << " Point Number :";
                        cout << pCOrganContour_Dll->m_nContourSize[ContourID] << "\n";

                        for (int PointID = 0;
                            PointID < pCOrganContour_Dll->m_nContourSize[ContourID];
                            PointID++)
                        {
                            cout << "Contour " << ContourID;
                            cout << " Point " << PointID;
                            cout << " (X,Y): ";
                            cout << pCOrganContour_Dll->m_nContourList[ContourID][PointID * 2];
                            cout << ",";
                            cout << pCOrganContour_Dll->m_nContourList[ContourID][PointID * 2 + 1];
                            cout << "\n";
                        }
                    }

Here is my code:

    public unsafe struct COrganContour_Dll
    {
        public IntPtr m_nContourList;
        public int* m_nContourSize;
        public int m_nContourListSize;

        public COrganContour_Dll(IntPtr contourCoordinate, int* contourCoordinateLength, int contourListSize)
        {
            m_nContourList = contourCoordinate;
            m_nContourSize = contourCoordinateLength;
            m_nContourListSize = contourListSize;
        }
    }



    class Progam
    {

        [System.Runtime.InteropServices.DllImport(dllroute, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
        static unsafe extern IntPtr GetContourByOrganName(IntPtr pContourManager, string strOrganName, int nPage);


        static unsafe void Main(string[] args)
        {

            IntPtr getTest2 = GetContourByOrganName(outFunction, getChar[7], 100);

            COrganContour_Dll getStructure = (COrganContour_Dll)Marshal.PtrToStructure(getTest2, typeof(COrganContour_Dll));

            IntPtr ptr = getStructure.m_nContourList;  
            int nEntries = getStructure.m_nContourListSize; 
            int contourSize = *getStructure.m_nContourSize;
            int[] ptrArray = new int[contourSize];
            Marshal.Copy(ptr, ptrArray, 0, contourSize);
            int intSize = sizeof(int);
            int p_s_cursor = 0;
            Array coordinateData = Array.CreateInstance(typeof(int), 1, contourSize);
            for (int i = 0; i < nEntries; i++)
            {
                for (int j = 0; j < contourSize; j++)
                {
                    int data = (int)Marshal.PtrToStructure(ptr + p_s_cursor, typeof(int));
                    coordinateData.SetValue(data, i, j);
                    p_s_cursor += intSize;
                }
            }

        }
    }

This can read the value, but the value is wrong. I have tried to change different sizeof() types, all types of values are wrong.
I need a type like int[][], what should I do?
Hope someone can help me and give me advice, Thanks.

C#
C#
An object-oriented and type-safe programming language that has its roots in the C family of languages and includes support for component-oriented programming.
10,306 questions
C++
C++
A high-level, general-purpose programming language, created as an extension of the C programming language, that has object-oriented, generic, and functional features in addition to facilities for low-level memory manipulation.
3,544 questions
{count} votes

Accepted answer
  1. RLWA32 40,771 Reputation points
    2021-10-02T11:15:37.037+00:00

    The following sample code demonstrates how an unmanaged C++ dll might create a 2D array of int values . The pointer to the array is returned in a structure that also contains the number of rows and columns. For demonstration purposes, unmanaged memory is allocated using LocalAlloc so that the allocations can be freed by managed code.

    C++ dll code -

    struct Test2DArray
    {
        int** p2D;
        int nRows;
        int nCols;
    };
    
    constexpr int ROWS = 2;
    constexpr int COLS = 10;
    
    // Unmanaged memory allocated with LocalAlloc so it can be freed in managed code with Marshal.FeeHGlobal
    // Return FALSE if any memory allocation fails, LocalAlloc sets Win32 extended error code on failure
    
    extern "C" __declspec(dllexport) BOOL Create2DIntArray(Test2DArray* pStruct)
    {
        int iValue{ 1 };
    
        // Allocate memory for rows
        int** pArray_ = static_cast<int**>(LocalAlloc(LMEM_FIXED, ROWS * sizeof(int*)));
    
        if (!pArray_)
            return FALSE;
    
        // Allocate memory for columns
        for (int i = 0; i < ROWS; i++)
        {
            pArray_[i] = static_cast<int*>(LocalAlloc(LMEM_FIXED, COLS * sizeof(int)));
            if (!pArray_[i])
                return FALSE;
        }
    
        // Fill 2d array with values
        for (int r = 0; r < ROWS; r++)
            for (int c = 0; c < COLS; c++)
                pArray_[r][c] = iValue++;
    
        pStruct->p2D = pArray_;
        pStruct->nRows = ROWS;
        pStruct->nCols = COLS;
    
        return TRUE;
    }
    

    Sample C# console application to read the 2D array and print the values -

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using System.Runtime.InteropServices;
    
    namespace CSconsoleApp
    {
        class Program
        {
            [StructLayout(LayoutKind.Sequential)]
            public struct Test2DArray
            {
                public IntPtr p2D;
                public int nRows;
                public int nCols;
            };
    
            [DllImport("Array2D.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
            public static extern bool Create2DIntArray(ref Test2DArray astruct);
    
            static void Main(string[] args)
            {
                Test2DArray s = new Test2DArray();
    
                var result = Create2DIntArray(ref s);
    
                if(result)
                {
                    int[][] int2DArray = new int[s.nRows][];
                    for (int i = 0; i < s.nRows; i++)
                        int2DArray[i] = new int[s.nCols];
    
    
                    IntPtr[] ptrRows = new IntPtr[s.nRows];
                    Marshal.Copy(s.p2D, ptrRows, 0, s.nRows);
    
                    for (int i = 0; i < s.nRows; i++)
                        Marshal.Copy(ptrRows[i], int2DArray[i], 0, s.nCols);
    
                    for (int r = 0; r < s.nRows; r++)
                        for (int c = 0; c < s.nCols; c++)
                            Console.WriteLine("int2DArray[{0}][{1}] = {2}", r, c, int2DArray[r][c]);
    
                    // Free unmanaged memory allocated for columns
                    for (int r = 0; r < s.nRows; r++)
                        Marshal.FreeHGlobal(ptrRows[r]);
    
                    // Free unmanaged memory allocated for rows
                    Marshal.FreeHGlobal(s.p2D);
                }
            }
        }
    }
    
    1 person found this answer helpful.

0 additional answers

Sort by: Most helpful