Programmation en C# sous Linux avec Mono

Lainé Vincent

Ce tutoriel a pour but de faire découvrir au lecteur le framework Mono qui permet de faire du développement en C# et VB.NET sous Linux.

N'hésitez pas à commenter cet article ! Commentez . ♪

- Titre : Programmation en C# sous Linux avec Mono

- Auteur : Lainé Vincent

- Parution : 9 mai 2005

- Licence : Copyright ® 2020 Lainé Vincent. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Installation

- III. Où se trouve quoi ?

- IV. Un petit tour de Mono

- IV-A. Compatibilité avec le framework .NET

- IV-B. Le cas des Winforms

- IV-C. Mais s'il n'y a pas de Winforms avec quoi faire des interfaces graphiques ?

- V. Un EDI pour Mono : MonoDevelop

- V-A. Installation

- V-B. Ça ressemble à quelque chose de connu…

- V-C. Mais où est le concepteur graphique ?

- V-D. Les fichiers générés par MonoDevelop

- VI. Programmes console avec Mono

- VI-A. Le fameux « Hello World »

- VI-B. Accès à MySql avec Mono

- VI-C. Les IO avec Mono, amélioration du programme d'accès à MySql

- VII. Conclusion

- VIII. Ressources

- IX. Remerciements

I - Introduction

.Net est un environnement managé utilisant un compilateur Just In Time (JIT) pour compiler à la volée le code IL (Intermediate Language) des assemblies formant les programmes écrits en .Net.

Le Framework gère en particulier l'allocation de mémoire et la sécurité des assemblies. De plus, il met en place le Global Assembly Cache (GAC) qui permet la centralisation et le contrôle des versions des bibliothèques grâce à une signature cryptographique.

Tous ces éléments et en particulier le code managé font de .Net un environnement potentiellement multiplate-forme. Toutefois, Microsoft (le créateur de .NET) n'a pas souhaité sortir le Framework .Net pour d'autres plates-formes comme Mac ou Unix/Linux.

C'est de cet oubli que viennent les projets Mono et DotGNU qui sont nés grâce à la disponibilité des spécifications. Dans ce tutoriel je ne parlerai pas de DotGNU tout simplement, car je ne l'ai pas testé.

Au cours de ce premier tutoriel consacré à la programmation en C# avec Mono nous allons voir les bases de Mono.

Ce tutoriel suppose que vous avez les bases en programmation avec .Net et C#.

Attention afin de faciliter la compréhension je parlerai de Framework .Net pour le Framework (sic) distribué par Microsoft et de Mono pour le Framework Mono.

Mono supporte actuellement le C# en standard et de plus un compilateur VB.NET est en cours de programmation. La version présente dans le paquetage 1.1.4 est la version 0.92 de ce compilateur nommé mbas (Mono Basic)

II - Installation

Autant le dire tout de suite : l'installation de logiciel sous Linux n'est pas chose aisée.

Mono ne déroge pas à la règle pour peu que l'on veuille l'installer à partir des sources avec GTK#. Personnellement, je n'ai pas réussi à installer Mono 1.0.6 et GTK# à partir des sources sur ma Mandrake 10.1 (problème de dépendances).

J'ai donc opté pour les paquets disponibles sur les sources contrib des miroirs Mandrake. Toutefois il existe de nombreux paquets précompilés, pour de nombreuses distributions, disponibles sur le site de Mono et les heureux utilisateurs de Gentoo (comme moi :-) ) trouveront les paquets portage pour leur distribution favorite.

De plus au moment de la rédaction de ce tutoriel des installateurs binaires de mono sont disponibles pour Windows et Linux. Sous Linux l'installateur ne requiert aucune dépendance à l'installation, mais l'exécution de certains programmes (comme MonoDoc) demande à ce que certains autres logiciels écrivent dans d'autres langages comme C/C++ (GTK+, GECKO…) soient installés. Personnellement je trouve que ce paquet binaire est une bonne chose, car il facilite le déploiement de Mono sur les machines cibles. Il faut cependant faire attention aux dépendances cachées de vos programmes et donc les tester sur une installation minimale de Mono et d'une distribution Linux.

Par exemple, pour une application écrite avec GTK#, tester votre application sur machine qui ne possède pas GTK+ et Gnome afin de voir s'il y a des dépendances à combler.

Pour ceux qui sont sous Mac ou qui veulent installer Mono à partir des sources je les invite à se rendre sur le site de Mono afin d'y trouver les informations nécessaires à la compilation et à l'installation.
Voilà un rapide récapitulatif des commandes :

$> tar xvfz mono-1.1.6.tar.gz
$> cd ./mono-1.1.6
$> ./configure
$> make
$> su
#> make install
#> exit

$> désigne le prompt du shell utilisateur et #> désigne le prompt du shell root (ou super utilisateur).

Après cela Mono est installé.
Pour installer GTK# :

$> tar xvfz gtk-sharp-1.9.2.tar.gz
$> cd gtk-sharp-1.9.2
$> ./configure
$> make
$> su
#> make install
#> exit

En cas d'erreur de la configuration de GTK# vérifiez que les fichiers de configuration d'application (.pc) de mono sont bien installés au bon endroit.

Dans mon cas, il a fallu que je les copie dans /usr/lib/pkgconfig/, car l'installation de mono les installait dans /usr/local/lib/pkgconfig.

Après l'exécution de ces commandes, Mono est installé et configuré pour utiliser les winforms managées (voir plus loin ) et GTK#.

III - Où se trouve quoi ?

Quels sont les éléments les plus importants que mono embarque ?
- mono : c'est l'exécutable le plus important, car il permet d'exécuter les applications .NET.
- gacutil : c'est l'utilitaire permettant d'ajouter des assemblies au Global Assembly Cache.

IV - Un petit tour de Mono

IV-A - Compatibilité avec le framework .NET

En théorie, la compatibilité entre le Framework .Net et Mono devrait être totale.
En effet, tous deux exploitent le code IL et les assemblies .Net sont compilés en IL.
Oui, mais voilà, les assemblies de base du Framework .Net font appel aux fonctions de l'API Win32, or ces fonctions ne sont pas présentes sous Linux ou Mac. C'est pourquoi Mono redéfinit les assemblies de base pour assurer la compatibilité avec les systèmes Linux et Mac.
Toutefois, les assemblies compilées sous Windows avec le Framework .Net, si elles ne comportent pas de référence à des bibliothèques que Mono n'a pas, peuvent tourner sans être recompilées sous Mono. Mais je ne pense pas que ce soit une bonne idée à moins que la compilation de votre bibliothèque/programme prenne plus de deux jours.
En effet, les deux frameworks même s'ils exploitent les mêmes langages, restent différents par certains points.

IV-B - Le cas des Winforms

Comme dit précédemment les classes de base du Framework .NET ont été portées pour tourner sous Linux et Mac.
Toutefois il existe une classe importante qui n'a pu être portée, la classe System.Windows.Forms.
En effet cette classe est dédiée à la gestion de l'interface graphique et des signaux aux applications. Or le traitement des signaux et les composants standard de l'interface graphique sont forts différents sur les différentes plates-formes. Ces nombreux appels aux fonctions de l'API WIN32 font que cette classe n'a pas pu être portée pour l'instant.
Mais le Projet MWF (Managed Windows Form) est un projet dynamique et dès maintenant la version 1.1.6 embarque les Winforms avec elle. Ce projet avance rapidement maintenant et à l'heure de l'écriture de ce tutoriel le projet est marqué comme fini à 93 %. C'est pourquoi je me permets de faire ici l'hypothèse qu’avant la fin de l'année 2005, les Winforms seront totalement supportées.
De plus j'ai réussi à faire tourner une petite application basique compilée sous Windows avec le framework 1.1 sous mono avec les Winforms Managées.

IV-C - Mais s'il n'y a pas de Winforms avec quoi faire des interfaces graphiques ?

