Click here to Skip to main content
Click here to Skip to main content

NativeWrapper: a tool for Native Interoperability

, 14 Aug 2005
Rate this:
Please Sign up or sign in to vote.
A managed wrapper for native DLLs to be used in .NET applications.

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:

  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 aid 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 generates automatically 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:

// 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:

// 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 aid 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:

// 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).

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.

License

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

Share

About the Author

pinturic
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

 
GeneralExporting Classes PinmembertheShane20-Sep-06 5:55 
GeneralRe: Exporting Classes Pinmemberpinturic20-Sep-06 6:15 
GeneralRe: Exporting Classes PinmembertheShane20-Sep-06 9:27 
GeneralRe: Exporting Classes Pinmemberpinturic20-Sep-06 9:58 

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

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

| Advertise | Privacy | Mobile
Web03 | 2.8.140821.2 | Last Updated 14 Aug 2005
Article Copyright 2005 by pinturic
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid