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.
Related
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...
I have the following function in C++ inside a .dll:
extern "C"
{
__declspec(dllexport) void ColorRamps_getColorRampTable(int n, int *table);
}
It takes in an array with n elements and sets the array values.
How do I call this from C#?
I understand that I must first DllImport the function (?):
[DllImport("ColorRamps.dll")]
static extern void ColorRamps_getColorRampTable(int n, int[] table);
public static void getColorRampTable(int[] table)
{
int n = table.Length;
ColorRamps_getColorRampTable(n, table);
}
Is this correct?
When I call getColorRampTable(int[] table) must I pin the array?
How do I do this?
I tried:
int[] table = new int[164];
GCHandle handle = GCHandle.Alloc(table, GCHandleType.Pinned);
getColorRampTable(table);
handle.Free();
You have two options. One is to rely on the .NET marshaller, another is to use unsafe code.
Both are actually quite simple for your case. The marshaller one:
[DllImport("ColorRamps.dll")]
static extern void ColorRamps_getColorRampTable(int n, [In, Out] int[] table);
public static void getColorRampTable(int[] table)
{
int n = table.Length;
ColorRamps_getColorRampTable(n, table);
}
This has a slight overhead in that the marshaller copies the array to unmanaged memory first, and then copies it back into your array again. EDIT: As Xanatos correctly noted, since int[] is a blittable type, the marshaller actually cheats and passes a pointer to your actual array. Or at least that's the documented behaviour.
If this is a performance problem for you (and it's a real, measured problem - don't do this otherwise), you can pass a pointer to the .NET array directly using unsafe code:
[DllImport("ColorRamps.dll")]
static extern void ColorRamps_getColorRampTable(int n, int* table);
public unsafe static void getColorRampTable(int[] table)
{
int n = table.Length;
fixed (int* pTable = &table)
{
ColorRamps_getColorRampTable(n, pTable);
}
}
The class has to be marked with the unsafe keyword, and you have to enable unsafe code in the project settings.
EDIT:
As above, this doesn't actually apply for the int[] case. I'm going to leave it in here because it's true for non-blittable types, but int[] is blittable, and the marshaller should just pass the pointer directly, while handling the pinning for you. You should still use the [In, Out] attribute, though - even though it will usually behave as In/Out by default, the contract should be clearly specified.
The main difference is that in the first case, the C(++) code never has access to any of the managed memory of yours - it always works on copies of the data. By adding the [In, Out] attributes, you're telling the marshaller to not only copy the data in, but also to copy it out after the call is done - otherwise your table array would never change.
On the other hand, the second variant passes a pointer to the actual array you're using on the C# side - this may or may not be faster, depending on way too many things, and it's generally slightly more unsafe (be very careful about specifying the correct length of the array, for example).
Another EDIT:
There's also an option to use IntPtr table in the P/Invoke method declaration, which allows you additional flexibility. The easiest example would be just pinning the array, which is equivalent to the fixed case:
var hTable = GCHandle.Alloc(table, GCHandleType.Pinned);
try
{
ColorRamps_getColorRampTable(n, hTable.AddrOfPinnedObject());
}
finally
{
hTable.Free();
}
And even allowing you to explicitly manage the copying quite easily:
var pTable = Marshal.AllocHGlobal(sizeof(int) * table.Length);
try
{
Marshal.Copy(table, 0, pTable, table.Length);
ColorRamps_getColorRampTable(n, pTable);
Marshal.Copy(pTable, table, 0, table.Length);
}
finally
{
Marshal.FreeHGlobal(pTable);
}
This is a decent workaround if you can't use unsafe code for some reason, but do remember that it's just as "unsafe" as unsafe code in most scenarios. The truth is, since you're interacting with native code, you're always (somewhat) unsafe - a bug in the native code can easily corrupt your whole application.
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.
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.
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.