Read Write C++ Dynamic Arrays in C# (InteropServices) - c#

I have a C++ class which has a header(matrixheader.h) such that :
#pragma once
class M
{
public:
M(int m,int n);
void MSet(int m,int n,double d);
double MGet(int m,int n);
~M();
private:
double** mat;
};
Class is defined as follows in (matrixbody.cpp):It is built in Win32 Platform.
#pragma once
#include "matrixhead.h"
M::M(int m,int n)
{
mat = new double*[m];
for (int i = 0; i < m; i++)
{
mat[i] = new double[n];
}
}
void M::MSet(int m,int n,double d)
{
mat[m][n] = d;
}
double M::MGet(int m,int n)
{
double d = mat[m][n];
return d;
}
M::~M()
{
delete[] mat;
}
I have made a wrapper for the Class like so(matrixwrapper.cpp):The wrapper is also built in Win32 platform.
#include "matrixhead.h"
#include "matrixbody.cpp"
extern "C" __declspec(dllexport) void* Make(int m,int n)
{
M o(m,n);
return &o;
}
extern "C" __declspec(dllexport) void setData(void* mp,int m,int n,double d)
{
M* ap = (M*)mp;
M a = *ap;
a.MSet(m,n,d);
}
extern "C" __declspec(dllexport) double getData(void* mp,int m,int n)
{
M* bp = (M*)mp;
M b = *bp;
double d = b.MGet(m,n);
return d;
}
I import the class to C# and try to call the C++ dl methods from C#:
using System;
using System.Runtime.InteropServices;
namespace wrappertest
{
class Program
{
[DllImport("matrixwrapper.dll")]
unsafe public static extern void* Make(int m,int n);
[DllImport("matrixwrapper.dll")]
unsafe public static extern void setData(void* mp,int m, int n,double d);
[DllImport("matrixwrapper.dll")]
unsafe public static extern double getData(void* mp,int m, int n);
static unsafe void Main(string[] args)
{
void* p = Make(10, 10);
setData(p,10,1,10);
Console.WriteLine(getData(p,10,1));
}
}
}
But when i try to run the C++ dll methods from C# i get the following error
1//Attempted to read or write protected memory.This is often an indication that other memory is corrupt when running C# code in x64.
2//An attempt was made to load a program with incorrect format when runnning in x86 Active/x86 or in AnyCPU platform.
Questions:
1//What is wrong in the above code ?
2//Considering that my final objective is to make a 2d dynamic array in C++ and read/write data in the array such as the one double**mat in the matrixheader.h file above from C#?is there any other way to implement it ?

Let's get the easy thing first:
An attempt was made to load a program with incorrect format when runnning in x86 Active/x86 or in AnyCPU platform.
This simply means you have a platform mismatch. You're either trying to load an x86 C++ dll in an x64 .NET runtime, or the reverse.
The following error is the real problem:
Attempted to read or write protected memory.This is often an indication that other memory is corrupt when running C# code in x64.
That's to be expected, because your Make function creates an object on the stack, then returns a pointer to it. By the time you read back this object, the contents on the stack has changed (the stack is being reused), and the mat pointer points somewhere else, most probably into unallocated memory.
Please see this answer where I go into deeper detail about this issue (it's C# but it's the same issue).
You have to allocate some dynamic memory to solve your problem. You may try:
extern "C" __declspec(dllexport) void* Make(int m,int n)
{
M* o = new M(m,n);
return o;
}
And of course, you'll have to create one more method to perform the matching delete if you don't want to leak memory.
Also, like Mgetz points out in the comments, you have a memory leak in the M class itself. The delete[] mat; call in the destructor won't free every allocated chunk of memory. You're calling new in the constructor m + 1 times, this means you have to call delete[] m + 1 times also in the destructor, once for each new. You probably should keep m and n as fields in your class (at least m is mandatory to know how many calls to delete[] you have to do).
A much better solution would be to use a single array instead of jagged arrays. You calculate the index i, j in that array as i * m + j. You may also use a std::vector or just do it in C# altogether:
public class M
{
private double[] _items;
private int _m;
private int _n;
public M(int m, int n)
{
_items = new double[m * n];
_m = m;
_n = n;
}
public this[int i, int j]
{
// Here, you should perform a bounds check on i and j against _m and _n
get { return _items[i * _m + j]; }
set { _items[i * _m + j] = value; }
}
}

