Klassen aus einer DLL exportieren

  • Um eine Klasse in einer DLL zu hinterlegen und diese dann beliebig in Anwendungen zu exportieren, kann man sich einfach eine Headerdatei mit der Klassendefinition erstellen und diese später in das gewünschte Projekt includieren (*.lib nicht vergessen!):
#ifndef __MYCLASS_H
#define __MYCLASS_H

#ifdef __DLL__
# define DLL_EXP __declspec(dllexport)
#else
# define DLL_EXP __declspec(dllimport)
#endif

class DLL_EXP MyClass
{
    private:
    //...
    protected:
    //...
    public:
    //...
};

#endif

DLLs zur Laufzeit einbinden (ohne *.lib und *.h)

  • eine DLL kann auch ohne *.lib-Datei in ein BC++-Projekt eingebunden werden (z.B. Delphi-DLLs)
  • wichtige Befehle: LoadLibrary(), GetProcAddress(), FreeLibrary()
  • Im Beispiel wird eine Delphi-DLL geladen, deren Funktion “Addiere” aufgerufen, die 2 int-Werte verlangt. Das Ergebnis der Addition wird in einem Label dargestellt.
  • *.h
#ifndef Unit1H
#define Unit1H
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <FileCtrl.hpp>
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
    __published: // Von der IDE verwaltete Komponenten
    TButton *Button1;
    TLabel *Label1;
    TButton *Button2;
    TButton *Button3;
    TEdit *Edit1;
    TEdit *Edit2;
    void __fastcall Button1Click(TObject *Sender);
    void __fastcall Button3Click(TObject *Sender);
    void __fastcall Button2Click(TObject *Sender);
    private: // Anwender-Deklarationen

        HINSTANCE DllInstance; // Instanz auf die DLL deklarieren

    public: // Anwender-Deklarationen
    __fastcall TForm1(TComponent* Owner);
};
extern PACKAGE TForm1 *Form1;
#endif
  • *.cpp
#include <vcl.h>
#pragma hdrstop
#include "windows.h" // Wichtig!!
#include "Unit1.h"
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    if (!DllInstance) DllInstance = LoadLibrary("DllName.dll"); // DLL laden

    if (DllInstance) Form1->Caption = "DLL wurde geladen.";
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button3Click(TObject *Sender)
{
    if (DllInstance)
       if (FreeLibrary(DllInstance)) DllInstance = NULL; // DLL entladen

    if (!DllInstance) Form1->Caption = "DLL nicht geladen.";
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
    // Funktionstyp deklarieren
    typedef int (__stdcall *IMPFUNC) (int, int);

    // würde die Funktion 2 Floatwerte benötigen und als Ergebnis einen Bool-Wert liefern
    // sähe die typdef-Deklaration folgendermaßen aus:
    // typedef bool (__stdcall *IMPFUNC) (float, float);

    // Deklaration für die DLL-Funktion
    IMPFUNC DllFunktion;
    int iSumme;

    if (DllInstance)
    {
        // Addresse der Funktion "Addiere" in der DLL herausfinden
        // Falls die Funktion nicht gefunden wird, einfach vor den
        // Funktionsnamen ein "_" setzen
        // Bsp.: statt "Addiere", "_Addiere" verwenden
        DllFunktion = (IMPFUNC)GetProcAddress(DllInstance, "Addiere");

        if (DllFunktion)
        {
            // DLL-Funktion aufrufen,
            // An die DLL werden 2 int-Werte übergeben (aus Edit1 und Edit2)
            // Rückgabewert ist ein int-Wert (Summe)
            iSumme = DllFunktion(Edit1->Text.ToIntDef(0), Edit2->Text.ToIntDef(0));

            Label1->Caption = IntToStr(iSumme);
        }
        else Label1->Caption = "Funktionsname existiert nicht!";
    }
    else
    {
        Label1->Caption = "Bitte DLL laden!";
    }
}

Verwenden von Datenbankkomponenten in einer DLL

  • Um Datenbankkomponenten in einer DLL verwenden zu können, muss im DLL-Projekt eine Form (zur Laufzeit erzeugt!) mit den Datenbankkomponenten vorhanden sein.
  • BCB starten und im Menü ‘Datei->Neu->DLL Experte’ wählen
  • Datei->Neu->Neues Formular wählen
  • auf das Formular die benötigten Datenbankkomponenten ziehen (im hier vorliegenden Bsp. wird mit den MySQLDAC-Komponenten gearbeitet, es funktioniert aber auch mit beliebigen anderen)
  • *.cpp der DLL (nicht die der Form!):
  • #include <vcl.h>
    #include <stdio.h>
    #include <windows.h>
    #pragma hdrstop
    #include "DLLDBForm.h"
    //---------------------------------------------------------------------------
    //USEFORM("DLLDBForm.cpp", frmDLLDBForm);
    //---------------------------------------------------------------------------
    //Funktion die exportiert wird
    //---------------------------------------------------------------------------
    extern "C" __declspec(dllexport) int ExportDBValue();
    
    #pragma argsused
    int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
    {
        return 1;
    }
    
    int ExportDBValue()
    {
        int iReturnWert; // Rückgabewert
    
        TfrmDLLDBForm *DBForm = new TfrmDLLDBForm(NULL); // Form zur Laufzeit erzeugen
    
        DBForm->mySQLQuery1->Close();
        DBForm->mySQLQuery1->SQL->Clear();
        DBForm->mySQLQuery1->SQL->Add("select id from table");
        DBForm->mySQLDatabase1->Open();
        DBForm->mySQLQuery1->Open();
    
        DBForm->ShowModal(); // Form zum Test mit anzeigen
    
        // Beispiel-int-Wert aus Spalte "id" lesen
        iReturnWert = DBForm->mySQLQuery1->FieldByName("id")->AsInteger;
    
        delete DBForm; // Form nach der Benutzung wieder zerstören!!!
    
        return iReturnWert; // Wert zurückgeben
    }
    
  • *.lib der DLL in das Projekt einbinden, in dem die DLL verwendet werden soll
  • *.cpp eines Beispielprogrammes, das die Funktion ExportDBValue() der DLL aufruft:
  • #include <vcl.h>
    #pragma hdrstop
    #include "Unit1.h"
    #pragma package(smart_init)
    #pragma resource "*.dfm"
    
    TForm1 *Form1;
    //Funktion, die aus der DLL importiert wird
    extern "C" __declspec(dllimport) int ExportDBValue();
    //---------------------------------------------------------------------------
    __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
    {
    }
    
    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
        int r;
        r = ExportDBValue();
        Edit1->Text = IntToStr(r);
    }
    

DLLs zur Designzeit in ein Projekt einbinden

Es gibt mehrere Möglichkeiten:

  1. *.lib der DLL einbinden (einfachster Fall):
    • Den BCB Menüpunkt ‘Projekt->Dem Projekt hinzufügen …’ wählen und die zur DLL zugehörige DLL-Libary (*.lib) ins Projekt einbinden
  2. falls keine *.lib existiert müssen die DLL-Funktionen mehr oder weniger mit Hand exportiert werden
    • falls ein Header-File existiert in dem die Export-Funktionen definiert sind, die *.h mit #include “dllheadername.h” in die entsprechende *.cpp includieren
    • Beispiel für eine solche Headerdatei (dllheader.h):
    #ifndef dllheaderH
    #define dllheaderH
    
    extern "C" __declspec(dllexport) int Funktion1(int *);
    extern "C" __declspec(dllexport) bool Funktion2(float *, int *);
    extern "C" __declspec(dllexport) DWORD Funktion3(float *,BYTE *);
    
    #endif
    
    • existiert kein Header-File, so wurden häufig vom DLL-Programmierer die Export-Funktionen schon fertig in der DLL-cpp festgelegt, wie in folgendem Quellcode demonstriert:
    //---------------------------------------------------------------------------
    // fiktiver Quellcode der DLL
    //---------------------------------------------------------------------------
    #include <vcl.h>
    #include <stdio.h>
    #include <windows.h>
    #pragma hdrstop
    //---------------------------------------------------------------------------
    extern "C" __declspec(dllexport) int Funktion1(int *);
    extern "C" __declspec(dllexport) bool Funktion2(float *, int *);
    extern "C" __declspec(dllexport) DWORD Funktion3(float *,BYTE *);
    
    #pragma argsused
    int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
    {
        return 1;
    }
    //---------------------------------------------------------------------------
    
    .
    .
    .
    
    • Diese vorher definierten Funktionen müssen dann entprechend in der *.cpp der Programmes, das die DLL-Funktionen nutzen soll, importiert werden:
    //---------------------------------------------------------------------------
    #include <vcl.h>
    #pragma hdrstop
    
    #include "Unit1.h"
    //---------------------------------------------------------------------------
    #pragma package(smart_init)
    #pragma resource "*.dfm"
    TForm1 *Form1;
    
    //Funktionen, die aus der DLL importiert werden
    extern "C" __declspec(dllimport) int Funktion1(int *);
    extern "C" __declspec(dllimport) bool Funktion2(float *, int *);
    extern "C" __declspec(dllimport) DWORD Funktion3(float *,BYTE *);
    
    //---------------------------------------------------------------------------
    __fastcall TForm1::TForm1(TComponent* Owner)
    : TForm(Owner)
    {
    }
    
    .
    .
    .
    
  3. Aufruf der exportierten DLL-Funktionen im Anwendungs-Quellcode:
  4. .
    .
    .
    
    void __fastcall TForm1::Button1Click(TObject *Sender)
    {
        int Wert1;
        DWORD Wert2;
    
        Wert1=Funktion1(10);
    
        if (Funktion2(4.5, 12)) ShowMessage("Test");
    
        Wert2=Funktion3(1.5, 0);
    }
    
    .
    .
    .
    

Beispiel für Erstellung einer einfachen DLL

  • BCB starten und im Menü ‘Datei->Neu->DLL Experte’ wählen
  • BCB erstellt nun das Grundgerüst einer DLL (Unit1.cpp):
//---------------------------------------------------------------------------
#include <vcl.h>
#include <windows.h>
#pragma hdrstop
//---------------------------------------------------------------------------
// Wichtiger Hinweis über die DLL-Speicherverwaltung, falls die DLL die statische
// Version der Laufzeitbibliothek (RTL) verwendet:
//
// Wenn die DLL Funktionen exportiert, die String-Objekte (oder Strukturen/
// Klassen, die verschachtelte Strings enthalten) als Parameter oder Funktionsergebnisse übergibt,
// muß die Bibliothek MEMMGR.LIB im DLL-Projekt und anderen Projekten,
// die die DLL verwenden, vorhanden sein. Sie benötigen MEMMGR.LIB auch dann,
// wenn andere Projekte, die die DLL verwenden, new- oder delete-Operationen
// auf Klassen anwenden, die nicht von TObject abgeleitet sind und die aus der DLL exportiert
// werden. Durch das Hinzufügen von MEMMGR.LIB wird die DLL und deren aufrufende EXEs
// angewiesen, BORLNDMM.DLL als Speicherverwaltung zu benutzen. In diesem Fall
// sollte die Datei BORLNDMM.DLL zusammen mit der DLL weitergegeben werden.
//
// Um die Verwendung von BORLNDMM.DLL, zu vermeiden, solten String-Informationen als "char *" oder
// ShortString-Parameter weitergegeben werden.
//
// Falls die DLL die dynamische Version der RTL verwendet, müssen Sie
// MEMMGR.LIB nicht explizit angeben.
//---------------------------------------------------------------------------
#pragma argsused
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
    return 1;
}
//---------------------------------------------------------------------------
  • Wir ändern den Quellcode dahingehend, dass er wie folgt aussieht (Kommentare entfernen):
#include <vcl.h>
#include <windows.h>
#pragma hdrstop

#pragma argsused
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
    return 1;
}
//---------------------------------------------------------------------------
  • Jetzt fügen wir zwei Beispielfunktionen und die entsprechenden Export-Deklarationen ein:
#include <vcl.h>
#include <windows.h>
#pragma hdrstop

// Funktionen für Export zur späteren Anwendung deklarieren
extern "C" __declspec(dllexport) int Addieren(int, int);
extern "C" __declspec(dllexport) void Addieren_mit_Pointer(int *, int*, int*);

#pragma argsused
int WINAPI DllEntryPoint(HINSTANCE hinst, unsigned long reason, void* lpReserved)
{
    return 1;
}
//---------------------------------------------------------------------------
// Funktion zur einfachen Addidtion zweier Zahlen
int Addieren(int Zahl1, int Zahl2)
{
    return Zahl1+Zahl2;
}

// Funktion zur Addition zweier Zahlen über Pointer
void Addieren_mit_Pointer(int *Zahl1, int *Zahl2, int *Ergebnis)
{
    // Die Summe der Pointer Zahl1 und Zahl2 an den Pointer Ergebnis übergeben
    *Ergebnis=*Zahl1+*Zahl2;
}
  • DLL mit Menüpunkt ‘Projekt->Projekt erzeugen’ generieren
  • Das zugehörige Beispielprogramm (Projekt1.bpr), welches die DLL verwendet, sieht dann wie folgt aus (das Einbinden der *.lib-Datei nicht vergessen!! [siehe Eine DLL in ein BCB Projekt einbinden]):
//---------------------------------------------------------------------------
// Das Beipielprogramm verwendet zwei Buttons und drei Editfelder
//
// Edit1: Zahl1
// Edit2: Zahl2
// Edit3: Ergebnis
// Button1: Zahl1 + Zahl2 = Ergebnis
// Button2: *Zahl1 + *Zahl2 = *Ergebnis
//---------------------------------------------------------------------------
#include <vcl.h>
#pragma hdrstop

#include "Unit1.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TForm1 *Form1;

// Funktionen, die aus der DLL importiert werden
extern "C" __declspec(dllimport) int Addieren(int, int);
extern "C" __declspec(dllimport) void Addieren_mit_Pointer(int *, int*, int*);
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner)
{
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    int Ergebnis;

    // DLL-Funktion aufrufen, Rückgabewert ist vom Typ int
    Ergebnis = Addieren(Edit1->Text.ToInt(), Edit2->Text.ToInt());

    Edit3->Text = IntToStr(Ergebnis);
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
    int* Zahl1 = new int;
    int* Zahl2 = new int;
    int* Ergebnis = new int;

    *Zahl1 = Edit1->Text.ToInt();
    *Zahl2 = Edit2->Text.ToInt();

    // DLL-Funktion aufrufen
    // Summe von *Zahl1, *Zahl2 steht in *Ergebnis

    Addieren_mit_Pointer(Zahl1, Zahl2, Ergebnis);

    Edit3->Text = IntToStr(*Ergebnis);

    delete Zahl1;
    delete Zahl2;
    delete Ergebnis;
}