1. Introduction

There are many reasons why you would want to reuse unmanaged C/C++ libraries; the most important one is perhaps that you want to use existing tools, utilities, and classes written in unmanaged C/C++. They could be third-party tools or in-house libraries. When choosing an approach to reusing unmanaged libraries, you normally have three options:

IJW or It Just Works. This is one of the greatest features that .NET Framework has provided to developers. You just recompile the old code on the new .NET platform. No or little changes are necessary. Don't forget though; it works in the C++ language only.

COM. The COM model works on both the unmanaged and managed environments. It's straightforward to perform a COM Invoke on .NET. But, if your unmanaged classes are not COM-ready, you probably won't rewrite all the old code to support COM.

P/Invoke or Platform Invoke. This mechanism allows you to import a class as functions at the attribute level. Basically, you import class methods one by one as individual functions, as you do with Win32 APIs.

If your unmanaged C++ libraries are not COM-ready, you can choose between IJW and P/Invloke. Also, you may combine the two approaches in your importing practice. As IJW requires C++ source code, if you don't have the source code, P/Invoke probably is the only option available. Using Win32 API via [DllImport] attributes is a typical example of P/Invoke in .NET development.

This article will discuss how we can use unmanaged C++ classes exported from a DLL. No source code for the unmanaged C++ libraries are required to be present. In particular, I will demonstrate how to wrap up your unmanaged classes into managed ones so that any .NET application can use them directly. I will take a practical approach and omit theoretical discussions where possible. All the samples and source code provided in this article are simple and for tutorial purposes only. In order to use the source code included in the article, you should have Visual Studio 2005 and .NET Framework 2.0 installed. However, the wrapping technique remains the same on VS 2003 and .NET Framework 1.x. The unmanaged DLL has been compiled on Visual C++ 6.0, which is not required if you don't recompile the unmanaged source.

2. Sample Unmanaged C++ Library

The following segment is the definition of a base class "Vehicle" and its derived class "Car":

// The following ifdef block is the standard way of creating macros which make exporting
// from a DLL simpler. All files within this DLL are compiled with the CPPWIN32DLL_EXPORTS
// symbol defined on the command line. this symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see
// CPPWIN32DLL_API functions as being imported from a DLL, whereas this DLL sees symbols
// defined with this macro as being exported.
#ifdef CPPWIN32DLL_EXPORTS
#define CPPWIN32DLL_API __declspec(dllexport)
#else#define CPPWIN32DLL_API __declspec(dllimport)
#endif// This class is exported from the CppWin32Dll.dllclass CPPWIN32DLL_API Vehicle
{
public:
Vehicle(char* idx);
// Define the virtual destructorvirtual ~Vehicle();
char* GetId() const;
// Define a virtual method
virtualvoid Move();
protected:
char* id;
};
class CPPWIN32DLL_API Car : public Vehicle
{
public:
~Car();
// Override this virtual methodvoid Move();
};

By all means, the two classes are very simple. However, they bear two most important characteristics:

The base class contains a virtual destructor.

The derived class overrides a virtual method of the base class.

To demonstrate the invoke sequence, I've inserted a printf statement in each method. For your reference, here is the complete source of "CppWin32Dll.cpp":

I have built the two classes into a Win32 DLL called "CppWin32Dll.dll" on Visual C++ 6.0. All our importing work will be based on this DLL and the header, "CppWin32Dll.h". We are not going to use the unmanaged source hereafter.

As with all unmanaged DLLs, we cannot use "CppWin32Dll.dll" as an assembly/reference. Although P/Invoke allows us to import functions exported by the DLL, we cannot import classes. What we can do is import all the methods in a class and wrap them in a managed class, which then can be used by .NET applications written in any .NET compatible language, C++, C#, VB, or J#.

3. Retrieve Exported Information from the DLL

As the first step, we are going to import class methods from the DLL. As we don't have access to the source code, we use the Microsoft dumping tool "dumpbin.exe" to retrieve the decorated name for each function from the DLL. After executing "dumpbin /exports CppWin32Dll.dll", we get:

The ordinal segment contains all the names for all the functions. Although it lists all the functions from the DLL, you should determine which functions are accessible methods based on the class definitions in the header. Mapping of the mangled names to the class members is listed in the following table:

C++ Decorated Name

Class Member

Note

??0Vehicle@@QAE@ABV0@@Z

Default constructor

Added by compiler

??0Vehicle@@QAE@PAD@Z

Vehicle::Vehicle(char *)

??1Vehicle@@UAE@XZ