Pour pallier le problème de la classe Winforms, l'équipe de Mono a décidé de créer une surcouche à GTK+ nommé GTK#.
Pourquoi avoir choisi GTK plutôt que Qt qui est écrit en C++ donc plus proche du C# ? Pour deux raisons : d'abord, Qt est un Framework commercial et surtout le chef de projet de Mono n'est autre que celui de Gnome (l'environnement de bureau) Miguel de Icaza.
Un projet pour créer une surcouche à Qt est en cours de développement, mais je n'ai pas pu le tester.
Plus généralement Mono est distribué avec de nombreux composants optionnels tels que les drivers pour MySql (déconseillé voir sur le site de MySql et l'exemple à la fin), Firebird (base de données), Mozilla Firefox (GECKO), et surtout OpenGL ainsi que bien d'autres.

V - Un EDI pour Mono : MonoDevelop

Afin de développer des applications C# avec Mono sous Linux, un EDI a été développé.
Il s'appelle MonoDevelop et est présent dans les installateurs binaires de Mono pour Linux.
Pour développer avec Mono sous Windows, l'EDI le plus approprié est l'excellent SharpDevelop qui permet de choisir le compilateur et l'interpréteur.

V-A - Installation

Afin de procéder à l'installation de MonoDevelop, il vous faut récupérer soit les paquets binaires, soit les sources ou encore l'installateur binaire.
Ensuite je vous invite à vous reporter à l'explication qui est fournie sur le site de MonoDevelop afin de le compiler et de l'installer à partir des sources.
Si vous avez choisi l'une des deux autres méthodes, MonoDevelop est déjà installé.

V-B - Ça ressemble à quelque chose de connu…

En effet, MonoDevelop est le portage partiel de SharpDevelop sous GTK#/Mono. Je dis partiel, car de nombreuses fonctionnalités de SharpDevelop ne sont pas présentes sous MonoDevelop.

V-C - Mais où est le concepteur graphique ?

Il a été simplement été supprimé, car il ne pouvait pas servir pour la création d'interfaces graphiques avec GTK#.
On ne peut qu'espérer qu'un outil de ce genre soit rapidement développé et intégré à MonoDevelop et apparemment ce projet est en cours de réalisation, mais aucune précision n'a été trouvée.

V-D - Les fichiers générés par MonoDevelop

À la création d'une nouvelle solution, MonoDevelop génère de nombreux fichiers. Toutefois, un seul va nous importer pour l'instant : c'est le fichier Makefile que tout 'Linuxien' connaît.
Je tenais à le préciser, car c'est un fichier important pour la compilation et surtout qui mérite d'être examiné afin de comprendre comment fonctionne le compilateur mcs.

Attention : dans la version 0.6 de MonoDevelop, les fichiers cités au-dessus ne sont pas générés automatiquement.

De plus, le format des fichiers de solutions a été modifié pour des raisons internes.

VI - Programmes console avec Mono

Nous allons voir maintenant une série d'applications toutes simples pour illustrer le développement avec Mono.

VI-A - Le fameux « Hello World »

Ce premier programme va simplement nous servir à illustrer la compilation en ligne de commande et les fichiers Makefile présents dans les répertoires des programmes.
Je tiens à ajouter une petite précision au sujet des fichiers Makefile contenus dans les répertoires des exemples.
Tout à l'heure, j'ai parlé des fichiers générés par MonoDevelop, toutefois les fichiers présents dans les exemples sont entièrement écrits à la main.

Le Hello world

using System;

namespace Hello_World
{
public class Hello_World
{
public Hello_World()
{

}

public static void Main(string[] args)
{
Console.WriteLine("Hello Linux World :-) ");

}

}
}

Ligne de commande pour compiler avec mcs

mcs -out:hello_world.exe ./src/*.cs

Voyons maintenant les options utilisées et celles qui ne le sont pas encore dans les exemples :

Commandes

Description

-reference: ou -r:

assembly référencé dans le programme. L'assembly doit être dans le GAC

-lib: ou -l:

La même chose, mais l'assembly ne doit pas être dans le GAC

-pkg:

lie le package (au sens Linux) avec le programme à compiler (exemple : GTK#)

-target ou /t:

la cible de la compilation : exe, winexe,library,module

-out:

nom du fichier de sortie

Ce tableau n'est qu'un petit aperçu des options du compilateur mcs. Si vous voulez toutes les connaître vous faites :

mcs --help
ou
man mcs

Je vais maintenant vous montrer rapidement un fichier Makefile que j'utilise pour compiler sous Linux/Mono

all:
mcs -out:MonoMySqlIO.exe -r:./MySql.Data.dll -r:System.Data.dll ./MonoMySqlIO/*.cs
echo "mono ./MonoMySqlIO.exe" > ./monoMySqlIO
chmod +x ./monoMySqlIO

La première déclare le nom de la règle pour les commandes.

La deuxième ligne effectue la compilation en elle-même.
mcs est le compilateur C# de Mono.
-out:MonoMySqlIO.exe indique que le nom du fichier de sortie du compilateur.
-r:./MySql.Data.dll indique que l'on doit lier l'assembly MySql.Data.dll située dans le répertoire courant (./) au programme.
./MonoMySqlIO/*.cs indique l'emplacement des fichiers pour la compilation.

La troisième ligne crée un fichier qui va permettre de lancer facilement le programme.
echo « mono ./MonoMySqlIO.exe » Permet d'afficher sur la sortie standard le texte entre '"'
> ./monoMySqlIO redirige la sortie standard vers le fichier monoMySqlIO.

La quatrième ligne rend le fichier monoMySqlIO exécutable.
chmod commande standard de Linux pour changer les droits sur un fichier.
-x ajoute le droit d'exécution pour l'utilisateur.
./monoMySqlIO le fichier sur lequel on applique ces nouveaux droits.

VI-B - Accès à MySql avec Mono

Dans cet exemple, nous allons voir comment accéder à une base MySql en C#. Le driver utilisé est celui de MySql.com (version compilée) :

Accès à MySql sous Mono/Linux

using System;
using System.Data;
using MySql.Data.MySqlClient;

namespace MonoMySql
{
class MainClass
{
private MySqlConnection connection;
private MySqlCommand command;
private MySqlDataReader dataReader;

private bool Open;

public MainClass()
{
this.Open = false;

}

public void Lunch()
{
Console.WriteLine("Entrez le nom du server :");
string ServerName = Console.ReadLine();
Console.WriteLine("Entrez le nom d'utilisateur");
string userName = Console.ReadLine();
Console.WriteLine("Entre le mot de passe");
string password = Console.ReadLine();
Console.WriteLine("Entrez la base à laquelle se connecter ");
string dataBase = Console.ReadLine();

//On ouvre la connexion avec les paramètres rentrés par l'utilisateur
this.connection = new MySqlConnection("Server=" + ServerName + ";Database="
+ dataBase + ";User ID="+ userName + ";Password=" + password + ";");

try
{
this.connection.Open();
this.Open = true;

this.Menu();

}
//On vérifie que MySql ne lève pas d'exception
catch(MySqlException sqlEx)
{
Console.WriteLine("Exception MySql :-(");
Console.WriteLine(sqlEx.Message);
return ;
}
catch(Exception ex)
{
Console.WriteLine("Exception générale :-( ");
Console.WriteLine(ex.Message);
return;
}

}

private void Menu()
{
bool exit = false;

while(exit == false)
{
Console.WriteLine("Menu :");
Console.WriteLine("---------------------");
Console.WriteLine("1 : Executer une requete SELECT)");
Console.WriteLine("2 : Exécuter une requête UPDATE,INSERT,CREATE,...");
Console.WriteLine("3 : Quitter");
Console.WriteLine("Choix ?");
int rep = Int32.Parse(Console.ReadLine());

switch(rep)
{
case 1 : this.ExecuteQuery();
break;
case 2 : this.ExecuteNoQuery();
break;
case 3 : exit = true;
break;
default : break;
}

}
}

private void ExecuteQuery()
{
Console.WriteLine("Entrez la requête à effectuer");
string req = Console.ReadLine();

//On crée une nouvelle command avec la requête à effectuer et on l'associe à la connexion ouverte
this.command = new MySqlCommand(req,this.connection);

try
{
this.dataReader = this.command.ExecuteReader();

while(this.dataReader.Read())
{
foreach(Object obj in this.dataReader)
{
Console.Write(obj.ToString() + " *** ");
}
Console.WriteLine("-----------------------------------------");
}
}
catch(MySqlException sqlEx)
{
Console.WriteLine(sqlEx.Message);
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
this.dataReader.Close();
}

}

private void ExecuteNoQuery()
{
Console.WriteLine("Entrez votre requête");
string req = Console.ReadLine();

//On crée une nouvelle commande avec la requête et la connexion ouverte
this.command = new MySqlCommand(req,this.connection);

try
{    //ExecuteNonQuery renvoie le nombre de lignes affectées par la requête
Console.WriteLine("Nombre de lignes affectées : " + this.command.ExecuteNonQuery());
}
catch(MySqlException sqlEx)
{
Console.WriteLine(sqlEx.Message);
}
catch(Exception ex)
{
Console.WriteLine(ex.Message);
}

}

public void CloseMySql()
{
if(this.Open == true)
{
this.connection.Close();
}
}

[STAThread]
static void Main(string[] args)
{
Console.WriteLine("Bienvenue dans MonoMySql");
MainClass mainClass = new MainClass();
mainClass.Lunch();

mainClass.CloseMySql();
Console.WriteLine("À bientôt dans MonoMySql");
}
}
}

VI-C - Les IO avec Mono, amélioration du programme d'accès à MySql

Dans cet exemple, nous allons voir la création et l'écriture dans un fichier :

Changement par rapport à la version précédente

private FileStream fileStream;
private StreamWriter sw;

...

private void RunLog()
{
this.fileStream = new FileStream("./sql.log",FileMode.Create);
this.sw = new StreamWriter(this.fileStream);
this.log = true;
}

private void SaveLog(string log)
{
this.sw.WriteLine(log);
}

private void StopLog()
{
if(this.log == true)
{
this.sw.Close();
this.fileStream.Close();
this.log = false;
}
}

Conclusion sur ces programmes console avec Mono
À l'issue de ces trois petits programmes, les développeurs qui connaissent un peu la programmation sous .NET me diront :
« Eh alors où est la différence ? ».
Justement, ces programmes n'ont d'autre but que de montrer que si on sait programmer avec .NET sous Windows, on sait le faire avec Mono sous Linux et Mac.
Il n'existe que très peu de différences entre les deux environnements de programmation et c'est un jeu d'enfant que de faire des programmes qui sont multiplates-formes pour peu que l'on sépare l'interface graphique du corps du programme.
Voici un exemple de portage d'une application sous Mono et GTK# et qui été écrite à l'origine sous .NET.
Une application portée sous Linux/Mac avec mono et GTK#.

VII - Conclusion

Mono est un produit fini qui mérite que l'on passe un peu de temps à le découvrir.
En effet, il permet de porter des applications écrites en C# sous Linux et Mac sans trop de problèmes à condition, pour l'instant, de ne pas avoir d'interfaces graphiques trop complexes.

À noter qu'au moment de l'écriture de cet article une nouvelle version de Mono est sortie avec le support des Winforms en code managé (C#). Toutefois, par manque de temps je n'ai pas pu la tester complètement, mais cela prouve que le projet avance. Reste que si vos projets sont bien compartimentés, Mono est une solution viable pour faire tourner vos applications C# sous d'autres plates-formes que Windows.
À savoir également que Mono implémente d'ores et déjà les spécifications du Framework 2.0 de Microsoft alors même que celui-ci n'est pas encore sorti.
De plus même si dans ce tutoriel seule la partie « Windows » a été abordée Mono embarque dans son paquet un petit serveur ASP.NET et est tout à fait capable d'héberger des sites ASP.NET. Mais les utilisateurs d'ASP.NET seront davantage satisfaits par « mod_mono » qui est un module d'Apache 2 pour supporter ASP.NET. Vous pouvez ainsi bénéficier de la stabilité et de la puissance d'Apache sous un serveur Linux en faisant tourner vos pages ASP.NET

En résumé
Essayez Mono. Vous ne serez pas déçu par le logiciel en lui-même, mais plutôt par le manque d'offre de solution de développement qui fait que Mono tarde à prendre toute l'ampleur qu'il devrait.

Je souhaite longue vie à Mono qui m'a permis de concilier mes deux passions informatiques : Linux et C#.

VIII - Ressources

Voici les différentes adresses ou vous trouverez les logiciels :
Mono
MonoDevelop
GTK#

Liens autres en rapport avec les exemples :
Le driver .NET de MySql (utilisable sous Mono et Windows)

Liens internes pour la programmation
L'article de Thomas Lebrun sur MySql et .NET

Liens pour télécharger les projets (MonoDevelop)
le hello world
Le projet pour se connecter à MySql
Le projet MySql avec les fichiers de log

IX - Remerciements

Je tiens à remercier Cerberes pour la correction du français et tous ceux qui m'ont aidé à mettre au point cet article en particulier Laurent Dardenne pour ses nombreuses remarques.