Klasse aus DLL importieren...
-
Hallo zusammen,
ich stehe mal wieder vor einem Problem und komme nicht weiter... Ich möchte gerne eine zunächst einmal eine Klasse aus einem bestehenden Projekt in eine DLL packen und diese dann in der Anwendungen zu verwenden
Aber von vorne (ich verwende MS Visual Studio 2005 C++)... zunächst möchte ich erstmal eine Klasse aus einer DLL importieren:
1.) Öffne ein neues Projekt und lege eine MFC-DLL an (Name DLLTest)
2.) Ich wähle eine "MFC-Erweiterungs-DLL", da meine Klasse von CButton abgeleitet ist und ich finde das passt irgendwie
3.) Ich verwende weder Automasierung (das geht ja auch nicht) noch verwende ich Windows Sockets
4.) Ich füge über den Klassenassistenten eine neue Klasse hinzu (TestButton) und schreibe __declspec(dllexport), so dass class __declspec(dllexport) TestButton ... dort steht
5.) Ich füge in den Konstruktur zu testzwecken ein bischen Code hinzu (eine Messagebox die mir Hallo ausgibt)
6.) Ich kompiliere die DLL und kopiere nur die DLL (DLLTest.dll) zu dem Projekt, welches diese verwenden soll
7.) Nun habe ich in meinem Programm folgendes geschrieben:m_hViewDll = NULL; m_hViewDll = ::AfxLoadLibrary("DLLTest.dll"); if (!m_hViewDll) AfxMessageBox("Error: Cannot find component \"DLLTest.Dll\"");und beim Beenden
if (m_hViewDll) { AfxFreeLibrary(m_hViewDll); m_hViewDll = NULL; }Und da ist schon der erste Fehler ich bekomme ein
Detected memory leaks!
Dumping objects ->
f:\rtm\vctools\vc7libs\ship\atlmfc\src\mfc\strcore.cpp(141) : {132} normal block at 0x003582A8, 19 bytes long.
Data: < >x > EC 97 3E 78 02 00 00 00 02 00 00 00 01 00 00 00
f:\rtm\vctools\vc7libs\ship\atlmfc\src\mfc\strcore.cpp(141) : {131} normal block at 0x00358258, 18 bytes long.
Data: < >x > EC 97 3E 78 01 00 00 00 01 00 00 00 01 00 00 00
{59} client block at 0x00352A90, subtype c0, 64 bytes long.
a CDynLinkLibrary object at $00352A90, 64 bytes long
a CDynLinkLibrary object at $00352A90, 64 bytes long
Object dump complete.Keine Ahnung wieso? :(...
Naja aber weiter :)...
8.) Ich versuche ein Objekt zu erzeugen... aber wie... ich stehe vor einem Problem... Bitte helft mir

Danke
Jogilein
-
1. Ich hoffe Du hast die DLL-Version der MFC verwendet!
2. Du musst nun die Klasse, die Du erzeugen möchtest von CObject ableiten und mit IMPLEMENT_/DECLARE_DYNCREATE ausstatten. Dann kannst Du die entsprechene Klasse erzeugen.
3. Ich würde einen anderen Weg gehen. Ich würde nur eine rohe Interface-Klasse erzeugen. Dieses Interface wird durch eine Factory Funktion returniert und durch eine Delete Funktion wieder entsorgt. Gemeinsamer Nenner ist damit nur das Interface und damit einfach zu pflegen.
Ähnlich arbeitet COM (was Du natürlich auch verwenden kannst).
-
Ich hoffe Du hast die DLL-Version der MFC verwendet!
Ich habs so gemacht wie geschrieben :D... was ist denn die DLL-Version der MFC?
Ich würde einen anderen Weg gehen. Ich würde nur eine rohe Interface-Klasse erzeugen. Dieses Interface wird durch eine Factory Funktion returniert und durch eine Delete Funktion wieder entsorgt. Gemeinsamer Nenner ist damit nur das Interface und damit einfach zu pflegen
Ok die rohe Interface Klasse habe ich schon lange :)... Factory-Method auch gut... aber ich möchte, dass mein Programm nicht immer neu kompiliert werden muss, wenn jemand anderes eine konforme DLL geschrieben hat (konform = der Schnittstelle entsprechenden), sondern diese nur in ein Verzeichnis kopieren muss und dann über einen INI-File Zugriff hat... ich hoffe man versteht mein vorhaben

