-> Ich kann kompilieren, allerdings wenn ich die DLL verwende, taucht C2001 auf.
Zeig doch mal die genaue Fehlermeldung und den Code, in dem der Fehler auftritt.
Mein Projekt umfasst .cpp und .hpp Dateien und hat als Ausgabe eine .lib und .dll Datei.
Ich linke im Demoprojekt gegen die .lib und #includere "Header.hpp".
klingt soweit richtig
-> Was hat es mit dem _declspec(dllimport/dllexport) auf sich ?
Der Unterschied zwischen einer statischen und einer dynamischen Library liegt darin, wie die Library gelinked wird. Eine statische Library wird während der Erzeugung einer Binary gelinked, indem die benötigten Teile der Library selbst zum Teil der Binary gemacht werden. Um Dinge aus einer statischen Library zu verwenden, generiert der Compiler Maschinencode einfach so, als wären diese Dinge Teil der Binary, da sie es am Ende ja werden. Eine dynamische Library ist dagegen eine eigenständige Binary, die erst während der Ausführung geladen wird. Die Adressen der Dinge in der dynamischen Library sind zum Zeitpunkt der Erzeugung der Binary, die die Library verwendet, nicht bekannt. Um Dinge aus einer dynamischen Library zu verwenden, muss der Compiler also Maschinencode generieren, der die entsprechenden Dinge nicht direkt, sondern indirekt anspricht. Jede Binary hat dazu eine sog.
Import Address Table (IAT), eine Tabelle, in die beim Laden der Binary die Adressen der benötigten Dinge aus der DLL eingetraten werden. Der Maschinencode in der Binary verwendet Dinge aus der DLL, indem er erst die Adresse im entsprechenden Eintrag der IAT nachschlägt und dann das Ding über diese Adresse anspricht.
Komplementär zur IAT, muss eine Binary, deren Inhalte dynamisch gelinked werden können sollen (also z.B. eine DLL), eine
Export Table (ET) haben, die zu jedem Ding in der Library, das von außen erreichbar sein soll, die Adresse enthält, sodass diese beim Laden nachgeschlagen und in die IAT der verwendenden Binary eingetragen werden kann.
Je nach Betriebssystem und Compiler varriieren die Mechanismen, nach denen all das genau abgewickelt wird, die Grundidee ist aber immer die gleiche. Im Falle von Windows und MSVC gibt es zusätzlich auch noch mehrere Vorgehensweisen, die am Ende alle das gleiche erreichen, was vermutlich der Grund für die scheinbare Widersprüchlichkeit dessen, was du im Internet dazu so gefunden hast, ist. Die wohl einfachste und definitiv am häufigsten angetroffene Form ist die mit
__declspec() und
dllexport bzw.
dllimport.
Um ein Ding wie beispielsweise eine Funktion
f() im Code verwenden zu können, muss dieses Ding zuvor deklariert worden sein. Eine Deklaration wie
|
C-/C++-Quelltext
|
1
|
void f();
|
sagt dem Compiler, dass es irgendwo anders eine Funktion
f() gibt und der Compiler generiert Maschinencode, der diese Funktion verwendet, als wäre sie Teil der Binary. Der Linker sucht beim Erzeugen der Binary aus den Object Files und statischen Libraries dann nach dieser Funkion
f, sorgt dafür, dass die Funktion Teil der fertigen Binary wird und ersetzt das Symbol
f durch die Adresse, die die Funktion in der fertigen Binary haben wird (wenn die Funktion nirgendwo zu finden ist, meldet sich der Linker mit seiner wohl berühmtesten Form der Beschwerde zu Wort: "nichtaufgelöstes externes Symbol f").
Um ein Ding wie beispielsweise eine Funktion
f(), die sich in einer DLL befindet, verwenden zu können, muss der Compiler, wie oben besprochen, Code generieren, der die Funktion f nicht direkt, sondern indirekt über die IAT anspricht. Um ihm dies mitzuteilen, deklarierst du die Funktion im Code, der die DLL verwenden soll, mit dem
__declspec(dllimport) Attribut:
|
C-/C++-Quelltext
|
1
|
__declspec(dllimport) void f();
|
Dies führt dazu, dass der Compiler Code generiert, der diese Funktion über die IAT anspricht.
Auf der anderen Seite, im Code der DLL, muss die Funktion
f entsprechend markiert werden, damit sie vom Linker in die ET der DLL aufgenommen wird, sodass sie beim Laden einer Binary, die die DLL verwendet, in der DLL gefunden werden und in die IAT eingetragen kann. Dies erreichst du, indem du die Definition der Funktion mit dem
__declspec(dllexport) Attribut versiehst:
|
C-/C++-Quelltext
|
1
2
3
4
5
|
__declspec(dllexport)
void f()
{
// ...
}
|
Die .lib Datei, die beim Erzeugen einer DLL standardmäßig miterzeugt wird, enthält effektiv Code und Informationen, die dafür sorgen, dass der Linker beim Erzeugen der die DLL verwendenden Binary die entsprechenden Einträge in der IAT erzeugt.