Inheriting From a Native C++ Class in C#

Hi, this is Jim Springfield, an architect on the Visual C++ team.I have blogged in the past about our IDE and Intellisense work.I am still heavily focused on that and we are working hard to deliver an improved experience, but this post is about a completely different topic.A few months ago, I started thinking about how to access C++ classes from managed code and came up with this technique, which I haven’t seen mentioned anywhere else.

There are many ways that native code and managed code can interact and call each other.If you have native code that you want to call from C# you have several choices depending on the nature of the API.If you have a flat “C” API, you can use P/Invoke to directly call the API.If the native code is exposed using COM, the CLR’s COM Interop can provide access.If you have a C++ class, you could go add COM support, or write a custom wrapper using C++/CLI and expose a new managed class.

I really wanted something more direct than these.Initially, I was just trying to see if I could call a native C++ class from C#, but as I started playing with it, I realized that I could actually “inherit” from the native class.I put “inherit” in quotes, because you could make an argument that it isn’t truly inheritance, but I will let the reader make the final decision.

Let’s say I have a C++ class exposed from a DLL that I want to consume in C#.The class looks like the following.

class__declspec(dllexport) CSimpleClass {

public:

int value;

CSimpleClass(int value) : value(value)

{

}

~CSimpleClass()

{

printf("~CSimpleClass\n");

}

void M1()

{

printf("C++/CSimpleClass::M1()\n");

V0();

V1(value);

V2();

}

virtualvoid V0()

{

printf("C++/CSimpleClass::V0()\n");

}

virtualvoid V1(int x)

{

printf("C++/CSimpleClass::V1(%d)\n", x);

}

virtualvoid V2()

{

printf("C++/CSimpleClass::V2()\n", value);

}

};

The __declspec(dllexport) means that the class is exported from the DLL.What this really means is that all of the class methods are exported from the DLL.If I look at the list of exports using dumpbin.exe or depends.exe, I see the following list of exports.

Other than the methods we explicitly defined, there is also a compiler generated assignment operator and a reference to the vtable for this class.OK, so I know that using P/Invoke, C# can call into native DLL entry points, and I just happen to have a list of native entry points.

First, however, we need to define a structure in C# that corresponds to the native class.Our native class only has one field: an int.However, it does have virtual methods, so there is also a vtable pointer at the beginning of the class.

(Note: I am only dealing with single inheritance here.With multiple inheritance, there are multiple vtables and vtable pointers.)

[StructLayout(LayoutKind.Sequential, Pack = 4)]

public unsafe struct __CSimpleClass

{

public IntPtr* _vtable;

publicint value;

}

Next, I am going to define a C# class that wraps the native class and mimics it.I want to expose synchronous destruction, so the C# equivalent of that is implementing IDisposable, which I do here.I also create a matching constructor and the “M1” method of CSimpleClass.I use “DllImport” to specify the DLL name, entrypoint, and calling convention.The “ThisCall” convention is the default for C++ member functions.

(Note: to be safer, I should explicitly specify calling conventions and structure packing in my native code, but that is left out for brevity.If they aren’t explicitly specified, compiler options can change the defaults.)

There are calls in the code below to Memory.Alloc and Memory.Free.These were implemented by me and just forward to HeapAlloc/Free in kernel32.dll.

OK, this is pretty cool, right?That’s what I was thinking anyway.A couple of days later, I picked up this code again and started thinking that it would be really cool if I could override a virtual function.I’ve already got the vtable pointer in my __CSimpleClass structure.I know that the vtable pointer points to an array of function pointers, at least in the simple single inheritance case.(Multiple inheritance and virtual inheritance can add some significant wrinkles to this.)If I can change a function in the vtable, then I’ve overridden it.The vtables themselves are shared by all instances of a class, so I can’t just go pound a slot in the vtable with my own function pointer.I need to actually create my own vtable.

I need to construct an array of native pointers to my virtual method overrides and replace the vtable pointer with a pointer to my vtable.As it turns out, the .Net libraries provides a mechanism to implement callbacks from native code.This is Marshal.GetFunctionPointerForDelegate, and it works just fine for our needs.

First of all, we need to use DllImport to get access to the virtual functions we are overriding.This is just like what we did to access the M1 method above.The example below only shows the code for V1, but we actually need it for V0 and V2 as well.I chose V1 for the example as it is the only virtual that takes a parameter.The others take no arguments.

Now, we need to implement our override in the managed version of CSimpleClass.It simple forwards to the _V1 that we defined above, which is a direct call to the native version in cppexp.dll.

publicvirtualvoid V1(int i)

{

_V1(_cpp, i);

}

The tricky part is to get our new virtual function V1 into the vtable.This can be done by creating a delegate in our class.We declare a delegate and specify an instance of it.Again, we need to do this for V0 and V2 as well.

publicdelegatevoidV1_Delegate(int i);publicV1_Delegate _v1_Delegate;

