IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)

Guide Pascal et Delphi

Guide Pascal et Delphi

Date de publication : 10/04/2000 , Date de mise à jour : 01/04/2008


XIX. Gestion des exceptions
XIX-A. Introduction
XIX-B. Try..Finally
XIX-C. Try..Except
XIX-C-1. Grammaire
XIX-C-2. Listes non exhaustive de classe d'exception
XIX-D. Exception personnalise
XIX-E. Raise
XIX-F. Conclusion


XIX. Gestion des exceptions

Le prsent tutoriel n'a pas la prtention de tout vous apprendre sur la gestion des exceptions (comme tous les tutoriaux que je fais mais celui-la plus que les autres). Mais vous devriez vous sentir l'aise avec la bte et ne plus rechigner les utiliser. Pour ceux qui ont dj pris l'habitude d'utiliser leurs services, je leur conseille de le parcourir quand mme, il se pourrait qu'ils apprennent quelque chose. Si tel n'tait pas le cas, toutes mes excuses (et mieux encore vous en connaissez plus sur le sujet, je vous invite complter/modifer ce tutorial).


XIX-A. Introduction

Tout d'abord qu'est qu'une exception ? Comme son nom l'indique une exception est un vnement imprvu par le programmeur (ou programme). Vous me direz comment peut-il grer quelques chose qu'il n'a pas prvu ? Trs simple, le programmeur sachant que le monde n'est pas parfait aura pris le soin de protger des blocs d'instructions sensibles.

Concrtement, lorsque vous dveloppez votre application, vous vous attendez ce que telle ou telle chose soit disponible (un fichier dll par exemple) ou que l'utilisateur entre tel type de donne dans telle variable (champ de saisie o il faut entrer des nombres) (par utilisateur, je considre une personne ou le programme lui-mme) or il arrive que les choses ne se passent pas tout fait comme vous les avez souhaites, fichier absent, caractre vers une variable numrique etc...
Pour protger votre application et plus prcisment un bloc de code, une solution est d'utiliser les exceptions et ainsi viter votre application de planter lamentablement. Une autre solution serait de faire des tests mais si pour des cas simples, les tests sont faciles mettre en place (l'exemple de la division par zro est assez parlant), il n'en est pas de mme pour des tches plus complexes ou sujettes diffrents types d'erreur (comme l'accs un fichier). D'autres diront que grer par exception, les cas problme, allge le code ( vous de voir).

Les exceptions peuvent tre de natures diffrentes suivant l'opration o elles se produisent. Ces diffrentes exceptions sont appeles type d'exception ou classe d'exception. Par exemple lors d'une tentative de conversion d'un string en float, si le string contient des lettres, on aura une exception de classe conversion (EConverError).

La protection d'un bloc de code se fait par l'utilisation d'un couple de mot cl. Suivant la faon dont l'on veut grer l'exception, deux couples existent, try..finally et try..except. Les deux couples protgent le code situ entre try et l'autre mot cl, si une exception arrive entre ces deux mots cls , le programme arrte de traiter le bloc protg et passe tout de suite aux instructions comprises entre le second mot cl du couple et poursuit ensuite les instructions suivant ce second bloc de code. La diffrence entre le couple try..finally et try..except se situe dans l'excution du second bloc d'instruction.

  • Avec try..finally, les instructions du second bloc d'instruction sont toujours excutes.
    
    instructions1
    try
       // Bloc de code  protger
       instructions protges
    finally
       // Second bloc de code
       // Ce bloc sera excut  la fin des instructions protges
       // ou ds qu'une erreur survient dans le bloc de code protg
       instructions2
    end;
    // suite des instructions qui seront excutes si elles ne provoquent pas d'erreur
    instructions3
    
  • Avec try..except, les instructions du second bloc d'instruction ne sont excutes que s'il y a eut une exception dans le bloc protg. Le couple try..except propose en plus de traiter, l'exception en fonction de la classe d'exception.
    
    instructions1
    try
       // Bloc de code  protger
       instructions protges
    except
       // Second bloc de code
       // Ce bloc ne sera excut que si une erreur survient dans la partie protge
       instructions2
    end;
    // suite des instructions qui seront excutes si elles ne provoquent pas d'erreur
    instructions3
    
Vous l'aurez remarqu, le bloc dit protg est celui qui le parat le moins. En fait par bloc protg, je pense que l'on dsigne la protection du programme contre un code sensible. Votre programme ne va plus planter lamentablement lorsqu'il rencontrera une erreur dans un bloc protg. Par contre vous continuerez d'avoir les messages d'erreur en l'excutant depuis Delphi, cela est destin vous aider vrifier que vous interceptez bien la bonne classe d'exception.


XIX-B. Try..Finally

La syntaxe de try..finally est :

try
   instruction1
finally
   instruction2
end;
Instruction1 est une partie sensible du code (manipulation d'un fichier, cration d'un objet, etc...) et instruction2 une partie du code qui doit tre excut quoiqu'il arrive (libration de ressource, fermeture d'une connexion etc...). Si une erreur survient dans les instructions du bloc instruction1, l'excution passe immdiatement l'excution d'instruction2 sinon l'excution termine les instructions et passe ensuite au instruction2.
Vous l'aurez compris, son utilisation est fortement recommande pour librer une ressource mme si le programme rencontre une erreur. Mais attention, l'instruction demandant la ressource doit se trouver l'extrieur du try..finally. Dans le cas contraire, s'il arrivait que le programme ne puisse pas allouer la ressource, tenter de la librer peut provoquer une erreur.