Vehicle::~Vehicle()

??4Vehicle@@QAEAAV0@ABV0@@Z

Class default structure

Added by compiler

??_7Vehicle@@6B@

Virtual table (VTB)

Added by compiler

?GetId@Vehicle@@QBEPADXZ

Vehicle::GetId()

?Move@Vehicle@@UAEXXZ

Vehicle::Move()

??0Car@@QAE@ABV0@@Z

Default constructor

Added by compiler

??1Car@@UAE@XZ

Car::~Car()

??4Car@@QAEAAV0@ABV0@@Z

Class default structure

Added by compiler

??_7Car@@6B@

Virtual table (VTB)

Added by compiler

?Move@Car@@UAEXXZ

Car::Move()

Be wary that the exact details of "name mangling" are compiler-dependent, and they may vary from one version to another. Interestingly, if you add/remove/change class members to the Win32 project, you will notice that the new DLL may have different "mangled names" for the constructor or other class members. This is because the "mangled name" contains all the information about the class member and its relationship with the rest of the class. Any changes to this relationship will be reflected in its "mangled name" in the new DLL.

Anyway, it appears the unmanaged DLLs built by VC++ 6.0 on NT-based platforms (NT/2000/XP) will work with .NET applications. At the time of this writing, it is difficult to verify whether unmanaged DLLs built by older compilers on older Windows will still work. This is more like a compatibility issue.

Don't import compiler-added members. They are mostly internals, and not all of them are accessible.

Every imported function takes the current pointer as an input parameter, in addition to the original input parameter(s). The DLL uses this pointer to call the function properly via the decorated name "@Vehicle" or "@Car", which is how the C++ compiler handles classes internally.

I added a virtual table or VTB manually to handle virtual methods, as an emulation of C++ virtual members internal handling. The VTB contains function pointers for all virtual methods.

As you may notice, I defined two extra methods: Vdtor and VMove, each to call its corresponding import. This actually is a hack/patch of function pointers in P/Invoke. As we know, a function pointer points to (the address of) a function. Here, it would point to an import, which doesn't have an address at compile time. It gets the address only through dynamical binding at run-time. The two delegates help to delay the binding between the function pointers and the actual functions.

Note that the source file should contain the initialization of the static VTB data:

Don't derive the managed "VehicleWrap" from the unmanaged "VehicleUnman". Unmanaged wrappers merely provide the storage for the original class members, including data and methods, whereas managed ones handle the class relationship. More importantly, you pass the unmanaged object to the DLL, not the managed one.

Use managed data types in managed classes whenever possible, particularly as input/output parameters and return types. This is more than just a good practice, rather a necessity, because other .NET developers do not have to marshal unmanaged data types at the application level.

Derive "CarWrap" from "VehicleWrap" to recover the original inheritance between the two unmanaged classes. This way, we don't have to handle the inheritance manually in the managed classes.

Assign the do-nothing function to VehicleUnman::vtb->dtor in the ~Car() destructor. This is a hack to mitigate the conflict between the unmanaged DLL internals and the managed class inheritance. I'll leave the detailed discussion of this issue to the next section.

Now, we put all the classes in a DLL named "CppManagedDll.dll". "VehicleWrap" and "CarWrap" are two managed classes, which are ready to be used by .NET applications. In order to test the "VehicleWrap" and the "CarWrap" classes, I created a .NET C++ CLR console application project, with this source code:

6. Inheritance, Polymorphism, and Virtual Destructor

As we saw earlier, I derived "CarWrap" from "VehicleWrap" to avoid the manual implementation of the original inheritance between the "Car" and "Vehicle" classes, with the assumption that the C++ DLL breaks down all the relationship between the derived classes. This turned out not to be true. The tests revealed that it only breaks the binding between the two Move() methods of "Vehicle" and "Car", but retains the virtual destructor binding. That is, whenever ~Car() is called from outside the DLL, ~Vehicle() gets called automatically. This has some adverse impact on our managed classes, because ~Vehicle() would be called twice, one by the managed class virtual destructor and the other by the original destructor inside the DLL. To test this, you can comment/uncomment the following line in ~CarWrap():

VehicleUnman::vtb->dtor = VehicleUnman::Ndtor;

This line allows the managed class to use its own binding, and meanwhile, to disable the unexpected binding in the DLL, which is achieved through the power of VTB and function pointer!

After we run "TestProgram.exe", we get the print-out as follows:

