
Introduction
Together with Visual Studio .NET programming environment, there is a useful utility that automatically generates a wrapper for COM and ActiveX components: the procedure by which these kinds of components are made available to .NET applications is called COM Interoperability. The way of accessing old_style C++ DLL library within managed application is called platform invoke. Unlike what happens for COM components, there is no automated way to access the functions present in native DLL and so each time a native function is needed for import, it is mandatory to write something like:
...
[DllImport("CppLib.dll", EntryPoint="interlaceString",
CallingConvention= CallingConvention::Cdecl)]
void interlaceString(String * var1,String * * var2,
int var3);
...
The same thing is needed for each function present in the DLL and every time a platform invoke is needed, it is not always straightforward to couple the correct pair of managed and unmanaged types for all the parameters and the return type of the function.
This article, together with the enclosed NativeWrapper
tool (written entirely in C#), illustrates an automated procedure useful to make native C++ available to managed applications: this kind of interoperability is performed with the aid of a managed C++ class that is intended to act as a wrapper between an unmanaged C++ DLL and a managed application.
From Unmanaged Functions to Managed Methods!
The main intention of the article is to show how NativeWrapper
is able to produce a well structured wrapper class for the native DLL that can be used by managed code in an easier way with respect to the usual platform invoke calls. To produce such a wrapper, the following steps are outperformed:
- Exports: The native DLL together with its LIB are scanned for available exports
- Managed Wrapper class: A managed C++ DLL that will contain the wrapper class is created
- Mappings: Each function of the native DLL is mapped (with the aid of platform invoke) over methods of the managed wrapper class
The previous procedure is explained here with the help of files in the demo project.
Exports
The enclosed sample project contains a C++ native DLL that exports two functions as expressed in the following picture:

Figure 1: The functions declared inside the DLL
To obtain all the functions exported by a LIB, the "dumpbin
" utility (shipped with Visual Studio .NET) is used. The results are shown in the following picture:

Figure 2: The functions exported by the LIB
As can be seen in the previous picture, this LIB file contains two functions: crossproduct
and interlaceString
.
NativeWrapper
parses and uses the output of dumpbin to collect the following information for each function in a library:
- Return type
- Function name
- Parameters
Managed Wrapped Class
After the output of dumpbin
is collected, and depending on the name of the DLL (<dllName>), NativeWrapper
automatically generates the following files representing a managed C++ DLL that contains one class:
- Na<dllName>Lib.vcproj
- Na<dllName>Lib.h
- Na<dllName>Lib.cpp
- Stdafx.h
- Stdafx.cpp
- AssemblyInfo.cpp
The class in the DLL will be Na<dllName>Lib
as shown below:
#pragma once
#using <mscorlib.dll>
using namespace System;
using namespace System::Runtime::InteropServices;
namespace NaCppLibLib
{
public __gc class NaCppLibLib
{
...
...
...
};
}
Mappings
The last step of NativeWrapper
is to map, with the aid of platform invoke, all the functions in the DLL over the methods of the Na<dllName>Lib
class:
#pragma once
#using <mscorlib.dll>
using namespace System;
using namespace System::Runtime::InteropServices;
namespace NaCppLibLib
{
[DllImport("CppLib.dll", EntryPoint="crossProduct",
CallingConvention= CallingConvention::Cdecl)]
void crossProduct(int var1 __gc [],int var2 __gc [],
int var3 __gc []);
[DllImport("CppLib.dll", EntryPoint="interlaceString",
CallingConvention= CallingConvention::Cdecl)]
void interlaceString(String * var1,String * * var2,
int var3);
public __gc class NaCppLibLib
{
public:
void crossProduct(int var1 __gc [],
int var2 __gc [],int var3 __gc [])
{
::NaCppLibLib::crossProduct ( var1, var2, var3);
}
void interlaceString(String * var1,
String * * var2,int var3)
{
::NaCppLibLib::interlaceString ( var1, var2, var3);
}
};
}
For each function in the native DLL, a static
method is introduced with the help of the DllImport
attribute within the Na<dllName>Lib
namespace. Subsequently, a method is inserted within the Na<dllName>Lib
class that calls the imported methods. The actual implementation of NativeWrapper
is able to deal only with C++ standard types and pointers. Obviously, the main task of this mapping is to convert the data passing from the Managed Application and the Native DLL through the Wrapper. The mappings between the dealt data types are contained in a StringDictionary
called UnMconversion
that is filled in "function.cs". The code below shows a portion of the function that fills the dictionary:
private void fillDictionary()
{
...
UnMconversion.Add("bool","bool");
UnMconversion.Add("short","short");
UnMconversion.Add("int","int");
UnMconversion.Add("long","long");
UnMconversion.Add("char","char");
UnMconversion.Add("char *","String *");
UnMconversion.Add("char * *","String * *");
...
}
What cannot be deduced from the DLL and the LIB files is the name of the parameters: for this reason, all the names of the function parameters are called var<i-th>
.
How to Use the New Managed Class
At this point, a complete Managed C++ DLL is ready and it encapsulates all the methods present in the original DLL. The newly created project (Na<dllName>Lib.vcproj) can be opened by Visual Studio to build a Managed DLL whose name will be Na<dllName>Lib.dll. This new DLL can be used from within .NET projects by adding a reference to it as shown in the picture below (this picture is extracted from the enclosed sample project CsCaller
).

Figure 3: Adding reference to the Managed DLL
After the reference is added, a new class (called Na<dllName>Lib
) will be available in the current project and its methods will be the functions present in the original native DLL:

Figure 4: Using the Managed Wrapper
As can be seen in Figure 4, the methods crossProduct(...)
and interlaceString(...)
are now available! One last step for the programmer is to manually copy the original native DLL file in the same folder in which the managed application is running.