In our C# CSimpleClass constructor, we need to create the delegates, use Marshal.GetFunctionPointerForDelegate for each delegate, put them into an array, and override the vtable pointer in the native class.Here is what the final class looks like.We remember the old vtable pointer as well, so that we can reset it in the Dispose method to the old value.C++ differs from C# in this regard in that as an object is constructed, its vtable pointer will change to match the level in the inheritance.If you look closely, you will see two other helper functions that I’ve defined:InitVtable and ResetVtable.InitVtable does the work of copying the function pointers from the managed array into some native memory and then patching the vtable of the object.ResetVtable puts the old vtable pointer back and frees the memory of the created vtable.In C++, a single copy of the vtable is shared by all instances of a class, but here we create a unique vtable for each instance.This is needed as the delegates encompass the actual managed object itself rather than just a pointer to a method that takes a “this” pointer.We don’t actually use the “this” pointer that is passed to us from native code as the delegate implicitly knows the managed object and the managed object contains a pointer to the native object.

We have a managed CSimpleClass with virtual methods that can be overridden in a derived class.If we create a new C# class that inherits from CSimpleClass, we can override any virtual functions.In CSimpleClassEx, we are overriding V2 and writing out some text.

classCSimpleClassEx : CSimpleClass

{

public CSimpleClassEx(int value)

: base(value)

{

}

publicoverridevoid V2()

{

Console.WriteLine("C#/CSimpleClassEx.V2()");

}

}

If we create in instance of CSimpleClassEx and call M1, we now get the following output.

So, what do you think?Is it really inheritance?Is it just a stupid trick?It definitely requires a lot of manual code writing to make this work, but let’s do some blue sky thinking for a bit.It is easy to get a list of exports from the DLL, and the mangled names encapsulate a good bit of information including calling convention, name, return type, parameters.I could probably write a tool to generate this.And if I have the PDB, I could get the structure of the class including data members, structure packing, etc.

Now, back to working on C++ IDE performance and scalability for Visual Studio 2010.

> C++/CLI is only viable for MS .Net, not Mono. This could enable cross platform wrappers of C++ classes?

First, it wouldn't enable cross-platform wrappers, because name mangling (and potentially vtable layout for more complicated cases such as multiple virtual inheritance) is different between C++ compilers. Furthermore, there's no guarantee that it won't change in future compiler versions.

On the other hand, Mono guys actually support C++/CLI so long as you do not use mixed-code assemblies. Which you can do in this case - reuse the header with C++ declaration from the native library in a program that you compile with /clr:safe, and effectively let the compiler generate all those CLR struct declarations for you.

My latest in a series of the weekly, or more often, summary of interesting links I come across related to Visual Studio. Channel 9 Stuff: Drag-Drop Data Binding Comes to WPF in Visual Studio 2010 This Week on C9- The Thanksgiving Episode Rico Mariani-

Brian Bourke-Martin

15 Dec 2008 3:58 PM

When you define the struct which represents the C++ class that you pass into the new operator and the methods, is this required outside of knowing the correct amount of memory to allocate? Can I get away with tossing around an IntPtr and having the size declared in a constant?

Christophe Pichaud

18 Dec 2008 5:57 AM

Nice to proove it works but not pretty cool for real app. The vtable mecanism... ugh...

SWIG has done this in a similar manner, but in a very automated fashion. I believe it also supports inheritance. www.swig.org

^BB^

2 Jan 2009 9:17 PM

And then a parameter was added to one of the methods in the C++ class.

Can you see yourself updating mangled names?

This article smells like bad practise to me.

Jey

23 Jan 2009 1:39 AM

we have an unmanaged class with 3 private varible..the varibales are initialized in constructor (takes 3 params) ... we wrote a unmanged function which creates the instance of class and retunrs the object pointer. this fn takes in 3 params and passes it to the constructor while creating the object...

no wwhen we call it from vb.net and and pass 3 params... the value goes in... but when we try to read it later it comes out as garbage... any ideas???

Micah

23 Jan 2009 11:07 PM

I'm curious; if I have a native C++ interface and I use this technique to implement it's member functions in C#, can I then pass an instance of my "derived" class to a native C++ function that expects an instance of the C++ interface?

Example:

--------

//C++ (unmanaged, dll)

// Exported class

class Foo

{

virtual void Bar();

}

// Exported function

void Baz(Foo* foo)

{

foo.Bar();

}

--------

// C# code

// Replace the following pseudo-code with the code above:

// C# implementation of the C++ native Foo class.

class MyFoo : Foo

{

virtual void Bar()

{

Console.WriteLine("Hello C#!");

}

}

class Main

{

public Main()

{

// Call the exported function, passing it a managed instance of the interface.

Baz(new MyFoo());

}

}

[System.Runtime.InteropServices.DllImport("MyDLL.dll")]

static extern void Baz(Foo* foo);

Zy

5 Feb 2009 5:31 AM

Hi, im a beginner in programming, i want to call a c++ function from C#. Is it possible?