Called Vehicle constructor with ID: 12345
Called Vehicle::GetId()
GetId() returned: 12345
Called Car::Move()
Called Car destructor
Called Vehicle destructor

To verify the polymorphism, modify the second line in the main:

VehicleWrap ^car1 = gcnew CarWrap("12345");

You will get the same printout. If you change the line to:

VehicleWrap ^car1 = gcnew VehicleWrap ("12345");

You will get:

Called Vehicle constructor with ID: 12345
Called Vehicle::GetId()
GetId() returned: 12345
Called Vehicle::Move()
Called Vehicle destructor

As we discussed earlier, if you comment out the VTB assignment in ~Car(), the "Vehicle" destructor in the DLL would be called twice:

Called Vehicle constructor with ID: 12345
Called Vehicle::GetId()
GetId() returned: 12345
Called Car::Move()
Called Car destructor
Called Vehicle destructor
Called Vehicle destructor

Surprisingly, although calling the same destructor twice is logically incorrect, it hasn't caused any crash. How could this happen? It did because the importing didn't create any object in the DLL. We will discuss this in more details in the next section.

Now, everything seems to work smoothly. We are ready to extend to multiple inheritance, another important unmanaged C++ specification. Well, not quite. This extension is not feasible, not because we cannot mimic multiple inheritance, but because managed C++ has abandoned this complicated concept completely. In order to comply with the managed C++ standard, you should avoid legacy multiple inheritance in .NET applications.

7. Imported Resource Disposal

To fully understand why the two calls to the exported destructor in the DLL didn't cause any memory problems, let's first analyze where unmanaged resources are allocated:

Created by your unmanaged wrapper. It's your responsibility to dispose all the resource allocated within the unmanaged wrapper.

Created by the imports? No. When we import, we don't create any instance inside the DLL. Instead, the instance will be created within our managed assembly, with the unmanaged wrapper (structure) as the placeholder for all the imported functions and other class data. The exported functions are allocated by the DLL on the stack. No dynamic allocation is performed during importing, as you don't "new" any objects inside the DLL. Thus, no disposing is necessary for the importing itself.

Created and disposed by the DLL internally. We assume that the DLL has taken care of this properly already; in other words, the DLL is bug-free. One thing to note though. If a DLL destructor contains code to dispose any other resources, multiple calls to it may cause problems to the DLL.

8. Concluding Remarks

This tutorial provides an alternative approach to reusing unmanaged C++ libraries, particularly when direct importing from unmanaged DLLs becomes necessary. I have demonstrated three steps to wrap unmanaged C++ DLLs for use in .NET applications:

Retrieve class member data from the DLL.

Import required class methods.

Wrap up all the imports in a managed class.

The tutorial also shows that the implementation of the approach is not trivial, mainly because you must recover the original relationship between unmanaged classes, such as inheritance, virtual functions, and polymorphism. Managed C++ can help, but when there are conflicts, you have to simulate some C++ compiler internals. In working with C++ internals, you will find virtual table and function pointer helpful.

Share

About the Author

Jun is an experienced software architect. He wrote his first computer code on the tape machine for a "super computer". The tape machine reads holes on the black pape tape as source code. When manually fixing code, you need a punch and tranparent tape. To delete code, you block holes or cut off a segment and glue two ends together. To change code, you block old holes and punch new holes. You already know how to add new code, don't you? Anyway, that was his programming story in early 1980's.

Currently, Jun is an architect at GuestLogix, the global leader in providing onboard retail solutions for airlines and other travel industries. He is also the founder of Intribute Dynamics, a consulting firm specialized in software development. He has a personal blog site, although he is hardly able to keep it up to date.

In his spare time, Jun loves classic music, table tennis, and NBA games. During the summer, he enjoyes camping out to the north and fishing on wild lakes.

Comments and Discussions

This is a nice article. What I'm seeing now is that, as a C# developer creating an application that will consume unmanaged DLLs, there would be additional coding effort involved to create these equivalent managed classes. Is there an existing utility that will automatically create these managed classes?

I have created a managed DLL as wrapper for my win32DLL (Having four classes) developed using VC++6. I am not able to create multiple objects for a same class. Also not able to create the object for the 4 class present in the DLL at a time. If i am using one class, the application is running fine. Did anything i am missing. please provide a help.

