DLL - Kurzanleitung
Die dynamische Verknüpfung ist ein Mechanismus, mit dem Anwendungen zur Laufzeit mit Bibliotheken verknüpft werden. Die Bibliotheken verbleiben in ihren eigenen Dateien und werden nicht in die ausführbaren Dateien der Anwendungen kopiert. DLLs werden beim Ausführen der Anwendung und nicht beim Erstellen mit einer Anwendung verknüpft. DLLs können Links zu anderen DLLs enthalten.
DLLs werden häufig in Dateien mit unterschiedlichen Erweiterungen wie .EXE, .DRV oder .DLL abgelegt.
Vorteile der DLL
Im Folgenden sind einige Vorteile von DLL-Dateien aufgeführt.
Verbraucht weniger Ressourcen
DLL-Dateien werden nicht zusammen mit dem Hauptprogramm in den RAM geladen. Sie belegen keinen Platz, es sei denn, dies ist erforderlich. Wenn eine DLL-Datei benötigt wird, wird sie geladen und ausgeführt. Solange beispielsweise ein Benutzer von Microsoft Word ein Dokument bearbeitet, ist die Drucker-DLL-Datei im RAM nicht erforderlich. Wenn der Benutzer das Dokument drucken möchte, bewirkt die Word-Anwendung, dass die Drucker-DLL-Datei geladen und ausgeführt wird.
Fördert die modulare Architektur
Eine DLL fördert die Entwicklung modularer Programme. Es hilft Ihnen bei der Entwicklung großer Programme, die mehrere Sprachversionen erfordern, oder eines Programms, das eine modulare Architektur erfordert. Ein Beispiel für ein modulares Programm ist ein Abrechnungsprogramm mit vielen Modulen, die zur Laufzeit dynamisch geladen werden können.
Hilft bei der einfachen Bereitstellung und Installation
Wenn eine Funktion innerhalb einer DLL ein Update oder einen Fix benötigt, muss für die Bereitstellung und Installation der DLL das Programm nicht erneut mit der DLL verknüpft werden. Wenn mehrere Programme dieselbe DLL verwenden, profitieren alle von dem Update oder dem Fix. Dieses Problem kann häufiger auftreten, wenn Sie eine DLL eines Drittanbieters verwenden, die regelmäßig aktualisiert oder behoben wird.
Anwendungen und DLLs können automatisch mit anderen DLLs verknüpft werden, wenn die DLL-Verknüpfung im Abschnitt IMPORTS der Moduldefinitionsdatei als Teil der Kompilierung angegeben ist. Andernfalls können Sie sie explizit mit der Windows LoadLibrary-Funktion laden.
Wichtige DLL-Dateien
COMDLG32.DLL - Steuert die Dialogfelder.
GDI32.DLL - Enthält zahlreiche Funktionen zum Zeichnen von Grafiken, Anzeigen von Text und Verwalten von Schriftarten.
KERNEL32.DLL - Enthält Hunderte von Funktionen zur Verwaltung des Speichers und verschiedener Prozesse.
USER32.DLL- Enthält zahlreiche Funktionen der Benutzeroberfläche. Beteiligt an der Erstellung von Programmfenstern und deren Interaktionen untereinander.
Zunächst werden die Probleme und Anforderungen besprochen, die Sie bei der Entwicklung Ihrer eigenen DLLs berücksichtigen sollten.
Arten von DLLs
Wenn Sie eine DLL in eine Anwendung laden, können Sie mit zwei Verknüpfungsmethoden die exportierten DLL-Funktionen aufrufen. Die zwei Verknüpfungsmethoden sind:
- Ladezeit dynamische Verknüpfung und
- Dynamische Laufzeitverknüpfung.
Dynamische Verknüpfung beim Laden
Bei der dynamischen Verknüpfung während der Ladezeit ruft eine Anwendung die exportierten DLL-Funktionen wie lokale Funktionen explizit auf. Um die dynamische Verknüpfung zum Laden zu verwenden, geben Sie beim Kompilieren und Verknüpfen der Anwendung eine Header-Datei (.h) und eine Importbibliothek-Datei (.lib) an. Wenn Sie dies tun, stellt der Linker dem System die Informationen zur Verfügung, die zum Laden der DLL und zum Auflösen der exportierten DLL-Funktionspositionen beim Laden erforderlich sind.
Dynamische Laufzeitverknüpfung
Bei der dynamischen Laufzeitverknüpfung ruft eine Anwendung entweder die LoadLibrary-Funktion oder die LoadLibraryEx-Funktion auf, um die DLL zur Laufzeit zu laden. Nachdem die DLL erfolgreich geladen wurde, verwenden Sie die Funktion GetProcAddress, um die Adresse der exportierten DLL-Funktion abzurufen, die Sie aufrufen möchten. Wenn Sie die dynamische Laufzeitverknüpfung verwenden, benötigen Sie keine Importbibliotheksdatei.
In der folgenden Liste werden die Anwendungskriterien für die Auswahl zwischen dynamischer Ladezeitverknüpfung und dynamischer Laufzeitverknüpfung beschrieben:
Startup performance : Wenn die anfängliche Startleistung der Anwendung wichtig ist, sollten Sie die dynamische Laufzeitverknüpfung verwenden.
Ease of use: Bei der dynamischen Verknüpfung beim Laden sind die exportierten DLL-Funktionen wie lokale Funktionen. Es hilft Ihnen, diese Funktionen einfach aufzurufen.
Application logic: Bei der dynamischen Laufzeitverknüpfung kann eine Anwendung verzweigen, um je nach Bedarf verschiedene Module zu laden. Dies ist wichtig, wenn Sie mehrsprachige Versionen entwickeln.
Der DLL-Einstiegspunkt
Wenn Sie eine DLL erstellen, können Sie optional eine Einstiegspunktfunktion angeben. Die Einstiegspunktfunktion wird aufgerufen, wenn sich Prozesse oder Threads an die DLL anhängen oder von der DLL trennen. Mit der Einstiegspunktfunktion können Sie Datenstrukturen gemäß den Anforderungen der DLL initialisieren oder zerstören.
Wenn die Anwendung Multithread-fähig ist, können Sie außerdem TLS (Thread Local Storage) verwenden, um jedem Thread in der Einstiegspunktfunktion Speicher zuzuweisen, der privat ist. Der folgende Code ist ein Beispiel für die DLL-Einstiegspunktfunktion.
BOOL APIENTRY DllMain(
HANDLE hModule, // Handle to DLL module DWORD ul_reason_for_call, LPVOID lpReserved ) // Reserved
{
switch ( ul_reason_for_call )
{
case DLL_PROCESS_ATTACHED:
// A process is loading the DLL.
break;
case DLL_THREAD_ATTACHED:
// A process is creating a new thread.
break;
case DLL_THREAD_DETACH:
// A thread exits normally.
break;
case DLL_PROCESS_DETACH:
// A process unloads the DLL.
break;
}
return TRUE;
}
Wenn die Einstiegspunktfunktion einen FALSE-Wert zurückgibt, wird die Anwendung nicht gestartet, wenn Sie eine dynamische Verknüpfung zum Laden verwenden. Wenn Sie eine dynamische Laufzeitverknüpfung verwenden, wird nur die einzelne DLL nicht geladen.
Die Einstiegspunktfunktion sollte nur einfache Initialisierungsaufgaben ausführen und keine anderen DLL-Lade- oder Beendigungsfunktionen aufrufen. Beispielsweise sollten Sie in der Einstiegspunktfunktion die nicht direkt oder indirekt aufrufenLoadLibrary Funktion oder die LoadLibraryExFunktion. Außerdem sollten Sie das nicht anrufenFreeLibrary Funktion, wenn der Prozess beendet wird.
WARNING: Stellen Sie in Multithread-Anwendungen sicher, dass der Zugriff auf die globalen DLL-Daten synchronisiert ist (threadsicher), um mögliche Datenbeschädigungen zu vermeiden. Verwenden Sie dazu TLS, um eindeutige Daten für jeden Thread bereitzustellen.
Exportieren von DLL-Funktionen
Um DLL-Funktionen zu exportieren, können Sie den exportierten DLL-Funktionen entweder ein Funktionsschlüsselwort hinzufügen oder eine .def-Datei (Module Definition) erstellen, in der die exportierten DLL-Funktionen aufgelistet sind.
Um ein Funktionsschlüsselwort zu verwenden, müssen Sie jede Funktion, die Sie exportieren möchten, mit dem folgenden Schlüsselwort deklarieren:
__declspec(dllexport)
Um exportierte DLL-Funktionen in der Anwendung zu verwenden, müssen Sie jede Funktion, die Sie importieren möchten, mit dem folgenden Schlüsselwort deklarieren:
__declspec(dllimport)
Normalerweise verwenden Sie eine Header-Datei mit define Aussage und ein ifdef Anweisung zum Trennen der Exportanweisung und der Importanweisung.
Sie können auch eine Moduldefinitionsdatei verwenden, um exportierte DLL-Funktionen zu deklarieren. Wenn Sie eine Moduldefinitionsdatei verwenden, müssen Sie den exportierten DLL-Funktionen nicht das Funktionsschlüsselwort hinzufügen. In der Moduldefinitionsdatei deklarieren Sie dieLIBRARY Aussage und die EXPORTSAnweisung für die DLL. Der folgende Code ist ein Beispiel für eine Definitionsdatei.
// SampleDLL.def
//
LIBRARY "sampleDLL"
EXPORTS
HelloWorld
Schreiben Sie eine Beispiel-DLL
In Microsoft Visual C ++ 6.0 können Sie eine DLL erstellen, indem Sie entweder die auswählen Win32 Dynamic-Link Library Projekttyp oder die MFC AppWizard (dll) Projekttyp.
Der folgende Code ist ein Beispiel für eine DLL, die in Visual C ++ mithilfe des Projekttyps Win32 Dynamic-Link Library erstellt wurde.
// SampleDLL.cpp
#include "stdafx.h"
#define EXPORTING_DLL
#include "sampleDLL.h"
BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved )
{
return TRUE;
}
void HelloWorld()
{
MessageBox( NULL, TEXT("Hello World"),
TEXT("In a DLL"), MB_OK);
}
// File: SampleDLL.h
//
#ifndef INDLL_H
#define INDLL_H
#ifdef EXPORTING_DLL
extern __declspec(dllexport) void HelloWorld() ;
#else
extern __declspec(dllimport) void HelloWorld() ;
#endif
#endif
Aufrufen einer Beispiel-DLL
Der folgende Code ist ein Beispiel für ein Win32-Anwendungsprojekt, das die exportierte DLL-Funktion in der SampleDLL-DLL aufruft.
// SampleApp.cpp
#include "stdafx.h"
#include "sampleDLL.h"
int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
HelloWorld();
return 0;
}
NOTE : Bei der dynamischen Verknüpfung zum Laden müssen Sie die Importbibliothek SampleDLL.lib verknüpfen, die beim Erstellen des SampleDLL-Projekts erstellt wird.
Bei der dynamischen Laufzeitverknüpfung verwenden Sie Code, der dem folgenden Code ähnelt, um die exportierte DLL-Funktion SampleDLL.dll aufzurufen.
...
typedef VOID (*DLLPROC) (LPTSTR);
...
HINSTANCE hinstDLL;
DLLPROC HelloWorld;
BOOL fFreeDLL;
hinstDLL = LoadLibrary("sampleDLL.dll");
if (hinstDLL != NULL)
{
HelloWorld = (DLLPROC) GetProcAddress(hinstDLL, "HelloWorld");
if (HelloWorld != NULL)
(HelloWorld);
fFreeDLL = FreeLibrary(hinstDLL);
}
...
Wenn Sie die SampleDLL-Anwendung kompilieren und verknüpfen, sucht das Windows-Betriebssystem an den folgenden Speicherorten in dieser Reihenfolge nach der SampleDLL-DLL:
Der Anwendungsordner
Der aktuelle Ordner
Der Windows-Systemordner (The GetSystemDirectory Funktion gibt den Pfad des Windows-Systemordners zurück.
Der Windows-Ordner (The GetWindowsDirectory Funktion gibt den Pfad des Windows-Ordners zurück.
Um eine DLL verwenden zu können, muss sie registriert werden, indem entsprechende Referenzen in die Registrierung eingegeben werden. Es kommt manchmal vor, dass eine Registrierungsreferenz beschädigt wird und die Funktionen der DLL nicht mehr verwendet werden können. Die DLL kann erneut registriert werden, indem Start-Run geöffnet und der folgende Befehl eingegeben wird:
regsvr32 somefile.dll
Dieser Befehl setzt voraus, dass sich somefile.dll in einem Verzeichnis oder Ordner befindet, der sich im PATH befindet. Andernfalls muss der vollständige Pfad für die DLL verwendet werden. Eine DLL-Datei kann auch mit dem Schalter "/ u" wie unten gezeigt abgemeldet werden.
regsvr32 /u somefile.dll
Dies kann verwendet werden, um einen Dienst ein- und auszuschalten.
Zur Behebung von DLL-Problemen stehen verschiedene Tools zur Verfügung. Einige von ihnen werden unten diskutiert.
Abhängigkeits-Walker
Das Abhängigkeits-Walker-Tool (depends.exe) kann rekursiv nach allen abhängigen DLLs suchen, die von einem Programm verwendet werden. Wenn Sie ein Programm in Dependency Walker öffnen, führt der Dependency Walker die folgenden Überprüfungen durch:
- Überprüft, ob DLLs fehlen.
- Überprüft, ob Programmdateien oder DLLs ungültig sind.
- Überprüft, ob Importfunktionen und Exportfunktionen übereinstimmen.
- Prüft auf zirkuläre Abhängigkeitsfehler.
- Überprüft, ob Module ungültig sind, da die Module für ein anderes Betriebssystem bestimmt sind.
Mit Dependency Walker können Sie alle von einem Programm verwendeten DLLs dokumentieren. Dies kann dazu beitragen, zukünftige DLL-Probleme zu vermeiden und zu beheben. Dependency Walker befindet sich bei der Installation von Microsoft Visual Studio 6.0 im folgenden Verzeichnis:
drive\Program Files\Microsoft Visual Studio\Common\Tools
DLL Universal Problem Solver
Das DUPS-Tool (DLL Universal Problem Solver) wird zum Überprüfen, Vergleichen, Dokumentieren und Anzeigen von DLL-Informationen verwendet. In der folgenden Liste werden die Dienstprogramme beschrieben, aus denen das DUPS-Tool besteht:
Dlister.exe - Dieses Dienstprogramm listet alle DLLs auf dem Computer auf und protokolliert die Informationen in einer Textdatei oder in einer Datenbankdatei.
Dcomp.exe - Dieses Dienstprogramm vergleicht die DLLs, die in zwei Textdateien aufgeführt sind, und erstellt eine dritte Textdatei, die die Unterschiede enthält.
Dtxt2DB.exe - Dieses Dienstprogramm lädt die Textdateien, die mit dem Dienstprogramm Dlister.exe und dem Dienstprogramm Dcomp.exe erstellt wurden, in die Datenbank dllHell.
DlgDtxt2DB.exe - Dieses Dienstprogramm bietet eine grafische Benutzeroberfläche (GUI) des Dienstprogramms Dtxt2DB.exe.
Beachten Sie beim Schreiben einer DLL die folgenden Tipps:
Verwenden Sie die richtige Aufrufkonvention (C oder stdcall).
Beachten Sie die korrekte Reihenfolge der an die Funktion übergebenen Argumente.
Ändern Sie NIEMALS die Größe von Arrays oder verketten Sie Zeichenfolgen mit den Argumenten, die direkt an eine Funktion übergeben werden. Denken Sie daran, dass die von Ihnen übergebenen Parameter LabVIEW-Daten sind. Das Ändern der Array- oder Zeichenfolgengröße kann zu einem Absturz führen, indem andere im LabVIEW-Speicher gespeicherte Daten überschrieben werden. Sie können die Größe von Arrays ändern oder Zeichenfolgen verketten, wenn Sie ein LabVIEW-Array-Handle oder ein LabVIEW-Zeichenfolgenhandle übergeben und den Visual C ++ - Compiler oder Symantec-Compiler zum Kompilieren Ihrer DLL verwenden.
Wählen Sie beim Übergeben von Zeichenfolgen an eine Funktion den richtigen Typ der zu übergebenden Zeichenfolge aus. C- oder Pascal- oder LabVIEW-String-Handle.
Pascal-Zeichenfolgen sind auf 255 Zeichen begrenzt.
C-Strings werden mit NULL abgeschlossen. Wenn Ihre DLL-Funktion numerische Daten in einem binären Zeichenfolgenformat zurückgibt (z. B. über GPIB oder die serielle Schnittstelle), gibt sie möglicherweise NULL-Werte als Teil der Datenzeichenfolge zurück. In solchen Fällen ist das Übergeben von Arrays mit kurzen (8-Bit) Ganzzahlen am zuverlässigsten.
Wenn Sie mit Arrays oder Datenzeichenfolgen arbeiten, übergeben Sie IMMER einen Puffer oder ein Array, das groß genug ist, um alle von der Funktion im Puffer platzierten Ergebnisse zu speichern, es sei denn, Sie übergeben sie als LabVIEW-Handles. In diesem Fall können Sie die Größe mithilfe von CIN ändern Funktionen unter Visual C ++ oder Symantec Compiler.
Listen Sie DLL-Funktionen im Abschnitt EXPORTS der Moduldefinitionsdatei auf, wenn Sie _stdcall verwenden.
Listen Sie DLL-Funktionen auf, die andere Anwendungen im Abschnitt EXPORTS der Moduldefinitionsdatei aufrufen, oder um das Schlüsselwort _declspec (dllexport) in die Funktionsdeklaration aufzunehmen.
Wenn Sie einen C ++ - Compiler verwenden, exportieren Sie Funktionen mit der externen Anweisung .C. {} In Ihrer Header-Datei, um eine Namensverfälschung zu verhindern.
Wenn Sie Ihre eigene DLL schreiben, sollten Sie eine DLL nicht neu kompilieren, während die DLL von einer anderen Anwendung in den Speicher geladen wird. Stellen Sie vor dem erneuten Kompilieren einer DLL sicher, dass alle Anwendungen, die diese bestimmte DLL verwenden, aus dem Speicher entladen werden. Es stellt sicher, dass die DLL selbst nicht in den Speicher geladen wird. Sie können möglicherweise nicht richtig neu erstellen, wenn Sie dies vergessen und Ihr Compiler Sie nicht warnt.
Testen Sie Ihre DLLs mit einem anderen Programm, um sicherzustellen, dass sich die Funktion (und die DLL) korrekt verhalten. Wenn Sie es mit dem Debugger Ihres Compilers oder einem einfachen C-Programm testen, in dem Sie eine Funktion in einer DLL aufrufen können, können Sie feststellen, ob mögliche Probleme mit der DLL oder LabVIEW verbunden sind.
Wir haben gesehen, wie man eine DLL schreibt und wie man ein "Hello World" -Programm erstellt. Dieses Beispiel muss Ihnen eine Vorstellung vom Grundkonzept zum Erstellen einer DLL gegeben haben.
Hier geben wir eine Beschreibung zum Erstellen von DLLs mit Delphi, Borland C ++ und erneut VC ++.
Nehmen wir diese Beispiele einzeln.
Schreiben und Aufrufen von DLLs in Delphi
Erstellen von DLLs aus der Borland C ++ Builder-IDE
Erstellen von DLLs in Microsoft Visual C ++ 6.0