Utiliser une librairie C/C++ non managée dans un code c#

Quiconque à déjà programmé en C, C++ et C# connaît les qualités et défauts de chacun de ses langages, le C rapide mais très bas niveau, le C# s’exécutant via une plateforme (.net) donc plus lent, mais par contre extrêmement pratique lorsque les sorties de ce que l’on peut appeler calculateur doivent être interfacée avec une Interface graphique, qu’il s’agisse de winform ou de sites ASP et le C++ qui permet une programmation objet tout en restant assez bas niveau (ami puristes ne vous offusquez pas il s’agit d’une description sommaire, mon but n’est pas de lister qualités défauts et implémentations de chacun de ses langages !).

Seulement voilà, des fois il est nécessaire d’utiliser du cote C++ dans une application codée en C#… si il se trouve que votre librairie est non managée là c’est le drame ! Enfin le drame, pas tout à fait: C# implémente des fonctions qui permettent d’adapter , ‘wrapper’ pour utiliser le mot en usage les fonctions de la dll et de les réutiliser dans votre application!

Par exemple j’ai du réutiliser la librairie GSL dans un projet devant être réalisé en C#. Problème numéro 1 : les sources telles quelles ne peuvent pas être utilisées dans un environnement Windows, heureusement de fiers codeurs en ont développé une version dll . Seulement voilà, une fois que j’ai la dll, que faut-il faire pour réutiliser les fonctions ?

Plusieurs solutions existent, en particulier utiliser du code « unsafe » autorisant les pointeurs en C#, mais je préfère ‘Marshaller » les types de données, c’est à dire indiquer comment doivent être gérés les passages de paramètres par rapport à leur type. Par exemple essayons d’utiliser la fonctions gsl_stat_mean décrite dans la documentation GSL comme : double

gsl_stats_mean (const double data[], size_t stride, size_t n)

This function returns the arithmetic mean of data, a dataset of length n with stride stride.

Il est dit : la fonction retourne un double, prend un paramètre un tableau de double et deux paramètre de type size_t, quelques recherches dans la documentation  et sur google nous apprennent que les paramètre size_t peuvent être assimilés à des int, on connaît donc toutes les informations sur la fonction, il ne reste plus qu’à indiquer ces données au code C#

[DllImport("libgsl.dll", EntryPoint = "gsl_stats_mean")]
internal static extern double mean(
[In] [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] double[] data,
[In] int stride,
[In] int n
);

Décomposons un peu cette fonction.

  1. [DllImport(« libgsl.dll », EntryPoint = « gsl_stats_mean »)] : on utilise une dll qui s’appelle « libgsl.dll » et le point d’entrée, c’est à dire la fonction de la dll que l’on appelle est : « gsl_stats_mean »
  2. internal static extern double mean( : internal , cette fonction ne peut être appelée que dans ce projet, static pas de soucis, extern : le code de la fonction n’est pas présent mais vient d’une source externe. double c’est le type qui est retourné par la fonction et mean correspond au nom C# de la fonction.
  3. [In] [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3)] double[] data, : premier paramètre de la fonction mean. [In] signifie que le paramètre doit être Marshaller du C# a la dll C++ mais qu’il ne sera pas modifié par la fonction C++ (sinon ou utilise [In, Out] voir [Out] tout seul! MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 3) : le type du paramètre est un tableau (LPArray) et c’est le troisième paramètre de la fonction qui en indique la taille (ceci est facultatif). double[] data : type c# du paramètre et nom du paramètre dans la fonction C++
  4. Idem pour les paramètres      [In] int stride et   [In] int n sauf que le type int n’a pas à être marshallé.

Pour appeler la fonction rien de plus simple :

Main(){
double [] test = {1,2,3};
Console.WriteLine(ClassGsl.mean(test,1,3));
Console.Read();
}

et on obtient : 2!

Il n’y a plus qu’à reproduire ce schéma pour toutes les fonctions que l’on souhaite appeler, évidement quand des structures s’en mêlent tout devient un peu plus compliqué… un prochain article sur le sujet dans très peu de temps !