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.
Related
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.
I know this can be done by mallocing in C, passing malloced pointer to delegate with parameter type IntPtr, marshalling to string[] and then freeing malloced memory with separate, exported C-function from managed code.
My question is: Can this be done simpler way? E.g. :
C# delegate parameter is of type string[]?
no separate free function to call from managed code
EDIT: I tried with delegate signature:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
MyManagedDelegate(string[] values, int valueCount)
and fucntion in C:
void NativeCallDelegate(char *pStringValues[], int nValues)
{
if (gSetStringValuesCB)
gSetStringValuesCB(pStringValues, nValues);
}
calling it in C:
char *Values[]= {"One", "Two", "Three"};
NativeCallDelegate(Values, 3);
This results in that i can use only 1st string in array.
Here's how to do it properly, I'll give a full example so it's reproductible.
The C side
typedef void(*setStringValuesCB_t)(char *pStringValues[], int nValues);
static setStringValuesCB_t gSetStringValuesCB;
void NativeCallDelegate(char *pStringValues[], int nValues)
{
if (gSetStringValuesCB)
gSetStringValuesCB(pStringValues, nValues);
}
__declspec(dllexport) void NativeLibCall(setStringValuesCB_t callback)
{
gSetStringValuesCB = callback;
char *Values[] = { "One", "Two", "Three" };
NativeCallDelegate(Values, 3);
}
Nothing fancy here, I just added the necessary glue code and left the rest alone.
The C# side
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void MyManagedDelegate(
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr, SizeParamIndex = 1)]
string[] values,
int valueCount);
[DllImport("NativeTemp", CallingConvention = CallingConvention.Cdecl)]
public static extern void NativeLibCall(MyManagedDelegate callback);
public static void Main()
{
NativeLibCall(PrintReceivedData);
}
public static void PrintReceivedData(string[] values, int valueCount)
{
foreach (var item in values)
Console.WriteLine(item);
}
The trick lies in the marshaling part:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void MyManagedDelegate(
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr, SizeParamIndex = 1)]
string[] values,
int valueCount);
The MarshalAs attribute tells the .NET marshaler the following:
UnmanagedType.LPArray You're getting an array...
ArraySubType = UnmanagedType.LPStr ...of standard C strings...
SizeParamIndex = 1 ...and the size of that array is specified by the second parameter.
The C strings are copied and converted to System.String instances by the .NET marshaler before the invocation of your C# method. So if you need to pass dynamically generated strings to C#, you malloc them, then you call gSetStringValuesCB, and you can free them immediately afterwards, all from the C code, as .NET has its own copy of the data.
You can refer to the docs:
UnmanagedType.LPArray:
A pointer to the first element of a C-style array. When marshaling from managed to unmanaged code, the length of the array is determined by the length of the managed array. When marshaling from unmanaged to managed code, the length of the array is determined from the MarshalAsAttribute.SizeConst and MarshalAsAttribute.SizeParamIndex fields, optionally followed by the unmanaged type of the elements within the array when it is necessary to differentiate among string types.
UnmanagedType.LPStr:
A single byte, null-terminated ANSI character string. You can use this member on the System.String and System.Text.StringBuilder data types.
MarshalAs.SizeParamIndex:
Indicates the zero-based parameter that contains the count of array elements, similar to size_is in COM.
I came up with far from optimal solution:
public delegate void MyManagedDelegate([MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr, SizeConst=10)]string[] values, int valueCount);
This is not working if called like this:
char *Values[]= {"One", "Two", "Three"};
NativeCallDelegate(Values, 3);
I could have fixed size 10 array where the values are copied and that is always passed to delegate. This is not what I want. I wonder if there is good solution to this...
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.
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).
Summary:
I have a bunch of C functions I have to call from C#. My current working solution is based on function overloading and I'm wondering if there is a more elegant solution.
The C stuff:
somewhere in a header file
typedef struct _unknown_stuff * handle; // a opaque pointer
an example of the function
func( uint num_entries,
handle * objects,
uint * n)
{ ... }
in C the function should be used similar to this:
// warning: i bet that the syntax is not correct but you should get the idea...
uint n;
func(0, null, &n);
handle * objects = malloc(n * sizeof(handle));
func(n, objects, null);
The C# stuff:
right now I'm doing the following in C#:
public struct handle
{
public IntPtr Pointer;
}
// version to get number of objects
[DllImport(dll, ...]
private static extern void
func( uint must_be_zero,
object must_be_null,
out uint n);
// version to get the actual data
[DllImport(dll, ...]
private static extern void
func( uint num_entries,
[Out] handle[] objects,
int must_be_zero);
and then:
handle[] objects;
uint n = 42;
func(0, null, out n);
objects = new handle[n];
func(n, objects, 0);
The Question
Since I'm a C# noob, I was wondering if this is the best way to do this. Especially I would like to know if there is a way around overloading the function.
First of all, your code as is is wrong, because in the second C# signature your third argument is int, while the corresponding C argument is still int*. It will work when targetting 32-bit Windows, because sizeof(int)==sizeof(int*) there - so when you pass a 0 int you end up passing a null pointer - but it is not 64-bit safe. If you still want to do it that way, you need to use IntPtr there.
Aside from that, you could try:
[DllImport(dll, ...]
private static extern void func(
uint num_entries,
[Out] handle[] objects,
[Out] int[] num_devices);
making the third argument an array so that you could pass null there.
Or just use pointers. There's nothing bad about it, and don't be afraid of unsafe keyword - any kind of P/Invoke is inherently unsafe anyway, so you might as well be explicit about it.
Wouldn't the C dll look at 0, and process that as null?
It only has one function, correct? That is, the C function is defined once, and not separately for a null case, and would have an if( objects == NULL ) check somewhere in there. On most systems (check the stdio.h of the target system) this is 0.
So calling from pinvoke with a 0 value in this place should do what you wanted overloading to do.