Swig Pass Std::vector by ref / out from C# to C++ - c#

Given the C++ function
void Foo(unsigned int _x, unsigned int _y, std::vector< unsigned int > &_results)
And the Swig interface file to map std::vector to type VectorUInt32 in C#
%include "std_vector.i"
namespace std {
%template(VectorUInt32) vector<unsigned int>;
};
I get the following result in C# code:
public static void Foo(uint _x, uint _y, VectorUInt32 _results)
Which is great, but what I was really hoping for was this:
public static void Foo(uint _x, uint _y, out VectorUInt32 _results)
Does anyone know how to map the std::vector from C++ to C# as a ref or out param?

Shame on you Stackoverflow for not having an answer!
Anyway, the answer is, if anyone else is interested... If you create the VectorUInt32 type in C# and pass it to the C++ function it is passed by reference, so can be modified inside C++ without a ref or out param.
The interface becomes:
C++
void Foo(unsigned int _x, unsigned int _y, std::vector< unsigned int > &_results)
Interface file
%include "std_vector.i"
namespace std {
%template(VectorUInt32) vector<unsigned int>;
};
C#
public static void Foo(uint _x, uint _y, VectorUInt32 _results)
Usage
// Create and pass vector to C++
var vec = new VectorUInt32();
Foo(x, y, vec);
// do something with vec, its been operated on!

Related

SWIG C# Passing binary data with directors="1"