I have a different problem from all of the above.
I have a DLL file and a header file related to it. DLL file is compiled in Visual Studio.
(I don't know which version. I know that projects using this DLL work in VS)
In the header file there is an abstract class. There ara also three functions. One of those functions returns a pointer to
an instance of the abstract class.
I want to use the functions of the abstract class in QT platform.

Note : MyLM is an abstract class. It only has member functions, no member attributes.
MyOM is a struct. It has no functions, only has variables.
--------------------------------------------------------------------------------------
MyLM.h has a function :

1) I have an unmanaged C++ Dll that I want to wrap so I can use it in C#.
2) It´s a 3rd party DLL so I can´t modify or even access the source code. I only have the Dll and some headers.
3) The Dll "encapsulates" several different classes. It´s not just one single class, but many. Some classes have functions that return "by value" objects of these other classes types (user-defined types).
4) I have reached the point where I have tried everything and don´t know what else to do to make this dll work. This is a bottleneck in my work. I can´t go on with anything else until I solve this :S

I followed this tutorial to make my wrapper. I can generate my own c++ wrapper dll which I can "sort of" use easily in c# (Visual C# Express 2008).

Right now I´m able to create new instances of all my wrapped classes from the original dll and use functions that return basic-datatypes (int, unsigned int..). My problem is that I don´t know how to import functions that return objects (by value) from other classes types of the original unmanaged dll. Following this tutorial, I have in my unmanaged dll a Vehicle Class that has a function that returns "by value" an object of the Car class. For example:

// I have the class ABC, the class XYZ and the function foo() from the class ABC that returns by value an object of XYZ.
ABC abc = new ABC(); //ok, done.. new instance of ABC
XYZ xyz = new XYZ(); //ok, done.. new instance of XYZ
//so I want this to happen
xyz = abc.foo();

I don´t know how to invoke them or what kind of marshalling is required. I tried,

but there are compilation errors. It doesn´t recognize XYZ as something valid... so I substituted the return value type from XYZ to IntPTr and it compiled and generated the managedDll which I can "sort of" use in c#. I´m not really sure if that´s a correct solution becaue the value that the original unmanaged dll returns is not a pointer but the actual object. I the tried to marshal it using:

XYZ xyz = (XYZ)Marshal.PtrToStructure(myReturnedIntPtr, typeof(XYZ));

it compiles and everything, but when I run my c# application I get an ArgumentException and the program breaks. I read in other post that it happens when one has not defined a Struct.Layout ..so I then defined it and ran my c#programm again but now it throws me an AccessViolationException.

I´m not even sure to "what" should I marshal it to. Following this tutorial, I don´t know if it should be marshalled into XYZunman or XYZwrap. I actually would want the original XYZ, but since the XYZwrap is the one used in c#, I suppose that at the end it should be into this way.

I needed to use concepts from this article in a C# program and saw others wanted to do the same thing. Here is the code (with the associated C++ code int he line above) to do the exact thing in C# with the same output.

Notes:
1. This was in VS2008, but it should be identical in VS2005
2. Add a reference to the CppManagedDll.DLL file in the project
2. Make sure CppWin32Dll.dll is in the path of the directory that you are running from.

In my code I have to call Car constructor so I added CarUnman::ctor calling instead of VehicleUnman::ctor calling. In any case Car::MyFunc works correctly if it is called from main() function. But I can't call MyFunc from nonvirtual GetId function. If I try do it Visual Studio 2005 reports about the error: "Attempted to read or write protected memory. This is often an indication that other memory is corrupt".
In my code I could solve this problem by means of importing GetId as virtual function. But in that case I have to add GetId method to CarWrap class and to all classes which are inherited from VehicleWrap. Besides I have to add it to vtables of all unmanaged structures. So I have written a lot of unnecessary code. And I can't understand why foregoing code doesn't work :(

It's an in depth article about importing native DLLs into .NET code. It has some C# examples. The article shows which types are interoperable, how to import a DLL, how to pass strings and how to de-reference/unreference pointers.

I have to use an unmanaged C++ DLL, I just have the .h of the class I need to use from DLL. I applied your example and it works fine, but I have some problems, probably it comes from DLL, any way I ask you:

When running a test program in managed VC++, as you do in the article, I can invoke the DLL properly, create a new instance of the class, and access to its methods, but when the test get closed, in the output window I can se the following error:

It seems that the DLL is calling to something that is null, but I don't know if it there is any problem in my code, because the error appears when the test program finish...

Another issue is that I have in the DLL some functions that returns arrays, for example CMod* getModules() that returns an array of CMod objects, I have read some other articles that explain how to do it, but I don't find enough useful.

I have also in the DLL several enum types, that I must use in my managed code, how can I do it?

Nice but I believe the article would have been more instructional if the managed code part that interfaced to the unmanaged DLL was in C# rather than C++.

The reasons for that would be:

- Make a more explicit difference between unmanaged and managed parts
- C# should be easy for you if you are a C++ guy (I guess)
- There are no destructors (as such) in C#, I mean you can do something like that.

Do you think it's possible to rewrite this code to C#? I tried but didn't find any way. To build this code into the project written in C# I had to make the wrapper dll written in Managed C++ and import classes from it.

Hi Guys,
Import statements are no longer required with the new version of .net 2.0 VC++, you only need to define the C++ class with the conditional header compilation definition and a managed C++ class will be able to find it if the dependency directories are set correctly and the unmanaged C++ dll is included as a dependency in the additional dependencies.

When I tried this method on my dll file, I met some problems, couldnot figure it out. 1) When use dumpbin, it doesnot export all of functions which dll file includes? 2) How to deal with callback, struct and vector?

