Dynamische Bibliotheken

Willemers Informatik-Ecke

Diese Seite basiert auf Inhalten aus dem Buch C++. Der Einstieg und ist bei Wrox im Verlag Wiley-VCH erschienen.



Mehr C++ programmieren? Mit Spaß? Dies ist das Buch für Sie!

Letzte Exemplare!

Spätestens seit dem Siegeszug der grafischen Oberflächen ist es gar nicht mehr möglich, alle Bibliotheken an jede Applikation zu binden. Ansonsten müsste jede Windows-Applikation eine eigene Windows-Umgebung mit sich herumschleppen. Stattdessen befinden sich im System dynamische Bibliotheken, die vom Betriebssystem verwaltet werden. Unter UNIX nennt man sie Dynamic Libraries. Unter Windows benennt man sie nach ihrer Endung: DLL steht für Dynamic Link Library. Die Verwendung der dynamischen Bibliotheken des Systems erfolgt für den Programmierer weit gehend unsichtbar. Durch das Einbinden der Header-Dateien werden die Aufrufe automatisch so weitergeleitet, dass die dynamischen Bibliotheken verwendet werden.

Erst wenn Sie eine eigene dynamische Bibliothek schreiben sollen, müssen Sie sich mit diesem Thema befassen. Leider ist der Umgang mit dynamischen Bibliotheken sehr systemabhängig. Selbst die verschiedenen Compiler desselben Systems können sich unterschiedlich verhalten.

Allerdings basieren die meisten dynamischen Bibliotheken noch auf C, was sich besonders im Aufruf niederschlägt. Darum müssen die aufrufbaren Funktionen meist mit einer extern "C"-Deklaration umgeben werden.

extern "C" {
   void aufruf()
   {
       ...
   }
}
Im Folgenden wird die Erstellung und Benutzung einer dynamischen Bibliothek kochrezeptartig vorgestellt.

MS-Windows

Sie können mit jeder IDE ein DLL-Projekt erstellen, und damit wird Ihnen schon ein Großteil der Arbeit abgenommen. Die Wizzards erstellen Ihnen auch bereits eine Funktion namens DllEntryPoint(), die jede DLL enthalten muss. Sie müssen nur noch die Funktionen schreiben, auf die Sie von außen zugreifen wollen.

Funktionsattribute

Die Windows-DLLs sind nicht im C++-Format gehalten, sondern halten sich an die klassische C-Schnittstelle. Die Funktionen müssen vom Compiler so umgesetzt werden, dass die Schnittstelle der eines C-Compilers entspricht. Dazu hat jeder Compiler seine spezielle Syntax. Als Beispiel wird eine Funktion fkt() verwendet, die einen Parameter und einen Rückgabewert vom Typ double hat. Der Borland-Compiler verwendet folgende Schreibweise: (vgl. Kaiser, Richard: C++ mit dem Borland C++ Builder. Springer, Berlin-Heidelberg. 2002. S. 658.)

__declspec(dllexport) double fkt(double w)
{
    ...

In Visual C++ von Microsoft sieht eine Funktion für eine DLL so aus: (vgl. Wigard, Susanne: Visual C++ 6. bhv, Kaarst, 1999. S. 557.)

extern "C" __declspec(dllexport) double __stdcall fkt(double w)
{
    ...
Nach der Generierung des Projekts entsteht eine Datei mit der Namenserweiterung .DLL, die bei der Ausführung des Programms im Arbeitsverzeichnis oder im Windows-Verzeichnis liegen muss. Daneben gibt es eine Import-Datei mit der Erweiterung .LIB, die vom aufrufenden Programm hinzugebunden werden muss, damit es die richtige DLL anfordert. Das funktioniert durch Eintrag der Datei in die Projekteinstellungen unter Bibliotheken. Das aufrufende Programm braucht dann noch den Prototyp, um die Funktion korrekt aufrufen zu können. Dieser lässt sich leicht aus der Funktion herleiten. Bei Borland C++ sieht der Prototyp so aus:

__declspec(dllexport) double fkt(double w);

Der eigentliche Aufruf unterscheidet sich nicht durch den Aufruf einer gewöhnlichen lokalen Funktion.

UNIX/Linux

Im Gegensatz dazu sind bei UNIX und Linux keine besonderen Vorkehrungen bei den Funktionen notwendig. Allerdings müssen die korrekten Compiler-Optionen eingesetzt werden. (vgl. Johnson, Michael K./Troan, Erik W.: Anwendungen entwickeln unter Linux. Addison Wesley, Bonn, 1998. S. 68f.)

Beispiel

Die dynamische Bibliothek hello soll einfach ein Wort auf dem Bildschirm ausgeben.

[libhello.cpp]

#include <iostream>
using namespace std;

void print_hello()
{
    cout << "Hello" << endl;
}

Der Aufruf erfolgt durch das Programm usehello.cpp. Hier wurde der Prototyp der Funktion print_hello() direkt angegeben, der in größeren Programmen natürlich in einer Header-Datei abgelegt würde.

void print_hello();

int main()
{
   print_hello();
}

Das Geheimnis besteht hier also ausschließlich darin, wie die beiden Module übersetzt werden. Die Datei libhello.cpp wird zunächst in eine Objektdatei kompiliert. Dabei sorgt die Option -fPIC dafür, dass positionsunabhängiger Code erzeugt wird. Der zweite Aufruf bewirkt das Binden der Objektdatei als dynamische Bibliothek. Die zentrale Option ist hier -shared. Sie bewirkt, dass die neue Bibliothek als dynamische Bibliothek gebunden wird.

[Kommandos zur Erzeugung einer dynamischen Bibliothek]

c++ -fPIC -c libhello.cpp
c++ -shared -o libhello.so libhello.o -lc

Zu guter Letzt muss noch das Programm usehello.cpp übersetzt werden.

[Übersetzen des Hauptprogramms]

c++ usehello.cpp -L. -lhello

Vor einem Aufruf muss die Bibliothek allerdings noch in ein Verzeichnis geschafft werden, in dem unter Linux dynamische Bibliotheken abgelegt werden.

sudo cp libhello.so /usr/lib


Informatik-Ecke Einstieg in C++ (C) Copyright 2005, 2014 Arnold Willemer