Tutoriel : Utilisation des bibliothèques de liaison dynamique dans les applications C++Builder

De RAD Studio
Aller à : navigation, rechercher

Remonter à Tutoriels d'introduction


Ce tutoriel décrit les principales étapes relatives à l'utilisation des bibliothèques de liaison dynamique (DLL) dans vos applications C++Builder (à la fois sur Windows 32 bits et Windows 64 bits).

Une DLL (bibliothèque de liaison dynamique) est une collection de routines qui peut être chargée à l'exécution. Pour de plus amples informations sur les DLL, voir Bibliothèques et packages (Delphi).

L'exemple suivant contient une DLL qui exporte une classe décrivant une pile d'entiers. La classe est déclarée et implémentée dans le projet DLL. Le but de cet exemple est de charger dynamiquement la DLL dans une application FireMonkey, puis d'importer et d'utiliser la classe de pile d'entiers.

Pour exporter la classe de la DLL :

  1. Ecrivez un fichier d'en-tête C++ qui déclare une classe de base virtuelle pour la pile. Cette classe de base virtuelle doit contenir les méthodes de la classe qui sera exportée. Ce fichier d'en-tête sera inclus dans le projet où la classe de la DLL est importée.
  2. Déclarez et implémentez une classe dérivant de la classe virtuelle de la pile. C'est là que les méthodes de la pile sont implémentées. Dans le tutoriel suivant, une implémentation d'entiers sera utilisée pour la pile.
  3. Implémentez et exportez une méthode qui renvoie une instance de la classe de la pile d'entiers. Cette méthode sera utilisée pour obtenir une instance de la classe déclarée dans le projet DLL.

Etapes et code source

Ce tutoriel vous guidera dans les étapes de création et d'exportation d'une classe utilisant des DLL (bibliothèques de liaison dynamique). Vous apprendrez aussi à importer et utiliser la classe précédemment exportée.

Ce tutoriel est composé de deux principaux projets :

  • StackLibrary.dll - C'est le projet DLL. La pile d'entiers est déclarée et implémentée ici. Ce projet contient également une méthode utilisée pour obtenir une instance de la classe de pile. Cette méthode est exportée de la DLL.
  • TestStackLibrary - C'est l'application FireMonkey. Dans ce projet, la DLL précédemment déclarée est chargée et la méthode exposée est importée. Après avoir invoqué cette méthode, une instance de la classe de pile est récupérée. Ce projet teste les méthodes de la classe de pile.

Voici les étapes de la création des deux projets :

La bibliothèque de liaison dynamique

  1. Sélectionnez Fichier > Nouveau > Autre > Projets C++Builder > Bibliothèque de liaison dynamique et cliquez sur OK.
  2. Dans la boîte de dialogue Nouvelle DLL qui s'ouvre, sélectionnez FireMonkey en tant que Framework cible et cliquez sur OK pour utiliser les paramètres par défaut.
    Remarque : Cette DLL n'utilise aucun framework, même si l'application qui charge la DLL est une application FireMonkey.
  3. Sélectionnez Fichier > Tout enregistrer et définissez les noms des fichiers créés comme suit :
    1. Nommez le fichier C++ du projet DLL StackExport.cpp.
    2. Nommez le fichier d'en-tête précompilé StackLibraryPCH1.h.
    3. Nommez le projet DLL StackLibrary.
  4. Selon le cas, sélectionnez Fichier > Nouveau > Autre > Fichiers C++Builder > Fichier en-tête ou Fichier > Nouveau > Autre > Fichiers C++Builder > Fichier CPP pour ajouter les trois nouveaux fichiers suivants au projet StackLibrary :
    • BaseStack.h contient la définition de la classe de base virtuelle de la pile. Cette classe est utilisée à la fois dans le projet DLL et le projet FireMonkey.
    • StackOfInt.h contient la déclaration de la classe Stack. Stack est un descendant de BaseStack, qui décrit une pile d'entiers.
    • StackOfInt.cpp contient l'implémentation de la classe Stack.
  5. Ajoutez du code aux fichiers créés précédemment, en procédant comme décrit dans les sections suivantes :
  6. Construisez votre projet pour générer la DLL StackLibrary.dll.

BaseStack.h

BaseStack est la classe de base virtuelle de Stack (déclarée dans StackOfInt.h). Par conséquent, aucune implémentation n'est requise pour ses méthodes.

 // Base Virtual class for Stack.
 // Declared to be used both in StackLibrary.dll and TestStackLibrary.exe projects
 class BaseStack {
 public:
         virtual bool IsEmpty() = 0;
         virtual int Top() = 0;
         virtual int Pop() = 0;
         virtual void Push(int value) = 0;
 };

StackOfInt.h