Ähnlich arbeitet COM (was Du natürlich auch verwenden kannst).
Ich würde mich ja gerne an COM ranwagen... jedoch habe ich da irgendwie Angst das es zu viel neues ist :D... bzw. weiß ich wirklich nicht wo ich bei COM anfangen muss...
-
In den Projekteinstellungen solltest Du Shared-DLL für die MFC verwenden!
Wieso wird Dein projekt neu kompiliert wenn sich eine externe DLL ändern und deren Interface (sprich Header) hat sich nicht geändert?
Bei mir passiert da gar nichts. Oder ziehst Du auch immer die geänderte Lib mit in das Projekt?Wenn du 3 Funktionen in der Dll hast: GetVersion, CreateObject, DestroyObject und diese exportierst, dann benötigst Du nicht mal eine LIB.
Die DLL wird geladen. Die 3 Funktionen bestimmt und entsprechend aufgerufen. Die Interface Zeiger werden returniert. Basta.Wenn sich was ändert dann erfolgt nur ein neues Compilieren wen die Interface Klassen sich ändern.
-
HI Martin,
erstmal danke für die Antwort... ich glaube da hat ich dich missverstanden... ich dachte du möchtest gar keien DLL mehr verwenden sondern meintest ich solle direkt alle Klassen in das eine Projekt laden

Nun gut... also das was du da beschreibst ist eigentlich das was ich letztendlich vor habe... aber irgendwie... naja es klappt nicht so ganz :)...
Irgendwie kann ich kein Objekt erzeugen... bzw. hab ich auch immer noch diese DumpObjekt Fehlermeldung :(...
Gibt es ein Beispiel für solch ein Projekt irgendwo? Ein kleines übersichtliches?! :)... ich habe dieses Beispiel in der MSDN "dllhusk" gefunden... aber ist das, dass was ich brauche?
Danke für Antworten!
-
Was meinst Du, du kannst kein Objekt erzeugen. Wie machst Du es denn?
-
Naja bis jetzt so wie ich es am Anfang beschrieben habe... ich lade die DLL über
::AfxLoadLibrary("DLLTest.dll");Danach habe ich nun also die DLL geladen... aber ich kann jetzt kein Objekt der Klasse TestButton erzeugen... weil der Compiler diese Klasse nicht kennt (sie ist ja in der DLL definiert).
Also bin ich jetzt deinen Weg gegangen und habe versucht das Interface in der DLL zu verwenden und eine Create und etc. Funktion zu schreiben in der DLL... Die würde ich dann nacher aufrufen und dann eben die über die Schnittstelle verwenden (die kennt ja mein Hauptprogramm)... wie komme ich denn nun an die Create etc dran mit
GetProcAddress(m_hViewDll, "Create");Ich werde über meine Misserfolge weiter berichten und versuche es erstmal so... schrei laut falls das alles blödsinn ist

