Erik
Erik

Reputation: 954

Call C from C#: How to marshall that one 2D array of doubles when the others go so well?

I call a C routine from C#, with some doubles, ints, a large string and some arrays as argument. Some arrays return correctly, with the correct values, only one array does not.The array is corrupted on returning to C#, NOT during execution of the C-routine.

C#-side:

[DllImport(@"Critias.dll", CallingConvention = CallingConvention.Cdecl, EntryPoint = "Cr", 
            CharSet = CharSet.Ansi, BestFitMapping =false,ThrowOnUnmappableChar =true)]
public static extern int Cr([In] StringBuilder isc, 
            int nrModes, 
            double[] eigenfrequencies, 
            ref double error, 
            double[,] defl,
            double[] reactions,
            double[,] force);

public double[,] Deflection;
public double[,] MemberEndforces;       
public double[] Reactions;
public double error;

    ....

var eigenFreq = new double[nrModes + 1]; // +1 for C-sides pseudo FORTRAN arrays
MemberEndforces = new double[mbCount,12];                
Deflection = new double[mbCount + 1,6];               
Reactions = new double[6];       
info = NativeMethods.Cr(
            isc, nrModes, eigenFreq, ref error,  Deflection, Reactions, MemberEndforces);

now, all arguments come back OK, except MemberEndforces. Also when I swap the order of Reactions and MemberEndforces on the caller AND callee-side, Reactions come back from Cr OK, but not MemberEndforces. I get "Unable to read memory" for MemberEndforces, in the VS watch pannel . Also, if I do NOT fill MemberEndforces C-side, so do nothing with it, it makes no difference: Still the same error message

And C-side:

extern "C"
{
    __declspec(dllexport) int __cdecl Cr(
        char *_isc,
        int nM_calc,
        double* f,
        double* error,
        double* deflection,     
        double* reactions ,
        double* endforces ) // this is the annoying one
     {
      double** Q; // malloc-ed somewhere.
      double* D;
      double *R;

     .....
     for (int ii = 0; ii < DoF; ii++)
            deflection[ii ] = D[ii+1];
    for (int ii = 0; ii < nE; ii++)
        for (int jj = 0; jj < 12; jj++)
        {
            endforces[ii * 12 + jj] = Q[ii+1][jj + 1];// values OK,but...
        }
    for (int ii = 0; ii < 6; ii++)
        reactions[ii] = R[ii+1];

            ......

        }

The question, of course, is: What do I do wrong (and why, maybe?)?

Upvotes: 0

Views: 553

Answers (2)

Erik
Erik

Reputation: 954

It was the eigenfrequencies/f array and the nrModes argument that caused the trouble. The problem was 100% C-side. The calling/receiving was OK. I just handled eigenfrequencies wrong inside the C function Cr, overwriting stuff. Thnx for thinking with me.

Upvotes: 0

Dweeberly
Dweeberly

Reputation: 4777

There seem to be some concept issues here. As to why the last parameter might be corrupted. You define it as a two-dimensional array (c#), then use it as a one dimensional array in C.

In C an array is a pointer to a block of memory, of some particular type (example double). Typically, you allocated it as *typeof(double) * arraysize*. A two dimensional array is a pointer to a block of memory of pointers to some type. To create a 2D array you have to first allocate the array of pointers, then set each pointer to an array. You might look at something like this: Multidimensional array initialization in C

C# has different options here, see: What are the differences between a multidimensional array and an array of arrays in C#? Plus, C# is a managed memory system, so passing pointers out of C# (to say C) is a problem, because at any moment the GC (Garbage Collector) might kick in and change the memory address of a variable (pulling the rug out from under your C running C code). C# has a way to fix memory (using 'unsafe' code), which allows you to 'lock' and address (prevent the GC from moving it) for a period of time (like during your call to unmanaged code).

In the end you are just using addresses to blocks of memory. Makes sure those blocks don't move during calls and make sure the format of the blocks interpreted the same way in both coding languages.

Upvotes: 1

Related Questions