Inheriting From a Native C++ Class in C#

Visual CPP Team

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(“~CSimpleClassn”);

      }

      void M1()

      {

            printf(“C++/CSimpleClass::M1()n”);

            V0();

            V1(value);

            V2();

      }

      virtual void V0()

      {

            printf(“C++/CSimpleClass::V0()n”);

      }

      virtual void V1(int x)

      {

            printf(“C++/CSimpleClass::V1(%d)n”, x);

      }

      virtual void 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.

??0CSimpleClass@@QAE@ABV0@@Z
??0CSimpleClass@@QAE@H@Z
??1CSimpleClass@@QAE@XZ
??4CSimpleClass@@QAEAAV0@ABV0@@Z
??_7CSimpleClass@@6B@
?M1@CSimpleClass@@QAEXXZ
?V0@CSimpleClass@@UAEXXZ
?V1@CSimpleClass@@UAEXH@Z
?V2@CSimpleClass@@UAEXXZ

These are decorated (i.e. “mangled”) names.  For most of these, you can probably guess what the name is actually referring to. 

(Note: Name mangling may change between versions of C++ and mangling is different between x86, x64, and Itanium platforms.  The example here works on both VS2008 and the CTP release of VS2010.)

There is a nifty tool called undname.exe that ships with Visual Studio, which can take a mangled name and undecorate it.  Running it on each of the names above gives the corresponding output.

public: __thiscall CSimpleClass::CSimpleClass(int)

public: __thiscall CSimpleClass::~CSimpleClass(void)

public: class CSimpleClass & __thiscall CSimpleClass::operator=(class CSimpleClass const &)

const CSimpleClass::`vftable’

public: void __thiscall CSimpleClass::M1(void)

public: virtual void __thiscall CSimpleClass::V0(void)

public: virtual void __thiscall CSimpleClass::V1(int)

public: virtual void __thiscall CSimpleClass::V2(void)

 

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;

public int 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.

public unsafe class CSimpleClass : IDisposable

{

    private __CSimpleClass* _cpp;

 

    // CSimpleClass constructor and destructor

    [DllImport(“cppexp.dll”, EntryPoint = “??0CSimpleClass@@QAE@H@Z”, CallingConvention = CallingConvention.ThisCall)]

    private static extern int _CSimpleClass_Constructor(__CSimpleClass* ths, int value);

    [DllImport(“cppexp.dll”, EntryPoint = “??1CSimpleClass@@QAE@XZ”, CallingConvention = Cal lingConvention.ThisCall)]

    private static extern int _CSimpleClass_Destructor(__CSimpleClass* ths);

 

    //      void M1();

    [DllImport(“cppexp.dll”, EntryPoint = “?M1@CSimpleClass@@QAEXXZ”, CallingConvention = CallingConvention.ThisCall)]

    private static extern void _M1(__CSimpleClass* ths);

 

    public CSimpleClass(int value)

    {

        //Allocate storage for object

        _cpp = (__CSimpleClass*)Memory.Alloc(sizeof(__CSimpleClass));

        //Call constructor

        _CSimpleClass_Constructor(_cpp, value);

    }

    public void Dispose()

    {

        //call destructor

        _CSimpleClass_Destructor(_cpp);

        //release memory

        Memory.Free(_cpp);

        _cpp = null;

    }

    public void M1()

    {

     

Posted in C++

0 comments

Discussion is closed.

Feedback usabilla icon