DllImport stuct parameters - c#

I have 3 C++ structs as well as two methods and I want to use them via C#. Here is my C# code as well as few comments on the C++ code.
[StructLayout(LayoutKind.Sequential)]
public unsafe struct smat
{
public long rows;
public long cols;
public long vals;
public long* pointr;
public long* rowind;
public double* value;
};
[StructLayout(LayoutKind.Sequential)]
public unsafe struct dmat {
long rows;
long cols;
double **value;
};
[StructLayout(LayoutKind.Sequential)]
public unsafe struct svdrec {
int d;
dmat Ut; // it was dmat* in C++
double *S;
dmat Vt; // it was dmat* in C++
};
[DllImport(#"file.dll", EntryPoint = "svdNewSMat", CallingConvention = CallingConvention.Cdecl)]
public static extern smat svdNewSMat(int rows, int cols, int vals); // it was smat* in C++
[DllImport(#"file.dll", EntryPoint = "svdLAS2", CallingConvention = CallingConvention.Cdecl)]
public static extern svdrec svdLAS2(smat a, long dimensions, long iterations, double[] las2end, double kappa); // it was smat* and svdrec* in C++
EDIT:
Here are the C++ headers
typedef struct smat *SMat;
typedef struct dmat *DMat;
typedef struct svdrec *SVDRec;
/* Harwell-Boeing sparse matrix. */
struct smat {
long rows;
long cols;
long vals; /* Total non-zero entries. */
long *pointr; /* For each col (plus 1), index of first non-zero entry. */
long *rowind; /* For each nz entry, the row index. */
double *value; /* For each nz entry, the value. */
};
/* Row-major dense matrix. Rows are consecutive vectors. */
struct dmat {
long rows;
long cols;
double **value; /* Accessed by [row][col]. Free value[0] and value to free.*/
};
struct svdrec {
int d; /* Dimensionality (rank) */
DMat Ut; /* Transpose of left singular vectors. (d by m)
The vectors are the rows of Ut. */
double *S; /* Array of singular values. (length d) */
DMat Vt; /* Transpose of right singular vectors. (d by n)
The vectors are the rows of Vt. */
};
extern DMat svdNewDMat(int rows, int cols);
extern SVDRec svdLAS2(SMat A, long dimensions, long iterations, double end[2],
double kappa);
I am not giving the full code because It includes a lot of libraries.
And here is what I excecute to test:
var a = svdNewSMat(3, 6, 3);
var m = new double[] {1, 2};
var r = svdLAS2(a, 1, 0, m, 1e-6); // I get the Exception here
The second line gives me Attempted to read or write protected memory error.
Any ideas? Thanks in advance.

You cannot simply replace dmat* with dmat on the C# side. A pointer is not automatically marshalled as reference
Please look at http://msdn.microsoft.com/en-us/library/0szztey7(v=vs.80).aspx on how to marshall embedded pointers correctly.

If it was smat* and dmat*, use IntPtr instead smad/dmat.

You can just use IntPtr if you're working with pointers from the DLL. Keeping in mind that a lot of boxing/unboxing will occur during this work.

I don't believe a c++ module can perform operations on a c# struct.
Try using a class instead of a struct.

In function declarations, the pinvoke equivalent to pointer-to-pointer is usually ref IntPtr In a struct declaration (and as others pointed out), it should be just IntPtr. Keep in mind that in function invocation, things are often more complicated than just getting your struct declarations correct (e.g. calling conventions may need to be modified).

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.

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.

Marshalling C structures to 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.

How to pass a C# double[] to a C++ function that requires constant double* pArr? C++, C#

I have a function in C++ written as:
MyFunc(const double* pArray, int length);
I need to pass a non-constant array into it:
//C#
double[] myDoubleArray = new double[] { 1, 2, 3, 4, 5 };
MyFunc(myDoubleArray, 5);
The program is breaking when I do this.
edit:
//C# declaration
[DllImport(#"RTATMATHLIB.dll", EntryPoint = "?MyFunc##YANPBNHHHH#Z")]
public static extern double MyFunc(double[] data, int length);
//C# usage
public static double MyFunc(double[] data)
{
return MyFunc(data, data.Length);
}
//C++ export
__declspec(dllexport) double MyFunc(const double* data, int length);
//C++ signature
double MyFunc(const double* data, int length)
{
return 0; //does not quite matter what it returns...
}
You can always pass a non-constant array to a function requiring a constant array. The const qualifier to the array implies that the function will not modify the contents of the array. The array does not need to be constant before passing to the function; the function will not modify the contents, due to the const in the declaration
Constness can be trivially added. Your program is not wrong. You must have missed some other detail.

Categories

Resources