6

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.

1
  • UnmanagedType.SafeArray is useful only for COM code. The corresponding type is SAFEARRAY, and is managed by the COM Array functions Commented Sep 5, 2011 at 15:44

2 Answers 2

5

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);
}
Sign up to request clarification or add additional context in comments.

4 Comments

Dear David, you hit the nail on the head very precisely! The solution you proposed is working perfectly, many many thanks...!
What if I want to marshal an array of custom objects, and not primitives?
@Tsury Possibly it would be a loop over the array calling Marshal.StructureToPtr. But I would not like to say for sure without knowing more about your types.
@DavidHeffernan thank you for the fast answer. My types are detailed here: stackoverflow.com/questions/29126836/…
5
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.

5 Comments

Thanks a lot Alex for your reply. As specified after I cannot work with ByVal, I have to work with pointers...! Any ideas?
Inputs parameter is passed by reference, not by value - the post is edited.
@SlashVincent: You can't work with pointers in C# and values in C++- they have to be value on both ends.
@DeadMG Or pointers on both ends. Can be either values or references, so long as both sides match.
I may have expressed my intentions incorrectly, but I meant to use pointers on both sides.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.