Related

Returning a struct from PInvoke

Im trying to return a struct from a call to a c++ dll from c#, and I get some complicated and bad behaviour that I dont understand. If my struct contains a constructor I get a memory access violation when returning it, if it is less than 12 bytes. If it is larger there is no problem. If I remove the constructor it works for all sizes. I suppose this could have something to do with my calls being c-style but I cannot find information about this. So it would be much appreciated if someone could explain or point my in a good direction of what is going on. Below are examples of code that does and does not work:
Code that works
C++ side header:
#define DLL_API __declspec(dllexport)
struct Struct4Byte
{
int x1;
};
struct Struct12Byte
{
int x1;
int x2;
int x3;
Struct12Byte() { x1 = 0; x2 = 1; x3 = 2; }
};
#ifdef __cplusplus
extern "C" {
#endif
DLL_API Struct4Byte Function4Byte(int x);
DLL_API Struct12Byte Function12Byte(int x);
#ifdef __cplusplus
}
#endif
C++ file:
Struct4Byte Function4Byte(int x)
{
Struct4Byte output;
output.x1 = 1 + x;
return output;
}
Struct12Byte Function12Byte(int x)
{
Struct12Byte output;
output.x1 = 1 + x;
output.x2 = 2 + x;
output.x3 = 3 + x;
return output;
}
On the calling side (C#) I do:
[StructLayout(LayoutKind.Sequential)]
internal struct Struct4Byte
{
public int x1;
}
[StructLayout(LayoutKind.Sequential)]
internal struct Struct12Byte
{
public int x1;
public int x2;
public int x3;
}
class Program
{
static void Main(string[] args)
{
Struct4Byte result1 = Function4Byte(3);
Struct12Byte result2 = Function12Byte(3);
}
[DllImport(#"PInvokeCheck.dll")]
internal static extern Struct4Byte Function4Byte(int x);
[DllImport(#"PInvokeCheck.dll")]
internal static extern Struct12Byte Function12Byte(int x);
}
Code that does not work
If I now change in the header file the definition of the Struct4Byte to:
struct Struct4Byte
{
int x1;
Struct4Byte(){ x1 = 0; }
};
then I get Memory Access violation.
I've noted something that might be of interest. The problem is already when calling Function4Byte. Putting a brakpoint and viewing x in the function (I put the function again below) shows that x get some random value.
Struct4Byte Function4Byte(int x)
{
Struct4Byte output;
output.x1 = 1 + x;
return output;
}
I might have found the issue. Their was a warning that I had missed that: warning C4190: 'Function4Byte' has C-linkage specified, but returns UDT 'Struct4Byte' which is incompatible with C. From this discussion it is clear that it can lead to some undefined behaviour and that the layout of the struct may change: Error in C++ code linkage: warning C4190: type has C-linkage specified, but returns UDT which is incompatible with C
So that seems explain the problem I've had.
Yea returning structs like that isn't supported and the none of Microsoft's mainline P/Invoke articles represent an example of returning structs like that. None that I read anyways.
Here's a git hub example of your project doing it the correct way.
https://github.com/ditchcode/PInvokeTesting
I found this article most helpful on the marshaling of Structs and Classes
https://learn.microsoft.com/en-us/dotnet/framework/interop/passing-structures
I think the reason is explained in the calling conventions.
x64 calling convention
User-defined types can be returned by value from global functions and static member functions. To return a user-defined type by value in RAX, it must have a length of 1, 2, 4, 8, 16, 32, or 64 bits. It must also have no user-defined constructor, destructor, or copy assignment operator. It can have no private or protected non-static data members, and no non-static data members of reference type. It can't have base classes or virtual functions. And, it can only have data members that also meet these requirements. (This definition is essentially the same as a C++03 POD type. Because the definition has changed in the C++11 standard, we don't recommend using std::is_pod for this test.) Otherwise, the caller must allocate memory for the return value and pass a pointer to it as the first argument. The remaining arguments are then shifted one argument to the right. The same pointer must be returned by the callee in RAX.
x86 calling convention
On x86 platforms, all arguments are widened to 32 bits when they are passed. Return values are also widened to 32 bits and returned in the EAX register, except for 8-byte structures, which are returned in the EDX:EAX register pair. Larger structures are returned in the EAX register as pointers to hidden return structures. Parameters are pushed onto the stack from right to left. Structures that are not PODs will not be returned in registers.
Doing what is says for x64 is a good alternative solution.
the caller must allocate memory for the return value and pass a pointer to it as the first argument
So your code would look like this
Struct4Byte Function4Byte(Struct4Byte& output, int x)
{
output.x1 = 1 + x;
}
Struct12Byte Function12Byte(Struct12Byte& output, int x)
{
output.x1 = 1 + x;
output.x2 = 2 + x;
output.x3 = 3 + x;
}
class Program
{
static void Main(string[] args)
{
Struct4Byte = Function4Byte(out var result1, 3);
Function12Byte(out var result2, 3);
}
[DllImport(#"PInvokeCheck.dll")]
internal static extern void Function4Byte(out Struct4Byte result, int x);
[DllImport(#"PInvokeCheck.dll")]
internal static extern void Function12Byte(out Struct12Byte result, intx);
}

How to pass array as parameter to function call from C++ to C# [duplicate]

I am seeing a pretty bizarre issue while trying to pass an array from C++ to C#. I am using Marshal.Copy (specifically: https://msdn.microsoft.com/en-us/library/a53bd6cz(v=vs.110).aspx).
Problem: float array from C++ to C# is yielding a few NaN's in the resulting array.
(Note: I am working in the context of the Unity game engine)
Code
Example C++ code:
extern "C" bool UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API getSomeFloats(float** points, int* count) {
std::vector<float> results;
std::vector<SOME_TYPE> key_points = <SOME_POINTS>
for (auto iter = key_points.begin(); iter < key_points.end(); iter++) {
results.push_back(static_cast<float>(iter->pt.x));
results.push_back(static_cast<float>(iter->pt.y));
}
*points = results.data();
*count = results.size();
//<Print results to csv here>
return true;
}
Example C# code:
[DllImport("NativePlugin")]
private static extern bool getSomeFloats (ref IntPtr ptrResultItems, ref int resultItemsLength);
private static float[] getFloatArrayFromNative() {
IntPtr ptrResultItems = IntPtr.Zero;
int resultItemsLength = 0;
bool success = getSomeFloats (ref ptrResultItems, ref resultItemsLength);
float[] resultItems = null;
if (success) {
// Load the results into a managed array.
resultItems = new float[resultItemsLength];
Marshal.Copy (ptrResultItems
, resultItems
, 0
, resultItemsLength);
// <PRINT out resultItems to csv here>
return resultItems;
} else {
Debug.Log ("Something went wrong getting some floats");
return new float[] { -1, -2 };
}
}
Example Ouput:
Take the following example:
C++ output (print_out.csv):
123, 456, 789
C# output (print_out_cs.csv):
123, NaN, 789
I'm completely stumped on this one. I just don't understand why only some (roughly 7/100) floats are returning NaN. Does anyone have any advice/insight that might help?
Thanks!
Found few problems in your code:
1. std::vector<float> results; is declared on the stack. It will be gone by the time the function has returned. Declare it as a pointer
std::vector<float> *results = new std::vector<float>(10);
but make sure to also declare a function that will free it on the C++ side.
2.The function parameter do not match.
Your C++:
getSomeFloats(float** points, int* count, CameraPose* pose)
Your C#:
getSomeFloats (ref IntPtr ptrResultItems, ref int resultItemsLength);
You either have to remove CameraPose* pose from the C++ side or add IntPtr pose to the C# side.
3. The use of UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API.
You don't need that. This is used when you want to use Unity's built in functions such as GL.IssuePluginEvent. You are not using it in this case.
This should do it:
#define DLLExport __declspec(dllexport)
extern "C"
{
DLLExport void fillArrayNative(float* data, int count, int* outValue)
{
}
}
4.C# has a garbage collector that moves variables around in the memory. You must pin C# array if you want to modify it from C++ side. You only need to pin C# array. Another option is to allocate the array on C++ side, return it to C# copy it to a temporary variable on the C# side then delete it on the C++ side.
5.Copy the result back to the array instead of assigning it.
Recommended method:
There are just many ways to do this and some of them are extremely slow. If you want to use Marshal.Copy, you have to allocate the array on the C++ side or else you will run into some undefined behavior.
The fastest and the most efficient way to do this is to allocate the array on the C# side as a global variable. Pass the array and its length to the native side. Also pass a third parameter which C++ can use to tell C# the amount of index that has been updated or written to.
This is much more better than creating new array, copying it to C# variable then destroying it each time the function is called.
This is what you should be using:
C++:
#define DLLExport __declspec(dllexport)
extern "C"
{
DLLExport void fillArrayNative(float* data, int count, int* outValue)
{
std::vector<float> results;
for (int i = 0; i < count; i++)
{
//Fill the array
data[i] = results[i];
}
*outValue = results.size();
}
}
You can also use: std::copy ( data, data+count, results.begin() ); instead of loop to copy the data too.
C#:
[DllImport("NativePlugin", CallingConvention = CallingConvention.Cdecl)]
private static extern void fillArrayNative(IntPtr data, int count, out int outValue);
public unsafe void getFillArrayNative(float[] outArray, int count, out int outValue)
{
//Pin Memory
fixed (float* p = outArray)
{
fillArrayNative((IntPtr)p, count, out outValue);
}
}
Usage:
const int arraySize = 44500;
float[] arrayToFill = new float[arraySize];
void Start()
{
int length = arrayToFill.Length;
int filledAmount = 0;
getFillArrayNative(arrayToFill, length, out filledAmount);
//You can then loop through it with with the returned filledAmount
for (int i = 0; i < filledAmount; i++)
{
//Do something with arrayToFill[i]
}
}
This is just an example and it is faster than all other methods I've used before. Avoid doing it the way you are currently doing it with Marshal.Copy. If you still want to do it your way or use Marshal.Copy then here is the appropriate way to do it which requires allocation, copying data and de-allocating memory in each call.
The pointer you return in getSomeFloats is owned by results. Before getSomeFloats returns, the vector destructor for results will free that memory. When the C# code tries to use the pointer, you are accessing unallocated memory, which results in Undefined Behavior. In your case most of the data hasn't been changed yet, but some of it has. Potentially any or all of the data could have been changed (if the memory has been reused), or even a program crash (if the freed memory has been returned to the OS).

Update float array from C++ native plugin

I am seeing a pretty bizarre issue while trying to pass an array from C++ to C#. I am using Marshal.Copy (specifically: https://msdn.microsoft.com/en-us/library/a53bd6cz(v=vs.110).aspx).
Problem: float array from C++ to C# is yielding a few NaN's in the resulting array.
(Note: I am working in the context of the Unity game engine)
Code
Example C++ code:
extern "C" bool UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API getSomeFloats(float** points, int* count) {
std::vector<float> results;
std::vector<SOME_TYPE> key_points = <SOME_POINTS>
for (auto iter = key_points.begin(); iter < key_points.end(); iter++) {
results.push_back(static_cast<float>(iter->pt.x));
results.push_back(static_cast<float>(iter->pt.y));
}
*points = results.data();
*count = results.size();
//<Print results to csv here>
return true;
}
Example C# code:
[DllImport("NativePlugin")]
private static extern bool getSomeFloats (ref IntPtr ptrResultItems, ref int resultItemsLength);
private static float[] getFloatArrayFromNative() {
IntPtr ptrResultItems = IntPtr.Zero;
int resultItemsLength = 0;
bool success = getSomeFloats (ref ptrResultItems, ref resultItemsLength);
float[] resultItems = null;
if (success) {
// Load the results into a managed array.
resultItems = new float[resultItemsLength];
Marshal.Copy (ptrResultItems
, resultItems
, 0
, resultItemsLength);
// <PRINT out resultItems to csv here>
return resultItems;
} else {
Debug.Log ("Something went wrong getting some floats");
return new float[] { -1, -2 };
}
}
Example Ouput:
Take the following example:
C++ output (print_out.csv):
123, 456, 789
C# output (print_out_cs.csv):
123, NaN, 789
I'm completely stumped on this one. I just don't understand why only some (roughly 7/100) floats are returning NaN. Does anyone have any advice/insight that might help?
Thanks!
Found few problems in your code:
1. std::vector<float> results; is declared on the stack. It will be gone by the time the function has returned. Declare it as a pointer
std::vector<float> *results = new std::vector<float>(10);
but make sure to also declare a function that will free it on the C++ side.
2.The function parameter do not match.
Your C++:
getSomeFloats(float** points, int* count, CameraPose* pose)
Your C#:
getSomeFloats (ref IntPtr ptrResultItems, ref int resultItemsLength);
You either have to remove CameraPose* pose from the C++ side or add IntPtr pose to the C# side.
3. The use of UNITY_INTERFACE_EXPORT UNITY_INTERFACE_API.
You don't need that. This is used when you want to use Unity's built in functions such as GL.IssuePluginEvent. You are not using it in this case.
This should do it:
#define DLLExport __declspec(dllexport)
extern "C"
{
DLLExport void fillArrayNative(float* data, int count, int* outValue)
{
}
}
4.C# has a garbage collector that moves variables around in the memory. You must pin C# array if you want to modify it from C++ side. You only need to pin C# array. Another option is to allocate the array on C++ side, return it to C# copy it to a temporary variable on the C# side then delete it on the C++ side.
5.Copy the result back to the array instead of assigning it.
Recommended method:
There are just many ways to do this and some of them are extremely slow. If you want to use Marshal.Copy, you have to allocate the array on the C++ side or else you will run into some undefined behavior.
The fastest and the most efficient way to do this is to allocate the array on the C# side as a global variable. Pass the array and its length to the native side. Also pass a third parameter which C++ can use to tell C# the amount of index that has been updated or written to.
This is much more better than creating new array, copying it to C# variable then destroying it each time the function is called.
This is what you should be using:
C++:
#define DLLExport __declspec(dllexport)
extern "C"
{
DLLExport void fillArrayNative(float* data, int count, int* outValue)
{
std::vector<float> results;
for (int i = 0; i < count; i++)
{
//Fill the array
data[i] = results[i];
}
*outValue = results.size();
}
}
You can also use: std::copy ( data, data+count, results.begin() ); instead of loop to copy the data too.
C#:
[DllImport("NativePlugin", CallingConvention = CallingConvention.Cdecl)]
private static extern void fillArrayNative(IntPtr data, int count, out int outValue);
public unsafe void getFillArrayNative(float[] outArray, int count, out int outValue)
{
//Pin Memory
fixed (float* p = outArray)
{
fillArrayNative((IntPtr)p, count, out outValue);
}
}
Usage:
const int arraySize = 44500;
float[] arrayToFill = new float[arraySize];
void Start()
{
int length = arrayToFill.Length;
int filledAmount = 0;
getFillArrayNative(arrayToFill, length, out filledAmount);
//You can then loop through it with with the returned filledAmount
for (int i = 0; i < filledAmount; i++)
{
//Do something with arrayToFill[i]
}
}
This is just an example and it is faster than all other methods I've used before. Avoid doing it the way you are currently doing it with Marshal.Copy. If you still want to do it your way or use Marshal.Copy then here is the appropriate way to do it which requires allocation, copying data and de-allocating memory in each call.
The pointer you return in getSomeFloats is owned by results. Before getSomeFloats returns, the vector destructor for results will free that memory. When the C# code tries to use the pointer, you are accessing unallocated memory, which results in Undefined Behavior. In your case most of the data hasn't been changed yet, but some of it has. Potentially any or all of the data could have been changed (if the memory has been reused), or even a program crash (if the freed memory has been returned to the OS).

interfacing with D Properly return an Array of Struct

this question is referring to the 'new' D :
DMD32 D Compiler v2.068.2
for TL;DR if you don't need details skip to the question below
working with visual studio(i am using v2010), by creating a new project -> D -> Dynamic Library
when the project creartion process is complete, in the solution explorer
there's 2 files:
dllmain.d
dll.def
leaving the .def file as it is, i have managed to understand that
by adding some new functions to the dllmain.d and prefexing with :
extern (Windows) export
will export the the function and it will be callable from c#, didn't try it with C or C++.
side note, do not touch any of the existing code unless you know what you're doing.
so the code below works as expected
extern (Windows) export uint D_mathPower(uint p)
{
return p * p;
}
calling it from C# with the following signature:
[DllImport(#"pathTo...\DynamicLib1.dll", CallingConvention = CallingConvention.StdCall), SuppressUnmanagedCodeSecurity]
public static extern uint D_mathPower(uint p);
I could easy use it as follows:
uint powD = D_mathPower(5);
my question is
how do i return an array of structs (preferably the most cost-efficient way) ?
struct dpack{ char* Name; uint Id; }
i have tried using both char[] and char* but with no success.
this is my code so far
extern (Windows) export
dpack[] D_getPacks(uint size)
{
dpack[] rtDpArr = new dpack[size];
char[] str = "someText".dup;
for(uint i=0; i<size; i++)
{
str[$ - 1] = cast(char)('0' + i % (126 - '0'));
rtDpArr[i].Id = i;
rtDpArr[i].Name= str.dup;
}
return rtDpArr;
}
void getPacksPtr(uint size, dpack** DpArr)
{
// this is the signature i have successfully implemented via c++
}
Because a D array has a special layout you should rather return a pointer to the first item. Then in C# you can cast each item from the base pointer by reading 8 bytes per 8 bytes (this matches dpack.sizeof), since you already know the count:
struct dpack{ immutable(char)* Name; uint Id; }
extern (Windows) export
void* D_getPacks(uint count)
{
dpack[] rtDpArr = new dpack[count];
char[] str = "someText".dup;
import std.string;
for(uint i=0; i<count; i++)
{
rtDpArr[i].Id = i;
// add a trailing '\0'
rtDpArr[i].Name = toStringz(str);
}
// pointer to the first item
return rtDpArr.ptr;
}
Also to cast the .Name member it's necessary to add a terminator, otherwise you cant know the length of the string. This is done by std.string.toStringz which will add a null character at the end of the string. The char* Name member can then be cast as are usually strings provided by a function in a dll with a C interface.
this is the most efficient way i could implement it .
extern (Windows) export
void D_getPacksPtr(uint size, dpack** DpArr)
{
*DpArr = cast(dpack*) malloc(size * dpack.sizeof);
dpack* curP = *DpArr;
char[] str = "abcdefghij".dup;
uint i=0;
while(i!=size){
str[$ - 1] = cast(char)('0' + i % (126 - '0'));
curP.Name = cast(char*)str.dup; curP.Id = i;
++i;++curP;
}
}
[DllImport(#"PathTo...\DynamicLib1.dll", CallingConvention = CallingConvention.StdCall), SuppressUnmanagedCodeSecurity]
public static extern void D_getPacksPtr(uint size, dpack** DPArr);
using it :
dpack* outDpack;
D_getPacksPtr(500000, &outDpack);

Marshalling Complex structures between C#/C++

I'm trying to populate an array of structures from C++ and pass the result back to C#.
I thought maybe creating a struct with an array of structures maybe the way forward as most examples I have come across use structures(but passing basic types). I have tried the following but no luck so far.
Found an example at: http://limbioliong.wordpress.com/2011/08/20/passing-a-pointer-to-a-structure-from-c-to-c-part-2/?relatedposts_exclude=542
I have the following in C#
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace CSharpDLLCall
{
class Program
{
[StructLayout(LayoutKind.Sequential,Pack=8)]
public struct Struct1
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public double[] d1;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public double[] d2;
}
[StructLayout(LayoutKind.Sequential)]
public struct TestStructOuter
{
public int length;
public IntPtr embedded;
}
static void Main(string[] args)
{
Program program = new Program();
program.demoArrayOfStructs();
}
public void demoArrayOfStructs()
{
TestStructOuter outer = new TestStructOuter();
testStructOuter.embedded = new Struct1[10];
outer.length = 10;
outer.embedded = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Struct1)) * 10);
Marshal.StructureToPtr(outer, outer.embedded, false);
testAPI2(ref outer);
outer = (TestStructOuter)(Marshal.PtrToStructure(outer.embedded, typeof(TestStructOuter)));
Marshal.FreeHGlobal(outer.embedded);
}
[DllImport(#"C:\CPP_Projects\DLL\DLLSample\Release\DLLSample.dll")]
static extern void testAPI2(IntPtr pTestStructOuter);
}
}
In C++ in the header i have
#ifdef DLLSAMPLE_EXPORTS
#define DLLSAMPLE_API __declspec(dllexport)
#else
#define DLLSAMPLE_API __declspec(dllimport)
#endif
#include <iostream>
using namespace std;
#pragma pack(1)
struct struct1
{
double d1[];
double d2[];
};
struct TestStructOuter
{
struct1* embedded;
};
extern "C"
{
DLLSAMPLE_API void __stdcall testAPI2(TestStructOuter* pTestStructOuter);
}
In the cpp I have:
#include "stdafx.h"
#include "DLLSample.h"
__declspec(dllexport) void __stdcall testAPI2(TestStructOuter* pTestStructOuter)
{
// not sure that this is necessary
// for (int i = 0; i < 10 ; ++i)
// {
// pTestStructOuter->embedded = new struct1;
// }
for (int i = 0; i < 10 ; ++i)
{
struct1 s1;
for (int idx = 0; idx < 10; ++idx)
{
s1.d1[i] = i+0.5;
s1.d2[i] = i+0.5;
}
pTestStructOuter->embedded[0] = s1;
}
}
This doesn't seem to work the error i get:
The parameter is incorrect.(Exception from HRESULT:0x80070057 (E_INVALIDARG))
Which probably means that its not recognizing the array of structures. Any ideas how I can do this? Thanks.
Okay, I have a working sample. I'm posting this as another answer because it's a very different approach.
So, on the C++ side, I've got this header file:
struct Struct1
{
int d1[10];
int d2[10];
};
extern "C" __declspec(dllexport) void __stdcall
TestApi2(int* pLength, Struct1 **pStructures);
And the following code:
__declspec(dllexport) void __stdcall
TestApi2(int* pLength, Struct1 **pStructures)
{
int len = 10;
*pLength = len;
*pStructures = (Struct1*)LocalAlloc(0, len * sizeof(Struct1));
Struct1 *pCur = *pStructures;
for (int i = 0; i < len; i++)
{
for (int idx = 0; idx < 10; ++idx)
{
pCur->d1[idx] = i + idx;
pCur->d2[idx] = i + idx;
}
pCur ++;
}
}
Now, the important bit is LocalAlloc. That's actually the place where I've had all the issues, since I allocated the memory wrong. LocalAlloc is the method .NET calls when it does Marshal.AllocHGlobal, which is very handy, since that means we can use the memory in the caller and dispose of it as needed.
Now, this method allows you to return an arbitrary length array of structures. The same approach can be used to eg. return a structure of an array of structures, you're just going deeper. The key is the LocalAlloc - that's memory you can easily access using the Marshal class, and it's memory that isn't thrown away.
The reason you have to also return the length of the array is because there's no way to know how much data you're "returning" otherwise. This is a common "problem" in unmanaged code, and if you've ever done any P/Invoking, you know everything about this.
And now, the C# side. I've tried hard to do this in a nice way, but the problem is that arrays of structures simply aren't blittable, which means you can't simply use MarshalAs(UnmanagedType.LPArray, ...). So, we have to go the IntPtr way.
The definitions look like this:
[StructLayout(LayoutKind.Sequential)]
public class Struct1
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public int[] d1;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public int[] d2;
}
[DllImport(#"Win32Project.dll", CallingConvention = CallingConvention.StdCall)]
static extern void TestApi2(out int length, out IntPtr structs);
Basically, we get a pointer to the length of the "array", and the pointer to the pointer to the first element of the array. That's all we need to read the data.
The code follows:
int length;
IntPtr pStructs;
TestApi2(out length, out pStructs);
// Prepare the C#-side array to copy the data to
Struct1[] structs = new Struct1[length];
IntPtr current = pStructs;
for (int i = 0; i < length; i++)
{
// Create a new struct and copy the unmanaged one to it
structs[i] = new Struct1();
Marshal.PtrToStructure(current, structs[i]);
// Clean-up
Marshal.DestroyStructure(current, typeof(Struct1));
// And move to the next structure in the array
current = (IntPtr)((long)current + Marshal.SizeOf(structs[i]));
}
// And finally, dispose of the whole block of unmanaged memory.
Marshal.FreeHGlobal(pStructs);
The only thing that changes if you want to really return a structure of an array of structures is that the method parameters move into the "wrapping" structure. The handy thing is that .NET can automatically handle the marshalling of the wrapper, the less-handy thing is that it can't handle the inner array, so you again have to use length + IntPtr to manage this manually.
First, don't use unknown-length arrays at all. You're killing any possible use of arrays between managed and unmanaged code - you can't tell the required size (Marshal.SizeOf is useless), you have to manually pass the array length etc. If it's not a fixed length array, use IntPtr:
[StructLayout(LayoutKind.Sequential,Pack=8)]
public struct Struct1
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public double[] d1;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
public double[] d2;
}
[StructLayout(LayoutKind.Sequential)]
public struct TestStructOuter
{
public int length;
public IntPtr embedded;
}
(if your array of embedded is fixed length, feel free to ignore this, but do include the ByValArray, SizeConst bit. You also have to add ArraySubType=System.Runtime.InteropServices.UnmanagedType.Struct to the attribute).
As you've probably noticed, the TestStructOuter is pretty much useless in this case, it only adds complexity (and do note that marshalling is one of the most expensive operations compared to native languages, so if you're calling this often, it's probably a good idea to keep things simple).
Now, you're only allocating memory for the outer struct. Even in your code, Marshal.SizeOf has no idea how big the struct should be; with IntPtr, this is even more so (or, to be more precise, you're only requesting a single IntPtr, ie. 4-8 bytes or so + the length). Most often, you'd want to pass the IntPtr directly, rather than doing this kind of wrapping (using the same thing in C++/CLI is a different thing entirely, although for your case this is unnecessary).
The signature for your method will be something like this:
[DllImport(#"C:\CPP_Projects\DLL\DLLSample\Release\DLLSample.dll",
CallingConvention=System.Runtime.InteropServices.CallingConvention.StdCall)]
public static extern void testAPI2(ref TestStructOuter pTestStructOuter) ;
Now, if you're going with the IntPtr approach, you want to keep allocating memory manually, only you have to do it properly, eg.:
TestStructOuter outer = new TestStructOuter();
testStructOuter.length = 1;
testStructOuter.embedded = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(Struct1)));
Marshal.StructureToPtr(embedded, testStructOuter.embedded, false);
And finally, you call the method:
// The marshalling is done automatically
testAPI2(ref outer);
Don't forget to release the memory, ideally in the finally clause of a try around everything since the memory allocation:
Marshal.FreeHGlobal(outer.embedded);
Now, this is overly complicated and not exactly optimal. Leaving out the TestStructOuter is one possibility, then you can simply pass the length and pointer to the embedded array directly, avoiding one unnecessary marshalling. Another option would be to use a fixed size array (if it needs to be an array at all :)) in TestStructOuter, which will cure a lot of your headaches and eliminate any need for manual marshalling. In other words, if TestStructOuter is defined as I've noted before:
[StructLayout(LayoutKind.Sequential)]
public struct TestStructOuter
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 10,
ArraySubType=UnmanagedType.Struct)]
public Struct1[] embedded;
}
Suddenly, your whole call and everything becomes as simple as
testAPI2(ref outer);
The whole marshalling is done automatically, no need for manual allocations or conversions.
Hope this helps :)
Hint: Leadn C++/CLI. I had to deal with complex interop two times in my life - once with ISDN (AVM devkits make it a lot easier - sadly C++, I needed C#) and now with Nanex (great real time complex and full market ata feeds, sadl complex C++, I need C#)
Both cases I make my own wrapper in C++/CLI, talking down to C++ and exposing a real object model in C#. Allows me to make things a lot nicer and handle a lot of edge cases that our friendly Marshaller can not handle efficiently.

Categories