Autor Beitrag
Kasko
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 97

Win 10
C# C++ (VS 2017/19), (Java, PHP)
BeitragVerfasst: Do 09.04.20 09:26 
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:

ausblenden 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

/* GLFWAPI is used to declare public API functions for export
 * from the DLL / shared library / dynamic library.
 */

#if defined(_WIN32) && defined(_GLFW_BUILD_DLL)
    /* We are building GLFW as a Win32 DLL */
    #define GLFWAPI __declspec(dllexport)                       <----- das hier ist in der API aktiv
#elif defined(_WIN32) && defined(GLFW_DLL)
    /* We are calling GLFW as a Win32 DLL */
    #define GLFWAPI __declspec(dllimport)                       <----- das hier ist im Projekt aktiv
#elif defined(__GNUC__) && defined(_GLFW_BUILD_DLL)
    /* We are building GLFW as a shared / dynamic library */
    #define GLFWAPI __attribute__((visibility("default")))
#else
    /* We are building or calling GLFW as a static library */
    #define GLFWAPI
#endif


GLEW_BUILD in der API:

ausblenden 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:

ausblenden 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:

ausblenden 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:

workupload.com/file/dRXSCZEweLW

Solution-Rebuild-Output:

ausblenden 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


Zuletzt bearbeitet von Kasko am Do 09.04.20 13:42, insgesamt 1-mal bearbeitet
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4249
Erhaltene Danke: 902

Win10
C#, C++ (VS 2015/17)
BeitragVerfasst: Do 09.04.20 12:02 
Sind "glfw3.lib" und "opengl32.lib" überhaupt statische Libs (denn nur diese können mittels /WHOLEARCHIVE überhaupt direkt eingebunden werden)?
Kasko Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 97

Win 10
C# C++ (VS 2017/19), (Java, PHP)
BeitragVerfasst: Do 09.04.20 12: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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4249
Erhaltene Danke: 902

Win10
C#, C++ (VS 2015/17)
BeitragVerfasst: Do 09.04.20 14:07 
Aber dann paßt doch das Makro nicht:
ausblenden C++-Quelltext
1:
2:
3:
#else
    /* We are building or calling GLFW as a static library */
    #define GLFWAPI
Kasko Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 97

Win 10
C# C++ (VS 2017/19), (Java, PHP)
BeitragVerfasst: Do 09.04.20 14: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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4249
Erhaltene Danke: 902

Win10
C#, C++ (VS 2015/17)
BeitragVerfasst: Do 09.04.20 17: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?

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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 97

Win 10
C# C++ (VS 2017/19), (Java, PHP)
BeitragVerfasst: Do 09.04.20 18: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.
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4249
Erhaltene Danke: 902

Win10
C#, C++ (VS 2015/17)
BeitragVerfasst: Fr 10.04.20 09:10 
Probiere mal mit der Konsolenapplikation "lib.exe": How to merge two windows vc static library into one, s.a. Lib-Referenz ff.

Wenn du Module-Definition (.Def) Files hast, dann kannst du diese auch mit diesen Befehl und "/def:<foo>.def" als Parameter verwenden: lib.exe, Visual Studio, generating .lib files from dll's and def files (aber dies ist nur ein Ersatz für __declspec(dllexport) und daher nur für dynamische Libs (d.h. DLLs) sinnvoll).
Kasko Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 97

Win 10
C# C++ (VS 2017/19), (Java, PHP)
BeitragVerfasst: Fr 10.04.20 14: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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4249
Erhaltene Danke: 902

Win10
C#, C++ (VS 2015/17)
BeitragVerfasst: Fr 10.04.20 14: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, ...)).

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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 97

Win 10
C# C++ (VS 2017/19), (Java, PHP)
BeitragVerfasst: So 12.04.20 01: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.
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4249
Erhaltene Danke: 902

Win10
C#, C++ (VS 2015/17)
BeitragVerfasst: So 12.04.20 09:18 
1. Eine LIB-Datei kann immer nur genau auf eine DLL verweisen - ist technisch bedingt.
2. Auch SFML verwendet nicht nur eine LIB bzw. DLL (z.B. "sfml-main", "sfml-graphics", "sfml-system", "sfml-windows"), diese gibt es aber in vier verschiedenen Versionen:
ausblenden Quelltext
1:
2:
3:
4:
- dynamisch (DLL) Release: "" (ohne Suffix)
- dynamisch (DLL) Debug:   "-d"
- statisch Release:        "-s"
- statisch Debug:          "-s-d"

Desweiteren gibt es weitere 3rd-Party Libs im "extlib"-Unterordner (die man dann auch bei Verwendung explizit einbinden muß).

s.a. SFML-Frequently Asked Questions (FAQ): Building and Using SFML
In How do I link SFML statically? stehen auch explizit die (externen) Abhängigkeiten (u.a. bei "sfml-window": "opengl32").

Du meinst wahrscheinlich die ältere Umsetzung? Dies wurde aber extra geändert, da dies einige Probleme verursacht (s.a. den Link zu der Diskussion dort: General discussions - Linking dependencies - insb. den Beitrag von "binary1248").

Da du explizit eine DLL (anstatt einer statischen Lib) erstellen lassen möchtest, kann ich dir daher nur dazu abraten (also das Mixen beider Lib-Typen).