-
Hast Du sicher die Klasse auch per DYNCREATE deklariert und verwendest Du CRuntimeClass::CreateObject um das Objekt zu erzeugen?
Wenn Du eine Factory Funktion verwenden willst besorgst Du dir die entsprechenden Funktionszeiger per GetProcAddress. Vergiss nicht eine Versionierung vorzusehen!
-
Hallo Martin und auch hallo der Rest...
ich verzweifle gerade... also ich hab jetzt folgendes in eine DLL gepackt:
// FISButton.cpp : Definiert die Initialisierungsroutinen für die DLL. #include "../#header/GUIElement.h" #include "stdafx.h" #include <afxdllx.h> static AFX_EXTENSION_MODULE GUIElementDLL = { NULL, NULL }; extern "C" int APIENTRY DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) { // Entfernen Sie dies, wenn Sie lpReserved verwenden. UNREFERENCED_PARAMETER(lpReserved); if (dwReason == DLL_PROCESS_ATTACH) { TRACE0("DLL wird initialisiert!\n"); // Einmalige Initialisierung der Erweiterungs-DLL if (!AfxInitExtensionModule(GUIElementDLL, hInstance)) return 0; new CDynLinkLibrary(GUIElementDLL); } else if (dwReason == DLL_PROCESS_DETACH) { TRACE0("DLL wird abgebrochen!\n"); // Bibliothek vor dem Aufruf der Destruktoren schließen. AfxTermExtensionModule(GUIElementDLL); } return 1; // OK } AFX_EXT_CLASS IGUIElement* Create() { TRACE("HALLO"); return NULL; }Der Header der gemeinsamen Schnittstelle GUIElement.h sieht so aus:
#include "stdafx.h" #ifndef GUIELEMENT_H #define GUIELEMENT_H // Klassen die in diesem Header deklariert werden class IGUIElement; // Klassendeklaration class IGUIElement abstract { public: IGUIElement() {}; virtual ~IGUIElement() {}; virtual BOOL Create(CWnd* pParent) = 0; virtual BOOL ReceiveInformation() = 0; virtual CString GetID() = 0; }; AFX_EXT_CLASS IGUIElement* Create(); #endifIm Hauptprogramm habe ich jetzt folgendes
HINSTANCE m_hViewDll; typedef IGUIElement* (CALLBACK* LPFNDLLFUNC1)(); ... BOOL CTHEApp::InitInstance() { m_hViewDll = NULL; m_hViewDll = ::AfxLoadLibrary("DLL/GUIElement.dll"); if (!m_hViewDll) AfxMessageBox("Error: Cannot find component \"GUIElement.Dll\""); LPFNDLLFUNC1 lpfnDllFunc1 = (LPFNDLLFUNC1) GetProcAddress(m_hViewDll, "Create"); if (!lpfnDllFunc1) { // handle the error AfxFreeLibrary(m_hViewDll); m_hViewDll = NULL; AfxMessageBox("FEHLER BEIM LADEN VON CREATE"); } else { // call the function IGUIElement* pTest = lpfnDllFunc1(); }Es kommt immer die Messagebox "FEHLER BEIM LADEN VON CREATE"...
Wieso? Ich hab alles hier gepostet glaube ich... ich hab nicht mehr... irgendwo muss da doch ein Fehler sein... ich hab außerdem noch immer dieses DumpObject...
Danke für jegliche Hilfe ...
ein verzweifelter Jogilein
-
Muss ich unter Umständen noch irgendwas in diese DEF-Datei eintragen?
Zur Zeit sieht der EXPORT-Bereich so aus:
EXPORTS ; Explizite Exporte können hier eingefügt werden.Soweit ich jedoch die MSDN verstehe... muss das nicht sein udn es reicht die Angabe von __declspec(dllexport)...
-
Die Anwort gibt Dir Depends.exe, da kanst Du sehen was wirklich exportiert wird.
Ohne DEF-Datei heißt der Export _Create und ist zusätzlich gemangelt.
Funktion Create als extern "C" definieren und in die Def-Datei eintragen.
-
Danke für die Antwort Martin werde ich ausprobieren... ist denn sonst so alles soweit richtig?!
Wieso bekomme ich die DumpObject-Meldungen am Ende... ich meine die DLL ist mit dem Assistenten generiert und trotzdem bekomme ich die

-
Inwieweit "zusätzlich gemangelt"?
-
Name mangeling ist die Art und Weise wie der C++ Compiler Symbole verschlüsselt, damit Sie der Linker versteht.
Hast Du ein Mini-Sample? Schick es mir per Email.
-
Hi Martin,
Hast Du ein Mini-Sample
Ich kann dir das ganze Projekt zippen bzw. die Mappe und dir mailen... ich hab da nicht wirklich was zu verstecken :)... aber es könnte ein bischen größer werden

Danke für das Angebot
JogileinPS: Ich warte noch auf deine Antwort, ob es ok ist...
-
HI Martin,
ich sehe gerade, dass das Paket nicht so groß ist (ohne Intellisense etc... nur 230KB)... ich schick es einfach mal

Nochmals danke für jede Anmerkung die du hast, sei es wegen schlechten Programmierstyles oder unmöglichen Konstruktionen

-
Ich bin eben erst dazu gekommen Dein Projekt anzusehen.
Erster Eindruck: Eine einzige Katastrophe.
Selbst wenn man Testcode baut, sollte er leserlich sein und die Indents stimmen.
Würdest Du in meiner Firma arbeiten und solchen Code (selbst als Testproject) schreiben, würde ich Dir auf die Finger klopfen...
Ich befürchte ja sogar das dieser Code direkt Produktiv-Code werden soll...- Das Memory Leak ensteht dadurch, dass Dein DLL und Deine EXE nicht das selbe MFC Projekt verwenden. EXE ist Multibyte, DLL ist UNICODE! Die beiden Projekte verwenden komplett unterschiedliche DLL Kerne.
- Du hast nicht auf mich gehört und die Create Funktion als extern "C" definiert.
- Du hättest Dir auch das Entwicklen leicher machen können, indem Du bei beiden Projekten ein identisches Ausgabevezeichnis angibst.
-
Erster Eindruck: Eine einzige Katastrophe.
Autsch...
Selbst wenn man Testcode baut, sollte er leserlich sein und die Indents stimmen.
Nur zu meiner Verteidigung :D... das mach ich nicht damit ich den Testcode schneller finden :)... schlechte Ausrede
...Ich befürchte ja sogar das dieser Code direkt Produktiv-Code werden soll...
Nein... es ist erstmal ein Test :)... aber ich würde schon später was feines daraus bauen :D...
Nun aber erstmal vielen Dank dafür, dass du dich durch dieses Chaos gekämpft hast :)...
Du hast nicht auf mich gehört und die Create Funktion als extern "C" definiert.
Mist... das hab ich mal wieder vergessen :(... aber die Create-Funktion kann ich nun aufrufen... Danke!
Das Memory Leak ensteht dadurch, dass Dein DLL und Deine EXE nicht das selbe MFC Projekt verwenden. EXE ist Multibyte, DLL ist UNICODE! Die beiden Projekte verwenden komplett unterschiedliche DLL Kerne.
Ich hab jetzt beide auf Multibyte umgestellt... aber der Leak ist noch immer da

Du hättest Dir auch das Entwicklen leicher machen können, indem Du bei beiden Projekten ein identisches Ausgabevezeichnis angibst.
Du meinst damit die DLL direkt im Verzeichnis der EXE liegt... Stimmt :D...
Also nochmals vielen vielen Dank Martin für die Hilfe und dafür das du dir den Code angeschaut hast... ich versuche mich in Sachen "Erster Eindruck" zu bessern :D... vielleicht findest du kurz Zeit einige Tipps zu geben... damit er leserlicher wird (Indents hab ich notiert).
Gruß
Jogilein
-
Bekomme nun folgendes bei der Ausgabe, zusätzlich zu den Memory Leaks... was bedeutet das?
Eine Ausnahme (erste Chance) bei 0x7c809e3a in FIS.exe: 0xC0000005: Zugriffsverletzung beim Lesen an Position 0x10024dc0. #File Error#(51) : {147} client block at 0x00358B28, subtype c0, 104 bytes long. Eine Ausnahme (erste Chance) bei 0x7821f400 (mfc80d.dll) in FIS.exe: 0xC0000005: Zugriffsverletzung beim Lesen an Position 0x10024f14. faulted while dumping object at $00358B28, 104 bytes long Object dump complete.
-
Habs gelöst :)... aber vielleicht könnte mir jemand erklären was die Fehlermeldung nun bedeutete :D...
Danke