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);
Related
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).
I am developing a c .dll that should be accessed from a C# program. Ideally, the .dll should receive any struct defined in C# and do something with it. So, initially, the struct type and size is unknown for the C dll. I was able to pass the struct through the C extern function, and it is supossed to be received alright, but, is there a way to find out the size and characteristics of this receive structure? is there a way to iterate over its members?
This is the C function defined for the dll
extern int __cdecl testCSharp(mystruct * Test){
//sizeof(Test) is 4, so it is ok
for(int i=0;i < sizeof(Test) ; i++){
char * value = (char*) Test; //This access the first element.
cout << value << endl; //Prints "some random string", so, it is received ok
}
return 1;
}
This is the C# code
[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi)]
unsafe public struct myStruct{
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 100)]
public string value1;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 100)]
public string value2;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 100)]
public string value3;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 100)]
public string value4;
};
[DllImport("SMKcomUploadDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int testCSharp(ref myStruct message);
static void Main()
{
int result;
myStruct message = new myStruct();
message.value1 = "Some randome string";
message.value2 = "0";
message.value3 = "olkujhikvb";
message.value4 = "isabedfbfmlk";
result = testCSharp(ref message);
}
All the types are String in C#, and it is suppossed to remain like that, so it is the only thing I know about the struct that is going to be passed.
Any idea?
Thanks in advance
As you're marshalling them as ByValTStr with a length of 100, I'm not sure you'll be able to work any any more than what you already have (i.e. the first element).
From the MSDN (here)
.NET Framework ByValTStr types behave like C-style, fixed-size strings
inside a structure (for example, char s[5])
If you used LPStr or LPWStr null-termination instead you would be able to work out their lengths.
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've created a DLL function for using inside C# using DLLImport but having troubles in calling the method, as I'm getting memory corruption problems;
[DllImport("mydll.dll", EntryPoint = "callinmydll")]
public static extern int testdllcall(double *firstinput, long firstcount, double *secondoutput, long secondcount);
Here's part of the C++ library header;
extern "C" {
mydll_API int callinmydll(double *in, long firstcount, double *out, long secondcount);
}
Implementation.
mydll_API int callinmydll(double *in, long firstcount, double *out, long secondcount)
{
for( int i =0 ; i < 10 ; i++ )
{
*(out + i) = (*(in + i) + 10 );
}
return 0;
}
Now when my DLLImport function calls the callinmydll function and passes valid data to it, this is where things get interesting. The pointer in contains data, as does firstcount. Although everything beyond this point is corrupted. Why? Curiously I rearrange my function to be double, double*, long, long now the corruption happens after third parameter. I'm curious as to what's going on, as I'm passing valid data; two valid pointers, and int cast to int64.
Help!
In Win32 C, a long is still 32-bits. Your C# signature is using a long which is 64-bits. Your second and forth parameter should be an int in the C# signature.
See this table for more information.
So your signature looks like so:
[DllImport("mydll.dll", EntryPoint = "callinmydll")]
public static extern int testdllcall(double *firstinput, int firstcount, double *secondoutput, int secondcount);
Additionally, make sure your calling convention is correct as Ramhound pointed out in the comments. Your C function looks like it is using the CDecl convention, and .NET defaults to StdCall. You can set the calling convention in the attribute:
[DllImport("mydll.dll", EntryPoint = "callinmydll", CallingConvention = CallingConvention.Cdecl)]
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.