Au cours du développement d’un projet j’ai été confronté à la problématique suivante: Comment utiliser une librairie dynamique (.lib) en C++ dans un projet C# ?

1. Présentation du contexte

La solution est composée des 3 projets suivants :

  • MonApplication.exe [C++, MFC]
    Un client lourd qui doit fonctionner de Windows XP à Seven sans prérequis (donc impossible d’utiliser le CLR).
  • Cryptlib [C++]
    Librairie permettant de crypter et décrypter des strings.
  • MonService [WCF, C#]
    Il permet de faire l’intermédiaire entre la base de données et le client lourd.

Le client et le service manipulent des fichiers xml cryptés par la librairie Cryptlib. Côté client, le problème ne se pose pas car j’utilise une librairie C++ native dans un client C++. En revanche, le service doit pouvoir appeler les méthodes Encrypt et Decrypt de cette même libraire.

2. Solution proposée

Reprenons le schéma actuel de la solution, à la manière du design pattern « Façade », nous créons un projet CryptlibWrapper qui va appeler les méthodes de CryptLib et renvoyer des objets de type .Net au service.

Comme présenté ci-dessus, le service ne fera pas ses appels directement sur la librairie native mais sur le Wrapper.

Nous allons créer une solution simplifiée afin de mieux présenter le concept.

Elle sera composée de 3 projets :

  • Console : Ecrit en C#, on affichera le résultat de la méthode SayHello
  • NativeLibrary : Ecrit en C++ natif
  • WrapperLibrary : Ecrit en C++ également mais en supportant le CLR

Commençons par écrire la couche de plus bas niveau : NativeLibrary.

2.1. NativeLibrary

Nous créons une solution nommée UseCppInCsharp et un premier projet de type « Empty Project» nommé NativeLibrary.

 

 

Nous ajoutons une classe Utils avec le code suivant:
Utils.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#pragma once
 
#include 
#include 
 
using namespace std;
 
namespace NativeLibrary
{
	class Utils
	{
	public:
		Utils(void);
		~Utils(void);
 
		string SayHello(string name);
	};
}

Utils.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include "Utils.h"
 
namespace NativeLibrary
{
	Utils::Utils(void)
	{
	}
 
	Utils::~Utils(void)
	{
	}
 
	string Utils::SayHello(string name)
	{
		return "Hello World " + name;
	}
}

Il reste à configurer les options du projet :
Configuration Properties > General > Configuration Type : Static Library (.lib)

2.2. WrapperLibrary

Nous ajoutons un projet de type « CLR Empty Project» nommé WrapperLibrary à la solution.


Nous ajoutons une classe WrapperUtils :
WrapperUtils.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#pragma once
#include "Utils.h"
 
//Use to Convert System::String to std::string
#include <msclr\marshal_cppstd.h>
 
// Allow to use System namespace with CLR
using namespace System;
 
namespace WrapperUtils
{
	public ref class WrapperUtils
	{
		public:
			System::String^ SayHello(System::String^ input);
	};
}

WrapperUtils.cpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include "stdafx.h"
 
#include "WrapperUtils.h"
 
namespace WrapperUtils
{
	System::String^ WrapperUtils::SayHello(System::String^ input)
	{
		//Conversion of System::String to, std::string
		msclr::interop::marshal_context context;
		std::string stdInput = context.marshal_as(input);
 
		NativeLibrary::Utils cUtils;
		string tmp = cUtils.SayHello(stdInput);
 
		String^ MyString = gcnew String(tmp.c_str());
		return MyString;
	}
}

Il reste à configurer les options du projet :

  • Configuration Properties > General > Configuration Type : Dynamic Library (.dll)
  • Check that the “Common Language Runtime Support” property is set to /clr
  • Configuration Properties > C/C++ >General > Additional Include Directories
  • Common Properties : Add new reference to NativeLibrary

2.3. Console

Dernière étape pour utiliser la librairie : le client C#.
J’ajoute un projet de type « Console Application » nommé Console à la solution.

 

Nous ajoutons la référence vers le projet WrapperLibrary :

 

Il ne reste plus qu’à appeler la fonction depuis le Main :

Program.cs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
 
namespace Console
{
    class Program
    {
        static void Main(string[] args)
        {
            //Call C++ function
            WrapperUtils.WrapperUtils _utils = new WrapperUtils.WrapperUtils();
            string str = _utils.SayHello("Olivier");
 
            //Write result
            System.Console.WriteLine(str);
 
            //Wait before closing
            System.Console.ReadLine();
        }
    }
}

A l’exécution, le message s’affiche correctement :

Last modified: 20 November 2014

Author

Comments

Petite erreur sur le sujet :

.lib = lib statique
.dll = lib dynamique

Très juste, merci de l’avoir signaler!

Write a Reply or Comment

Your email address will not be published.