I have some legacy code I want to port to C#. I cannot modify the C++ code, I just have to make do with what I'm given.
So, the situation. I'm using SwIG, and I came across this function:
void MarshalMe(int iNum, FooClass** ioFooClassArray);
If I ran SWIG over this, it wouldn't know what to do with the array, so it will create a SWIGTYPE_p_pFooClass. Fair enough!
C# code for this would look like
void MarshalMe(int iNum, SWIGTYPE_p_p_FooClass ioFooClassArray); // Not ideal!
There are some techniques for marshalling this kind of code correctly, so I tried a few of them:
%typemap(ctype) FooClass** "FooClass**"
%typemap(cstype) FooClass** "FooClass[]"
%typemap(imtype, inattributes="[In, Out, MarshalAs(UnmanagedType.LPArray)]") FooClass** "FooClass[]"
%typemap(csin) FooClass** "$csinput"
%typemap(in) FooClass** "$1 = $input;"
%typemap(freearg) FooClass** ""
%typemap(argout) FooClass** ""
This effectively creates a nicer signature:
void MarshalMe(int iNum, FooClass[] ioFooClassArray); // Looks good! Would it work?
However, when I try to run it, I get the following error:
{"Exception of type 'System.ExecutionEngineException' was thrown."}
Any ideas about the actual typemap?
The exception tells you that the function has corrupted the garbage collected heap. It writing past the end of the array. If it is actually a FooClass[], big question, then you'll have to create the array first:
FooClass[] array = new FooClass[666];
MarshalMe(42, array);
Which assumes that the function will fill the array with FooClass objects. The size of the array really matters here, you'll have to have some kind of idea how many elements you'll get back. That can only work reliable if "iNum" is an argument that says how long the array is. Pass array.Length.
It could also mean that the function will create the array itself and return a pointer to it. You're really screwed if that's the case, you cannot release the memory for the array.
[Swig] Java: Another way to pass pointer-to-pointer
I had a similar problem with a C-function (API) which returned a pointer-to-pointer as an input argument. I was trying to call the C-function from JAVA and I had no way modify the API.
The API.h header file contained:
extern int ReadMessage(HEADER **hdr);
The original C-call looked like:
HEADER *hdr;
int status;
status = ReadMessage(&hdr);
The function of the API was to store data at the memory location specified by the pointer-to-pointer.
I tried to use SWIG to create the appropriate interface file. SWIG.i created the file SWIGTYPE_p_p_header.java from API.h. The problem is the SWIGTYPE_p_p_header constructor initialized swigCPtr to 0.
The JAVA call looked like:
SWIGTYPE_p_p_header hdr = new SWIGTYPE_p_p_header();
status = SWIG.ReadMessage(hdr);
But when I called the API from JAVA the ptr was always 0.
I finally gave up passing the pointer-to-pointer as an input argument. Instead I defined another C-function in SWIG.i to return the pointer-to-pointer in a return value. I thought it was a Kludge ... but it worked!
You may want to try this:
SWIG.i looks like:
// return pointer-to-pointer
%inline %{
HEADER *ReadMessageHelper() {
HEADER *hdr;
int returnValue;
returnValue = ReadMessage(&hdr);
if (returnValue!= 1) hdr = NULL;
return hdr;
}%}
The inline function above could leak memory as Java won't take ownership of the memory created by ReadMessageHelper, since the HEADER instance iscreated on the heap.
The fix for the memory leak is to define ReadMessageHelper as a newobject in order for Java to take control of the memory.
%newobject ReadMessageHelper();
JAVA call now would look like:
HEADER hdr;
hdr = SWIG.ReadMessageHelper();
If you are lucky, as I was, you may have another API available to release the message buffer. In which case, you wouldn’t have to do step 4.
William Fulton, the SWIG guru, had this to say about the approach above:
“I wouldn't see the helper function as a kludge, more the simplest solution to a tricky problem. Consider what the equivalent pure 100% Java code would be for ReadMessage(). I don't think there is an equivalent as Java classes are passed by reference and there is no such thing as a reference to a reference, or pointer to a pointer in Java. In the C function you have, a HEADER instances is created by ReadMessage and passed back to the caller. I don't see how one can do the equivalent in Java without providing some wrapper class around HEADER and passing the wrapper to the ReadMessage function. At the end of the day, ReadMessage returns a newly created HEADER and the Java way of returning newly created objects is to return it in the return value, not via a parameter.”
Using SWIG typemap to pass pointer-to-pointer:
Here is another approach using typemaps. It is targetting Perl, not Java, but the concepts are the same. And I finally managed to get it working using typemaps and no helper functions:
For this function:
typedef void * MyType;
int getblock( int a, int b, MyType *block );
I have 2 typemaps:
%typemap(perl5, in, numinputs=0) void ** data( void * scrap )
{
$1 = &scrap;
}
%typemap(perl5, argout) void ** data
{
SV* tempsv = sv_newmortal();
if ( argvi >= items ) EXTEND(sp,1);
SWIG_MakePtr( tempsv, (void *)*$1, $descriptor(void *), 0);
$result = tempsv;
argvi++;
}
And the function is defined as:
int getblock( int a, int b, void ** data );
In my swig .i file. Now, this passes back an opaque pointer in the argout typemap, becaust that's what useful for this particular situation, however, you could replace the SWIG_MakePtr line with stuff to actually do stuff with the data in the pointer if you wanted to. Also, when I want to pass the pointer into a function, I have a typemap that looks like this:
%typemap(perl5, in) void * data
{
if ( !(SvROK($input)) croak( "Not a reference...\n" );
if ( SWIG_ConvertPtr($input, (void **) &$1, $1_descriptor, 0 ) == -1 )
croak( "Couldn't convert $1 to $1_descriptor\n");
}
And the function is defined as:
int useblock( void * data );
In my swig .i file.
Obviously, this is all perl, but should map pretty directly to Java as far as the typemap architecture goes. Hope it helps...
I managed to solve this using Swig Managed Arrays and Pinning documentation.
Given a function in C++
void myArrayCopy(int *sourceArray, int *targetArray, int nitems);
Declare the method as unsafe in C#
%csmethodmodifiers myArrayCopy "public unsafe";
Add the appropriate typemaps
%include "arrays_csharp.i"
%apply int FIXED[] {int *sourceArray}
%apply int FIXED[] {int *targetArray}
As a result, we get the following method in the module class:
public unsafe static void myArrayCopy(int[] sourceArray, int[] targetArray, int nitems)
{
fixed ( int *swig_ptrTo_sourceArray = sourceArray )
{
fixed ( int *swig_ptrTo_targetArray = targetArray )
{
examplePINVOKE.myArrayCopy((IntPtr)swig_ptrTo_sourceArray,
(IntPtr)swig_ptrTo_targetArray,
nitems);
}
}
}
In practice this might differ a little with FooClass** but Swig does support direct pointer to pointer marshalling, which also avoids a copy as well so can be considered better performance
Related
I've read the various MSDN pages on C++ Interop with P/Invoke here and here but I am still confused.
I have some large arrays of doubles that I need to get into native code, and some resulting arrays that need to get back. I do not know the sizes of the output arrays in advance. For simplicity, I will use only a single array in the example. The platform is x64; I read that marshalling internals are quite different between 32- and 64-bit environments so this might be important.
C#
[DllImport("NativeLib.dll")]
public static extern void ComputeSomething(double[] inputs, int inlen,
[Out] out IntPtr outputs, [Out] out int outlen);
[DllImport("NativeLib.dll")]
public static extern void FreeArray(IntPtr outputs);
public void Compute(double[] inputs, out double[] outputs)
{
IntPtr output_ptr;
int outlen;
ComputeSomething(inputs, inputs.Length, out output_ptr, out outlen);
outputs = new double[outlen];
Marshal.Copy(output_ptr, outputs, 0, outlen);
FreeArray(output_ptr);
}
C++
extern "C"
{
void ComputeSomething(double* inputs, int input_length,
double** outputs, int* output_length)
{
//...
*output_length = ...;
*outputs = new double[output_length];
//...
}
void FreeArray(double* outputs)
{
delete[] outputs;
}
}
It works, that is, I can read out the doubles I wrote into the array on the C++ side. However, I wonder:
Is this really the right way to use P/Invoke?
Aren't my signatures needlessly complicated?
Can P/Invoke be used more efficiently to solve this problem?
I believe I read that marshalling for single dimensional arrays of built-in types can be avoided. Is there a way around Marshal.Copy?
Note that we have a working C++/Cli version, but there are some problems related to local statics in third-party library code that lead to crashes. Microsoft marked this issue as WONTFIX, which is why I am looking for alternatives.
It is okayish. The complete lack of a way to return an error code is pretty bad, that's going to hurt when the arrays are large and the program runs out of memory. The hard crash you get is pretty undiagnosable.
The need to copy the arrays and to explicitly release them doesn't win any prizes of course. You solve that by letting the caller pass a pointer to its own array and you just write the elements. You however need a protocol to let the caller figure out how large the array needs to be, that is going to require calling the method twice. The first call returns the required size, the second call gets the job done.
A boilerplate example would be:
[DllImport("foo.dll")]
private static int ReturnData(double[] data, ref int dataLength);
And a sample usage:
int len = 0;
double[] data = null;
int err = ReturnData(data, ref len);
if (err == ERROR_MORE_DATA) { // NOTE: expected
data = new double[len];
err = ReturnData(data, len);
}
No need to copy, no need to release memory, good thing. The native code can corrupt the GC heap if it doesn't pay attention to the passed len, not such a good thing. But of course easy to avoid.
If it were practical to separate the code that determines the output length from the code that populates the output then you could:
Export a function that returned the output length.
Call that from the C# code and then allocate the output buffer.
Call the unmanaged code again, this time asking it to populate the output buffer.
But I'm assuming that you have rejected this option because it is impractical. In which case your code is a perfectly reasonable way to solve your problem. In fact I would say that you've done a very good job.
The code will work just the same in x86 once you fix the calling convention mismatch. On the C++ side the calling convention is cdecl, but on the C# side it is stdcall. That doesn't matter on x64 since there is only one calling convention. But it would be a problem under x86.
Some comments:
You don't need to use [Out] as well as out. The latter implies the former.
You can avoid exporting the deallocator by allocating off a shared heap. For instance CoTaskMemAlloc on the C++ side, and then deallocate with Mashal.FreeCoTaskMem on the C# side.
If you knew the array size beforehand, you could write a C++/CLI DLL that takes the managed array as parameter, pins it, and calls the native C++ DLL on the pinned pointer it obtains.
But if it's output-only, I don't see any version without a copy. You can use a SAFEARRAY so P/Invoke does the copying instead of you, but that's all.
I'm trying to figure out how to return a complex object from a C++ dll to a calling C# application. I have a simple method which is returning an int that is working fine. Can anyone tell me what I'm doing wrong?
C# Application:
class Program
{
static void Main(string[] args)
{
// Error on this line: "PInvoke: Cannot return variants"
var token = LexerInterop.next_token();
}
}
C# LexerInterop Code:
public class LexerInterop
{
[DllImport("Lexer.dll")]
public static extern object next_token();
[DllImport("Lexer.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int get_int(int i);
}
C++ Lexer.cpp
extern "C" __declspec(dllexport) Kaleidoscope::Token get_token()
{
int lastChar = ' ';
Kaleidoscope::Token token = Kaleidoscope::Token();
while(isspace(lastChar))
{
lastChar = getchar();
}
... Remainder of method body removed ...
return token;
}
extern "C" __declspec(dllexport) int get_int(int i)
{
return i * 2;
}
C++ Lexer.h
#include "Token.h"
namespace Kaleidoscope
{
class Lexer
{
public:
int get_int();
Token get_token();
};
}
C++ Token.h
#include <string>
namespace Kaleidoscope
{
enum TokenType
{
tok_eof = -1,
tok_def = -2,
tok_extern = -3,
tok_identifier = -4,
tok_number = -5
};
class Token
{
public:
TokenType Type;
std::string IdentifierString;
double NumberValue;
};
}
I'm sure it's something to do with the Token return type but I can't find any decent information what's missing.
Any assistance or direction is appreciated!
You cannot return C++ objects to non-C++ callers. (It doesn't even work when you try to export a C++ object to a caller if it's compiled with a different C++ compiler.) The reason is that the actual in-memory layout of the C++ object is not standardized and different compilers may do it differently.
The .NET runtime has no idea how to deal with your object and it doesn't know about the various types that make up your token object. Similarly, std::string makes no sense to .NET, since it's a C++ type, relying on unmanaged memory allocation and with a different internal string format and different semantics than C#.
You can try turning your C++ objects into COM objects and then you can return COM interfaces to your C# caller. This will take some work since COM types are, again, incompatible with C++ types (for similar reasons as above).
You can also try to serialize your C++ object into a byte array and then just return a buffer / length to C# that will first deserialize the object into a .NET representation. You'll have to duplicate the classes you're trying to handle this way.
Yet another possibility is that you return all data as independent out parameters from your function calls - that is, you don't try to return token but return each of its properties as a separate out parameter from C++. You'll still have to convert some of your data types to something recognizable by .NET. (std::string cannot be returned, you'll have to copy it to a byte array or allocate it as a BSTR, etc.)
Finally, a fairly involved option: you can return the address of token as a void* from your C++ function and then create a number of exported plain C functions to read the various properties based on an input pointer. You'd also need a function to free up the C++ object when you're done with it. Something like:
__declspec(dllexport) TokenType WINAPI GetTokenType ( Kaleidoscope::Token* pToken )
{
return ( pToken->Type );
}
You'd then declare these functions in C# like so:
[DllImport ( "MyDll.dll" )]
public static extern int GetTokenType ( UIntPtr pObj );
The UIntPtr instance would be initialized from the void* that your next_token function returns. You'd then call each of the exported property reader functions to get the individual properties of token.
I'd probably go with a custom serializer because that's the least amount of work (in my opinion) and I think it's the cleanest solution (in terms of readability and maintainability).
Note: using COM objects could be less work but you'd have to move away from any C++ types (at least in the public interface).
Edit:
I forgot to mention earlier a somewhat obvious option - using mixed code and C++/CLI. This way you can have unmanaged code and managed code at the same time in the same DLL and you can perform all conversions in C++ code. If you define most of your classes in C++/CLI, you can just pass the relevant (managed) instances to your C# application without additional conversion.
(You will, of course, still have to convert between std:string and System.String if you use the former but at least you can have all your conversion logic in the same single project.)
This solution is simple but the resulting DLL now requires the .NET runtime.
I'm currently trying to write a wrapper for a USB camera that uses an unmanaged C DLL to interface. The supplied software is written in C++ using Qt, and I'm trying to integrate this camera in a C# application I've already written (the application already uses a different camera that is supplied with a C# interface). I've read a bunch about unmanaged/managed interfacing, but I still am getting stuck with this specific DLL.
I'm having trouble dealing with P/Invoking the DLL. I've done a bunch of reading on ways to deal with managed/unmanaged code, and it seems that since I only have the functions/parameters (and nothing else), I can't write a C++/CLI wrapper and am forced to stick to P/Invoke. If this is untrue, some help with an alternate method would be appreciated, if more efficient.
Anyhow, I'm currently having trouble calling some (read: all) of the functions. For examples sake, let's pick this function (taken from dijSDK.h, the header file to be used when using this interface in unmanaged C++ in Qt):
/** \brief Find cameras currently connected to the host
*
* \param[out] pGuidList List of unique identifiers of connected cameras; memory is allocated by user
* \param[in,out] pNumGuids Pointer to the number of elements in pGuidList to limit the search
* \n Pointer to the number of cameras found
* \param[in] mask optional mask to limit the results in pGuidList
*
* \note
* - the function lists all supported cameras that are connected to the host having a driver installed
* - the cameras are identified by a string in a defined style:
* \n <i>[Name of the camera library resp. camera class]:[camera name]:[Serial number of the camera]</i>
* - the optional parameter mask may be used to limit the results returned in pGuidList;
* \n mask is in the same style as the results in pGuidList
* therefore the results can be limited to certain camera classes, camera types,
* and cameras with a given serial number */
DIJSDK_EXPORT error_t DijSDK_FindCameras(DijSDK_CamGuid* pGuidList, unsigned int* pNumGuids, const DijSDK_CamGuid mask = NULL);
DIJSDK_EXPORT is a define as follows:
#ifndef DIJSDK_EXPORT
#define DIJSDK_EXPORT externC DLLEXPORT
#endif
And the two typedefs used in the above function:
/// Return value of all DijSDK functions
/// \note All return values of type error_t can be casted to the enum <b>DijSDK_EErrorCodeList</b>, see header errorlistinstall.h
typedef int error_t;
// unique DijSDK types
/// Globally unique identifier for all supported cameras. It is used to establish a relation between <b>physical</b> and <b>logical</b> cameras.
typedef char DijSDK_CamGuid[64];
So looking at the function, the user passes in an allocated string array and the number of allocated strings in that array, and the function should return a bunch of strings and the number of strings it returns. I have absolutely no idea how to implement this in C#. I've tried different DllImport declarations of the function, like passing pGuidList as a...
StringBuilder
Where the variable I passed in was initialized with var = new StringBuilder(64) and the uint passed in was just 1.
StringBuilder[]
Where the variable I passed in was initialized with var = new StringBuilder[length], where length was the uint passed to the function.
IntPtr
Where the variable I passed in was initialized with Marshal.AllocCoTaskMem(64 * length * Marshal.SystemDefaultCharSize), where length is the same uint as above
And then passed in pNumGuids as ref uint. I don't hand in an argument for mask since it is optional, and I don't need to use it.
I constantly get a PInvokeStackImbalance since the signature of the managed import and the unmanaged DLL don't match, but I can't figure out what the correct signature is.
I've got questions about all of the functions I'm dealing with, but I'll be dealing with them one at a time. The first function I use is an "init" function that takes in a function pointer argument and a void pointer argument for a callback and it's associated data, but I can call it with no arguments if I don't need to use the callback and that is P/Invoking just fine. The function in this question is the one I need to call after to get a list of attached devices. I think if I can get this one working, I'll be able to figure out most of the rest of the functions.
The SDK and documentation I'm working with isn't publicly available online from the company that supplies it. If anyone would like it to see all of the code/documentation, I can post it on a fileserver and post a link to it.
Any help would be much appreciated! If there's anything that I left out, make sure to heckle me about it =P
Solution:
After the help, I managed to get it working:
The DllImport:
[DllImport(DLL,CallingConvention=CallingConvention.Cdecl)]
public static extern error_t DijSDK_FindCameras([Out] DijSDK_CamGuid[] pCamGuid, ref uint pNumGuids, string mask);
The struct to use for DijSDK_CamGuid:
[StructLayout(LayoutKind.Sequential,Size=64)]
public struct DijSDK_CamGuid
{
[MarshalAs(UnmanagedType.ByValArray,SizeConst=64)]
public char[] id;
}
And the findCameras() function:
private void findCameras()
{
uint cameralistlen = 5;
DijSDK_CamGuid[] cameralist = new DijSDK_CamGuid[cameralistlen];
result = Jenoptik.DijSDK_FindCameras(cameralist, ref cameralistlen, null);
}
Try declaring this way:
[DllImport("whatever.dll", CallingConvention = CallingConvention.Cdecl)]
extern public static int DijSDK_FindCameras(byte[] pGuidList, ref int pNumGuids, byte[] mask);
and using it like this:
byte[] CamGuids = new byte[64 * 32]; // Up to 32 cameras
int GuidsCount = CamGuids.Length / 64;
int result = DijSDK_FindCameras(CamGuids, ref GuidsCount, null);
Do you need to deal with the id in managed code as a string? If not, perhaps something like this would work, treating the bytes as an opaque byte array:
[StructLayout(LayoutKind.Sequential)]
public struct DijSDK_CamGuid
{
[MarshalAs(UnmanagedType.ByValAray,SizeConst=64)]
public byte[] id;
}
Then declare your p/invoke signature to use take a parameter of type:
DijSDK_CamGuid*
I've been going round and round in circles on Google on this, and I can find all kinds of discussion, lots of suggestions, but nothing seems to work. I have an ActiveX component which takes an image as a byte array. When I do a TLB import, it comes in with this signature:
int HandleImage([MarshalAs(UnmanagedType.Struct)] ref object Bitmap);
How do I pass a byte[] to that?
There's another function which can return the data with a similar signature, and it works because I can pass "null" in. The type that comes back is a byte[1..size] (non-zero bounded byte[]). But even if I try to pass in what came back, it still gets a type mismatch exception.
More details:
I've been editing the method in the IDispatch interface signature (using ILSpy to extract the interface from the auto-generated interop assembly). I've tried just about every combination of the following, it always gets Type mismatch exception:
Adding and removing the "ref"
Changing the parameter datatype to "byte[]" or "Array"
Marshalling as [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_UI1)]. After playing around with MarshalAs quite a bit, I'm becoming convinced that IDispatch does not use those attributes.
Also tried using the "ref object" interface as is, and passing it different types: byte[], Array.CreateInstance(typeof(byte) (which I think are both identical, but I found someone suggesting it, so it couldn't hurt to try).
Here's an example of Delphi code that creates a proper array to pass in:
var
image: OLEVariant;
buf: Pointer;
image := VarArrayCreate([0, Stream.Size], VarByte);
Buf := VarArrayLock(image);
Stream.ReadBuffer(Buf^, Stream.Size);
VarArrayUnlock(image);
Here's the C code to do the same thing. I guess if I can't get it to work from C#, I can invoke it through managed C++, although I'd rather have everything in one project:
long HandleImage(unsigned char* Bitmap, int Length)
{
VARIANT vBitmap;
VariantInit (&vBitmap);
VariantClear(&vBitmap);
SAFEARRAYBOUND bounds[1];
bounds[0].cElements = Length;
bounds[0].lLbound = 1;
SAFEARRAY* arr = SafeArrayCreate(VT_UI1, 1, bounds);
SafeArrayLock(arr);
memcpy(arr->pvData, Bitmap, Length);
SafeArrayUnlock(arr);
vBitmap.parray = arr;
vBitmap.vt = VT_ARRAY | VT_UI1;
long result;
static BYTE parms[] = VTS_PVARIANT;
InvokeHelper(0x5e, DISPATCH_METHOD, VT_I4, (void*)&result, parms,
&vBitmap);
SafeArrayDestroy(arr);
VariantClear(&vBitmap);
return result;
}
I finally figured out how to do it in 100% C# code. Apparently Microsoft never considered the idea that someone might use a method with this signature to pass data in, since it marshals correctly going the other direction (it properly comes back as a byte[]).
Also, ICustomMarshaler doesn't get called on IDispatch calls, it never hit the breakpoints in the custom marshaler (except the static method to get an instance of it).
The answer by Hans Passant in this question got me on the right track: Calling a member of IDispatch COM interface from C#
The copy of IDispatch there doesn't contain the "Invoke" method on IUnknown, but it can be added to the interface, using types in System.Runtime.InteropServices.ComTypes as appropriate: http://msdn.microsoft.com/en-us/library/windows/desktop/ms221479%28v=vs.85%29.aspx
That means you get 100% control over marshaling arguments. Microsoft doesn't expose an implementation of the VARIANT structure, so you have to define your own: http://limbioliong.wordpress.com/2011/09/19/defining-a-variant-structure-in-managed-code-part-2/
The input parameters of Invoke are a variant array, so you have to marshal those to an unmanaged array, and there's a variant output parameter.
So now that we have a variant, what should it contain? This is where automatic marshaling falls down. Instead of directly embedding a pointer to the SAFEARRAY, it needs a pointer to another variant, and that variant should point to the SAFEARRAY.
You can build SAFEARRAYs via P/Invoking these methods: http://msdn.microsoft.com/en-us/library/windows/desktop/ms221145%28v=vs.85%29.aspx
So main variant should be VT_VARIANT | VT_BYREF, it should point to another variant VT_UI8 | VT_ARRAY, and that should point to a SAFEARRAY generated via SafeArrayCreate(). That outermost variant should then be copied into a block of memory, and its IntPtr sent to the Invoke method.
I am trying to use a C# DLL which has multiple references to .NET classes and C# Classes in Embarcadero C++ Builder.
Things like the Point class and String class as well as Delegates.
I am wondering if the >NET references or C# ones will mess me up somehow. I am just about to getting it hooked up, but I am wondering if some of the problems I am having could be caused by C++ not wanting to play nice.
I gave a similar answer to your problem in this question.
You basically want a C++/CLI interface to your C# code.
If you want to pass a C# delegate to C++ code, you can translate it using Marshal::GetFunctionPointerForDelegate() (MSDN). That gives you a IntPtr that you can call ToPointer() on to pass in as a function pointer.
You will need to use C++/CLI to use any .NET content in C++. You might also be able to set up your own AppDomain, but those are the only two choices, as C++ has no native ability to interact with .NET, at all.
Or you can use the mono CLR embedder
Managed code can invoke unmanaged code in two ways, [using P/Invoke or] using the low-level Mono embedding API.
This works a lot like the oldfashioned embedding of a Perl, Python or Ruby 'interpreter' (actually, virtual machines) in your C/C++ executable. I don't think there is actually such a thing as a Swig(++) wrapper generator for this (yet), but here is a snippet of what a call into CIL code looks like:
class MyClass {
static void Foo (int value) {
...
}
int Bar (string name) {
...
}
}
assuming you got the corresponding MonoMethod* in foo_method and bar_method and this_arg is a MonoObject* of type MyClass, you simply execute:
/* we execute methods that take one argument */
void *args [1];
int val = 10;
/* Note we put the address of the value type in the args array */
args [0] = &val;
/* execute Foo (10);
* it's a static method, so use NULL as the second argument.
*/
mono_runtime_invoke (foo_method, NULL, args, NULL);
/* a string is a reference, so we put it directly in the args array */
args [0] = mono_string_new (domain, "Hello");
/* execute my_class_instance.Bar ("Hello");
* See the Creating Objects section to learn how to get this_arg.
*/
MonoObject *result = mono_runtime_invoke (bar_method, this_arg, args, NULL);
/* we always get a MonoObject* from mono_runtime_invoke (), so to get
* the integer value we need to unbox (which returns a pointer to
* the value stored in the object) and dereference.
*/
int int_result = *(int*)mono_object_unbox (result);
For extra entertainment value: if you AOT-compile all of your CIL code, you'll be able to statically link your assembly into your native binary (effectively doing what Managed C++ (c++-cli) calls mixed mode assemblies). Look at
mono --aot=static myassembly.dll
and
mkbundle