I see a java example about how to pass binary data in director callback from C++ to java , https://github.com/swig/swig/blob/90cdbee6a69d13b39d734083b9f91069533b0d7b/Examples/test-suite/director_binary_string.i , I'd like to do the same thing on C# with the same swig directive,
%apply (char *STRING, size_t LENGTH) { (char *dataBufferAA, int sizeAA) };
but it does not take effect, and shows
Warning 453: Can't apply (char const *STRING,size_t LENGTH). No typemaps are defined
Basically my code's structure is like below, I'd expect a callback function like void run(byte[] aData) can be invoked in C#.
%module(directors="1") director_binary_string;
%feature("director") Callback;
%apply (char *STRING, size_t LENGTH) { (char* aData, size_t aLen) };
%inline %{
#include <stdlib.h>
class Callback {
public:
virtual ~Callback() {}
virtual void run(char* aData, size_t aLen) {}
};
%}
And I tries a typemap like below, but it does not work as expected.
%typemap(imtype) (const char *STRING, size_t LENGTH) "byte[]"
%typemap(cstype) (const char *STRING, size_t LENGTH) "byte[]"
%typemap(directorin, excode=SWIGEXCODE) (const char *STRING, size_t LENGTH) %{
{
$input = new byte[(int)$2];
IntPtr data = $imcall;
System.Runtime.InteropServices.Marshal.Copy(data, $input, 0, (int)$2);
}
%}
Anyone can show how to write a proper typemap or any other better solution.
Finally I got an alternative solution. Instead of passing binary data to C# side from C++, within the callback, the caller from C# proactively retrieves binary data. For example
Swig Interface
%module(directors="1") director_binary_string;
%feature("director") Callback;
%include "arrays_csharp.i"
%inline %{
#include <stdlib.h>
class CallbackData
{
public:
%apply unsigned char INOUT[] { unsigned char* aBlockData}
bool GetBlockData(unsigned char* aBlockData);
size_t GetSize();
};
class Callback {
public:
virtual ~Callback() {}
virtual void run(CallbackData aCB) {}
};
C#
class csharp_callback : Callback
{
public override void run(CallbackData aCB)
{
byte[] aData = new byte[aCB.GetSize()];
aCB.GetBlockData(aData);
//aData is the expected binary data.
}
}

How to pass array of doubles from c# to c++ via DllImport

I have a c++ function with the method signature as
MyMethod(std::vector<double> tissueData, std::vector<double> BGData, std::vector<double> TFData, std::vector<double> colMeans, std::vector<double> colStds, std::vector<double> model)
I wish to call this c++ function in c# via dllimport. While creating the dll library I have defined the function from the c++ side as
extern "C" __declspec(dllexport) int MyMethod(double *tissue, double *bg, double *tf, double *colMeans, double *colStds, double* model);
I plan to pass an array of doubles from the c# side to the c++ dll function.
However, I am not sure how I should define the DllImport from the c# side and also how should i convert the double array when i parse it into the dllImport function ?
I read a little about marshalling but i still dont quite get it and Im not sure if it can be applied here ?
You cannot interop with C++ classes (such as std::vector), only with basic C-style data types and pointers. (As a side note) that was one of the problems Microsoft attempted to solve when it invented COM.
To make it work, you should export a different function, which receives plain C arrays and their respective lengths:
C++ side
extern "C" __declspec(dllexport) int MyExternMethod(
double *tissue, int tissueLen,
double *bg, int bgLen,
/* ... the rest ... */
);
// implementation
int MyExternMethod(
double* tissue, int tissueLen,
double* bg, int bgLen,
/* ... the rest ... */ )
{
// call your original method from here:
std::vector<double> tissueData(tissue, tissue + tissueLen);
std::vector<double> bgData(bg, bg + bgLen);
/* ... the rest ... */
return MyMethod(tissueData, bgData, /* ...the rest... */);
}
The interop import on C# side would be:
C# side
public static class MyLibMethods
{
[DllImport("MyLib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int MyExternMethod(
double[] tissue, int tissueLen,
double[] bg, int bgLen,
/*...the rest...*/
);
}
And you can call it in C# like this:
C# side
public int CallMyExternMethod(double[] tissue, double[] bg, /*... the rest ...*/)
{
return MyLibMethods.MyExternMethod(
tissue, tissue.Length,
bg, bg.Length,
/*...the rest...*/
);
}

PInvoke only works in 64 bit, when returning a struct

I have the following C++ code, that compiles to a dll:
typedef struct _RGB {
unsigned char R, G, B;
} RGB;
extern "C" __declspec(dllexport) RGB __stdcall TestMethod1() {
RGB rgb{1,2,3};
return rgb;
}
and am calling it in C# using:
static void Main(string[] args)
{
var res = TestMethod1();
}
[DllImport(#"D:\Develop\res\VSProjects\ConsoleApp1\Debug\Dll1.dll", CallingConvention = CallingConvention.StdCall)]
static extern RGB TestMethod1();
[StructLayout(LayoutKind.Sequential)]
struct RGB { public byte R, G, B; }
When running it as x86, after building the dll as x86, I get an error Attempted to read or write protected memory.. In x64 it works fine.
When I use a managed/native debugger, I see it's crashing on return rgb;.
When changing the return type to a long (int in C#) it works fine even as x86.
The RGB struct is blittable so why am I getting this issue?
Don't use struct for "complex" return types, prefer something like this:
C++:
extern "C" __declspec(dllexport) void __stdcall TestMethod2(RGB *prgb) {
prgb->R = 1;
prgb->G = 2;
prgb->B = 3;
}
C#:
[DllImport(#"D:\smo\source\repos\ConsoleApplication4\Debug\Dll1.dll")]
static extern void TestMethod2(ref RGB rgb);
static void Main(string[] args)
{
var rgb = new RGB();
TestMethod2(ref rgb);
}
Note in your particular case, it fails because the structure size is 3, so you can make it work if you change the structure like this for example:
C++:
typedef struct _RGB {
unsigned char R, G, B, A;
} RGB;
C#
[StructLayout(LayoutKind.Sequential)]
struct RGB { public byte R, G, B, A; }
With this definition, the size will be 4, so the C++ compiler will generate a code that will return an int32 value instead of returning - probably - a reference to some internal memory that will be gone by the time execution reaches the .NET side. This is pure luck (or hack) and depends on the C++ compiler I guess.

Marshalling an array of strucs to call an unmanaged function from C#

I have to call an unmanaged function from C# and must provide an array of coordinates (doubles) to it. How does the marshalling work for this case correctly?
On the unmanaged side:
typedef struct dPoint3dTag
{
double x, y, z;
} dPoint3d;
void UnmanagedModifyGeometry(char *strFeaId, dPoint3d *pnts, int iNumPnts);
I defined a managed Structure for DPoint3d on the managed side:
[StructLayout(LayoutKind.Sequential)]
public struct DPoint3d
{
// Constructor
public DPoint3d(double x, double y, double z)
{
this.x = x;
this.y = y;
this.z = z;
}
public double x, y, z;
}
I'm trying to call the unmanaged function from C# in this way:
// Import of the unmanaged function
[DllImport("Unmanaged.dll")]
public static extern void UnmanagedModifyGeometry([MarshalAs(UnmanagedType.LPStr)] string strFeaId, DPoint3d[] pnts, int iNumPnts);
// Using the unmanaged function from C#
// Allocating points
DPoint3d[] pnts = new DPoint3d[iPntCnt];
String strFeaId = "4711";
// After filling in the points call the unmanaged function
UnmanagedModifyGeometry(strFeaId, pnts, iPntCnt);
Is this workflow correct?
Regards
tomtorell
First of all on the unmanaged side, char* is a modifiable string. You should use const here to indicate that the data flows from caller to callee. And it makes sense to do the same for the other parameters:
void UnmanagedModifyGeometry(
const char *strFeaId,
const dPoint3d *pnts,
const int iNumPnts
);
Now it is clear to all how the data flows.
On the managed side, there is one obvious problem with the declaration which is that you don't specify the calling convention. The default is stdcall, but your unmanaged code will be cdecl, assuming that the declaration in the question is accurate.
The struct declarations that you show match perfectly. There is nothing more to say on that subject.
You can also make use of the default marshalling to simplify the p/invoke. I'd write it like this:
[DllImport("Unmanaged.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void UnmanagedModifyGeometry(
string strFeaId,
[In] DPoint3d[] pnts,
int iNumPnts
);
And call it like this:
DPoint3d[] pnts = new DPoint3d[...]; // supply appropriate value for array length
// populate pnts
UnmanagedModifyGeometry("4711", pnts, pnts.Length);

passing structure reference from a c# code to call a c++ DLL function that accept structure reference in its prototype

I hav a function in c++ DLL that has following prototype
int function(RefPar &params);
how can i call this function from a c# program using "DLLImport".
when i tried like below, AccessViolationException happened while running in visual studio 2008..
[DllImport("VistaGMMDLL.dll", EntryPoint = "function"]
unsafe static extern int function(ref RefPar params);
and called as..
int ret=function(ref params);
Note:RefPar structure has many
unsigned integer values and 1 enum
value as its members.
pls anyone help me to call the function correctly..
A couple of things jump out at me. First of all I don't see why you need to use unsafe. Secondly, you probably have a calling convention mismatch, cdecl in the C++ and stdcall in the C#.
I'd do it like this:
C++
struct RESOURCE_PARAMETERS{
unsigned int uSurfaceHeight;
unsigned int uSurfaceDepth;
unsigned int uSurfaceWidth;
unsigned int uMSAAHeight;
unsigned int uMSAAWidth;
unsigned int uArraySize;
unsigned int uNumSamples;
unsigned int uMaxLod;
unsigned int uBpp;
unsigned int uprefFlag;
unsigned int uusageFlag;
RESOURCE_TYPE_REC ResourceType;
int ResourceFormat;
int iBuildNumber;
};
int function(RefPar &parameters)
{
}
C#
[StructLayout(LayoutKind.Sequential)]
public struct RESOURCE_PARAMETERS
{
uint uSurfaceHeight;
uint uSurfaceDepth;
uint uSurfaceWidth;
uint uMSAAHeight;
uint uMSAAWidth;
uint uArraySize;
uint uNumSamples;
uint uMaxLod;
uint uBpp;
uint uprefFlag;
uint uusageFlag;
[MarshalAs(UnmanagedType.U4)]
ResourceType ResourceType;
int ResourceFormat;
int iBuildNumber;
}
[DllImport("VistaGMMDLL.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int function(ref RESOURCE_PARAMETERS parameters);
RESOURCE_PARAMETERS parameters = new RESOURCE_PARAMETERS();
int result = function(ref parameters);
I'm not sure how big the enum is on the C++ size. That's why I've put an explicit MarshalAs in the C# code. If it's just a single byte, then use UnmanagedType.U1 instead. I trust you get the idea.
If your C++ function treats its parameter as an in/out parameter then using ref on the C# side is correct. If its actually an out parameter then change the code to be like this:
[DllImport("VistaGMMDLL.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int function(out RESOURCE_PARAMETERS parameters);
RESOURCE_PARAMETERS parameters;
int result = function(out parameters);
Try in this way:
[StructLayout(LayoutKind.Sequential)]
public struct RefPar
{
UInt32 uint1;
UInt32 unti2;
....
}
[DllImport("VistaGMMDLL.dll", EntryPoint = "function"]
unsafe static extern int function(IntPtr params);
//calling
//fill the refParStructure
//create the IntPtr
refParStruct rs = new RefPar();
IntPtr refparPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(refPar)));
Marshal.StructureToPtr(refParStruct, refparPtr,false);
ret=function(refParPtr);
Let Me know if you need more details
Another very simple way to call this function is:
Create a c++ dll warapper that link your original dll and contains this function
//c++ code
function2(uint param1, uint param2.....)
{
RefPar refpar;
refpar.param1=param1
refpar.param2=param2
function(&refpar)
}
in this way you have just to import (in C#) the dll wrapper function in this way
[DllImport("wrapperdll.dll", EntryPoint = "function2"]
static extern int function2(Uint32 param1,Uint32 param2....);
that is very simple to call.
Regards

Categories

Resources