Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Improper marshaling: C# array to a C++ unmanaged array

I have the following C# code with a structure definition (CInput), obj definition and init, and a call to a C++ (native) DLL function (that has also been written by me).

//C# code

 public struct CInput
 {

  [MarshalAsAttribute(UnmanagedType.R8)] 
  public double Time;

  [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_R8)]
  public double[] Database;

  /* other similar fields*/

}     

CInput Inputs = new CInput();

/* init of Inputs fields*/

int bfr = Example(ref Inputs); //'Example' being the C++ DLL call

Messagebox.Show(bfr.ToString());

There is an error in the marshaling of the second parameter, I don't know where. Then:

//C++ code

struct CInput {

  double Time;                       
  double Database[3650];     
  /*etc*/   
}

int Example(CInput& ObjIn) {

    return ObjIn.Database[0];        // just an example
}

If I'm not careful and specify only "SafeArray" in the Database marshaling I get a 'error in reading/writing memory, probably corrupt' etc.

if "Database" was marshaled as ByValArray everything is fine, the values show up correctly. Unfortunately I get an internal size exception because I have many arrays of that size, therefore I have to go for pointers - but anything with "SizeArray" will bring the following results (with the code just posted):

(from C++):

Database[0] = **0**

Database[1..etc] = values of the next parameters in the struct marshaled with ByValArray.

I think I should mention that I need the same identical structure from C# to C++, I'm not looking for anything fancy. So Array in a Struct >>> Array in a Struct.

ANY insight in reference to this would be of great value. I have been looking for hours and I don't have yet a solution.

Many thanks in advance.

like image 332
Nicholas H Kirk Avatar asked Sep 05 '11 15:09

Nicholas H Kirk


2 Answers

As I understand your question, you can't use ByValArray with SizeConst because your real struct has a large number of such arrays which results in the stack overflowing.

You opine that maybe you need to use pointers in your structs and I agree with you. Here is how to do it.

On the C++ side you should declare each array as a pointer to the element type:

struct CInput {
  double *array;
}

You may also wish to include the length of the arrays in the struct to avoid excessive amounts of hard-coded constants.

All the hard work happens on the C# side.

public struct CInput
{
    public IntPtr array;
}
...
double[] theArray = new double[3650];
CInput input = new CInput();
input.array = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(double))*theArray.Length);
try 
{
    Marshal.Copy(theArray, 0, input.array, theArray.Length);
    //call your C++ function here
}
finally
{
    Marshal.FreeHGlobal(input.array);
}
like image 181
David Heffernan Avatar answered Nov 12 '22 20:11

David Heffernan


public struct CInput  
{    
    public double Time;    
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 3650)] 
    public double[] Database;
}       

CInput Inputs = new CInput();  
int bfr = Example(ref Inputs);

Edit. If you need to have dynamically allocated Database array, both C++ and C# code should be changed. In C++ Database should be defined as double*, and you need to add array length somewhere. In C# Database should be declared as IntPtr, and allocated using Marshal.AllocHGlobal method. Please correct C++ structure according to your requirements, and then C# code can be fixed according to this.

like image 5
Alex F Avatar answered Nov 12 '22 19:11

Alex F