Recherche
1 connecté

  Actuellement : 1 commentaire Gestion des exceptions en Delphi

Gestion des exceptions sous Delphi

1. Introduction

En tant que programmeur Delphi (ou une autre language orienté objet), vous avez été ou serez amenés à rencontrer des exceptions.
En programmation, il est difficile et alourdissant pour le code de contrôler toutes les valeurs renvoyées par les fonctions, les entrées utilisateurs, ...
Heureusement, le language (le pascal objet) nous informe des erreurs et interrompt le programme et affiche un message descriptif de l'erreur.
Dans ce cas, comme on a une erreur et que par conséquent les données sont incertaines, le programme n'exécute pas la fin de la procédure (ou fonction, ...) en cours.
Le but de cette page est de pouvoir gérer ces exceptions afin de modifier le comportement du programme sans pour autant l'arrêter.

2. Le bloc TRY .. EXCEPT

Le bloc TRY EXCEPT offre au programmeur la possibilité d'exécuter un bloc d'instructions dans le cas d'une erreur d'exception.
Voici un exemple simple:


function Diviser(a,b:integer):integer;
begin
   try
      result := a div b;
   except
      result := 0;//Si on a une division par 0, 
	         //on force le résultat à 0 
   end;
end;

Exemple 1 : la gestion d'une erreur de division par zéro par un bloc TRY .. EXCEPT

Cette fonction gère donc ainsi la division par zéro en remplaçant le résultat par 0 dans le cas d'une exception.
Les instructions contenues entre EXCEPT et END (result := 0; dans ce cas-ci) ne sont exécutées que si une exception a été générée dans le bloc d'instructions contenu entre le TRY et le EXCEPT (dans ce cas-ci: result := a div b;).
Ceci évite donc un message d'erreur en mode exécution en stand-alone , le message d'exception étant quand même affiché en mode exécution sous environnement Delphi.

Dans ce cas-ci, on ne protège qu'une ligne de code, voici maintenant un autre exemple avec plusieurs lignes protégées par un TRY .. EXCEPT (on considère a et b comme integer):


try
   a := StrToInt(edit1.text);//Ici, on peut tomber sur 
		//des valeurs bizarres (lettres) dans le TEdit 
   b := b div a;  //Ici, on peut avoir une division par 0 
except
   on EConvertError do ShowMessage('La valeur entrée est incorrecte!');
   on EDivByZero do ShowMessage('Vous avez entré une valeur nulle!');
end;
Exemple 2 : Traiter plusieurs exceptions dans le même bloc

Remarquez que pour définir un bloc de plusieurs ligne entre un TRY et un EXCEPT, aucun begin n'est nécessaire (pareil entre EXCEPT et son END).
Dans l'exemple, on remarque qu'il y a deux raisons possibles pour qu'une exception soit générée: un mauvais paramètre pour la fonction StrToInt ou bien une division par 0.
Il est donc nécessaire de pouvoir savoir laquelle nous a fait entrer dans le bloc EXCEPT.
Ceci est possible grâce à la formule:


on Nom de l'exception do Bloc d'instructions;

Deux exemples concrets afin de comparer une programmation avec et une sans gestion des exceptions:

Exemple 3 :
Sans gestion des exceptions
Exemple 4 :
Avec gestion des exceptions

var a,b : integer;

b := 0;
ShowMessage('Message 1');
a := a div b;
ShowMessage(inttostr(a));
ShowMessage('Message 2');



var a,b : integer;

try
   b := 0;
   ShowMessage('Message 1');
   a := a div b;
   ShowMessage(inttostr(a));
except
   ShowMessage(
   'Vous avez effectué une division par zéro!');
end;


Résultat affiché:

Résultat affiché:

3. Le bloc TRY .. FINALLY

Contrairement à EXCEPT qui délimite les instructions à exécuter en cas d'exception, FINALLY force le programme à exécuter les lignes de son bloc dans tous les cas (exception ou pas).


var a : integer;

try
  a := StrToInt(edit1.text);//Ici, on peut tomber sur des 
		//valeurs bizarres (lettres) dans le TEdit
finally
  ShowMessage('Erreur ou pas, ce message doit être affiché');
end;


Exemple 5 : Une utilisation simple du bloc TRY .. FINALLY

L'exemple est assez inutile dans ce cas-ci, mais il vous montre que qu'il y ait une exception ou pas, on peut exécuter un bloc d'instructions.
Un exemple plus significatif de l'utilité d'un try .. finally me vient à l'esprit:
Soit un programme qui doit lire un fichier qui contient 10 valeurs, une par ligne et en afficher la somme.
Si vous n'utilisez pas de TRY .. FINALLY, votre code ressemblera à ceci:


function Somme(NomFichier:String):integer;
var fic : TextFile;
    Nombre, i : integer;