Ce fichier déclare la classe Stack. Stack décrit une pile d'entiers et déclare ses méthodes : Push, Pop, Top et IsEmpty.

 #include "BaseStack.h"
 
 // BaseStack implementation
 // Here the methods from BaseStack are implemented
 class Stack : public BaseStack {
 private:
         // items holds the integers from the stack
         int* items;
         // represents the number of integers that are in the stack
         int count;
 public:
         // Stack constructor
         Stack();
         // Stack destructor
         ~Stack();
         // Checks whether the stack is empty
         bool IsEmpty();
         // Returns the top of the stack
         int Top();
         // Returns the top of the stack and removes the element from the stack
         int Pop();
         // Inserts <code>value</code> into the stack
         void Push(int value);
 };

StackOfInt.cpp

 #include "StackOfInt.h"
 #define MAX_SIZE 30
 #define INDEX_OUT_OF_BOUNDS_EXCEPTION 1
 #define EMPTY_STACK 2
 
 // Stack constructor
 Stack::Stack() {
         count = 0;
         items = new int[MAX_SIZE];
 }
 
 // Stack destructor
 Stack::~Stack() {
         delete[]items;
 }
 
 // Returns the top of the stack
 int Stack::Top() {
         if (count > 0) {
                 return items[count - 1];
         }
         else {
                 throw EMPTY_STACK;
         }
 }
 
 // Returns the top of the stack and removes the element from the stack
 int Stack::Pop() {
         if (count > 0) {
                 return items[--count];
         }
         else {
                 throw EMPTY_STACK;
         }
 }
 
 // Checks whether the stack is empty
 bool Stack::IsEmpty() {
         if (count == 0) {
                 return true;
         }
         else {
                 return false;
         }
 }
 
 // Inserts <code>value</code> into the stack
 void Stack::Push(int value) {
         if (count < MAX_SIZE) {
                 items[count++] = value;
         }
         else {
                 throw INDEX_OUT_OF_BOUNDS_EXCEPTION;
         }
 }

StackExport.cpp

Ce fichier exporte la fonction GetClassInstance. Cette fonction renvoie une instance de Stack.

 // If your DLL exports any functions that pass String objects (or structs/
 // classes containing nested Strings) as parameter or function results,
 // you will need to add the library MEMMGR.LIB to both the DLL project and
 // any other projects that use the DLL.  You will also need to use MEMMGR.LIB
 // if any other projects which use the DLL will be performing new or delete
 // operations on any non-TObject-derived classes which are exported from the
 // DLL. Adding MEMMGR.LIB to your project will change the DLL and its calling
 // EXE's to use the BORLNDMM.DLL as their memory manager.  In these cases,
 // the file BORLNDMM.DLL should be deployed along with your DLL.
 //
 // To avoid using BORLNDMM.DLL, pass string information using "char *" or
 // ShortString parameters.
 //
 // If your DLL uses the dynamic version of the RTL, you do not need to
 // explicitly add MEMMGR.LIB as this will be done implicitly for you
 
 #pragma hdrstop
 #pragma argsused
 
 #include "StackOfInt.h"
 
 // This is the syntax for exporting methods from DLLs:
 //
 //   extern "C" __declspec(dllexport) void* __stdcall GetClassInstance();
 //
 // This method returns an instance of Stack:
 
 extern "C" __declspec(dllexport) void* __stdcall GetClassInstance() {
         return static_cast<void*>(new Stack);
 }
 
 extern "C" int _libmain(unsigned long reason) {
         return 1;
 }

L'application FireMonkey

  1. Cliquez avec le bouton droit sur le groupe de projets dans le Gestionnaire de projets et sélectionnez Ajouter nouveau > Fiche multi-périphérique - C++Builder.
  2. Sélectionnez Fichier > Tout enregistrer et définissez les noms des fichiers créés comme suit :
    1. Nommez le projet FireMonkey TestStackLibrary.
    2. Nommez l'unité C++ du projet Fiches FireMonkey TestUnit.
    Remarque : Le code source fourni ultérieurement suppose que vous enregistrez ces fichiers dans le même dossier que les fichiers bibliothèque.
  3. Dans la fiche TestStackLibrary, ajoutez les composants suivants :
    1. Ajoutez cinq contrôles TButton. Nommez-les comme suit et définissez leurs libellés en conséquence :
      • LoadButton
      • PushButton
      • TopButton
      • PopButton
      • IsEmptyButton
    2. Pour chaque bouton, ajoutez un gestionnaire d'événement OnClick. Vous pouvez ajouter le code pour les gestionnaires d'événement ultérieurement.
    3. Ajoutez un contrôle TEdit.
      Voici à quoi devrait ressembler la fiche FireMonkey :
      StackForm.png
  4. Dans TestUnit.h, ajoutez les deux membres privés suivants :
    • BaseStack* stack, pour contenir une instance de la pile.
    • void __fastcall EnableButtons().
    Voir TestUnit.h ci-dessous.
  5. Implémentez TestUnit.cpp comme décrit ci-dessous.

