FAQ DelphiConsultez toutes les FAQ
Nombre d'auteurs : 124, nombre de questions : 934, dernière mise à jour : 23 octobre 2024 Ajouter une question
Cette FAQ a été réalisée à partir des questions fréquemment posées sur les forums Delphi et Delphi et bases de données de www.developpez.com et de l'expérience personnelle des auteurs.
Nous tenons à souligner que cette FAQ ne garantit en aucun cas que les informations qu'elle propose soient correctes. Les auteurs font le maximum, mais l'erreur est humaine. Cette FAQ ne prétend pas non plus être complète. Si vous souhaitez y apporter des corrections ou la compléter, contactez un responsable (lien au bas de cette page).
Nous espérons que cette FAQ saura répondre à vos attentes. Nous vous en souhaitons une bonne lecture.
L'équipe Delphi de Developpez.com.
- Pourquoi mon projet ne se compile-t-il pas ?
- Comment réaliser de la compilation conditionnelle ?
- Comment trouver l'origine des codes d'erreurs internes de Delphi ?
- Pourquoi Delphi réclame-t-il un .pas alors que je n'ai qu'un .dcu ?
- Comment résoudre le problème des références circulaires ?
- Comment tester les versions de Delphi avec des directives de compilation ?
- Comment simuler des variables de classe ?
- Comment enregistrer une propriété de type tableau/pointeur/etc dans les dfm ?
- Comment créer une fenêtre à partir de son nom de classe ?
- Comment réduire la taille de son exécutable ?
- Comment résoudre l'erreur "L'unité X a été compilée avec une version différente de Z.Y" ?
- Comment désactiver localement une directive de compilation ?
Il peut arriver que votre projet ne se compile pas pourtant le code semble correct.
Cela peut être dû au fait que le nom du répertoire de votre projet contienne des accents. Il faut donc tout simplement utiliser des caractères non accentués dans le nom du répertoire pour corriger le problème.
La compilation conditionnelle est la compilation d'une partie du code ou non suivant des conditions. Ces conditions sont déterminées par une série de {$IFDEF ... } ... {$ENDIF} portant sur des identificateurs déclarés par {$DEFINE} ou {$UNDEF}.
Les utilisations courantes sont les suivantes :
Ajout de messages pour la mise au point :
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 | {$DEFINE MISEAUPOINT} procedure TForm1.Button1Click(Sender: TObject); Var i:Integer; begin i:=FaitUnCalcul; {$IFDEF MISEAUPOINT} { Cette ligne n'est compilée que pour la mise au point } ShowMessage(IntToStr(i)); {$ENDIF} TraiteLeCalcul(i); end; |
Compilation suivant les versions de Delphi :
Des identificateurs sont prédéfinis pour chaque version de Delphi, ce qui permet de compiler différemment suivant la version.
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 | uses Windows, Messages, SysUtils, Classes, Controls, Forms, StdCtrls, AppEvnts, {$IfDef VER130} DsgnIntf; { Pour Delphi 5 } {$Else} DesignEditors, DesignIntf; { Pour Delphi 6 et 7 } {$EndIf} |
- VER80 est prédéfini pour Delphi 1 ;
- VER90 est prédéfini pour Delphi 2 ;
- VER100 est prédéfini pour Delphi 3 ;
- VER120 est prédéfini pour Delphi 4 ;
- VER130 est prédéfini pour Delphi 5 ;
- VER140 est prédéfini pour Delphi 6 ;
- VER150 est prédéfini pour Delphi 7 ;
- VER160 est prédéfini pour Delphi 8 ;
- VER170 est prédéfini pour Delphi 2005 ;
- VER180 est prédéfini pour Delphi 2006 ;
- VER185 est prédéfini pour Delphi 2007 pour Win32 ;
- VER190 est prédéfini pour Delphi 2007 pour .Net ;
- VER200 est prédéfini pour Delphi 2009 ;
- VER210 est prédéfini pour Delphi 2010 ;
- VER220 est prédéfini pour Delphi XE ;
- VER230 est prédéfini pour Delphi XE2 ;
- VER240 est prédéfini pour Delphi XE3 ;
- VER250 est prédéfini pour Delphi XE4.
Compilation suivant l'OS :
Cet exemple est tiré de la définition du type TSearchRec utilisé par FindFirst/FindNext.
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | TSearchRec = record Time: Integer; Size: Integer; Attr: Integer; Name: TFileName; ExcludeAttr: Integer; {$IFDEF MSWINDOWS} FindHandle: THandle platform; FindData: TWin32FindData platform; {$ENDIF} {$IFDEF LINUX} Mode: mode_t platform; FindHandle: Pointer platform; PathOnly: String platform; Pattern: String platform; {$ENDIF} end; |
En dehors des messages d'erreurs de compilation classiques, Delphi produit quelquefois des messages d'erreur interne.
Ils sont difficiles à résoudre car ils ne sont pas tous documentés loin s'en faut, mais il peut être utile de se faire une idée de l'origine du problème en faisant attention à la première lettre du code du message :
- B : debugger
- BC : debugger
- BR : browser
- C : codegen
- CM : command line version of the compiler
- D : parser
- DB : debugger
- DBG: debug info output
- DM : IDE version of the compiler
- E : parser
- EO : debugger/evaluator
- FN : filename / pathname parsing
- GH : HPP generator
- I : code generator
- IN : inspectors
- L : linker
- LI : BPI file writing
- LO : object file loading
- M : memory allocation
- MA : name mangling
- MB : multi-byte (MBCS) support
- O : object (OMF) file handling
- P : package managment
- R : resource writing
- S : scanner
- ST : standard procedure handling
- SY : symbol table
- T : code generator
- TI : RTTI generator
- U : parser
- UD : IDE version of the compiler
- UI : error handling
- URW: DCU reading/writing
- W : Object file (OMF) writing
- X : code generator
Dans la plupart des cas la fermeture de Delphi, la suppression des fichiers .DSM et la construction complète de projet permettent de corriger cette erreur.
Le numéro donné en plus correspond à un numéro de ligne interne au compilateur. Seul Borland peut exploiter cette information.
Les fichiers .dcu de Delphi correspondent à la version compilée des unités (.pas).
Les spécifications de Borland stipulent que ces fichiers ne sont pas compatibles d'une version à l'autre de Delphi. Lorsque Delphi vous réclame un fichier .pas alors que vous n'avez que le .dcu, cela signifie que ce .dcu a été compilé avec une version différente de la vôtre. Vous devez donc obtenir une version de votre composant spécifique à votre version de Delphi.
Note : lorsque l'on parle ici de version de Delphi, il s'agit du numéro de version et non du type (personnel, entreprise, etc.).
Quand deux fiches doivent se référencer mutuellement, on ne doit pas placer les deux clauses uses dans la section interface de leur fichier unité respectif. Cela provoque l'erreur "Référence circulaire" à la compilation.
Dans le cas le plus simple de deux unités mutuellement dépendantes, cela signifie que les unités ne peuvent se référencer mutuellement dans la clause uses de leur section interface. Ainsi, l'exemple suivant produit une erreur de compilation :
Première unité
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 | Unit Unit1; interface uses Unit2; ... end; |
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 | unit Unit2; interface uses Unit1; // Impossible dans ce cas d'utilisation ... end; |
- Placez les deux clauses uses, avec les identificateurs d'unités, dans la section implementation de leur fichier unité respectif, c'est ce que fait la commande du menu Fichier|Utiliser l'unité.
- Placez l'une des clauses uses dans la section interface et l'autre dans la section implementation.
Utilisons la solution numéro 2 :
Les deux unités peuvent sans problème se référencer mutuellement si l'une des références est déplacée dans la section implémentation :
Première unité, elle n'est pas modifiée.
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 | Unit Unit1; interface uses Unit2; ... end; |
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 | unit Unit2; interface ... implementation uses Unit1; //Modification ... end; |
Les versions du compilateur Delphi peuvent être testées avec des directives de compilation.
- VER80 -> Delphi 1
- VER90 -> Delphi 2
- VER100 -> Delphi 3
- VER120 -> Delphi 4
- VER130 -> Delphi 5
- VER140 -> Delphi 6
- VER150 -> Delphi 7
- VER160 -> Delphi 8
- VER170 -> Delphi 2005
Exemple : pour faire une action différente pour Delphi 5 seulement :
Code delphi : | Sélectionner tout |
1 2 3 4 5 | {$IFDEF VER130} à faire si Delphi5 {$ELSE} à faire sinon {$ENDIF} |
On utilise pour cela la constante RTLVersion déclarée dans System.pas.
Exemple : pour tester la version du compilateur Delphi sur une plage :
Code delphi : | Sélectionner tout |
1 2 3 4 5 | {$IF RTLVersion >= 13} //si delphi5 ou supérieur à faire si Delphi >= Delphi5 {$ELSE} à faire sinon {$IFEND} |
Exemple pour Delphi7 :
Code delphi : | Sélectionner tout |
1 2 | const RTLVersion = 15.00; |
À la différence des méthodes de classes il n'existe pas de variables de classes en Delphi. Pour les simuler on doit donc déclarer des méthodes de classe associées à une variable globale.
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | // Solution proposée par Cpdump, Sjrd et Nono40 interface type TMaClasse = class public class function GetDefaultValue : Char; class procedure SetDefaultValue(New : Char); end; implementation var TMaClasse_DefaultValue : Char = ' '; class function TMaClasse.GetDefaultValue : Char; begin Result := TMaClasse_DefaultValue; end; class procedure TMaClasse.SetDefaultValue(New : Char); begin TMaClasse_DefaultValue := New; end; end. |
La solution consiste en l'utilisation d'une seule méthode héritée de TPersistent : DefineProperties. Il faut la réimplémenter et y appeler autant de fois que nécessaire la méthode DefineProperty / DefineBinaryProperty de l'objet TFiler passé en paramètre.
DefineProperty doit être utilisé si vous devez écrire/lire des types de base. DefineBinaryProperty doit être utilisé si vous devez écrire vos propriétés dans des flux.
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | type TMonPersistent = class(TAncetre) // TAncetre hérite de TPersistent private ... procedure LoadMyProp1(Reader : TReader); procedure SaveMyProp1(Writer : TWriter); procedure LoadMyProp2(Stream : TStream); procedure SaveMyProp2(Stream : TStream); protected ... procedure DefineProperties(Filer : TFiler); override; public ... published ... end; |
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 | procedure TMonPersistent.DefineProperties(Filer : TFiler); begin inherited; // Surtout ne pas oublier la méthode héritée ! Filer.DefineProperty('MyProp1', LoadMyProp1, SaveMyProp2, True); Filer.DefineBinaryProperty('MyProp2', LoadMyProp2, SaveMyProp2, True); end; |
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | procedure TMonPersistent.LoadMyProp1(Reader : TReader); begin // Lire avec les méthodes de TReader end; procedure TMonPersistent.SaveMyProp1(Writer : TWriter); begin // Ecrire avec les méthodes de TWriter end; procedure TMonPersistent.LoadMyProp2(Stream : TStream); begin // Lecture depuis Stream end; procedure TMonPersistent.SaveMyProp2(Stream : TStream); begin // Ecriture dans Stream end; |
En regardant de près dans l'unité Classes vous trouverez :
- procedure RegisterClass(AClass: TPersistentClass);
Cette procédure permet de recenser une classe d'objet persistant pour que le type de classe puisse être retrouvé. - function FindClass(const ClassName: string): TPersistentClass ;
Cette fonction permet de localiser un type de classe par son nom. FindClass recherche les classes connues du système d'enregistrement.
Mécanisme :
En appelant RegisterClass, la classe sera recensée par le système d'enregistrement de la VCL. La méthode FindClass tente de localiser dans le système d'enregistrement, la classe dont le nom est passé en paramètre. Si la classe n'est pas trouvée alors FindClass déclenche une exception.
Le code ci-dessous met en œuvre ce mécanisme et vous permet de retrouver une fenêtre par son nom de classe.
Recommandations:
Dans un nouveau projet, créez deux nouvelles fiches TForm2 et TForm3 respectivement dans Unit2 et Unit3.
Sur TForm1 (Unit1), placez un TEdit et un TButton.
Remplacez le code de l'unit1 par celui fourni ci-dessous.
Utilisation:
À l'exécution renseignez le Tedit avec soit la valeur TForm2, soit la valeur TForm3 puis validez en cliquant sur le TButton.
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 | unit Unit1; interface uses Unit2, unit3, Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Edit1: TEdit; Button1: TButton; procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.FormCreate(Sender: TObject); begin RegisterClass(Tform2); RegisterClass(Tform3); end; procedure TForm1.Button1Click(Sender: TObject); var f : Tformclass; begin try f := tformclass(findClass(edit1.text)); with f.create(self) do show; except raise; end; end; end. |
Il existe plusieurs méthodes pour alléger un exécutable sous Delphi.
Il est évident qu'il n'existe pas de solution miracle mais quelques options de compilation permettent de gagner quelques Kilo octets superflus.
Nous ne verrons ici que les méthodes les plus courantes.
- Limiter la taille des ressources images: Une TImageList avec des Bitmap de 500x500, ça grossit vite… Donc préférez des types d'images moins lourds (jpg, png, gif, etc.). Il existe de plus des descendants de TImageList qui prennent en charge ces formats.
Plusieurs paramètres dans les options du projet spécifient si le compilateur doit inclure ou non des données à l'exécutable.
Certaines peuvent être désactivées. Ces options se trouvent dans Projet\Options… onglet Linker.
- TD32 : {$D+} L'aide de Delphi a écrit :
Code other : | Sélectionner tout |
1 2 | Active ou désactive la génération d'informations de débogage. Ces informations se présentent sous la forme de tables de numéros de lignes propres à chaque procédure, qui contiennent les adresses du code correspondant à chaque ligne du texte source. Pour les unités, les informations de débogage sont enregistrées dans le fichier unité, avec le code objet de l'unité. Elles augmentent la taille des fichiers unité et l'espace mémoire occupé lors de la compilation des programmes utilisant cette unité, mais n'affectent ni la taille ni la vitesse d'exécution du programme exécutable. |
- Symbole locaux : {$L+} L'aide de Delphi a écrit :
Code other : | Sélectionner tout |
Active ou désactive la génération d'informations concernant les symboles locaux. Il s'agit des noms et types de toutes les variables et constantes locales au module, c'est-à-dire tous les symboles de la partie implémentation du module (y compris les symboles des procédures et fonctions). Pour les unités, les informations concernant les symboles locaux sont stockés dans le fichier unité avec le code objet de l'unité. Ces informations augmentent la taille des fichiers unité et l'espace mémoire nécessaire à la compilation des programmes utilisant l'unité.
- Utiliser DCUIL/DCU de débogage : L'aide de Delphi a écrit :
Code other : | Sélectionner tout |
Les DCUIL/DCU de débogage contiennent des informations de débogage et sont construits avec les cadres de piles. Lorsque cette option est cochée, le compilateur ajoute le chemin des DCUIL/DCU de débogage au début du chemin de recherche de l'unité
- Construire avec les paquets d'exécution :
Si cette option est décochée, tous les packages nécessaires à l'exécution de l'application seront intégrés au fichier.
Sinon, le fichier sera beaucoup plus petit mais nécessitera, pour son déploiement, de fournir les fichiers .bpl qu'il utilise
Sur d'anciens projets, la compilation peut échouer en indiquant un message d'erreur dans la clause Uses d'une unité :
Code : | Sélectionner tout |
1 2 | [Erreur fatale] info.pas(8): L'unité XXXX a été compilée avec une version différente de ZZZ.yyyy exe System.RTLVersion |
Il existe, dans le répertoire de l'unité en cause, un fichier .dcu compilé avec une autre version de Delphi que celle que vous utilisez actuellement. La présence de ces unités compilées provoque des conflits que le compilateur ne sait résoudre.
La suppression des fichiers .dcu suivi d'une recompilation du projet résout ce problème.
À noter que si vous ne disposez pas des fichiers sources, cas de composants shareware ou freeware, il n'est pas possible de résoudre ce problème.
Certaines directives de compilation ayant une portée locale il reste possible de contrôler leurs activation ou désactivation pour une portion de code.
Prenons ici le cas de la directive I, l'aide en ligne de Delphi nous indique :
La directive bascule $I active ou désactive la génération automatique du code vérifiant le résultat des appels aux procédures d'E/S. Si une procédure d'E/S renvoie un code retour d'E/S non nul alors que cette bascule est activée, une exception EInOutError est déclenchée (ou le programme termine son exécution si la gestion des exceptions est désactivée). Lorsque cette bascule est désactivée, le programme doit tester les codes retour des procédures d'E/S à l'aide de la fonction IOResult.
Dans notre exemple, les directives de compilation du projet active la directive $I, elle sera donc utilisée par tout le code. Mais ici, on souhaite pouvoir tester le résultat de la fonction IOResult au lieu de déclencher une exception et seulement pour cette procédure. On donc doit désactiver provisoirement cette directive afin de ne pas avoir d'effet de bord sur le reste du code.
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | Procedure Log(Chaine:String; LogDate : Boolean); var F : Textfile; S : String; begin Assignfile(F, 'C:\Temp\Test.txt'); {$IFOPT I+} // Si la directive I est activée {$DEFINE I_Plus} // Alors défini le symbole (local) {$I-} // Et désactive la directive {$ENDIF} Append(F); //Section de code concerné {$IFDEF I_Plus} // Si la directive I était précédemment activée {$I+} // Alors on réactive la directive {$ENDIF} {$UNDEF I_Plus} //On supprime le symbole précédemment défini if IOresult <> 0 then Rewrite(F); Try if LogDate then S:=FormatDateTime('DDDD DD MMMM YYYY HH:NN:SS',Now) else S:=''; Writeln(F,S+' '+Chaine); Finally Closefile(F); end; end; |
Code delphi : | Sélectionner tout |
1 2 3 4 5 6 7 8 9 | {$IFOPT I-} // Si la directive I est désactivée {$DEFINE I_Moins} // Alors défini le symbole (local) {$I+} // Et active la directive {$ENDIF} Append(F); //Section de code concerné {$IFDEF I_Moins} // Si la directive I était précédemment désactivée {$I-} // Alors on déactive la directive {$ENDIF} {$UNDEF I_Moins} //On supprime le symbole précédemment défini |
Proposer une nouvelle réponse sur la FAQ
Ce n'est pas l'endroit pour poser des questions, allez plutôt sur le forum de la rubrique pour çaLes sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright © 2024 Developpez Developpez LLC. Tous droits réservés Developpez LLC. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents et images sans l'autorisation expresse de Developpez LLC. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.