First off - fantastic article! Easy to read, follow and understand. Thank you.

Second, and maybe I missed the section where you explained it, but, I'm lost as to how you determined (without source code) the data types of (and count of) the parameters(for the output of dumpbin.)

It's one thing to know '??0Vehicle@@QAE@PAD@Z'. It's quite another to know '??0Vehicle@@QAE@PAD@Z' has one parameter of char*. How would one determine what the parameter(s) is(are) without the source code?

Third, how does one determine what the return data-type is from '??0Vehicle@@QAE@PAD@Z'?

The unmanaged header "CppWin32Dll.h" provides information such as function parameters and return type. So when applying this approach, you should have the unmanaged header along with the DLL. Note you don't include the unmanaged header in your managed code directly.

But I wonder why you don't get any memory leaks. You don't use a delete in the destructors of the managed classes. And the static pointer of vtables will not be deleted either. When I add a 'delete' in one of the managed destructors the code crashes. So everything works fine in your code as it is, but I don't understand why. It seems like the unmanaged wrapper is mapped into the memory used by the class within the DLL (or vice versa). So declared members in the unmanaged wrapper are magically filled. Therefore I think calling the destructor of the DLLed class will free the memory.

But in my own project there are memory leaks. Every member in the unmanaged struct leaves a memory leak. And if there are no members there is still a leak of 1 byte. And the static vtable also causes a memory leak. What am I doing wrong?

BTW: There seems to be a problem with your dtor hack. Consider the following test code:

/// Delete instances to test virtual destructor
delete car1, s;
/// dtor for VehiceUnman has been replace with ntor just a line before
delete v1;

return 0;
}

The destructor for v1 will never be called because you hacked the vtable of VehicleUnman in ~CarWrap. If the vtable isn't static but a member this would work fine. But it is static, so you hacked the vtable for all instances of VehicleUnman. Is there any solution? It seems to me that using a vtable for every instance could solve the problem, but this will need a lot more memory. And perhaps we running in some trouble because of the mapped memory (see my assumption above).

I think a found the problem. It's not in my code, it's in the sample code!

When you add a include of afx.h, you will see all the wonderful memory leaks caused by the sample. So we really have to call delete in the destructors of the managed classes. But this will only work if the unmanaged class has (at least) the size of the class within the DLL. I tried this with VehicleUnman und CarUnman and found another error: VTB. To use a struct called __VTB is a try to mimic the original C++ vtable. Nice try, but not correct in detail. A better try (perhaps not a perfect one) is not to use a static member pointer but a static member (solves also the problem of memory leak caused by static member pointers). We then must add a pointer to this static member to the class. This means replacestatic __VTB *vtb;bystatic __VTB s_vtb;
__VTB* vtb;

In the cpp file we have to replaceVehicleUnman::__VTB *VehicleUnman::vtb = new VehicleUnman::__VTB;
CarUnman::__VTB *CarUnman::vtb = new CarUnman::__VTB;byVehicleUnman::__VTB VehicleUnman::s_vtb;
CarUnman::__VTB CarUnman::s_vtb;

In the constructors of the classes we have to add a line like thistv->vtb = &VehicleUnman::s_vtb;right after calling the ctor method of the unamanged class.

The last thing to do is to use the member pointer instead of the static member pointer. And of course to delete the pointers to the unmanaged class in the destructor of the managed class right after calling the dtor method.

I think there still remains the problem of hacking the vtb in the destructor of CarWrap. The hack only works once (and again when a new instance of CarWrap or VehicleWrap is created) because the static vtb is hacked. What we need is a hack that only modifies the vtb for a single instance and not for a class.