Vous pouvez maintenant exécuter votre projet. Dans la fenêtre principale, cliquez sur Load Library pour charger la bibliothèque. Après le chargement manuel de la bibliothèque, le reste des boutons de la fiche fonctionne par le biais de la bibliothèque chargée dynamiquement.

TestUnit.h

//---------------------------------------------------------------------------

#ifndef TestUnitH
#define TestUnitH
//---------------------------------------------------------------------------
#include <System.Classes.hpp>
#include <FMX.Controls.hpp>
#include <FMX.Edit.hpp>
#include <FMX.StdCtrls.hpp>
#include <FMX.Types.hpp>
#include <FMX.Forms.hpp>
//---------------------------------------------------------------------------
#include "BaseStack.h"
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published:    // IDE-managed Components
        TButton *PushButton;
        TButton *PopButton;
        TButton *TopButton;
        TButton *IsEmptyButton;
        TButton *LoadButton;
        TEdit *Edit1;
        void __fastcall PushButtonClick(TObject *Sender);
        void __fastcall PopButtonClick(TObject *Sender);
        void __fastcall TopButtonClick(TObject *Sender);
        void __fastcall IsEmptyButtonClick(TObject *Sender);
        void __fastcall LoadButtonClick(TObject *Sender);
private:        // User declarations
        BaseStack* stack;
        void __fastcall EnableButtons();
public:         // User declarations
        __fastcall TForm1(TComponent* Owner);
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------
#endif

TestUnit.cpp

// ---------------------------------------------------------------------------

#include <fmx.h>
#pragma hdrstop

#include "TestUnit.h"

#include "StackOfInt.h"

// ---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.fmx"
TForm1 *Form1;
// Holds the name of the dynamic-link library.
const wchar_t* library = L"StackLibrary.dll";
// type definition for pointer to functions that return (void*)
typedef void* (*VoidReturnFunc)();
// syntax that declares an imported function
extern "C" __declspec(dllimport) void* __stdcall GetClassInstance();

// ---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) {
}

// ---------------------------------------------------------------------------
// Method used to enable the buttons when the library is loaded
void __fastcall TForm1::EnableButtons() {
        PushButton->Enabled = true;
        PopButton->Enabled = true;
        IsEmptyButton->Enabled = true;
        TopButton->Enabled = true;
}

// ---------------------------------------------------------------------------
// OnClick event handler for LoadButton
void __fastcall TForm1::LoadButtonClick(TObject *Sender) {
        // Dynamically loads the library
        HINSTANCE load = LoadLibrary(library);
        if (load) {
                ShowMessage("Library loaded!");
                EnableButtons();

                VoidReturnFunc myFunc;

                // GetProcAddress returns a pointer to the loaded method
                myFunc = (VoidReturnFunc)GetProcAddress(load, "GetClassInstance");

                // Invoke GetClassInstance method
                stack = (BaseStack*) myFunc();

                // Note:
                // The BaseStack methods are pure virtual methods, but myFunc() returns an instance of the Stack class.
                // Wherever we call stack->methodName() later, we are actually calling Stack::methodName(), not
                // BaseStack::methodName().
        }
        else {
                ShowMessage("Library not loaded!");
        }
}

// ---------------------------------------------------------------------------
// OnCLick event handler for PushButton
void __fastcall TForm1::PushButtonClick(TObject *Sender) {
        if (stack) {
                try {
                        int value = StrToInt(Edit1->Text);
                        stack->Push(value);
                        Edit1->Text = "";
                }
                catch (int) {
                        ShowMessage("Please insert an integer into the editbox!");
                }
        }
}

// ---------------------------------------------------------------------------
// OnClick event handler for IsEmptyButton
void __fastcall TForm1::IsEmptyButtonClick(TObject *Sender) {
        if (stack) {
                if (stack->IsEmpty()) {
                        ShowMessage("The stack is empty!");
                }
                else {
                        ShowMessage("The stack is not empty!");
                }
        }
}

// ---------------------------------------------------------------------------
// OnClick event handler for PopButton
void __fastcall TForm1::PopButtonClick(TObject *Sender) {
        if (stack) {
                try {
                        int value = stack->Pop();
                        ShowMessage("Popped " + IntToStr(value));
                }
                catch (int Exception) {
                        ShowMessage("Exception " + IntToStr(Exception));
                }
        }
}

// ---------------------------------------------------------------------------
// OnClick event handler for TopButton
void __fastcall TForm1::TopButtonClick(TObject *Sender) {
        if (stack) {
                try {
                        int value = stack->Top();
                        ShowMessage("The top of the stack is " + IntToStr(value));
                }
                catch (int Exception) {
                        ShowMessage("Exception " + IntToStr(Exception));
                }
        }
}
// ---------------------------------------------------------------------------

Section Uses

Voir aussi