begin
result:=0;
AssignFile(fic, NomFichier);
reset(fic);
for i := 1 to 10 do
  begin
  ReadLn(Nombre);
  result:=result+Nombre;
  end;
closeFile(fic);
end;


Exemple 6 : Lecture de fichier sans gestion des exceptions

Ce code fonctionnera parfaitement dans les conditions idéales.
Mais imaginons maintenant que le fichier ne contienne que 5 nombres: on aura un message d'erreur qui indiquera une lecture au-delà de la fin de fichier et le programme passera la fin de la fonction sans exécuter les dernières lignes qui contiennent pourtant une ligne très importante: le CloseFile.
Ce qui veut dire que le fichier est toujours ouvert et que le programme ne saura plus y accéder...

Voici maintenant le code modifié pour parer à cette erreur éventuelle:


function Somme(NomFichier:String):integer;
var fic : TextFile;
    Nombre, i : integer;
begin
result:=0;
AssignFile(fic, NomFichier);
reset(fic);
  try
    for i := 1 to 10 do
      begin
      ReadLn(Nombre);
      result:=result+Nombre;
      end;
  finally
      closeFile(fic);
  end;
end;


Exemple 7 : Lecture de fichier avec gestion des exceptions

Dans ce cas-ci, le fichier sera fermé lorsque la fonction est terminée, qu'il y ait rencontré une erreur ou pas.
Remarquez que les blocs TRY .. FINALLY ne suppriment pas les messages d'erreurs comme les font les blocs TRY .. EXCEPT.

Les blocs TRY .. FINALLY sont aussi beaucoup utilisés pour libérer une instance d'objet qui a une durée de vie limitée à une fonction (ou procedure) avant de quitter cette fonction (ou procedure).

4. Utiliser les 2 blocs imbiqués

Il est parfois nécessaire d'utiliser un bloc TRY .. EXCEPT à l'intérieur d'un autre bloc TRY .. FINALLY
Reprenons l'exemple précédent: on veut que la fonction renvoie 0 si elle a rencontré une erreur et on ne veut surtout pas de message d'erreur.
Le code sera donc le suivant:


function Somme(NomFichier:String):integer;
var fic : TextFile;
    Nombre, i : integer;
begin
result:=0;
AssignFile(fic, NomFichier);
reset(fic);
  try
    try
      for i := 1 to 10 do
        begin
        ReadLn(Nombre);
        result:=result+Nombre;
        end
    except
        result:=0;
    end
  finally
    closeFile(fic);
  end;
end;


Exemple 8 : Imbrication d'un bloc TRY .. EXCEPT dans un bloc TRY .. FINALLY

5. Générer une exception

Vous pouvez évidemment créer vos propres exceptions pour prévenir des erreurs par exemple dans l'utilisation des composants que vous distribuez.
La déclaration en est simple:


type EMonException = class(Exception);

Et maintenant pour la générer:


raise EMonException.Create('Ceci est mon exception');

Vous remarquerez qu'on appelle la méthode Create et qu'on ne libère pas l'objet.
En fait, l'objet exception est libéré automatiquement par le gestionnaire d'exception.

6. Conclusion

Les exceptions sont des armes puissantes qui optimiseront la stabilité de vos applications.
De leur utilisation va dépendre la qualité de vos programmes.
Elles leur donnent aussi un aspect plus professionnel en personnalisant les messages d'erreur et en les rendant plus explicites et plus compréhensibles pour l'utilisateur.

7. Remarque

Par défaut, Delphi s'arrête sur toutes les exceptions lorsque l'application est lancée à partir de l'environnement de conception.
Ceci permet de se rendre compte des exceptions générées et de mieux les cibler.
Pour se mettre en situation réelle (telle que l'utilisateur le verra), vous avez 2 possibilités:

  • Vous compilez votre programme et l'exécutez à partir de l'explorateur
  • Vous pouvez désactiver les exceptions dans le menu Options du debugger du menu Outils

8. Remerciements

Je voudrais remercier les auteurs de

  • FirstPage 2000, un formidable éditeur HTML très complet. (gratuit)

Grand merci à Michel qui rassemble toute cette communauté de développeurs et toutes ces portions de code si précieux.

8. Contacts/Auteur

Actuellement développeur pour Electronic Design, je programme des applications PC et microprocesseurs pour les domaines de la parlophonie, téléphonie, domotique et systèmes d'appels.
Pour tout contact, vous pouvez m'envoyer un mail, je suis égalemnt sur icq sous l'UIN 30687888.

Marchioni Valérian, le 12 avril 2002.
Version du document: 1.0
Les exemples utilisés et l'article lui-même sont libres d'utilisation, de publication et de redistribution sans accord préalable nécessaire de l'auteur.