Marshaling unmanaged C 2D array

How to access this:

__declspec(dllexport) void __stdcall TestArray(int nI, int nJ, double** arr1, double** arr2)
{
int x1;
int x2;

for(x1 = 0; x1 < nI; x1++)
{
for(x2 = 0; x2 < nJ; x2++)
{
arr1[x1][x2] = 5;
arr2[x1][x2] = arr1[x1][x2];
}
}

return;

from c#

i tried with:

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

namespace Runner
{
class Program
{
[DllImport("../../../debug/testArraysUnmanaged.dll")]
public static extern void TestArray(int i, int j, double[,] arr1, double[,] arr2);

static void Main(string[] args)
{
int nI = 100;
int nJ = 50;

double[,] array1 = new double[nI, nJ];
double[,] array2 = new double[nI, nJ];

TestArray(nI, nJ, array1, array2);

return;
}
}
}

but then I get "{"Attempted to read or write protected memory. This is often an indication that other memory is corrupt."}"

is there any way to access ** array's in c from c#

thanks!


Answer this question

Marshaling unmanaged C 2D array

  • robear

    Lucian thanks for the link I was there before but didn't get much help from that.

    Miroslaw thanks for the response. If I can choose I would like to go for option 1 (would like to keep c code as is). I tried already this:

    - send 2d array from c# with double[,] arr1

    - that data is received in wrapper function in C which accepts double [] array

    - in wrapper function make double** tempArr and copy data from double[] array to double** tempArr

    - call original function with double** tempArr as param

    and guess what happens Access Violation Exception.........

    aaaaaaaaaaaaaaarrrrrgggggggghhhh

    Any ideas


  • AKA13_Intrepid

    const int COL_DIM = 5;

    extern "C" PINVOKELIB_API int TestMatrixOfInts( int pMatrix[][COL_DIM], int row );

    // Declares a managed prototype for a matrix of integers by value.
    [ DllImport( "..\\LIB\\PinvokeLib.dll" )]
    public static extern int TestMatrixOfInts([In, Out] int[,] pMatrix, int row );

    // matrix ByVal
    const int DIM = 5;
    int[,] matrix = new int[ DIM, DIM ];

    Console.WriteLine( "\n\nMatrix before call:" );
    for( int i = 0; i < DIM; i++ )
    {
    for( int j = 0; j < DIM; j++ )
    {
    matrix[ i, j ] = j;
    Console.Write( " " + matrix[ i, j ] );
    }
    Console.WriteLine( "" );
    }
    int sum3 = LibWrap.TestMatrixOfInts( matrix, DIM );
    Console.WriteLine( "\nSum of elements:" + sum3 );
    Console.WriteLine( "\nMatrix after call:" );
    for( int i = 0; i < DIM; i++ )
    {
    for( int j = 0; j < DIM; j++ )
    {
    Console.Write( " " + matrix[ i, j ] );
    }
    Console.WriteLine( "" );
    }


  • Jason D. Camp

    you c function expect an jugged array double **arr1 but the declaration in your c# example stays it were an ordinary array
    double[,] arr1.
    So you have to change the the C# declaration to
    public static extern void TestArray (..., ref double[][] arr1, ...)
    but jugged arrays are not supported by the marshaler.
    In order to get it working you have two options
    1. You have to marshal the arrays yourself to produce jugged arrays which results in unnecessary copy operations
    2. Change the C function to accept double *array
    insted of double **array. All other declaration have to be changed accordingly. The marshaler does support simple arrays. See the another answer to you question for references how to do it


  • kbromer

  • &amp;#64;nt

    Lucian
    if you declare in C the following double **array it is a jugged array with two
    dimensionis. This means you have a pointer list of pointers to lists of of
    values. By the intention of the code it happens that the lists of values are
    all equally long.
    Your example (copied from the msdn array marshaling demo) does not help here. It deals with two dimensional regular array a list of values logicaly partitioned in rows and columns. One would have to change the C function signature to accept double *array which is not the option here.

    Perica
    The simple solution is to allocate memory for the both the list on the first level and on the lists on the second level and copy the values accordingly. After the C call returns you must marshal the values back. I hope to helpt you.

    Here is the code sample:

    [DllImport("../../../debug/testArraysUnmanaged.dll")]
    public static extern void TestArray(int i, int j, IntPtr arr1, IntPtr arr2);

    private void doArray()
    {
    int nI = 3;
    int nJ = 4;

    double[][] array1 = new double[nI][];
    double[][] array2 = new double[nI][];

    for (int i = 0; i < nI; i++)
    {
    array1Idea = new double[nJ];
    array2Idea = new double[nJ];

    for (int j = 0; j < nJ; j++)
    {
    array1Idea[j] = 1000000 + 1000 * i + j;
    array2Idea[j] = 2000000 + 1000 * i + j;
    }
    }

    IntPtr pa1 = marshalJuggedToC(array1);
    IntPtr pa2 = marshalJuggedToC(array2);

    TestArray(nI, nJ, pa1, pa2);

    array1 = marshalJuggedFromC(pa1, nI, nJ);
    array2 = marshalJuggedFromC(pa2, nI, nJ);

    for (int i = 0; i < array1.Length; i++)
    for (int j = 0; j < array1Idea.Length; j++)
    {
    Console.WriteLine("arr1[{0}][{1}] = {2}", i, j, array1Idea[j]);
    Console.WriteLine("arr2[{0}][{1}] = {2}", i, j, array2Idea[j]);
    }
    }

    private IntPtr marshalJuggedToC(double[][] array)
    {
    int sizeofPtr = Marshal.SizeOf(typeof(IntPtr));
    int sizeofDouble = Marshal.SizeOf(typeof(double));

    IntPtr p1 = Marshal.AllocCoTaskMem(array.Length * sizeofPtr);
    for (int i = 0; i < array.Length; i++)
    {
    IntPtr v1 = Marshal.AllocCoTaskMem(arrayIdea.Length * sizeofDouble);
    Marshal.Copy(arrayIdea, 0, v1, arrayIdea.Length);
    Marshal.WriteIntPtr(p1, i * sizeofPtr, v1);
    }
    return p1;
    }

    private double[][] marshalJuggedFromC(IntPtr carray, int nI, int nJ)
    {
    int sizeofPtr = Marshal.SizeOf(typeof(IntPtr));
    double[][] retval = new double[nI][];
    for (int i = 0; i < retval.Length; i++)
    {
    retvalIdea = new double[nJ];
    IntPtr v1 = Marshal.ReadIntPtr(carray, i * sizeofPtr);
    Marshal.Copy(v1, retvalIdea, 0, nJ);
    Marshal.FreeCoTaskMem(v1);
    }
    Marshal.FreeCoTaskMem(carray);
    return retval;
    }



  • Tom_Liu

    Miroslaw,

    This works great. I was trying something like this before but without WriteIntPtr and ReadIntPtr and ofcourse it was not working.

    thanks for help!

    grtz

    Perica


  • Marshaling unmanaged C 2D array