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.
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);
}
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.
精彩评论