Visual C++ und DLLs; Linkerfehler: 2020
-
Hallo,
ich habe mir vor Kurzem die Express Edition von Visual C++ 2005 heruntergeladen. Nun versuche ich alte Projekte in dieser neuen Umgebung zu kompilieren. Dabei handelt es sich um eine .exe, die ich fast völlig neu erstellen will und einige DLLs.
Beim Erstellen der Projektmappe erhalte ich dann den Linkerfehler 2020. Das passiert aber nur, wenn ich die DLLs wie ein "normaler" Programmierer in .h-Dateien für die Klassendeklarationen /-definitionen und in .cpp-Dateien für die eigentliche Implementierung der Klassenfunktionen unterteile. Erstelle ich die DLL(s) nur in den Headern, bekomme ich keine derartigen Fehlermeldungen. Leider kann (und will) ich nicht alles nur in den Headern implementieren. Das geht auch nicht, weil manche meiner Klassen sich gegenseitig "brauchen" und die Header sich dann gegenseitig includieren würden.Gibt es da irgendwelche Tricks (Schlüsselwörter oder Optionen), um diese Fehlermeldung zu vermeiden?
In der tollen Onlinehilfe von Microsoft sind meistens nur Code-Beispiele, wie man solche Fehlermeldungen auslöst - sehr hilfreich!!!!!
Gruß RastowMax
-
Forwarddeklaration ist ein Stichwort, ansonsten liefer doch mal mehr informationen - z.B. die Fehlermeldung.
Ist die DLL eine .Net Komponente ?
-
Hallo Knuddlbaer,
das will ich gerne tun. Hier als erstes einmal einige Fehlermeldungen:
------ Erstellen gestartet: Projekt: CncWrk30, Konfiguration: Debug Win32 ------
Kompilieren...
CncWrk30.cpp
CncChild.cpp
Code wird generiert...
Verknüpfen...
CncWrk30.obj : error LNK2020: Nicht aufgelöstes Token (060000E4) CNC_Edit.JournalEntry::.ctor.
CncChild.obj : error LNK2020: Nicht aufgelöstes Token (060000E4) CNC_Edit.JournalEntry::.ctor.
CncWrk30.obj : error LNK2020: Nicht aufgelöstes Token (060000EB) CNC_Edit.JEInsert::Undo.
CncChild.obj : error LNK2020: Nicht aufgelöstes Token (060000EB) CNC_Edit.JEInsert::Undo.
CncWrk30.obj : error LNK2020: Nicht aufgelöstes Token (060000ED) CNC_Edit.JEDelete::.ctor.
CncChild.obj : error LNK2020: Nicht aufgelöstes Token (060000ED) CNC_Edit.JEDelete::.ctor.
CncWrk30.obj : error LNK2020: Nicht aufgelöstes Token (060000EE) CNC_Edit.JEDelete::~JEDelete.
CncChild.obj : error LNK2020: Nicht aufgelöstes Token (060000EE) CNC_Edit.JEDelete::~JEDelete.
CncWrk30.obj : error LNK2020: Nicht aufgelöstes Token (060000EF) CNC_Edit.JEDelete::Undo.
CncChild.obj : error LNK2020: Nicht aufgelöstes Token (060000EF) CNC_Edit.JEDelete::Undo.
CncWrk30.obj : error LNK2020: Nicht aufgelöstes Token (060000F2) CNC_Edit.CncEditor::.ctor.
CncChild.obj : error LNK2020: Nicht aufgelöstes Token (060000F2) CNC_Edit.CncEditor::.ctor.
CncWrk30.obj : error LNK2020: Nicht aufgelöstes Token (060000F3) CNC_Edit.CncEditor::~CncEditor.
CncChild.obj : error LNK2020: Nicht aufgelöstes Token (060000F3) CNC_Edit.CncEditor::~CncEditor.
CncWrk30.obj : error LNK2020: Nicht aufgelöstes Token (060000F4) CNC_Edit.CncEditor::ClearUndoStack.
CncChild.obj : error LNK2020: Nicht aufgelöstes Token (060000F4) CNC_Edit.CncEditor::ClearUndoStack.
CncWrk30.obj : error LNK2020: Nicht aufgelöstes Token (060000F5) CNC_Edit.CncEditor::SetEditMode.
CncChild.obj : error LNK2020: Nicht aufgelöstes Token (060000F5) CNC_Edit.CncEditor::SetEditMode.Betroffen sind all die Klassen, die ich in .h und .cpp aufgeteilt habe. Soll heißen, für jede Funktion, die in einer Quelldatei (.cpp) definiert wurde, gibt es zwei gleiche Fehlermeldungen.
Die DLL ist zwar keine .NET-Komponente im engeren Sinn, ich habe sie aber mit Datei/Neu/Projekt/CLR/Klassenbibliothek erstellt. Sie enthält auch Deklarationen mit neuen Zeigern und Schlüsselworten (z.B. gcnew statt new) usw.
Ich glaube, Microsoft nennt das verwalteten Code oder so ähnlich. Der soll wohl sicher sein. Sicher ist nur, dass sich vergewaltigter Code nicht ohne weiteres compilieren lässt.Vorwärtsdeklarationen benutze ich ja schon. Das aber nur um der benutzenden Klasse die Existenz der benutzten Klasse zu erklären. Diese benutzende Klasse wird aber auch wieder von der benutzten Klasse benutzt. (Verwirrung komplett!) Also, ich habe eine Klasse Editor für eine CNC-Steuerung. Die CNC-Steuerungsklasse ist die Elternklasse, damit die Editerfunktionen immer auf die richtige Steuerung angewandt werden. Die CNC-Steuerungsklasse besitzt aber gleichzeitig eine Instanz der Editorklasse. Gleiches trifft für die Undo-Klassen und den Editor zu. Genau wegen dieser Vorwärtsdeklarationen kann ich den Quellcode nicht in den Headern definieren. Bisher ist es so, dass ich in der Headerdatei des Editors eine Vorwärtsdeklaration der Klasse CncControl habe und die Headerdatei der CNC-Steuerung nur in die .cpp-Datei der Editorklasse einbinde. Andernfalls wird die Compilierung schon mit Fehlermeldungen beendet.
Gruß RastowMax
-
Hm,
das scheint mir erstmal weniger ein dll problem zu sein, denn die Sachen die fehlen sollten in der .cpp sein.
Ist das ein template (nein nehm ich an) , namensraum richtig angegeben ?
Poste mal den CTor und die Deklaration von CncChild , wenn diese in Namensräumen sind, diesen mit schicken.
Den Quellcode aufzuteilen in .h und .cpp ist eigentlich normal
Also, poste mal die deklaration und definition von z.B. CncChild und prüfe ob das alles im gleichen Namensraum liegt.
-
Auweia,
ich fürchte, dass ich gar keine DLL importiere, sondern eigentlich nur die in den Headern deklarierten und auch dort definierten Fkten der Klassen in das .exe-Projekt einbinde. Das habe ich festgestellt, als ich mir die Größe meiner .exe ansah. Eine meiner DLLs habe ich nämlich komplett in den Headern deklariert und implementiert. Dann habe ich die zu exportierenden Klassen ( also die Header) in die Hauptheaderdatei der DLL eingebunden. Diesen Hauptheader habe ich natürlich im Vertrauen auf die Projektabhängigkeiten einfach ins .exe-Projekt eingebunden - und es funktionierte. Nachdem ich dann feststellte, dass die .exe doch zeimlich groß geworden ist, habe ich einfach die fertige DLL gelöscht... Die .exe ließ sich ohne Probleme starten und ausführen. Damit ist mir dann auch klar, warum die in den .cpp-Dateien definierten Fkten nicht gefunden werden.
Weiterhin habe ich festgetellt, dass gar keine Importbibliothek erstellt wird und auch keine .def-Datei. Es ist also noch viel schlimmer, als ursprünglich angenommen.
Ich sollte vielleicht noch erklären, dass ich bisher mit dem BCB in der Prof-Version 5 und gelegentlich mit dem VC 6 (Enterprice Edition) gearbeitet habe. Dort gab es derartige Probleme nicht.
Ich hatte zuerst versucht, die DLLs unverändert zu übernehmen - denn die funktionieren ja. Dann bekam ich aber vom Compiler auf die Finger, weil der der "Meinung" ist, das man nicht einfach managed und unmaged code vemischen darf. Deshalb also der Versuch, die DLLs neu zu implementieren, was ja nun wohl gründlich daneben ging.Gibt es denn einen Weg, alte DLLs in Windows-Forms-Anwendungen nutzbar/verfügbar zu machen?
Gruß RastowMax
-
Was mir jetzt unklar wird:
Verwendest Du eine reine DLL oder verwendest Du eine lib die hinzu gelinkt wird ?
-
Hallo,
es gibt ja nun zwei Methoden, eine DLL in eine .exe einzubinden. Eine davon wäre die "dynamische". Davon mache ich auch Gebrauch. Hier ein Beispiel:
typedef void TShowSplash(const char*, const char*); // Fktszeiger fuer DLL-Fkt char app_name[300]; TShowSplash *splash; ::GetCurrentDirectory(300, LPWSTR(app_name)); // aktuelles Verzeichnis holen... strcat(app_name, "\\Splash\\CncWrk03.spl"); // und Dateinamen anhaengen HINSTANCE hdll = ::LoadLibrary(LPCWSTR("SplshScr.dll")); // DLL laden if(hdll != NULL){ // DLL geladen... splash = (TShowSplash*)::GetProcAddress(hdll, "_ShowSplashScreen"); // Fktszeiger setzen if(splash != NULL) // Fkt gefunden... splash("CNC-Workbench 3.0", app_name); // SplashScreen anzeigen und... ::FreeLibrary(hdll); // DLL freigeben }
Dieser Code wird vom Compiler anstandslos übersetzt. Allerdings erkennt die .exe dann nicht einmal HINSTANCE hdll. Beim Debuggen steht dort, beim Versuch einer Auswertung, immer "undefiniertes Symbol".
Die zweite Methode wäre das "statische" Einbinden, also über eine Importbibliothek. Diese will der Linker aber absolut nicht herausrücken, d.h. es wird keine Importbibliothek erstellt. Wie schon beschrieben, wollte ich die "statisch" eingebundenen DLLs mit dieser IDE neu erzeugen, damit ich "saubere" Bedingungen für das Einbinden erhalte. Ich war der Annahme, dass durch die Projektabhängigkeiten alles automatisch geht. Das war wohl ein Trugschluss.
Gruß RastowMax
-
Wo ist CncWrk30 und CncChild deklariert/definiert ?
-
Hallo,
die im Beitrag von gestern beschriebe SplashScreen-DLL rufe ich in der Fkt. main vor der eigentlichen Anwendung auf. CncWork30 heißen sowohl die .exe als auch der Namensraum, in dem auch CncChild (eine MDI-Child-Klasse) deklariert und definiert wird. Hier ist das erste "Stück" der Headerdatei von CncChild:
#pragma once #pragma comment(lib ,"Test_Dll.lib") #include "D:\\A_VC_NET_Projects\\Projects\\CncWrk30\\DLLs\\GLRender\\GLRender.h" #include "D:\\A_VC_NET_Projects\\Projects\\CncWrk30\\DLLs\\Test_Dll\\Test_Dll.h" using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; using namespace GLRender; //using namespace CNC_Edit; namespace CncWrk30 { /// <summary> /// Zusammenfassung für CncChild /// /// Warnung: Wenn Sie den Namen dieser Klasse ändern, müssen Sie auch /// die Ressourcendateiname-Eigenschaft für das Tool zur Kompilierung verwalteter Ressourcen ändern, /// das allen RESX-Dateien zugewiesen ist, von denen diese Klasse abhängt. /// Anderenfalls können die Designer nicht korrekt mit den lokalisierten Ressourcen /// arbeiten, die diesem Formular zugewiesen sind. /// </summary> public ref class CncChild : public System::Windows::Forms::Form { public: CncChild(void) { first = true; InitializeComponent(); // //TODO: Konstruktorcode hier hinzufügen. // GLContext = gcnew GL3DCNCContext((HWND)this->splitContainer1->Panel2->Handle.ToPointer(), (HFONT)this->splitContainer1->Panel2->Font->ToHfont().ToPointer(), this->splitContainer1->Panel2->Width, this->splitContainer1->Panel2->Height); first = false; GLContext->RenderScene(); // Szene darstellen fnTest_Dll(); } protected: /// <summary> /// Verwendete Ressourcen bereinigen. /// </summary> ~CncChild() { if (components) { delete components; } } private: GL3DCNCContext ^GLContext; bool first; private: System::Windows::Forms::SplitContainer^ splitContainer1; private: System::Windows::Forms::Button^ Cancel_Btn; . . .
Dann habe ich mit dem VC 2005 gestern noch eine Test-DLL erstellt und versucht diese einzubinden (Zeile 5 im obigen Listing). Aus der DLL werden je eine Variable, eine Fkt und eine Klasse exportiert. Das habe ich so bei der Erstellung der DLL durch den Anwendungsassi generieren lassen (Falls das jemand nachvollziehen will: mit Datei/Neu.../Projekt/Win32/Konsolenanwendung ->Namen eingeben; dann auf "OK"; im nächsten Dialogfeld auf "Weiter"; bei "Anwendungstyp" "DLL" auswählen und die Option "Symbole exportieren" wählen und auf "Fertig stellen" klicken). Ich versuche nun die exportierte Fkt fnTest_DLL() in Zeile 45 aufzurufen. Beim Übersetzen und Linken der DLL gibt es keine Beanstandungen - noch nicht! Danach wird aber die .exe erzeugt. Das Ganze wird von Compiler und Linker wie folgt quittiert:
------ Neues Erstellen gestartet: Projekt: Test_Dll, Konfiguration: Debug Win32 ------
Die Zwischen- und Ausgabedateien für das Projekt "Test_Dll" mit der Konfiguration "Debug|Win32" werden gelöscht.
Kompilieren...
stdafx.cpp
Kompilieren...
Test_Dll.cpp
Manifest in Ressourcen wird kompiliert...
Verknüpfen...
Bibliothek "d:\A_VC_NET_Projects\Projects\CncWrk30\CncWrk30\Debug\Test_Dll.lib" und Objekt "d:\A_VC_NET_Projects\Projects\CncWrk30\CncWrk30\Debug\Test_Dll.exp" werden erstellt.
Das Manifest wird eingebettet...
Das Buildprotokoll wurde unter "file://d:\A_VC_NET_Projects\Projects\CncWrk30\DLLs\Test_Dll\Debug\BuildLog.htm" gespeichert.
Test_Dll - 0 Fehler, 0 Warnung(en)
------ Neues Erstellen gestartet: Projekt: CncWrk30, Konfiguration: Debug Win32 ------
Die Zwischen- und Ausgabedateien für das Projekt "CncWrk30" mit der Konfiguration "Debug|Win32" werden gelöscht.
Kompilieren...
stdafx.cpp
Kompilieren...
CncWrk30.cpp
D:\\A_VC_NET_Projects\\Projects\\CncWrk30\\DLLs\\Test_Dll\\Test_Dll.h(17) : warning C4272: "CTest_Dll::CTest_Dll": Ist als __declspec(dllimport) markiert. Beim Importieren einer Funktion muss eine systemeigene Aufrufkonvention angegeben werden.
D:\\A_VC_NET_Projects\\Projects\\CncWrk30\\DLLs\\Test_Dll\\Test_Dll.h(19) : warning C4272: "CTest_Dll::~CTest_Dll": Ist als __declspec(dllimport) markiert. Beim Importieren einer Funktion muss eine systemeigene Aufrufkonvention angegeben werden.
D:\\A_VC_NET_Projects\\Projects\\CncWrk30\\DLLs\\Test_Dll\\Test_Dll.h(19) : warning C4272: "CTest_Dll::CTest_Dll": Ist als __declspec(dllimport) markiert. Beim Importieren einer Funktion muss eine systemeigene Aufrufkonvention angegeben werden.
D:\\A_VC_NET_Projects\\Projects\\CncWrk30\\DLLs\\Test_Dll\\Test_Dll.h(19) : warning C4272: "CTest_Dll::operator =": Ist als __declspec(dllimport) markiert. Beim Importieren einer Funktion muss eine systemeigene Aufrufkonvention angegeben werden.
D:\\A_VC_NET_Projects\\Projects\\CncWrk30\\DLLs\\Test_Dll\\Test_Dll.h(21) : warning C4394: "nTest_Dll": Ein anwendungsdomänenspezifisches Symbol sollte nicht mit __declspec(dllimport) markiert werden.
D:\\A_VC_NET_Projects\\Projects\\CncWrk30\\DLLs\\Test_Dll\\Test_Dll.h(23) : warning C4272: "fnTest_Dll": Ist als __declspec(dllimport) markiert. Beim Importieren einer Funktion muss eine systemeigene Aufrufkonvention angegeben werden.
CncChild.cpp
D:\\A_VC_NET_Projects\\Projects\\CncWrk30\\DLLs\\Test_Dll\\Test_Dll.h(17) : warning C4272: "CTest_Dll::CTest_Dll": Ist als __declspec(dllimport) markiert. Beim Importieren einer Funktion muss eine systemeigene Aufrufkonvention angegeben werden.
D:\\A_VC_NET_Projects\\Projects\\CncWrk30\\DLLs\\Test_Dll\\Test_Dll.h(19) : warning C4272: "CTest_Dll::~CTest_Dll": Ist als __declspec(dllimport) markiert. Beim Importieren einer Funktion muss eine systemeigene Aufrufkonvention angegeben werden.
D:\\A_VC_NET_Projects\\Projects\\CncWrk30\\DLLs\\Test_Dll\\Test_Dll.h(19) : warning C4272: "CTest_Dll::CTest_Dll": Ist als __declspec(dllimport) markiert. Beim Importieren einer Funktion muss eine systemeigene Aufrufkonvention angegeben werden.
D:\\A_VC_NET_Projects\\Projects\\CncWrk30\\DLLs\\Test_Dll\\Test_Dll.h(19) : warning C4272: "CTest_Dll::operator =": Ist als __declspec(dllimport) markiert. Beim Importieren einer Funktion muss eine systemeigene Aufrufkonvention angegeben werden.
D:\\A_VC_NET_Projects\\Projects\\CncWrk30\\DLLs\\Test_Dll\\Test_Dll.h(21) : warning C4394: "nTest_Dll": Ein anwendungsdomänenspezifisches Symbol sollte nicht mit __declspec(dllimport) markiert werden.
D:\\A_VC_NET_Projects\\Projects\\CncWrk30\\DLLs\\Test_Dll\\Test_Dll.h(23) : warning C4272: "fnTest_Dll": Ist als __declspec(dllimport) markiert. Beim Importieren einer Funktion muss eine systemeigene Aufrufkonvention angegeben werden.
AssemblyInfo.cpp
AboutBox.cpp
Code wird generiert...
Verwaltete Ressourcen werden kompiliert...
Liest in den 29-Ressourcen von "d:\A_VC_NET_Projects\Projects\CncWrk30\CncWrk30\Form1.resx".
Ressourcendatei wird geschrieben... Fertig.
Liest in den 11-Ressourcen von "d:\A_VC_NET_Projects\Projects\CncWrk30\CncWrk30\CncChild.resx".
Ressourcendatei wird geschrieben... Fertig.
Liest in den 3-Ressourcen von "d:\A_VC_NET_Projects\Projects\CncWrk30\CncWrk30\AboutBox.resx".
Ressourcendatei wird geschrieben... Fertig.
Ressourcen werden kompiliert...
Verknüpfen...
CncWrk30.obj : error LNK2028: Nicht aufgelöstes Token (0A00007E) ""int __clrcall fnTest_Dll(void)" (?fnTest_Dll@@$$FYMHXZ)", auf das in Funktion ""public: __clrcall CncWrk30::CncChild::CncChild(void)" (??0CncChild@CncWrk30@@$$FQAAM@XZ)" verwiesen wird. CncChild.obj : error LNK2028: Nicht aufgelöstes Token (0A000052) ""int \_\_clrcall fnTest\_Dll(void)" (?fnTest\_Dll@@AAM@XZ)" verwiesen wird.
CncChild.obj : error LNK2019: Verweis auf nicht aufgelöstes externes Symbol ""int __clrcall fnTest_Dll(void)" (?fnTest_Dll@@$$FYMHXZ)" in Funktion ""public: __clrcall CncWrk30::CncChild::CncChild(void)" (??0CncChild@CncWrk30@@$$FQAAM@XZ)". CncWrk30.obj : error LNK2001: Nicht aufgelöstes externes Symbol ""int \_\_clrcall fnTest\_Dll(void)" (?fnTest_Dll@@$FYMHXZ)".
D:\A_VC_NET_Projects\Projects\CncWrk30\CncWrk30\Debug\CncWrk30.exe : fatal error LNK1120: 3 nicht aufgelöste externe Verweise.
Das Buildprotokoll wurde unter "file://d:\A_VC_NET_Projects\Projects\CncWrk30\CncWrk30\Debug\BuildLog.htm" gespeichert.
CncWrk30 - 5 Fehler, 12 Warnung(en)Wenn ich die DLL so erstelle, wird auch eine Importbibliothek erzeugt. Diese versuche ich in das .exe-Projekt mit einzubinden (Zeile 2).
Das Einbinden der bereits "fertigen DLL" (siehe meinen Beitrag hier vom 07.11. morgens beginnend mit "Auweia,") wollte ich über deren Hauptheader realisieren (Zeile 4). Das ist die DLL, die ich getrost löschen kann, da die gesamte Implementierung in den Headern erfolgte und folglich auch OHNE DLL von der .exe genutzt werden kann. Einen meiner Renderingkontexte aus dieser "DLL" habe ich als privaten Zeiger in CncChild angelegt (Zeile 61). Diesen Zeiger initialisiere ich im Konstruktor von CncChild (Zeile 38) und zeichne auch gleich die Szene (Zeile 43). Aber das ist ja ohnehin der falsche Weg, mit DLLs zu arbeiten.
Gruß RastowMax