Exemple : Cration d'un objet

var
   Ob : TObjetExemple;
begin
   Ob := TObjetExemple.Create; // Cration de l'objet
   try
      {instruction}
   finally
      Ob.Free; // Libration
   end;
end; 
Exemple : assignation d'un fichier

function OuvrirF(Nom : TFileName) : boolean;
var
   F : Textfile;
   S : string;
   i, j, valeur :integer;
begin
   AssignFile(F,Nom);
   try
      Reset(F);
      readln(F,S);

      {instruction}

      Result := True;
   finally
      CloseFile(F);
   end;
end; 
Notez la position de demande d'allocation de ressource par rapport au try..finally.

idea Attention :

Tant que vous n'avez pas appel CloseFile(F), vous ne pouvez pas manipuler le fichier (renommer, dtruire, dplacer etc...). Ne l'oubliez pas ! Ceci est valable pour les fichiers mais aussi pour d'autres ressources (base de donne, priphrique...).
idea Prcision :

La protection d'un bloc de code permet d'viter la propagation du message d'erreur mais dans certain cas, il peut tre ncessaire de relancer sa diffusion. La commande raise peut tre utilise cet effet voir son chapitre pour plus de prcision.

XIX-C. Try..Except


XIX-C-1. Grammaire

Examinons une application fournissant un champ de saisie n'attendant que des nombres comme saisie. Deux solutions s'offrent vous, la premire empcher l'utilisateur d'entrer autre chose que des caractres numriques (assez compliqu mettre en place mais mieux au niveau de l'ergonomie) et la seconde utiliser les exceptions. Laissons la premire de ct et intressons-nous la seconde. Ce que nous devons protger est le moment o la saisie de l'utilisateur doit tre affecte une variable de type numrique. Vous pouvez tester en ralisant une application faisant une telle opration. Lors de l'excution, un message d'erreur se produira ds que vous affecterez des lettres la variables, plantant le plus souvent votre application. Par contre en protgeant votre bloc de code, non seulement, vous limitez l'erreur cette portion de code et vous pouvez en plus raliser un traitement spcifique au problme (rinitialiser des variables, informer l'utilisateur...).

La syntaxe est la suivante :

try
   instruction1
except
   instruction2
end;
instruction3
Instruction1 est comme pour le try..finally la partie sensible du code tandis qu'instruction2 le code qui sera excut si instruction1 provoque une erreur. Si une erreur survient dans les instructions du bloc instruction1, l'excution passe immdiatement l'excution d'instruction2 sinon l'excution termine les instructions et passe ensuite au instruction3.

Exemple : Gestion des erreurs lies une conversion de Type.

procedure TForm1.Button1Click(Sender: TObject);
var
   param1 : Double;
begin
   try
      param1 := StrToFloat(Edit1.Text);

      {suite des instructions}

   except
      on EconvertError do
         MessageDlg('Erreur : Vous devez entrer un rel'
            +#10#13+'Le sparateur dcimal est : '+DecimalSeparator, mtError, [mbOk], 0);
   end;
   {Autre instruction non sensible}
end;
Essayez cet exemple, en cas d'erreur de saisie vous aurez droit un message d'erreur un peu plus clair que ceux distills par Windows. Pour le vrifier, tapez votre chiffre en vous trompant dans le sparateur dcimal (le point au lieu de la virgule et vice versa), Sans la gestion d'erreur vous saurez seulement que votre saisie n'est pas valide sans comprendre pourquoi car vous avez bien entr un nombre alors que grce la gestion des exceptions, vous aurez le droit :

En plus, vous pouvez ajouter des instructions remettant votre programme dans un tat stable (rinitialisation de variable par exemple).

L'exemple ci dessous est une des faons d'crire la gestion des exceptions par try..except. Dans ce cas prcis, nous savions ce qui pouvait provoquer une erreur dans le code protg (une erreur de conversion) et nous n'avons trait que ce cas.
D'une manire plus gnrale, on peut considrer que la gestion d'exception peut intercepter des erreurs prvisibles et d'autre plus alatoires (non prvues) et que l'on peut soit traiter les erreurs prvisibles soit les autres ou les deux (ce qui quand mme prfrable).
Quand on veut traiter une erreur prvisible, il faut savoir quelle classe elle appartient, par exemple une erreur de conversion appartient la classe EConvertError (on peut savoir ceci en consultant dans l'aide, le type d'erreur souleve par une fonction particulire).

Le try..except pourra se prsenter ainsi :

try
   {instructions}
except
    {instruction ventuelle commun  tous les cas possibles d'erreur}

    // Gestion des cas prvisibles d'erreur
    on Exception1 do InstructionTraitantErr1;
    on Exception2 do InstructionTraitantErr2;
    ....
    on Exception(n) do InstructionTraitantErr(n);
else
    InstructionTraitantLesCasNonPrevue;
end; 

XIX-C-2. Listes non exhaustive de classe d'exception

Ceci est une liste incomplte des classes d'exception que vous pouvez tre amen utiliser.

  • EconvertError : Erreur de conversion, vous essayez de convertir un type en un autre alors qu'ils sont incompatibles.
    Exemple : un string en integer (StrToInt) avec un string ne correspondant pas un nombre entier.
  • EDivByZero : Le programme a tent de faire une division par zro (pas Cool).
  • EFOpenError : Le programme ne peut ouvrir un fichier spcifi (le fichier n'existe pas par exemple)
  • EInOutError : Erreur d'entre-sortie, sur le fichier spcifi
  • EReadError : le programme tente de lire des donnes dans un flux mais ne peut lire le nombre spcifi d'octets.
  • ERangeError : Dbordement de taille. Le programme dpasse les borne d'un type entier ou les limites d'un tableau.
  • EAbort : Exception spciale car elle n'affiche pas de message d'erreur. On peut s'en servir pour annuler une tache en cours si une condition arrive (une erreur par exemple). Pour la dclencher un simple appel Abort; suffit, une exception EAbort est alors gnre. C'est un moyen simple de crer une exception personnalise, il suffit alors de traiter l'exception on EAbort do.
Si vous n'arrivez pas trouver la classe d'exception correspondant votre code, tenter de provoquer l'erreur. Dans le message d'erreur, Delphi vous indiquera la classe d'exception (si elle existe), ce message permet aussi de tester si on a intercept la bonne classe d'exception.


XIX-D. Exception personnalise

Toutes les exceptions drivent de la Class exception, vous pouvez donc crer vos propres classes d'exception en hritant de cette classe. Ceci peut vous permettre de grer votre programme par exception en supprimant tous les test des cas qui ne vous intresse pas (exemple : la vrification qu'un diviseur est non nul).

En disant que toutes les exceptions drivent de la classe Exception, je ne suis pas tout fait exact. En fait n'importe quel objet peut tre dclench en tant qu'exception. Cependant, les gestionnaires d'exception standard ne grent que les exceptions drivant de la classe exception.

Dans votre programme, si vous voulez dclarer une nouvelle classe d'exception, vous aurez entrer le code suivant :

type
  MonException = class(Exception)
    [HelpContext : THelpContext; // Context dans l'aide]
    [Message : string; // Message d'erreur]
  public
    FonctionGerantErreur(); // Fonction  appeler en cas d'erreur
  end;

FonctionGerantErreur();
begin
  {instructions}
end;
Seule la premire ligne est obligatoire. Si vous ne prcisez pas le reste, la seule information disponible lors du dclenchement de votre exception sera son nom. FonctionGerantErreur est le nouveau gestionnaire de l'exception, dans cette fonction vous mettrez le code assurant la stabilit de votre application.

idea Remarque :

Pour accder au message ou la Mthode d'une Exception (FonctionGerantErreur) tapez E.Message ou E.MaFonction. Dans ce cas le try..except doit s'crire ainsi :

try
  instruction
except
  on E : Exception do
    ShowMessage('Message : ' + E.Message);
    // Et/Ou
    E.MaFonction; // Permet de Centraliser le code grant un type d'erreur
end; 
Exemple : Exception personnalise

type
   EValeurIncorrect = class(Exception);
et dans le code

if Valeur <> ValeurCorrect then
    raise EValeurIncorrect.Create('Valeur ne fait pas partie des valeurs autorises');
La proprit Message de la classe Exception (y compris les classes drives) et l'affichage de message personnalis dans le bloc except/end sont quivalents. Pour les classes d'exception dj existantes, on prfrera sans doute rendre le message plus explicite tandis que pour les classes personnalises on aura recours la proprit Message plutt que d'indiquer chaque fois le texte.
Il en est de mme pour les instructions grant l'erreur. On n'utilisera la Mthode de la classe que pour ceux personnalises, vitant d'utiliser une fonction orpheline ou pire de rcrire chaque fois le code.

Reportez-vous sur les classes pour plus de renseignement sur l'hritage.


XIX-E. Raise

Protger ainsi votre code, vous permet d'intercepter les messages d'erreur. Toutefois dans certaine situation, vous souhaiterez que le message d'erreur soit propag pour qu'il soit intercept par une autre gestion des exceptions.
Prenons l'exemple de l'assignation de fichier, plutt que d'utiliser une variable de retour pour indiquer le rsultat de l'opration, on pourrait transmettre le message d'erreur ventuel. A cet effet, la commande raise est votre disposition.
Raise ne sert pas uniquement propager un message d'erreur, on peut aussi s'en servir pour dclencher une exception (en gnrale pour dclencher une exception personnalise).

Ci-dessous, un exemple de dclenchement d'exception personnalise.

if Valeur <> ValeurCorrect then
    raise EValeurIncorrect.Create('Valeur ne fait pas partie des valeurs autorises');
Exemple complet de l'utilisation de raise :

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

type
  TForm1 = class(TForm)
    Edit1: TEdit;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Dclarations prives }
  public
    { Dclarations publiques }
  end;

  MonException = class(Exception)
  public
     function GestErr():string;
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

function MonException.GestErr():string;
begin
   if MessageDlg('La variable transmise est incorrect continuer avec la valeur par dfaut', mtInformation, [mbYes, mbNo], 0) =
       mrYes then
   begin
      Result := 'trs petit';
   end;
end;

function DoQuelqueChose(valeur : double):string;
begin
   // La fonction ne travaille que sur des nombres rels positifs
   if valeur < 0 then
   begin
      raise MonException.Create('Erreur: Travaille impossible !');
   end;

   // Travaille sur valeur compltement sans intrt
   if valeur < 10 then
      result := 'trs petit'
   else if valeur <= 50 then
      result := 'moiti de cent'
   else if valeur <= 100 then
      result := 'gal  cent'
   else
      result := 'trs grand';
end;

procedure TForm1.Button1Click(Sender: TObject);
var
   r : double;
   tmpstr : string;
begin
   try
      r := StrToFloat(Edit1.Text);
      tmpstr := DoQuelqueChose(r);
   except
    on E : MonException do
      tmpstr := E.GestErr;
    on EconvertError do
    begin
      ShowMessage('Erreur de Saisie : nombre attendu'
         +#10#13+'Sparteur dcimal : '+DecimalSeparator);
      tmpstr := 'Mauvaise saisie';
    end;
    else
    begin
       ShowMessage('Erreur Inconnue');
       tmpstr := 'Invalid';
    end;
   end;
   ShowMessage('Rsultat : '+tmpStr);
end;

end.

XIX-F. Conclusion

Grce aux exceptions apparues avec la programmation objet, le dveloppeur a maintenant sa disposition un outil efficace pour protger son programme des alas de l'informatique. J'espre que le prsent tutorial a t pour vous une mine d'information et que dsormais vous aborderez la gestion des exceptions avec srnit.

Copyright © 2008 Frédéric Beaulieu. Aucune reproduction, même partielle, ne peut être faite de ce site ni 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.