Rémi Thomas
Intégrez du code C++ natif dans vos applications .NET
Cet article s'adresse aux personnes qui connaissent C++ et .NET et a pour objectif de montrer comment appeler du code C++ natif depuis des applications .NET
Par Rémi Thomas publié le 12/01/2007 à 09:38, lu 8374 fois, 4 pages
 3 | Exemple d'intégration de code C++ natif dans une applications .NET C#
Dans un exemple nous allons créer le squelette d'une application utilisant un wrapper. Pour cela nous créons un projet de type C# console puis nous ajoutons à la solution un projet de type C++ CLR Class Library. Enfin nous ajoutons un projet de type C++ Win32 static library.
CDemo est ma classe native. Elle est compilée dans le projet bibliothèque C++ Win32.

class CDemo

{

public:

    std::string        VerlantA    (const char *chaine);

    std::wstring    VerlantW    (const WCHAR *chaine);

    int            Moyenne    (int *values, int count);

    void            Reverse    (unsigned char *src, unsigned char *dst, int size);                   

};

CDemoWrapper est ma classe managed en C++/CLI (ou C++ managé) qui fait le lien entre monde managé et natif

public ref class CDemoWrapper

{

    CDemo *m_pDemo;

public:

    CDemoWrapper();

    ~CDemoWrapper();

 

    String^        VerlantA    (String^ chaine);

    String^        VerlantW    (String^ chaine);

    int            Moyenne    (array<int>^ values);

    array<byte>^    Reverse    (array<byte>^ values);

};

Dans un premier temps il faut inclure les bons headers. Wrapper.h inclut lui même demo.h, soit la classe Cdemo

#include "stdafx.h"

#include "wrapper.h"

#include <vcclr.h>

Dans le constructeur et le destructeur nous instancions les classes CDemo. Une classe C++/CLI ne peut avoir de variable membre native, uniquement des pointeurs vers les variables membres

wrapper::CDemoWrapper::CDemoWrapper()

{

    m_pDemo = new CDemo;

}

 

wrapper::CDemoWrapper::~CDemoWrapper()

{

    delete m_pDemo;

}

Dans l'exemple, deux gestions de chaîne pour montrer comment passer d'une chaîne .NET vers une chaîne C++. La classe System::Runtime::InteropServices::Marshal offre de nombreux outils passerelles entre managé et natif. Si nous désirons une chaîne ANSI, l'API StringToHGlobalAnsi fait la conversion, et System::String convertit une chaîne native ANSI en chaine managé.

String^ wrapper::CDemoWrapper::VerlantA(String^ chaine)

{

    IntPtr tmp = System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(chaine);

    String^ result = gcnew String(m_pDemo->VerlantA((char *)tmp.ToPointer()).c_str());

    System::Runtime::InteropServices::Marshal::FreeHGlobal(tmp);

    return result;

}

Pour une chaîne Unicode, pin_ptr fige le pointeur en mémoire et interdit au GC le déplacement de la variable. Nous obtenons un pointeur vers la chaîne Unicode dans la mémoire managed.

String^ wrapper::CDemoWrapper::VerlantW(String^ chaine)

{

    pin_ptr<const wchar_t> tmp = PtrToStringChars(chaine);

    return gcnew String(m_pDemo->VerlantW(tmp).c_str());

}

La même technique du pointeur figé est utilisée pour un tableau d'entier.

int    wrapper::CDemoWrapper::Moyenne(array<int>^ values)

{

    pin_ptr<int> tmp = &values[0];

    return m_pDemo->Moyenne(tmp, values->Length);

}

Maintenant j'utilise volontairement un buffer temporaire non managé pour vous faire découvrir l'API Copy de la classe Marshal. Elle permet de copier des tableaux d'un mode à l'autre.

array<byte>^ wrapper::CDemoWrapper::Reverse    (array<byte>^ values)

{

    // En passant par une copie

    unsigned char *tmp = new unsigned char[values->Length];

    pin_ptr<unsigned char> src = &values[0];

    m_pDemo->Reverse(src, tmp, values->Length);

    array<byte>^ result = gcnew array<byte>(values->Length);

    System::Runtime::InteropServices::Marshal::Copy(System::IntPtr(tmp), result, 0, values->Length);

    delete[] tmp;

    return result;

}

La classe CDemoWrapper s'utilise directement dans notre code C#.

static void Main(string[] args)

{

    wrapper.CDemoWrapper demo = new wrapper.CDemoWrapper();

    int[] test = new int[] { 3, 4, 5 };

    Console.WriteLine("Moyenne = {0}", demo.Moyenne(test));

    Console.WriteLine("Verlant = {0}", demo.VerlantA("verlant"));

}

 
» Démarrer une discussion
 
Discussion démarée par lzw1015bean le 27/03/2008 à 23:55, 1 commentaire(s).
Discussion démarée par floflo8731 le 09/06/2008 à 14:49, 1 commentaire(s).