Tutorial: Verwenden von dynamischen Link-Bibliotheken in C++Builder-Anwendungen

Aus RAD Studio
Wechseln zu: Navigation, Suche

Nach oben zu Einführende Tutorials


In diesem Tutorial werden die Hauptschritte bei der Verwendung von dynamischen Link-Bibliotheken (DLLs) in C++Builder-Anwendungen (für 32-Bit-Windows und 64-Bit-Windows) beschrieben.

Eine dynamische Link-Bibliothek ist eine Sammlung von Routinen, die zur Laufzeit geladen werden können. Weitere Informationen zu DLLs finden Sie unter Bibliotheken und Packages (Delphi).

Das folgende Beispiel enthält eine DLL, die eine Klasse zur Beschreibung eines Stacks mit Ganzzahlen exportiert. Die Klasse wird im DLL-Projekt deklariert und implementiert. In diesem Beispiel soll die DLL dynamisch in eine Firemonkey-Anwendung geladen und der Stack der Integer-Klasse importiert und verwendet werden.

Zum Exportieren der Klasse aus der DLL:

  1. Schreiben Sie eine C++-Header-Datei, in der eine virtuelle Basisklasse für den Stack deklariert wird. Diese virtuelle Basisklasse muss die Methoden der zu exportierenden Klasse enthalten. Diese Header-Datei wird in das Projekt einbezogen, in das die Klasse aus der DLL importiert wird.
  2. Deklarieren und implementieren Sie eine Klasse, die von der virtuellen Klasse des Stacks abgeleitet ist. Hier werden die Stack-Methoden implementiert. Im folgenden Tutorial wird für den Stack eine Integer-Implementierung verwendet.
  3. Implementieren und exportieren Sie eine Methode, die eine Instanz des Stacks der Integer-Klasse zurückgibt. Mit dieser Methode wird eine Instanz der in dem DLL-Projekt deklarierten Klasse abgerufen.

Schritte und Quellcode

Dieses Tutorial führt Sie durch die Schritte zum Erstellen und Exportieren einer Klasse, die dynamische Link-Bibliotheken verwendet. Außerdem wird gezeigt, wie die zuvor exportierte Klasse importiert und verwendet wird.

Das Tutorial umfasst zwei Hauptprojekte:

  • StackLibrary.dll – Dies ist das DLL-Projekt. Hier wird der Integer-Stack deklariert und implementiert. Dieses Projekt enthält auch eine Methode zum Abrufen einer Instanz der Stack-Klasse. Diese Methode wird aus der DLL exportiert.
  • TestStackLibrary – Dies ist eine FireMonkey-Anwendung. In dieses Projekt wird die zuvor deklarierte DLL geladen und die bereitgestellte Methode importiert. Nach Aufruf dieser Methode wird eine Instanz der Stack-Klasse abgerufen. In diesem Projekt werden die Methoden der Stack-Klasse getestet.

Im Folgenden finden Sie die Schritte zum Erstellen der beiden Projekte:

Die dynamische Link-Bibliothek

  1. Wählen Sie Datei > Neu > Weitere > C++Builder-Projekte > Dynamische Link-Bibliothek, und klicken Sie auf OK.
  2. Wählen Sie im Dialogfeld Neue dynamische Link-Bibliothek, das geöffnet wird, FireMonkey als die Zielplattform und klicken auf OK.
    Hinweis: Diese DLL verwendet kein Framework, auch wenn die Anwendung, die die DLL lädt, eine Firemonkey-Anwendung ist.
  3. Wählen Sie Datei > Alles speichern, und vergeben Sie für die erstellten Dateien die folgenden Namen:
    1. Name der C++-Datei im DLL-Projekt StackExport.cpp.
    2. Name der vorcompilierten Header-Datei StackLibraryPCH1.h.
    3. Name des DLL-Projekts StackLibrary.
  4. Wählen Sie entweder Datei > Neu > Weitere > C++Builder-Dateien > Header-Datei oder Datei > Neu > Weitere > C++Builder-Dateien > CPP-Datei, um dem StackLibrary-Projekt die folgenden drei neuen Dateien hinzuzufügen:
    • BaseStack.h enthält die Definition der virtuellen Basisklasse für den Stack. Diese Klasse wird im DLL- und im Firemonkey-Projekt verwendet.
    • StackOfInt.h enthält die Deklaration für die Stack-Klasse. Stack ist von BaseStack abgeleitet, das einen Stack mit Ganzzahlen beschreibt.
    • StackOfInt.cpp enthält die Implementierung der Stack-Klasse.
  5. Fügen Sie den oben erstellen Dateien Code hinzu, wie in den folgenden Unterabschnitten beschrieben.
  6. Erzeugen Sie Ihr Projekt, um die DLL StackLibrary.dll zu generieren.

BaseStack.h

BaseStack ist die virtuelle Basisklasse für Stack (in StackOfInt.h deklariert). Deshalb ist für deren Methoden keine Implementierung erforderlich.

 // 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

In dieser Datei wird die Stack-Klasse deklariert. Stack beschreibt einen Stack mit Ganzzahlen und deklariert dessen Methoden: Push, Pop, Top und 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

Diese Datei exportiert die Funktion GetClassInstance. Diese Funktion gibt eine Instanz von Stack zurück.

 // 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;
 }

Die Firemonkey-Anwendung

  1. Klicken Sie in der Projektverwaltung mit der rechten Maustaste auf die Projektgruppe, und wählen Sie Neue hinzufügen > Geräteübergreifendes Formular - C++Builder.
  2. Wählen Sie Datei > Alles speichern, und vergeben Sie für die erstellten Dateien die folgenden Namen:
    1. Name des Firemonkey-Projekts TestStackLibrary.
    2. Name der C++-Unit im Firemonkey-Projekt TestUnit.
    Hinweis: Im Quellcode (siehe weiter unten) wird davon ausgegangen, dass diese Dateien im selben Ordner wie die Bibliotheksdateien gespeichert sind.
  3. Fügen Sie dem TestStackLibrary-Formular die folgenden Komponenten hinzu:
    1. Fügen Sie fünf TButton-Steuerelemente hinzu. Benennen Sie sie wie folgt, und legen Sie die Beschriftungen entsprechend fest:
      • LoadButton
      • PushButton
      • TopButton
      • PopButton
      • IsEmptyButton
    2. Fügen Sie jeder Schaltfläche eine OnClick-Ereignisbehandlungsroutine hinzu. Sie können den Code für die Ereignisbehandlungsroutinen später hinzufügen.
    3. Fügen Sie ein TEdit-Steuerelement hinzu.
      Das Firemonkey-Formular sollte nun folgendermaßen aussehen:
      StackForm.png
  4. Fügen Sie in TestUnit.h die beiden folgenden private-Member hinzu:
    • BaseStack* stack für die Aufnahme einer Instanz des Stacks.
    • void __fastcall EnableButtons().
    Siehe TestUnit.h weiter unten.
  5. Implementieren Sie TestUnit.cpp wie weiter unten beschrieben.

Sie können Ihr Projekt jetzt ausführen. Klicken Sie im Hauptfenster auf Load Library, um die Bibliothek zu laden. Nach dem manuellen Laden der Bibliothek können mit den übrigen Schaltflächen auf dem Formular Aktionen für die dynamisch geladene Bibliothek ausgeführt werden.

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));
                 }
         }
 }
 // ---------------------------------------------------------------------------

Verwendet

Siehe auch