Click here to Skip to main content
15,868,014 members
Articles / Programming Languages / C#

NativeWrapper: A Tool for Native Interoperability

Rate me:
Please Sign up or sign in to vote.
2.84/5 (16 votes)
14 Aug 2005CPOL4 min read 96.6K   1.4K   24   33
A managed wrapper for native DLLs to be used in .NET applications

Image 1

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:

C#
...
[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:

  1. Exports: The native DLL together with its LIB are scanned for available exports
  2. Managed Wrapper class: A managed C++ DLL that will contain the wrapper class is created
  3. 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:

Image 2

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:

Image 3

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:

C++
// NaCppLibLib
#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:

C++
// NaCppLibLib
#pragma once
#using <mscorlib.dll>
using namespace System;
using namespace System::Runtime::InteropServices; 
namespace NaCppLibLib
{
    // generated from ?crossProduct@@YAXQAH00@Z (void 
    //  __cdecl crossProduct(int * const,int * const,
    //                                   int * const))
    [DllImport("CppLib.dll", EntryPoint="crossProduct",
             CallingConvention= CallingConvention::Cdecl)]
    void crossProduct(int var1 __gc [],int var2 __gc [],
                                        int var3 __gc []);

    // generated from ?interlaceString@@YAXPADPAPADH@Z (void 
    //          __cdecl interlaceString(char *,char * *,int))
    [DllImport("CppLib.dll", EntryPoint="interlaceString",
                CallingConvention= CallingConvention::Cdecl)]
    void interlaceString(String * var1,String * * var2,
                                                   int var3);

    //#pragma comment(lib, "NaCppLibLib.lib")
    public __gc class NaCppLibLib
    {
    public:
    // wrapper for ?crossProduct@@YAXQAH00@Z (void 
    //  __cdecl crossProduct(int * const,int * const,
    //                                    int * const))
        void crossProduct(int var1 __gc [],
                     int var2 __gc [],int var3 __gc [])
        {
            ::NaCppLibLib::crossProduct ( var1, var2, var3);
        }

    // wrapper for ?interlaceString@@YAXPADPAPADH@Z (void 
    //         __cdecl interlaceString(char *,char * *,int))
        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:

C#
// this dictionary converts the type from the 
// unmanaged c++ to the managed c++
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).

Image 4

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:

Image 5

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.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Italy Italy
2008 - Working on my own
2005 - ... still programming
2005 - Working at the Digigroup of Torino (Italy)
2004 - Got my PhD at the "Politecnico di Torino"
2001 - Got Graduated at the "Politecnico di Torino"
2000 - Got Graduated at the UIC (Chicago)
1983 - Started programming ...
1976 - Born

Comments and Discussions

 
GeneralMarshalling: Using native DLLs in .NET Pin
meukjeboer29-Aug-08 4:35
meukjeboer29-Aug-08 4:35 
GeneralRe: Marshalling: Using native DLLs in .NET Pin
pinturic29-Aug-08 4:38
pinturic29-Aug-08 4:38 
QuestionGreat Article! But how to convert some sdk files with .h file? thanks Pin
Yuejun5-Jun-07 12:52
Yuejun5-Jun-07 12:52 
Generaldo u have something to convert natice C dll for use in VS2005 Pin
praneettiwari4-Jun-07 21:17
praneettiwari4-Jun-07 21:17 
GeneralRe: do u have something to convert natice C dll for use in VS2005 Pin
pinturic4-Jun-07 22:48
pinturic4-Jun-07 22:48 
GeneralBad practice Pin
Roubachof28-Feb-07 5:06
Roubachof28-Feb-07 5:06 
Bravo for your project!
However I detected a bad practice in your source code:

You use a string to build your wrapper code: this is evil !
You should use a StringBuilder instead.

That's all for now,

Keep up the good work!
GeneralRe: Bad practice Pin
pinturic28-Feb-07 5:17
pinturic28-Feb-07 5:17 
GeneralExporting Classes Pin
theShane20-Sep-06 5:55
theShane20-Sep-06 5:55 
GeneralRe: Exporting Classes Pin
pinturic20-Sep-06 6:15
pinturic20-Sep-06 6:15 
GeneralRe: Exporting Classes Pin
theShane20-Sep-06 9:27
theShane20-Sep-06 9:27 
GeneralRe: Exporting Classes Pin
pinturic20-Sep-06 9:58
pinturic20-Sep-06 9:58 
GeneralDictionary Conversion Pin
PankajMishra10-Sep-06 22:02
PankajMishra10-Sep-06 22:02 
GeneralRe: Dictionary Conversion Pin
pinturic10-Sep-06 22:09
pinturic10-Sep-06 22:09 
Questionvs2005 version? Pin
alias4723-Aug-06 20:09
alias4723-Aug-06 20:09 
AnswerRe: vs2005 version? Pin
pinturic23-Aug-06 21:29
pinturic23-Aug-06 21:29 
GeneralI try it with FreeImage,does not work Pin
RubyPdf21-May-06 22:17
RubyPdf21-May-06 22:17 
GeneralRe: I try it with FreeImage,does not work Pin
pinturic21-May-06 23:33
pinturic21-May-06 23:33 
GeneralMissing something... Pin
DEWright_CA28-Feb-06 5:00
DEWright_CA28-Feb-06 5:00 
GeneralRe: Missing something... Pin
pinturic28-Feb-06 5:25
pinturic28-Feb-06 5:25 
GeneralRe: Missing something... Pin
DEWright_CA28-Feb-06 5:42
DEWright_CA28-Feb-06 5:42 
GeneralRe: Missing something... Pin
pinturic28-Feb-06 6:02
pinturic28-Feb-06 6:02 
GeneralRe: Missing something... Pin
pinturic28-Feb-06 6:16
pinturic28-Feb-06 6:16 
GeneralRe: Missing something... Pin
Ankhnesmerira1-Mar-06 21:24
Ankhnesmerira1-Mar-06 21:24 
GeneralRe: Missing something... Pin
pinturic1-Mar-06 21:40
pinturic1-Mar-06 21:40 
GeneralRe: Missing something... Pin
pinturic1-Mar-06 21:43
pinturic1-Mar-06 21:43 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.