Entwickler-Ecke
Alle Sprachen - Alle Plattformen - declspec(dllexport) mehrerer static linked libs zu einer dll
Kasko - Do 09.04.20 08:26
Titel: declspec(dllexport) mehrerer static linked libs zu einer dll
Ich habe ein einfaches Projekt-Setup für ein OpenGL-Projekt für die Universität.
Ein API-Projekt, in dem jede Bibliothek, die ich verwenden möchte (GLEW, GLFW, GLM), statisch verlinkt ist. Diese Bibliotheken sollten mit meinem eigenen API-Code in einer einzigen DLL-Datei exportiert werden.
Das andere Projekt (das eigentliche) sollte nur eine Abhängigkeit haben, die DLL. Über diese DLL soll das Projekt Zugriff auf den API-Code und alle in der API verlinkten Bibliotheken zu erhalten.
Mein Problem ist, dass ich innerhalb der API Zugriff auf alle Funktionen aller von mir verknüpften Bibliotheken habe. Aber innerhalb des eigentlichen Projekts, das die API als Abhängigkeit hat, kann ich die Funktionen zwar aufrufen und der Compiler gibt keinen Fehler aus, da sich die Funktionsdeklaration in den verknüpften Header-Dateien befindet, der Linker findet die Funktionsdefinition jedoch nicht in der DLL, dh der Build der API exportiert die verknüpften Bibliotheken nicht in die DLL.
Ich habe im Projekt also nur Zugriff auf meinen selbstgeschrieben Code und nicht auf die LIBS innerhalb der API
Im API-Projekt habe ich auch die erforderlichen Präprozessordefinitionen definiert:
_GLFW_BUILD_DLL in der API und GLFW_DLL im Project:
C++-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| from "glfw3.h" l. 233-245
#if defined(_WIN32) && defined(_GLFW_BUILD_DLL) #define GLFWAPI __declspec(dllexport) <----- das hier ist in der API aktiv #elif defined(_WIN32) && defined(GLFW_DLL) #define GLFWAPI __declspec(dllimport) <----- das hier ist im Projekt aktiv #elif defined(__GNUC__) && defined(_GLFW_BUILD_DLL) #define GLFWAPI __attribute__((visibility("default"))) #else #define GLFWAPI #endif |
GLEW_BUILD in der API:
C++-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11:
| from "glew.h" l. 200-208
#ifdef GLEW_STATIC #define GLEWAPI extern #else #ifdef GLEW_BUILD #define GLEWAPI extern __declspec(dllexport) <----- das hier ist in der API aktiv #else #define GLEWAPI extern __declspec(dllimport) <----- das hier ist im Projekt aktiv #endif #endif |
Außerdem habe ich die Linkeroption /WHOLEARCHIVE für die 3 LIBS verwendet:
Meine Linker-Optionen für Debug x86:
Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20:
| /OUT:"D:\Programmierung\C++-Projekte\CG-Project\CGAPI\bin\Debug\Win32\CGAPI.dll" /MANIFEST /NXCOMPAT /PDB:"D:\Programmierung\C++-Projekte\CG-Project\CGAPI\bin\Debug\Win32\CGAPI.pdb" /DYNAMICBASE "glew32sd.lib" "glfw3.lib" "opengl32.lib" "kernel32.lib" "user32.lib" "gdi32.lib" "winspool.lib" "comdlg32.lib" "advapi32.lib" "shell32.lib" "ole32.lib" "oleaut32.lib" "uuid.lib" "odbc32.lib" "odbccp32.lib" /IMPLIB:"D:\Programmierung\C++-Projekte\CG-Project\CGAPI\bin\Debug\Win32\CGAPI.lib" /DEBUG /DLL /MACHINE:X86 /INCREMENTAL /PGD:"D:\Programmierung\C++-Projekte\CG-Project\CGAPI\bin\Debug\Win32\CGAPI.pgd" /SUBSYSTEM:WINDOWS /MANIFESTUAC:NO /ManifestFile:"D:\Programmierung\C++-Projekte\CG-Project\CGAPI\bin-int\Debug\Win32\CGAPI.dll.intermediate.manifest" /ERRORREPORT:PROMPT /NOLOGO /LIBPATH:"D:\Programmierung\C++-Projekte\CG-Project\Dependencies\GLFW\Win32\lib-vc2019\" /LIBPATH:"D:\Programmierung\C++-Projekte\CG-Project\Dependencies\GLEW\lib\Release\Win32\" /TLBID:1 |
Die von mir hinzugefügten Linker-Optionen:
Quelltext
1: 2: 3:
| /WHOLEARCHIVE:glew32sd.lib (in Debug) /WHOLEARCHIVE:glew32s.lib (in Release) /WHOLEARCHIVE:glfw3.lib /WHOLEARCHIVE:opengl32.lib |
Nachdem ich diese Linker Option verwendet habe, bekam ich den Fehler, dass die glew.res Datei bereits spezifiziert ist. Daher musste ich den glew Quell-Code herunterladen und die statischen Bibliotheken selbst builden und zwar ohne eine .res Datei. Nachdem ich die neuen LIBS gebuildet und verlinkt hatte, bekam ich beim build der API keine Fehler mehr, allerdings sind die Funktionen der verlinkten LIBS vom Linker immer noch nicht zu finden, was heißt, das sie nachwievor nicht in die DLL exportiert werden.
Hier ein Link zum Downmload des Projekt-Setups mit den selbst gebuildeten GLEW LIBS, da der Anhang angeblich mit 5MB zu klein ist, obwohl die Datei nur 4,6MB groß ist:
https://workupload.com/file/dRXSCZEweLW
Solution-Rebuild-Output:
Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14:
| 1>------ Rebuild All started: Project: CGAPI, Configuration: Debug Win32 ------ 1>CGWindow.cpp 1> Creating library D:\Programmierung\C++-Projekte\CG-Project\CGAPI\bin\Debug\Win32\CGAPI.lib and object D:\Programmierung\C++-Projekte\CG-Project\CGAPI\bin\Debug\Win32\CGAPI.exp 1>LINK : warning LNK4098: defaultlib 'MSVCRT' conflicts with use of other libs; use /NODEFAULTLIB:library 1>CGAPI.vcxproj -> D:\Programmierung\C++-Projekte\CG-Project\CGAPI\bin\Debug\Win32\CGAPI.dll 1>Done building project "CGAPI.vcxproj". 2>------ Rebuild All started: Project: CG-Project, Configuration: Debug Win32 ------ 2>main.cpp 2>main.obj : error LNK2019: unresolved external symbol __imp__glGetString@4 referenced in function _main 2>main.obj : error LNK2019: unresolved external symbol __imp__glewInit@0 referenced in function _main 2>main.obj : error LNK2019: unresolved external symbol __imp__glfwMakeContextCurrent referenced in function _main 2>D:\Programmierung\C++-Projekte\CG-Project\CG-Project\bin\Debug\Win32\CG-Project.exe : fatal error LNK1120: 3 unresolved externals 2>Done building project "CG-Project.vcxproj" -- FAILED. ========== Rebuild All: 1 succeeded, 1 failed, 0 skipped ========== |
Hoffe auf Hilfe
LG Kasko
Th69 - Do 09.04.20 11:02
Sind "glfw3.lib" und "opengl32.lib" überhaupt statische Libs (denn nur diese können mittels /WHOLEARCHIVE überhaupt direkt eingebunden werden)?
Kasko - Do 09.04.20 11:30
glfw3.lib ist auf jeden Fall statisch. Die LIB zum dynamischen linking ist glfw3dll.lib. Opengl32.lib glaube ich nicht. Aber selbst wenn ich /WHOLEARCHIVE:opengl32lib entferne funktioniert es nicht. Wenn es funktionieren würde, würde ich keine LNKErrors für GLFW oder GLEW Funktionen bekommen. Höchstens für OpenGL Funktionen die innerhalb von GLEW oder GLFW aufgerufen werden, die dann der Linker nicht zuordnen kann.
Th69 - Do 09.04.20 13:07
Aber dann paßt doch das Makro nicht:
C++-Quelltext
1: 2: 3:
| #else #define GLFWAPI |
Kasko - Do 09.04.20 13:17
Und was soll da nicht passen. Soll da wie bei GLEW extern drin stehen? Das statische verlinken innerhalb von CGAPI funktioniert ja, auch mit der leeren Definition. Nur das exportieren in die DLL funktioniert nicht.
Th69 - Do 09.04.20 16:34
Nein, da habe ich mich wohl falsch ausgedrückt.
Ich meine, daß du dieses Makro benutzen sollst (denn du willst ja eine statische Lib einbinden) und nicht
__declspec(dllexport) bzw.
__declspec(dllimport), s.a.
Why can't I __declspec(dllexport) a function from a static library? [
https://devblogs.microsoft.com/oldnewthing/20140321-00/?p=1433]
PS: Das
extern ist nicht unbedingt notwendig (s. Unterschiede beim Code von "GLFW" und "GLEW"), da dies nur bei globalen Variablen wichtig ist (Funktionen haben immer "external linkage", außer sie sind explizit als
static (bzw.
inline) gekennzeichnet). Und nur Funktionen mit "external linkage" können überhaupt per
__declspec(dllexport) bei einer DLL exportiert werden.
Kasko - Do 09.04.20 17:04
Dem verlinkten Artikel bin ich auch schon begegnet.
Das macht leider keinen Unterschied ob dort __declspec(dllexport) oder extern/nichts steht. Habe es bevor ich diese Frage gepostet habe auch schon ausprobiert. Wenn ich in der API die Definitionen ändere (_GLFW_BUILD_DLL nicht definiere und GLEW_STATIC definiere), sodass beide auch von den Makros her statisch verlinkt sind, kommen exakt die selben Fehler bei raus. Innerhalb der API kann ich die Methoden nutzen aber in die DLL werden die LIBs nicht exportiert.
Und wie ich es mit einer .def file lösen soll (wie im Artikel angesprochen), weiß ich auch nicht.
Kasko - Fr 10.04.20 13:06
Die lib.exe erstellt doch aber auch eine LIB. Ich möchte aber, dass alle LIBs in eine DLL gebuildet werden zusammen mit meinem eigenen Code. Ich möchte keine LIB builden was die lib.exe machen würde. Das muss doch irgendwie möglich sein ohne sein ganzen Projekt über den Haufen werfen zu müssen. Durch mir nicht bekannte Linker-Einstellungen oder etwas dergleichen.
Th69 - Fr 10.04.20 13:45
Ich glaube dir ist der Unterschied zwischen einer statischen und dynamischen Lib nicht klar?
Du möchtest eine DLL erstellen, welche keine weiteren Abhängigkeiten mehr hat. Dazu muß aber der gesamte Source-Code komplett einkompiliert werden. Wenn du jetzt zwar statische Libs mitverlinkst, so mußt du auch dessen Funktionen komplett benutzen, d.h. für jede Funktion eine Wrapper-Funktion erstellen (und diese dann per
__declspec(dllexport) exportieren).
Aber solange diese Libs auf "Opengl32.lib" basieren, so wirst du immer in deinem Haupt-Projekt auf die zugehörige DLL "Opengl32.dll" angewiesen sein (eingebunden entweder als Verweis oder im Header per
#pragma comment(lib, ...) [
https://docs.microsoft.com/en-us/cpp/preprocessor/comment-c-cpp?view=vs-2019]).
Alternativ müßtest du (wie du es für die GLEW-Lib gemacht hast), den Source-Code auch der anderen Libs dir besorgen. Und dann bei deinem DLL-Projekt (API) direkt allen Source-Code einkompilieren (und diesmal aber alle Funktionen per
__declspec(dllexport) exportieren).
Nur welche Einsparungen erhoffst du dir davon? Die Headerdateien mußt du sowieso in dem Hauptprojekt benutzen und ob du nun 1 oder mehrere Libs dort angibst, ist doch egal.
Bei den Standard-Libs ("kernel32.lib" "user32.lib" "gdi32.lib", ...) hast du ja auch alle angegeben (obwohl du wohl nicht wirklich alle benötigst, oder?).
Kasko - So 12.04.20 00:01
Mir ist der Unterschied zwischen einer lib und einer dll + lib durchaus bekannt. Zudem wollte ich nach einer Möglichkeit suchen Wrapper-Funktionen zu umgehen. Aber vielleicht stell ich mir das zu einfach vor.
Was ich mir vorstelle/denke:
In einer statischen LIB müsste der source code komplett einkompiliert sein. Theoretisch müsste man also mehr oder weniger "einfach" die LIBs zusammenfügen können, was ja die lib.exe machen würde. Könnte man jetzt diese lib in eine dll und eine lib aufteilen. In der DLL die kompilierte Implementierung der Funktionen und in der LIB die Informationen für den Linker, es aus der DLL zu holen. Dann könnte man diese LIB die keine Implementierung enthält mit der opengl32.lib kombinieren. Dann würde der Linker aus der entstandenen LIB auf 2 DLLs verwiesen werden.
Warum will ich das so machen:
1. Erstmal grundsätzlich möchte ich wissen ob und wenn ja wie es geht.
2. Die API soll eine vollständige funktionsfähige Bibliothek sein, die ich vielleicht mehrmals verwenden und wenn sie ausgereift ist auch an Kommilitonen weiterreichen möchte. Dabei soll sie auch funktionieren wenn man nichts weiter einbindet, also soll sie alle Abhängigkeiten mitbringen. Zum Beispiel bei der Library SFML wird das ja auch so gemacht. Intern werden einige Dinge mit OpenGL gerendert. Trotzdem muss man weder die opengl32.lib noch GLEW einbinden. Es wird alles mitgeliefert. Die Einbindung der API soll also so einfach wie möglich sein. Die includes würde ich ja natürlich auch mitliefern zusammen mit den includes für den eigenen Code.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2024 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!