C# p/invoke: marshaling class between C# and C++ class
Jin
Tu
Blog:
http://www.inblogs.net/havdone
1.0 Abstraction
This article provides basic information to realize how pass c# function pointer and class object to a c++ function as parameters.
2.0 Mechanism
The c++ function parameters could be an object pointer or a function pointer. Passing c# class object or a function as parameter to c++ function actually is passing a C# reference or a function pointer. To realize this mechanism properly, first should make sense of the basic knowledge in its scope.
3.0 Basic knowledge
3.1 C# pointer (*)
The c# unsafe code can use pointer directive
(*). However, c# pointer has many constraints. As stated in MSDN, “C# pointer types do not
inherit from object and no conversions exist between pointer types and object.
Also, boxing and unboxing do not support pointers. However, you can convert
between different pointer types and between pointer types and integral types.”
“Any of the following types may be a pointer type:
- sbyte, byte, short, ushort, int, uint, long, ulong,
char, float, double, decimal, or bool.
- Any enum type.
- Any pointer type.
- Any user-defined struct type that contains fields of
unmanaged types only.”
That means c# cannot make conversion
from class object reference to pointer type (*). Using “&” operator on a
reference, will cause a compiling error “Cannot take the address of, get the
size of, or declare a pointer to a managed type”.
3.2 Reference parameters in C#
While
reference object, such as a class object, is pass to function as a parameter,
actually the address of object is passed, as reference is a pointer type.
3.3 Class
data type in memory
[System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)]
class Class1
{
public int
i;
private int
_i;
public System.Collections.ArrayList arr = new
System.Collections.ArrayList();
public void
test()
{
Console.WriteLine("hello world!");
}
public Class1()
{
_i = 400;
}
};
The above graph shows how a Class1 object is allocated memory.
3.4 Delegate
in C#
“The delegate
keyword is used to declare a reference type that can be used to encapsulate a
named or an anonymous method.”(MSDN)
4.0 Marshaling Example
This example passes class object that mentioned in chapter 3.3, to a c++ dll. C++ code use a class type object to accept the fields values of c# object and use a function pointer parameter to accept c# object function.
/////////////////////////////////////////////////////////
//C# Code
///////////////////////////////////////////////////////////
using
System;
using
System.Collections.Generic;
using
System.Linq;
using
System.Text;
using
System.Runtime.InteropServices;
namespace
interoperation
{
class Program
{
//////////////////////////////////////////////////////////
//P/Invoke a c++ function declare
///////////////////////////////////////////////////////////
//if do not explcit point out entrypoint
//the c# function name must be the same with
c++ code
[System.Runtime.InteropServices.DllImport("..\\..\\..\\debug\\sort.dll")]
public static
extern void
func(Class1 arrays, Program.delgFunc func);
//////////////////////////////////////////////////////////
//C# function pointer declare
///////////////////////////////////////////////////////////
public delegate
void delgFunc();
static void
Main(string[] args)
{
Class1
obj = new Class1();
Program.delgFunc delg = obj.test;
//pass a
class reference and a function pointer to c++ function
func(obj, delg);
}
};
[System.Runtime.InteropServices.StructLayout(LayoutKind.Sequential)]
class Class1
{
public void
test()
{
Console.WriteLine("c# function is called");
Console.WriteLine("_i="+this._i.ToString());
}
public System.Collections.ArrayList arr = new
System.Collections.ArrayList();
private int
_i;
public int
i;
public Class1()
{
_i = 400;
i = 333;
}
};
}
/////////////////////////////////////////////////////////
//C++ Code
///////////////////////////////////////////////////////////
// Sort.cpp : Defines the
exported functions for the DLL application.
//
#include "stdafx.h"
#include <iostream>
using namespace std;
extern "C"
{
//////////////////////////////////////////////////////////////////////
//To accept C# class object,
//remember c++ fileds are allocated memory sequentially
///////////////////////////////////////////////////////////////////////
class Class1
{
public :
void * unknow;//accept arr
of c# object
int _i;//accept _i of c#
object
int i; //accept i of c# object
};
__declspec(dllexport) void
func(Class1* class1,void (* test)())
{
cout<<"(C# object is passed !) The value of private field '_i': "<<class1->_i<<"\n";
cout<<"(C# object is passed !) The value of public field 'i': "<<class1->i<<"\n";
test();
}
}
The output like this: