menu
  Home  ==>  articles  ==>  prog_objet_composants  ==>  packages_delphi   

Packages Delphi - John COLIBRI.



1 - Principe des Packages Delphi

Les Packages Delphi ont la même fonction que les DLL: ils permettent de compiler des parties de codes qui peuvent être utilisées par plusieurs applications.

Mais lorsque nous utilisons des DLL Windows (Dynamic Link Library), il faut ajouter des instructions qui permettent d'exporter des informations Delphi, comme les CLASSes ou les tForm.

Lorsque nous utilisons des Packages c'est le compilateur Delphi qui se charge de mettre en forme ces exportation.



Les Packages sont utilisés dans deux cas

  • pour ajouter des composants à la Palette
  • pour partitionner un .EXE en modules, afin de faciliter la mise à jour et le déploiement: il suffit de déployer la nouvelle version du Package, sans avoir besoin de recompiler le fichier .EXE. C'est en fait la technique utilisée par Delphi pour ajouter un nouveau composant à la Palette: vous n'avez en effet pas besoin de recompiler l'IDE Delphi (dont vous n'avez pas le source de toutes les façons)
Dans cet article, nous allons présenter les techniques et outils pour créer des Packages Delphi.




2 - Exemple de Package Delphi

2.1 - Objectif

Nous allons montrer comment construire et utiliser un Package qui contient
  • une fonction de calcul
  • une tForm Delphi.


2.2 - L'unité de Calcul

Voici une unité qui contient une fonction qui additionne deux nombres:

unit u_adder;
  interface

    function f_one_plus_two(p_onep_twoInteger): Integerstdcall;

    // -- Export the functions.
    exports
      f_one_plus_two;

  implementation

    function f_one_plus_two(p_onep_twoInteger): Integer;
      begin
        Result:= p_onep_two;
      end// f_one_plus_two

    end

Notez que:

  • nous avons forcé notre fonction à utiliser la convention de passage de paramètres Windows stdcall
  • nous avons ajouté une clause d'exportation exports


2.3 - L'éditeur de Package

Pour englober notre unité dans un Package, nous utilisons d'Editeur de Package:
   cliquez "File | New | Other "
   Delphi propose plusieurs éléments:

image

   sélectionnez et cliquez "Package" (l'icône dans le cercle rouge)
   Delphi ouvre l'Editeur de Package:

image

   cliquez "Add" pour ajouter notre unité
   _Delphi présente un dialogue de recherche d'unité

image

   cliquez "Browse", localisez U_ADDER.PAS et cliquez "OK"
   l'unité a été ajoutée au Package

image

   cliquez le bouton "compile"

   Delphi compile le Package, et génère le code .DCU dans le répertoire du package:

image

De plus, le répertoire par défaut

  C:\Program Files\Borland\Delphi6\ProjectsBpl

contient un fichier .DCP et un fichier .BPL:

image



Si nous souhaitons charger un autre projet, Delphi nous demande si nous souhaitons sauvegarder le fichier PACKAGE1.DPK.

Puisque nous sommes de toutes les façons obligés de sauvegarder le fichier .DPK, autant le renommer tout de suite. Par conséquent:
   sélectionnez "File | Save Project As", et tapez, par exemple

  PK_PACKAGE_FORM_FUNCTION.DPK

   recompilez le package avec le nouveau nom
   fermez par "File | Close All"


2.4 - Inclure le Package dans un Projet

Nous allons inclure le Package dynamiquement dans notre projet et appeler la fonction exportée par le Package.



Le chargement dynamique d'un Package et similaire à celui d'une .DLL: il faut appeler la fonction LoadPackage qui nous retourne la poignée du Package:

var my_package_handleTHandle;

  my_package_handle:= LoadPackage('xxx.bpl');

A l'aide de la poignée, nous utilisons la fonction GetProcAddr pour récupérer l'adresse mémoire de n'importe quelle fonction exportée par le Package:

var my_addressdWord;

  my_address:= GetProcAddress(my_package_handlePChar('my_routine'));

Lorsque la routine est une procédure sans paramètre, nous pouvons utiliser cette address pour appeler la procédure. Mais si la procédure a des paramètres, ou s'il s'agit d'une fonction, il faut surtyper cette adresse pour que le compilateur comprenne qu'il faut pousser les paramètres sur la pile, appeler la routine, et éventuellement retourner des valeurs. Le plus simple est alors

  • de définir le type procédural de la routine
  • de déclarer une variable procédurale
  • d'initialiser l'adresse de la variable procédurale par GetProcAddr
  • d'appeler la routine.
Voici un exemple:

type t_f_my_functionfunction (p_paramInteger): integerstdcall;
var my_f_functiont_f_my_function;

  @my_f_function:= GetProcAddress(my_package_handlePChar('my_f'));

  my_result:= my_f_function(1234);



Par conséquent, dans notre cas:
   1- créez un nouveau projet, P_LOAD_PACKAGE
   2- ajoutez 2 tEdit qui contiendront les valeurs à ajouter, un tButton pour appeler notre fonction et un tLabel pour afficher le résultat
   3- tapez le code qui charge le Package, appelle la fonction et affiche le résultat:

const k_bpl_file_name'pk_add.bpl';
      k_one_plus_two'f_one_plus_two';
type t_f_one_plus_twofunction (p_onep_twoInteger): integerstdcall;

procedure TForm1.add_Click(SenderTObject);
  var l_package_handleTHandle;
      l_f_one_plus_twot_f_one_plus_two;
      l_resultInteger;
      l_onel_twointeger;
  begin
    l_package_handle:= LoadPackage(k_bpl_file_name);

    @l_f_one_plus_two:= GetProcAddress(l_package_handlePChar(k_one_plus_two));

    l_one:= StrToInt(one_edit_.Text);
    l_two:= StrToInt(two_edit_.Text);
    l_result:= l_f_one_plus_two(l_onel_two);
    result_label_.Caption:= IntToStr(l_result);

    UnloadPackage(l_package_handle);
  end// add_Click

   4- compilez et exécutez. Cliquez "Add"
   voici le résultat:

image



Notez que:

  • nous n'avons pas besoin d'inclure l'unité dans la clause USES
  • le fichier Package.bpl doit être accessible par Delphi. Dans notre cas, comme le .BPL est dans le répertoire des Packages de Delphi, il suffit de fournir le nom (sans avoir à fournir de chemin)


3 - Anatomie d'un Package Delphi

3.1 - Les types de Packages

Delphi peut utiliser plusieurs types de Packages:
  • les Package d'exécution (runtime) qui sont incorporés, ou chargés, par les .EXE
  • les Package de conception (design), destinés à être utilisés par l'Interface Delphi (IDE)


3.1.1 - Les Packages d'exécution

Ces Packages sont destinés à être incorporés à des .EXE distribués chez les utilisateurs. Ils ne contiennent aucun éditeur de propriétés (l'utilisateur ne va pas recompiler le projet).



3.1.2 - Les Package de conception

Ces Package accompagnent en général des composants, et contiennent
  • des éditeurs de propriétés
  • des éditeurs de composants
  • des Experts
Pour éviter tout piratage facile, Delphi ne fournit plus en source (depuis Delphi 6) les éditeurs de composants, et interdit par la license le déploiement de Packages contenant des éditeurs de propriétés chez des utilisateurs qui n'ont pas acheté Delphi.

Ceci limite donc les Packages de conception à l'ajout de fonctionalités à l'Interface de développement Delphi (l'IDE), ce qui, après tout, est bien leur vocation.

Néanmoins, si nous souhaitons fournir un composant qui contient du code qui sera utilisé aussi à l'exécution, nous sommes obligés de créer deux versions: une pour la conception, et une pour l'exécution.



3.2 - Choix du type de Package

Comment choisir le type de Package:
  • s'il s'agit d'un composant qui sera incorporé à la Palette, nous recommandons de toujours créer deux Packages: un Package de conception et un Package d'exécution
  • pour les Packages destinés à débiter un .EXE gigantesque en modules sur disque (un .EXE principal et plusieurs Packages), nous utilisons des Packages d'exécution uniquement
Il est exact que si un composant n'a besoin d'aucun éditeur de propriété propre (les éditeurs standard d'Integer, de String etc lui suffisent), nous pourrions créer des Package mixtes (conception et exécution). Toutefois dès que vous ajouterez à votre composant un élément "conception" (design) vous retomberez dans le cas où la séparation est nécessaire.

La spécification du type de Package se fait par le dialogue des Options que nous examinerons ci-dessous ??



3.3 - Les fichiers d'un Package

Lorsque nous utilisons un Package, Delphi va placer sur disque les fichiers suivants:
  • .DPK (Delphi PacKage) : le fichier qui contient le "texte" du Package. Ce fichier ASCII spécifie les options de compilation, quelles UNITés sont inclues dans le Package et quels autres packages sont nécessaires pour pouvoir compiler ces UNITés
  • .DCU (Delphi Compiled Units) : le résultat de la compilation d'une UNIT. C'est un fichier intermédiaire évitant de recompiler le .PAS si le texte du .PAS n'a pas été modifié depuis la dernière compilation
  • .DCP (Delphi Compiled Package) : le fichier qui contient la partie compilée (fichier partiel, évitant les recompilations, l'équivalent pour le .DPK d'un .DCU)
  • .BPL (Delphi Package Library) : la .DLL utilisable par les autres applications. C'est un "module windows", équivalent à une .DLL, mais avec les propriété Delphi permettant l'inclusion de CLASSes


3.4 - Structure d'un Package

Nous pouvons voir le texte d'un fichier .DPK
  • soit en utilisant NotePad ("clic droit souris | ouvrir avec | NotePad")
  • soit en utilisant l'Editeur de Package, en cliquant sur , Cet éditeur correspond à un fichier sur disque (par défaut PACKAGE1.DPK), dont vous pouvez visualiser le source:

    image

et voici le texte obtenu pour notre exemple:

package pk_add;
  {$R *.res}

  {$ALIGN 8}
  {$ASSERTIONS ON}
  {$BOOLEVAL OFF}
  {$DEBUGINFO ON}
  {$EXTENDEDSYNTAX ON}
  {$IMPORTEDDATA ON}
  {$IOCHECKS ON}
  {$LOCALSYMBOLS ON}
  {$LONGSTRINGS ON}
  {$OPENSTRINGS ON}
  {$OPTIMIZATION ON}
  {$OVERFLOWCHECKS OFF}
  {$RANGECHECKS OFF}
  {$REFERENCEINFO ON}
  {$SAFEDIVIDE OFF}
  {$STACKFRAMES OFF}
  {$TYPEDADDRESS OFF}
  {$VARSTRINGCHECKS ON}
  {$WRITEABLECONST OFF}
  {$MINENUMSIZE 1}
  {$IMAGEBASE $400000}
  {$IMPLICITBUILD OFF}

  requires
    rtl;

  contains
    u_adder in 'u_adder.pas';

end.

Ce texte est composé de 3 parties essentielles

  • les options de compilation
  • la partie CONTAINS
  • la partie REQUIRES


3.4.1 - Les Options d'un Package

Cette liste correspond à des options de compilation. La plupart sont des options que nous pouvons aussi utiliser avec des .DPR ou des UNITés Delphi, comme $R+ pour vérifier les bornes des intervalles.

Les options spécifiques aux Packages sont les suivantes:

  • les plus importantes:
    • {$DESIGNONLY ON} et {$RUNONLY ON} : spécifie un Package de conception / d'exécution
    • {$LIBPREFIX 'my_string'}: ajoute automatiquement le préfixe au début du fichier généré (par exemple "IP" pour l'Institut Pascal)
    • {$LIBSUFFIX 'my_string'}: ajoute automatiquement le suffixe au début du fichier généré. En général nous plaçons là le numéro de la version Delphi: "60" pour Delphi 6
  • moins fréquemment utilisées
    • {$LIBVERSION 'my_string'}: version Linux
    • {$DESCRIPTION 'my_string'}: une description, qui sera affichée dans l'IDE
    • {$IMAGEBASE $xxxxxx}: l'adresse de chargement. Utilisé si nous souhaitons une adresse de chargement particulière. Les valeurs possibles recommandées sont dans la plage $4000.0000..$7FFF.FFFF
    • {$IMPLICITBUILD ON | OFF}: la valeur OFF évite la reconstruction du Package. A utiliser pour les Package qui évoluent peu
Les unités d'un Package peuvent aussi indiquer comment l'unité sera utilisée dans le Package:
  • {$IMPORTEDDATA ON | OFF}: la valeur OFF empêche l'accès aux variables des autres Packages. Cette optimisation est rarement utilisée
  • {$DENYPACAGEUNIT}: interdit à une unité d'être placée dans un Package
  • {$WEAKPACKAGEUNIT}: utilisé essentiellement par les éditeurs de librairies



3.4.2 - La clause Contains

Nous indiquons ici les unités que nous souhaitons inclure dans le Package

3.4.3 - La clause Requires

Spécifie la liste des Packages qui sont nécessaires pour pouvoir compiler les unités listées dans Contains.

Si les unités citées dans Contains nécessitent d'autres Packages, ils doivent être ajoutés à la clause Requires.

Si nous oublions de lister un Package, le compilateur présentera un avertissement, et ajoutera les Packages nécessaires implicitement.

Les importations Requires ne peuvent former un cycle. Le compilateur nous le signalera.

De plus un Package ne peut apparaître dans la clause Contains. Et une unité ne peut être citée que dans la clause Contains de l'un des Packages (mais nous pouvons ajouter le premier Package à la clause Requires du second Package). Notez que cette règle interdit d'installer dans l'IDE un composant dont le Package contient dans Contains une unité de la VCL (mais le Package du composant peut incorporer les Packages correspondants dans Requires)

Notez que:

  • lorsque nous importons les packages par la clause Requires, Delphi utilise le nom du .DCP (et non pas le nom du .DPK ou du .BPL)
  • le nom du .DCP est SANS préfixe ("RTL.DCP" et non pas "RTL60.DCP")
  • en revanche, le suffixe que nous avons indiqué sera ajouté au .BPL: avec le préfixe "IP_" et le suffixe "60", le fichier sur disque sera IP_pk_add60.bpl
L'intérêt est que lorsque nous changeons de version Delphi, il suffit de recompiler les packages, sans avoir à modifier manuellement les clauses Requires



3.5 - L'emplacement des fichiers

Par défaut, les fichiers .DCP et .BPL sont stockés dans le même répertoire que le source du Package (le .DPK). Toutefois, il est important que les fichiers .BPL et .DCP soient dans des répertoires où soit l'IDE Delphi, soit les projets utilisateurs du Package pourront les trouver. Très souvent ces fichiers sont placés dans C:\WINDOWS\WIN32. Mais nous pouvons aussi les placer dans des sous répertoires de Delphi ("C:\PROGRAM FILES\BORLAND\DELPHI6\") ou tout autre répertoire accessible par une entrée dans le PATH de Windows.

Pour indiquer au compilateur de Package où placer les différents fichiers, nous utilisons le dialogue des Options, qui sert aussi a choisir les options d'un Package



3.6 - Le Dialogue des Options

Pour indiquer quelles options nous souhaitons utiliser, nous pouvons utiliser le bouton "Options" de l'Editeur de Packages

image



3.6.1 - Spécification des chemins

Pour spécifier dans quel dossier seront placés les fichiers du Package:
   cliquez sur le bouton "Options", puis sélectionnez l'onglet "Directories"

   Delphi ouvre un dialogue similaire à celui de "Project | Options"

image

Et:
  • "Output Directory" indique où sera placé le .BPL (la .DLL finale)
  • "Unit Output Directory" indique où seront placés les .DCU
  • "Search Path" permet d'aller charger les unités depuis d'autres répertoires
  • "DCP Output Directory" désigne le répertoire du .DCP (fichier intermédiaire)


3.6.2 - Type de Package

Le type conception / exécution est spécifié dans l'onglet "Description":
   cliquez sur le bouton "Options", puis sélectionnez l'onglet "Description"

   Delphi présente une page avec plusieurs options, dont le type de Package sous "Usage Options":

image



3.6.3 - Préfixes et Suffixes

Les Préfixes et Suffixes
  • Comme les Package doivent être uniques pour un projet donné, Delphi recommande d'utiliser pour chaque Package un préfixe qui est propre au développeur ou à la société qui propose le Package. Par exemple nous pouvons préfixer nos Packages par IP_ (Institut Pascal)
  • en ce qui concerne la version Delphi, Delphi propose d'ajouter le suffixe 60 pour Delphi 6, 70 pour Delphi 7 etc
Nous pouvons saisir ces préfixes et suffixes manuellement dans les options du source du Package.

Mais nous pouvons aussi utiliser l'onglet "Description" du dialogue des Options de l'Editeur de Package:

image




4 - Utilisation d'un Package Delphi

4.1 - Composant dans un Package

Si notre Package contient un composant, l'inclusion est simple
  • nous plaçons le composant sur la Palette
  • lorsque nous déposons le composant sur une tForm, Delphi ajoutera automatiquement le nom des UNITés nécessaires à la clause USES

4.2 - Package d'Exécution

Si notre Package correspond à un Package d'exécution, nous avons deux possibilités:
  • inclure le code du Package dans le fichier .EXE
  • charger dynamiquement le Package .BPL pendant l'exécution du projet


4.2.1 - Inclusion du .BPL dans l'.EXE

Dans le premier cas, il suffit de placer dans les clauses USES les unités qui nous intéressent. En fait, si c'est notre seule utilisation de ce Package, il n'aucune utilité:autant importer directement les unités dans les clauses USES.



4.2.2 - Chargement Dynamique du .BPL

Dans le second cas, nous souhaitons que le fichier .EXE ne contienne PAS le code binaire des UNITés du Package. Pour cela, il suffit de l'indiquer dans le dialogue des options de d'Editeur de Packages:
   sélectionnez "Project | Options | Packages"
   Delphi présente les options des Packages

image

   cochez la Checkbox "Build with runtime packages"
   compilez
   éventuellement Delphi proposera de convertir d'anciens .BPL, ou signalera que certains .BPL qui figuraient historiquement dans cette liste ne sont plus disponibles
Si c'est le cas, acceptez les conversion, et supprimez les .BPL obsolètes.
   cliquez "Browse" pour inclure "pk_add" dans la liste des Packages à exclure de l'.EXE
   après compilation, la taille du fichier .EXE est devenue 24 K, et c'st Delphi qui chargera automatiquement les .BPL nécessaires pendant l'exécution


Mentionnons que le dialogue "Project | Options | Packages" comporte aussi un tEdit contenant un certain nombre de fichier. Ces noms correspondent à des fichiers .DCP disponibles pour votre projet. Et si une des UNITés de votre projet a besoin d'un élément de l'un de ces .DCP, le binaire correspondant ne sera PAS incorporé au fichier .EXE, mais proviendra du chargement du fichier .BPL correspondant.

Ce tEdit n'est pas là, comme nous le pensions, pour nous permettre d'ajouter certains Packages à notre projet, mais pour indiquer, parmi tous les Packages dont notre projet a besoin, lesquels devront être chargés dynamiquement, et lesquels seront, par différence, inclus dans le fichier .EXE.

Prenons un exemple:

  • nous souhaitons utiliser des bases de données (VCLDB)
  • si VclDb ne figure pas dans l'Edit, le binaire de DB.PAS etc seront dans le fichier .EXE
  • si VclDb est dans l'Edit, le fichier .EXE ne contiendra rien concernant DB.PAS, mais le .BPL qui contient ce code sera chargé dès que nous toucherons aux tDataSet etc


4.3 - Chargement statique et Dynamique

Notez que en cochant "build with Runtime packages" ne préjuge pas du type de chargement: statique ou dynamique.

Pour provoquer un chargement statique, ajoutez l'unité à la clause USES.

Pour provoquer un chargement dynamique, il faut appeler les fonction LoadPackage / UnloadPackage en fournissant le (chemin et le) nom du fichier .BPL




5 - Télécharger le code source Delphi

Vous pouvez télécharger:

Ce .ZIP qui comprend:

  • le .DPR, la forme principale, les formes annexes eventuelles
  • les fichiers de paramètres (le schéma et le batch de création)
  • dans chaque .ZIP, toutes les librairies nécessaires à chaque projet (chaque .ZIP est autonaume)
Ces .ZIP, pour les projets en Delphi 6, contiennent des chemins RELATIFS. Par conséquent:
  • créez un répertoire n'importe où sur votre machine
  • placez le .ZIP dans ce répertoire
  • dézippez et les sous-répertoires nécessaires seront créés
  • compilez et exécutez
Ces .ZIP ne modifient pas votre PC (pas de changement de la Base de Registre, de DLL ou autre). Pour supprimer le projet, effacez le répertoire.

La notation utilisée est la notation alsacienne qui consiste à préfixer les identificateurs par la zone de compilation: K_onstant, T_ype, G_lobal, L_ocal, P_arametre, F_unction, C_lasse. Elle est présentée plus en détail dans l'article La Notation Alsacienne



Comme d'habitude:

  • nous vous remercions de nous signaler toute erreur, inexactitude ou problème de téléchargement en envoyant un e-mail à jcolibri@jcolibri.com. Les corrections qui en résulteront pourront aider les prochains lecteurs
  • tous vos commentaires, remarques, questions, critiques, suggestion d'article, ou mentions d'autres sources sur le même sujet seront de même les bienvenus à jcolibri@jcolibri.com.
  • plus simplement, vous pouvez taper (anonymement ou en fournissant votre e-mail pour une réponse) vos commentaires ci-dessus et nous les envoyer en cliquant "envoyer" :
    Nom :
    E-mail :
    Commentaires * :
     

  • et si vous avez apprécié cet article, faites connaître notre site, ajoutez un lien dans vos listes de liens ou citez-nous dans vos blogs ou réponses sur les messageries. C'est très simple: plus nous aurons de visiteurs et de références Google, plus nous écrirons d'articles.



6 - L'auteur

John COLIBRI est passionné par le développement Delphi et les applications de Bases de Données. Il a écrit de nombreux livres et articles, et partage son temps entre le développement de projets (nouveaux projets, maintenance, audit, migration BDE, migration Xe_n, refactoring) pour ses clients, le conseil (composants, architecture, test) et la formation. Son site contient des articles avec code source, ainsi que le programme et le calendrier des stages de formation Delphi, base de données, programmation objet, Services Web, Tcp/Ip et UML qu'il anime personellement tous les mois, à Paris, en province ou sur site client.
Créé: mar-07. Maj: aou-15  148 articles, 471 sources .ZIP, 2.021 figures
Contact : John COLIBRI - Tel: 01.42.83.69.36 / 06.87.88.23.91 - email:jcolibri@jcolibri.com
Copyright © J.Colibri   http://www.jcolibri.com - 2001 - 2015
Retour:  Home  Articles  Formations  Développement Delphi  Livres  Pascalissime  Liens  Download
l'Institut Pascal

John COLIBRI

+ Home
  + articles_avec_sources
    + bases_de_donnees
    + web_internet_sockets
    + prog_objet_composants
      – dump_interface
      – packages_delphi
      – ecriture_de_composant
      – c_list_of_double
      – interfaces_delphi
      – delphi_generics
      – delphi_rtti
    + office_com_automation
    + colibri_utilities
    + uml_design_patterns
    + graphique
    + delphi
    + outils
    + firemonkey
    + vcl_rtl
    + colibri_helpers
    + colibri_skelettons
  + formations
  + developpement_delphi
  + présentations
  + pascalissime
  + livres
  + entre_nous
  – télécharger

contacts
plan_du_site
– chercher :

RSS feed  
Blog

Formation Bases de Données Sql Server avec Delphi Gestion de bases de données Sql Serveur : connexion, accès aux tables, édition d'états - 3 jours
Formation Threads Delphi et Multi Tâche Les Threads Delphi : création, synchronisation, communication entre threads - 1 jour