Marshalling C structures to C# - c#

Suppose I have a structure:
typedef struct {
float x;
float y;
float z;
int ID;
} Vertex;
and a C++ function:
float first(Vertex* ptr, int length){ //really silly function, just an example
Vertex u,v;
u.x = ptr[0].x; //...and so on, copy x,y,z,ID
v.x = ptr[1].x;
return (u.x * v.x + u.y * v.y + u.z * v.z);
}
Vertex* another(float a, int desired_size){
Vertex v = (Vertex*)malloc(desired_size*sizeof(Vertex));
v[0].x = a;
v[1].x = -a; //..and so on.. make some Vertices.
return v;
}
First - my IDE. I'm using Visual Studio 2010, building a C# (4.0) application; The C++ part is also built in VS2010.
I know how to build a DLL of C/C++ code and use it in C# application, but until today I used only primitive arguments and return values like:
[DllImport("library.dll", CharSet = CharSet.Ansi, SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern int simple(int a, int b);
Today I need to pass an array of structs (as in the example above).. and perhaps also receive one back..
How to I "translate" a C# class to a C struct (and vice versa) ??

The struct can be declared like this:
[StructLayout(LayoutKind.Sequential)]
public struct Vertex {
public float x;
public float y;
public float z;
public int ID;
}
Next you need to settle on a calling convention. Your C++ code is almost certainly compiled with cdecl. Let's stick with that.
The function first is easy to call from C#:
[DllImport("library.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern float first(Vertex[] vertices);
Note that you should not use SetLastError here–that's for Windows API functions. And there's no need to set the CharSet since there is no text here.
Now, for another things get more complex. If you can allocate the memory in the C# code then that is definitely the way to go.
void PopulateVertices(Vertex *vertices, int count)
{
for (int i=0; i<count; i++)
{
vertices[i].x = ....
}
}
On the C# side you declare it like this:
[DllImport("library.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void PopulateVertices(Vertex[] vertices, int count);
and call it like this
Vertex[] vertices = new Vertex[2];
PopulateVertices(vertices, vertices.Length);
If you don't want to allocate on the C# side of the fence then do it like this:
Return a pointer from the C++ code and allocate it with CoTaskMemAlloc.
In C# declare the return value of the imported function as IntPtr.
Use Marshal.PtrToStructure and some pointer arithmetic to marshal the return array into a C# array.
Call Marshal.FreeCoTaskMem to free the memory allocated in the native module.
But if you want my advice, try and stick to allocating the array in the managed code.

It should be this easy:
[StructLayout(LayoutKind.Sequential)]
public struct Vertex {
float x;
float y;
float z;
int ID;
}
[DllImport("library.dll", CallingConvention=CallingConvention.StdCall)]
public static extern float first(Vertex[] verticies, int arrLen);
The issues you may run into would be if there is any packing done on the C version of the struct, and possibly the struct layout. If the layout doesn't match, you may want to change it to LayoutKind.Explicit and use the [FieldOffset(0)] attribute on each field. C would also have no idea, the length of the verticies array passed, so if that changes, you'd want to pass that along to the method.
To get an array back:
[DllImport("library.dll", CallingConvention=CallingConvention.StdCall)]
public static extern Vertex[] another(float a);
The marshaler handles all of the memory issues when passing arguments in, but returning the array it can't do anything. Since the memory is allocated on the unmanaged heap, the GC has no idea about it, and you will wind up with a memory leak. The marshaller will simply copy the native structs to the managed struct array, but it can't free the memory you've allocated with malloc.
The easiest way to get around it, if you can change the C++ code, would be to change the signature of another to take in an array of verticies (and the length of the array) instead of returning one. I don't need to write out any code for you that does this, #DavidHeffernan has already done this in his answer, the part of the break.

Related

Pass multiple object arrays to C++ function from C#

I am trying to execute a c++ function (32 bit) from c# code (console application).
C++ function signature:
EngError GUIFN eUpdateRecord(
Order::X * odrX, //array of objects
Order::Y * odrY, //array of objects
Order::P * odrP, //array of objects
Order::Q * odrQ, //single objects
ulong A,
ulong B,
int & C)
where Order is the C++ namespace containing the below classes:
class X
{
public:
pb::Uint XID; //Uint is unsigned short
pb::String No; //String is char *
pb::String Msg;
};
class Y
{
public:
pb::Uint YID;
pb::String Code;
pb::Uint OrderId;
pb::String Msg;
pb::Char Status;
pb::Long Capacity;
};
Classes P & Q are very similar to above classes with just change in property names (excluding them for now).
I am invoking the C++ function from C# as below
C# Method
[DllImport("x3iface.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public extern unsafe EngErr eUpdateRecord([In,Out] X[] odrX, [In, Out] Y[] odrY, [In, Out] P[] odrP, Q* odrQ, uint A, uint B, ref int C);
Sample C# Class:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
public unsafe struct Y
{
public UInt16 YID;
public char* Code;
public UInt16 OrderId;
public char* Msg;
public byte Status;
public Int32 Capacity;
}
All arrays passed to the C++ function have pre-assigned values and the C++ function will update values of each array object based on some internal logic.
When i run the code and step into the C++ function, the values are mapped correctly for arrays X and Y, but i am getting the error 'Unable to read memory' for the array P and object Q. All the other values A,B and C are mapped correctly.
If i swap the position of inputs X and Y with P and Q in C++( the parameter positions are changed accordingly in C# method too), then P and Q starts working fine, but then i get the error 'Unable to read memory' for the arrays X and Y.
public extern unsafe EngErr eUpdateRecord([In,Out] P[] odrX, Q* odrQ,[In, Out] X[] odrX, [In, Out] Y[] odrY, uint A, uint B, ref int C);
Do we have any limitation in the number of [In,Out] object arrays we can marshal and pass to a C++ function? How can i best trouble shoot this error?
I do not have a provision to alter the functionality in C++ code as it is being reused in other applications. Kindly suggest possible solutions.

Marshalling an array of reference type objects from C# to C++

I was able to pass a reference type object successfully from C# to C++ by decorating it with StructLayout(LayoutKind.Sequential)
The class in C#:
StructLayout(LayoutKind.Sequential)
public class Point
{
[DataMember]
public double X { get; set; }
[DataMember]
public double Y { get; set; }
}
The equivalent in C++:
struct Point
{
public:
double x;
double y;
};
The function body in C++:
extern "C" __declspec(dllexport) void Translate(Point* pt, double dx, double dy)
{
pt->x += dx;
pt->y += dy;
}
and the equivalent in C#:
[DllImport("myDll.dll", CallingConvention = CallingConvention.Cdecl)]
public extern static void Translate(Point pt, double dx, double dy);
I had no problems till now, the problem happens when I pass an array of these points between the next 2 functions:
extern "C" __declspec(dllexport) double GetArea(Point** vertices,int count, bool isClosed)
{
double area = 0;
if (!isClosed || count < 3) return 0;
int j;
for (int i = 0; i < count; i++)
{
// j = i+1 except when i = count then j = 0
j = (i + 1) % count;
area += vertices[i]->x * vertices[j]->y;
area -= vertices[i]->y * vertices[j]->x;
}
area /= 2;
return fabs(area);
}
and the C# version:
[DllImport("myDll.dll", CallingConvention = CallingConvention.Cdecl)]
public extern static double GetArea(Point[] vertices, int count, bool isClosed);
is there a specific way to marshal this array? but with keeping the Point class as it is?
the calling method is something that looks like this:
public static double GetArea()
{
Point[] vertices = { new Point(100, 100), new Point(0, 0), new Point(0, 100) };
bool isClosed = true;
return NativeMethods.GetArea(vertices, vertices.Length, isClosed); //this is the imported C++ method
}
You shouldn't really be interopping classes at all (with the exception of those explicitly supported, e.g. string, arrays...). .NET's native interop is designed for C-style functions and structs, not C++. So either change the native code to be C-style, or use something like C++/CLI to provide a managed interface to the C++ objects and functions.
The simplest way of handling the interop is to use the marshaller, and change Point to be a value type, instead of a reference type:
struct Point
{
public double x;
public double y;
}
[DllImport("myDll.dll", CallingConvention = CallingConvention.Cdecl)]
public extern unsafe static double GetArea(Point[] vertices, int count, bool isClosed);
This of course requires Point to be a blittable type - just making it a struct instead of class should be enough. When vertices is a pointer to an array of values, that's all you have to do. The same way, you need to make it a value on the C++ side, rather than a pointer (and the signature of the function would be GetArea(Point *vertices, ...), instead of using a pointer to a pointer).
You have two main options for marshalling an array manually - unsafe code and IntPtr. This may be useful if you need to marshal something a bit more complicated
The unsafe signature is simple enough:
[DllImport("myDll.dll", CallingConvention = CallingConvention.Cdecl)]
public extern unsafe static double GetArea(Point** vertices, int count, bool isClosed);
The way to create and pass the array is the same as in C - I can't really provide anything specific, since it depends entirely on who owns what memory, and how that memory is structured. You can easily work with existing .NET arrays, you'll just need to pin them:
public static unsafe double CallGetArea(Point[] vertices, bool isClosed)
{
if (vertices.Length == 0) throw new ArgumentException("Vertices empty.", "vertices");
fixed (Point* pVertices = &vertices[0])
{
return GetArea(pVertices, vertices.Length);
}
}
This assumes that you just want to pass an array of Point values (the signature would have Point*). You can't really pass a pointer to an array like this - pVertices is read-only. And again, this only works when Point is blittable.
The IntPtr signature throws away the type information:
[DllImport("myDll.dll", CallingConvention = CallingConvention.Cdecl)]
public extern unsafe static double GetArea(IntPtr vertices, int count, bool isClosed);
Again, the call is similar to C, you'll just also have to do a lot of annoying marshaller calls both for reading and writing instead of having a C-like language to do that.
If you really want to stick with your idea of making a pointer to an array of pointers to Point, things get a bit more complicated. Arrays of pointers aren't supported by the marshaller, so you have to do the marshalling manually. Of course, this involves a lot of copying and allocations that wouldn't be necessary - so if you're trying to do this for performance reasons, don't. The basic idea goes like this:
unsafe double CallGetAreaPointers(Point[] vertices, bool isClosed)
{
var ipArray = Marshal.AllocHGlobal(vertices.Length * sizeof(Point));
var ipItems = new Stack<IntPtr>();
try
{
Point** pArray = (Point**)ipArray.ToPointer();
for (var i = 0; i < vertices.Length; i++)
{
var ipItem = Marshal.AllocHGlobal(sizeof(Point));
ipItems.Push(ipItem);
Marshal.StructureToPtr(vertices[i], ipItem, false);
pArray[i] = (Point*)ipItem.ToPointer();
}
GetArea(pArray, vertices.Length, isClosed);
}
finally
{
IntPtr ipItem;
while ((ipItem = ipItems.Pop()) != IntPtr.Zero) Marshal.FreeHGlobal(ipItem);
Marshal.FreeHGlobal(ipArray);
}
}
(Disclaimer: this is just a back-of-the-hand code; if you decide to go this way, make sure you do it right, this is just a general idea)
Note that for simplicity, I'm still using Point as a struct - it just allows you to use pointers on the C++ side. If you want to go full way and make it a reference type in C#, the easiest way would be to create a PointStruct type anyway (private if you want to, it doesn't matter) and copy the classes fields over to the struct. Of course, in either case, there's little point in allocating each and every Point instance separately - a simple array of values will work exactly the same way, while being a contiguous bit of memory, much simpler and cheaper to use and cleanup.

Marshalling array of structures that contain a std::vector

I am trying to marshal a vector of the following C++ class to C# that is used in a tree:
class CFileNode
{
private:
std::string m_name;
std::vector<CFileNode*> m_children;
bool m_isDirectory;
CFileNode* m_parent;
};
And:
extern DATAACCESSLAYERDLL_API size_t __stdcall Get_FileSystemNodeCount()
{
// GetFileNodeList() returns a std::vector<CFileNode*>&
return CFileSystem::GetFileNodeList().size();
}
extern DATAACCESSLAYERDLL_API void __stdcall Get_FileSystemNodes(size_t count, CFileNode** nodes)
{
nodes = CFileSystem::GetFileNodeList().data();
}
And in C#:
[SuppressUnmanagedCodeSecurity]
[DllImport("MyDll.dll")]
static private extern int Get_FileSystemNodeCount();
[SuppressUnmanagedCodeSecurity]
[DllImport("MyDll.dll")]
static private extern void Get_FileSystemNodes(int count, [In,Out] IntPtr[] outNodes);
And my struct:
[StructLayout(LayoutKind.Sequential, Pack = 1), Serializable]
public struct CFileNode
{
[MarshalAs(UnmanagedType.LPStr)]
string m_name;
[MarshalAs(UnmanagedType.SafeArray)]
IntPtr[] m_children;
[MarshalAs(UnmanagedType.Bool)]
bool m_isDirectory;
[MarshalAs(UnmanagedType.LPStruct)]
IntPtr m_parent;
}
And where I use it:
int count = Get_FileSystemNodeCount();
IntPtr[] results = new IntPtr[count];
Get_FileSystemNodes(count, results);
foreach (IntPtr ptr in results)
{
m_fileSystemNodes.Add(Marshal.PtrToStructure<CFileNode>(ptr));
}
In its current state, the IntPtr[] results is just a big array of zeroes.
If I modify C++ GetFileSystemNodes() to go through the vector and add everything to the array nodes, results will have a load of numbers that look like memory addresses (although I have no idea if they are garbage or not), but then the Marshal.PtrToStructure<CFileNode>(ptr) fails with an AccessViolationException.
I would like to transfer this whole tree structure from C++ to C# in as few PInvoke calls as possible, for speed. This is why, before any of this code is executed, my entire tree is flattened out into a list. Each node still has a pointer to its parent and children, so it's easy to reconstruct it. If it makes it easier, I could also just pass the root node.
Here are my questions:
1) Is marshalling vectors of objects that contain other vectors even possible? Should I make my C++ classes simpler, using arrays or something instead?
2) Is there a way to pass the vector of nodes through GetFileSystemNodes without moving everything in the vector to the array argument?
3) What is the correct way to write my C# struct so it matches my C++ one?
This can be done, I would recommend using SWIG. SWIG makes interop between C++ and other languages (including C#) much easier. It has built in support to handle some of the C++ STL containers, such as vectors.
Yes, SWIG can handle vectors of objects, you just have to build them up in the interface file.

int** in c and int[][] in c# is same?

I have a DLL file that is written in C. I am try to use C DLL in my c# code. C program method return int** . int** in c and int[][] in c# is same?
I am getting an error while returning a value from c program.
c method
__declspec(dllexport) int someMethod
(
size_t *foo,
int **bar
)
{
*foo = 10;
**bar = 10;
}
My c# code
[DllImport(#"my.dll", CallingConvention = CallingConvention.Cdecl, SetLastError = true, BestFitMapping = true, EntryPoint = "someMethod")]
public unsafe static extern int someMethod
(
out Int16 foo,
[MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(JaggedArrayMarshaler))]
out int[][] bar
);
C's int** and C#'s int[][] are similar in concept, but different at the binary level:
int** is a pointer to pointer, into the raw data in unmanaged heap memory.
int[][] is a managed reference to a managed array of managed references to managed arrays; each points to the array's header (which contains its length) instead of its raw data.
I don't know what would be easiest to marshal for C#. Can SAFEARRAY be nested? If so, it would probably easily map to C# arrays.
The solution that's sure to work is to use a C++/CLI DLL as glue between the two codes, to convert the int** into a cli::array<cli::array<int>^>^ (which involves copying the pointed data) before returning to C#.
Assuming the C function prototype is correct, I think you should declare it thusly on the C# side:
public unsafe static extern int someMethod
(
out Int16 foo,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0, ArraySubType=UnmanagedType.I2)]
out int[] bar
);
However, I fear a memory leak. I'm not sure how the unmanaged array can be deallocated.

Writing to a C# array from C with PInvoke

I have a function like this :
extern "C" __declspec(dllexport) void Step(int * oSamplesCount, float * oSamples){
// (approximative syntax for clarity)
*oSamplesCount = new_samples_count (some number between 0 and 16000)
oSamples[ 0 .. new_samples_count ] = some floats (sound data)
}
I'd like to call it from C# :
float [] mSamples = new float[16000];
[DllImport("Lib.dll")]
static extern void Step(ref Int32 oSamplesCount, [MarshalAs(UnmanagedType.LPArray,SizeConst=16000)] ref float [] oSamples);
void update(){
Int32 lSamplesCount = 0;
Step(ref lSamplesCount, ref mSamples);
}
The C function is called correctly, the for() loop that fills the samples array is ok, but it crashes somewhere between the return and the next C# line, so I guess it has something to do with unmarshalling, although I don't want any marshalling/unmarshalling (the array is blittable and must be written to from C)
I can't use /unsafe. I tried SizeConst and various other permutations.
Any help is appreciated !
Your pinvoke is wrong. The array parameter should not be passed by ref since a float[] is already a reference. Do it like this:
[DllImport("Lib.dll")]
static extern void Step(ref Int32 oSamplesCount, float[] oSamples);
Note that this will marshal from managed to native, and back again, all 16000 values, on each call to Step. If that's too expensive then I think you will need to perform manual marshalling.

Categories

Resources