I am wrapping a C++ object and it has certain methods that return by value, and I would like the C# wrapper method to return a native type without having to allocate and de-allocate memory in C++-land. In this situation I think copying data into the return type is appropriate but I can't figure out how to do it in a nice way.
As a simple working example the following C++ code is a file parser. I only list the the std::pair<int,int> version() method but you can imagine other that return a small constant std::array, list, etc.
#include <utility>
#include <tuple>
class MyFile {
public:
std::pair<int,int> version() const noexcept {
return {version_major_, version_minor_};
}
private:
int version_major_;
int version_minor_;
};
Using Out Arguments
I like this one the best, but I can't for the life of me figure out how to remove the out arguments for just the C# wrapper! C# is responsible for the memory and just passes it to C++ to fill in. This requires some C++ wrappers using %extend but that is ok.
I really want a final C# method with signature of public Tuple<int, int> version()
SWIG code
%module parser
%include "typemaps.i"
%rename("$ignore", fullname=1) MyFile::version() const noexcept;
%extend MyFile {
void version(int* major_out, int* minor_out) {
std::tie(*major_out, *minor_out) = self->version();
}
}
%typemap(ctype, out="void *") int* major_out, int* minor_out "int *"
%typemap(imtype, out="void") int* major_out, int* minor_out "out int"
%typemap(cstype) int* major_out, int* minor_out "out int"
%typemap(cstype) void MyFile::version "Tuple<int, int>"
%typemap(csin, pre=" int tmp_$csinput = 0;") int* major_out, int* minor_out "out tmp_$csinput"
// implment the C# function that returns the type specified above
%typemap(csout, noblock=1, excode=SWIGEXCODE) void MyFile::version
{
$imcall;$excode
major_out = tmp_major_out;
minor_out = tmp_minor_out;
return new Tuple<int, int>(tmp_major_out, tmp_minor_out);
}
Generated C++ wrapper
SWIGEXPORT void SWIGSTDCALL CSharp_parser_MyFile_version(void * jarg1, int * jarg2, int * jarg3) {
MyFile *arg1 = (MyFile *) 0 ;
int *arg2 = (int *) 0 ;
int *arg3 = (int *) 0 ;
arg1 = (MyFile *)jarg1;
arg2 = (int *)jarg2;
arg3 = (int *)jarg3;
MyFile_version(arg1,arg2,arg3);
}
Generated PInvoke Code
[global::System.Runtime.InteropServices.DllImport("parser", EntryPoint="CSharp_parser_MyFile_version")]
public static extern void MyFile_version(global::System.Runtime.InteropServices.HandleRef jarg1, out int jarg2, out int jarg3);
Generated C# Bindings
public Tuple<int, int> version(out int major_out, out int minor_out) {
int tmp_major_out = 0;
int tmp_minor_out = 0;
parserPINVOKE.MyFile_version(swigCPtr, out tmp_major_out, out tmp_minor_out);
major_out = tmp_major_out;
minor_out = tmp_minor_out;
return new Tuple<int, int>(tmp_major_out, tmp_minor_out);
}
Other methods
I have tried other methods but to keep the question short I won't post all the generated code here.
Returning pointer to a static array
this works, but requires static data for every method that I want to work this way. This doesn't scale. I could easily run into lifetime issues if I try to do this with member data that I assume will live long enough for C# to copy.
wrap the default generated code in a csout typemap
This also works but allocated and de-allocates a lot for a function just returning two ints! you end up with a C# wrapper something like:
public Tuple<int, int> version() {
System.IntPtr cPtr = parserPINVOKE.MyFile_version(swigCPtr);
pair_of_int tmp = new pair_of_int(cPtr, false);
Tuple<int, int> ret = new Tuple<int, int>(tmp.first, tmp.second);
tmp.Dispose();
return ret;
}
Other SO Questions
a similar question is this one, but that deals with java. Java has JNI and Pyhton has a C interface that lets you create memory/object in the C code and return it... I don't see a way to do that in .NET withou C++/CLI (which SWIG isn't using).
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 am using SWIG to generate a wrapper to a native 32 bit c++ dll. SWIG produces a C++ wrapper file and a lot of generated C# code, this is compiled up into a dll (both C++/CLI and C# projects built as x86) and the resulting functions are callable through C# fine, except those that contain enums. An example:
SWIG generated interface_wrap.cxx file:
SWIGEXPORT int SWIGSTDCALL CSharp_myMethod(long jarg1, long jarg2, void * jarg3, void * jarg4) {
int jresult ;
long arg1 ;
long arg2 ;
myEnum arg3 ;
double *arg4 = 0 ;
myEnum const *argp3 ;
int result;
arg1 = (long)jarg1;
arg2 = (long)jarg2;
argp3 = (myEnum *)jarg3;
if (!argp3) {
SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "Attempt to dereference null myEnum const", 0);
return 0;
}
arg3 = *argp3;
arg4 = (double *)jarg4;
if (!arg4) {
SWIG_CSharpSetPendingExceptionArgument(SWIG_CSharpArgumentNullException, "double & type is null", 0);
return 0;
}
result = (int)myMethod(arg1,arg2,arg3,*arg4);
jresult = result;
return jresult;
}
C# extern function delegate definition:
[global::System.Runtime.InteropServices.DllImport("ProjectWrapper", EntryPoint="CSharp_myMethod")]
public static extern int myMethod(int jarg1, int jarg2, [MarshalAs(UnmanagedType.U4)]myEnum jarg3, out double jarg4);
Interface.i file extract:
%module ProjectWrapper
%{
#include "myEnumDefinition.h"
%}
%include "enums.swg"
// %typemap(csbase) myEnum "short" // is something like this needed??
%typemap(cstype, out="myEnum") myEnum&, const myEnum& "ref myEnum"
%typemap(cstype, out="myEnum") myEnum, const myEnum "myEnum"
%typemap(imtype, inattributes="[MarshalAs(UnmanagedType.U4)]", outattributes="[return: MarshalAs(UnmanagedType.U4)]", out="myEnum") myEnum&, const myEnum& "ref myEnum"
%typemap(imtype, inattributes="[MarshalAs(UnmanagedType.U4)]", outattributes="[return: MarshalAs(UnmanagedType.U4)]") myEnum, const myEnum "myEnum"
%typemap(csin) myEnum&, const myEnum& "ref $csinput"
%typemap(csin) myEnum, const myEnum "$csinput"
int myMethod(const long start,const long end,const myEnum enumvalue, double& result);
C# definition of enum:
public enum myEnum {
myEnum_value1,
myEnum_value2,
myEnum_value3
}
C++ definition of enum (from myEnumDefinition.h)
enum myEnum
{
myEnum_value1,
myEnum_value2,
myEnum_value3
};
When it gets to the point of calling the C# myMethod extern delegate, it is throwing an AccessViolationException every time. If I try to call other methods without any enums as parameters, it works fine.
I don't understand what is wrong here. I have tried to MarshalAs the enum in different ways but I can't manage to avoid this exception being thrown. Am I missing something in the SWIG interface file when generating the wrapper?
It looks like Swig is expecting a pointer to the enum value, according to this line:
argp3 = (myEnum *)jarg3;
It looks like the C++ method is expecting a myEnum* value in its third argument. So you should probably pass your enum argument as a ref:
public static extern int myMethod(int jarg1, int jarg2, ref myEnum jarg3, out double jarg4);
I've been trying to invoke a method that have been created in Delphi in the following way:
function _Func1(arrParams: array of TParams): Integer;stdcall;
type
TParams = record
Type: int;
Name: string;
Amount : Real;
end;
My code is:
[DllImport("some.dll", EntryPoint = "_Func1", CallingConvention = CallingConvention.StdCall)]
public static extern int Func(
[MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.Struct)] TParams[] arrParams)
And the struct is:
[StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct TParams
{
public int Type;
[MarshalAs(UnmanagedType.AnsiBStr)]
public string Name;
public double Amount;
}
When I am calling this method I'm getting the error:
Cannot marshal field 'Name' of type 'TParams': Invalid managed/unmanaged type combination (String fields must be paired with LPStr, LPWStr, BStr or ByValTStr).
However none of those combinations works, as Delphi's strings are prefixed with its length and it is Ansi for sure (I've tried it with other string parameters). Does anyone have a clue how to solve this?
There are two main problems with this, use of open arrays and use of Delphi string.
Open arrays
Delphi open arrays are implemented by passing a pointer to the first element of the array and an extra parameter specifying the index of the last item, high in Delphi terminology. For more information see this answer.
Delphi strings
The C# marshaller cannot interop with a Delphi string. Delphi strings are private types, only to be used internally to a Delphi module. Instead you should use a null-terminated string, PAnsiChar.
Putting it all together you can write it like this:
Delphi
type
TParams = record
_Type: Integer;//Type is a reserved word in Delphi
Name: PAnsiChar;
Amount: Double;
end;
function Func(const arrParams: array of TParams): Integer; stdcall;
C#
[StructLayoutAttribute(LayoutKind.Sequential)]
public struct TParams
{
public int Type;
public string Name;
public double Amount;
}
[DllImport("some.dll")]
public static extern int Func(TParams[] arrParams, int high);
TParams[] params = new TParams[len];
...populate params
int retval = Func(params, params.Length-1);
To compliment David's answer, you can marshal to a Delphi string, but it's ugly. In C#, you have to replace all of your strings in the struct with IntPtr.
private static IntPtr AllocDelphiString(string str)
{
byte[] unicodeData = Encoding.Unicode.GetBytes(str);
int bufferSize = unicodeData.Length + 6;
IntPtr hMem = Marshal.AllocHGlobal(bufferSize);
Marshal.WriteInt32(hMem, 0, unicodeData.Length); // prepended length value
for (int i = 0; i < unicodeData.Length; i++)
Marshal.WriteByte(hMem, i + 4, unicodeData[i]);
Marshal.WriteInt16(hMem, bufferSize - 2, 0); // null-terminate
return new IntPtr(hMem.ToInt64() + 4);
}
This can directly be sent to Delphi, where it'll properly be read as a string.
Remember that you must free this string when you're done with it. However, GlobalFree() can't be called directly on the pointer to the string, because it doesn't point to the start of the allocation. You'll have to cast that pointer to a long, then subtract 4, then cast it back to a pointer. This compensates for the length prefix.
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.
I'm calling functions from C++ that returns a pointer to an array of struct and I'm having problems since I'm new to this operation/implementation.
My C++ codes:
// My C++ Structs
typedef struct _MainData {
double dCount;
DataS1 *DS1;
int iCount1;
DataS2 *DS2;
int iCount2;
}MainData;
typedef struct _DataS1 {
unsigned int uiCount1;
unsigned int uiCount2;
int iCount;
void *pA;
void *pB;
} DataS1;
typedef struct _DataS2 {
unsigned int uiCount1;
unsigned int uiCount2;
unsigned int uiCount3;
unsigned int uiCount4;
double dCount;
int iCount1;
char strLbl[64];
} DataS2;
// My C++ Function
MainData* GetData(const int ID)
{
MainData* mData;
int iLength = Get_Count();
mData = new MainData[iLength];
for(int x = 0;x < VarCounter; x++)
{
// Codes here assign data to mData[x]
}
return mData;
}
Question:
How can I call the C++ function GetData to C#?
My current codes in C# are:
[DllImport(".\\sdata.dll")]
[return: MarshalAs(UnmanagedType.LPArray)]
private static unsafe extern MainData[] GetData(int ID);
// The struct MainData in my C# side is already "Marshalled"...
//My function call is here:
MainData[] SmpMapData = GetData(ID);
When I compiled it, there's an exception:
"Cannot marshal 'return value': Invalid managed/unmanaged type combination."
Sorry for the poor coding... Please help...
I tried to do exactly the same thing but didn't succeed due to lack of time but I learned something in process:
Allocate memory in C#
To pass array of structs, struct must be blittable.
Good luck with that, I couldn't make it to work.
One problem is that the marshaller doesn't know how many items are in the array returned by the C++ code. An alternative approach could be to have two C++ methods - one which returns the number of items, and one which returns a single MainData given an index.
What do your structures look like on the C# side?
Since you are coding both the C++ and C# side, it may be easier to use C++/CLI to interface them instead of PInvoke.