* Introduction
* Sample Unmanaged C++ Library
* Retrieve Exported Information from the DLL
* Perform Platform Invoke
* Wrap all Imports in Managed Classes
* Inheritance, Polymorphism, and Virtual Destructor
* Imported Resource Disposal
* Concluding Remarks
* Revision History
1. Introduction
This article has been revised. Check here for updates.
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:
1. 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.
2. 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.
3. 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
Go to Top
The following segment is the definition of a base class "Vehicle" and its derived class "Car":
Collapse
// 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.dll
class CPPWIN32DLL_API Vehicle
{
public:
Vehicle(char* idx);
// Define the virtual destructor
virtual ~Vehicle();
char* GetId() const;
// Define a virtual method
virtual void Move();
protected:
char* id;
};
class CPPWIN32DLL_API Car : public Vehicle
{
public:
~Car();
// Override this virtual method
void Move();
};
By all means, the two classes are very simple. However, they bear two most important characteristics:
1. The base class contains a virtual destructor.
2. 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":
Collapse
#include "stdafx.h"
#include "CppWin32Dll.h"
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
};
// This is the constructor of a class that has been exported.
// see CppWin32Dll.h for the class definition
Vehicle::Vehicle(char* idx) : id(idx)
{
printf("Called Vehicle constructor with ID: %s\n", idx);
};
Vehicle::~Vehicle()