question

BananaLin-4905 avatar image
0 Votes"
BananaLin-4905 asked BananaLin-4905 commented

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

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.

dotnet-csharpc++
· 2
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

I need a type like int[][]

Does the function exported from the unmanaged C++ dll provide the number of rows and columns in the 2D array?

What memory allocation method is used by the dll? If you want to be able to free the unmanaged memory that holds the 2D array from managed code then it needs to be allocated using LocalAlloc or CoTaskMemAlloc.


1 Vote 1 ·

@BananaLin-4905

You should allocate the size of the array before calling the c++ code. I suggest you could try to use Marshal.AllocHGlobal
to allocate memory from the unmanaged memory of the process.


0 Votes 0 ·

1 Answer

RLWA32-6355 avatar image
1 Vote"
RLWA32-6355 answered BananaLin-4905 commented

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
5 |1600 characters needed characters left characters exceeded

Up to 10 attachments (including images) can be used with a maximum of 3.0 MiB each and 30.0 MiB total.

Thank you very much, your explanation is very clear

0 Votes 0 ·