Du bist nicht angemeldet.

Werbeanzeige

1

23.08.2004, 01:20

Eine Klasse aus einer DLL laden

Hi, mein aktuelles Problem sind so aus:

Ich lade meine .dll Datei explizit mit LoadLibrary.

Wenn diese erfolgreich geladen wurde möchte ich ein Objekt der Klasse, die in der .dll Datei ist, anlegen.

Muss ich dazu ausser dem laden der DLL noch was hinzufügen, z.B. die Headerdatei der Klasse. Das wäre aber dumm, weil dann hätte das laden der DLL keinen Vorteil mehr.

Wie kann ich die Klasse im aktuellen Projekt benutzen?
Einfach ein Objekt bilden funktioniert nicht.

Die Testklasse sieht so aus:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class TEST_DLL Test
{
    public:
        void FunkA(int a, int b)
        {
            if(a > b)
                b = a;
        }

        int FunkB(int c)
        {
            return c >> 1;
        }
};


Ist also nur Schrott, aber ist ja auch nur zum Testen!

dot

Supermoderator

Beiträge: 9 833

Wohnort: Graz

  • Private Nachricht senden

2

23.08.2004, 14:42

das geht über interfaces...

du erstellst eine abstrakte basisklasse, die dem prog das eine instanz der klasse haben will bekannt sein muss ( header ).
dann leitest du in der dll eine klasse von dieser basisklasse ab.
die dll enthält jetzt nur noch eine funktion die ein neues objekt der abgeleiteten klasse erzeugt und einen zeiger auf die basisklasse zurückgibt der auf die erzeugte instanz zeigt. z.b. sowas:

C-/C++-Quelltext

1
2
3
4
5
myBase* CreateObject( void )
{

    return ( myBase* ) new myClass;    //wobei myClass von MyBase abgeleitet ist!

}


diese funktion kannst du per LoadLibrary() und GetProcAddress() dynamisch aufrufen und fertig. ziemlich praktisch sowas. damit lässt sich z.b. auch ein plugin system realisieren ( siehe hier ).

3

23.08.2004, 15:00

Hi, ja für ein Pluginsystem brauch ich das auch :-)

Ich dachte das würde auch einfacher gehen. Sicherlich sind Interfaceklassen immer eine gute Wahl, aber dadurch habe ich wieder Abhängigkeiten in mehreren Klassen, dadurch das ich Headerdateien includen muss.

Aber Ok, klingt logisch und einfach und wenn es keine "bessere" Möglichkeit gibt, dann werd ich das so versuchen.

Danke dot!!

4

23.08.2004, 19:14

dot hat das wesendliche eigentlich schon angesprochen. Fakt ist das wenn du eine DLL mit LoadLibrary lädst und dann eine Klasse benutzen willst, die in der DLL Implementiert ist, müssen zwei Dinge erfüllt werden.

1) Du brauchst eine Funktion die den Einstieg in die DLL ermöglicht. Du kannst z.B. auch die d3d9.dll mit LoadLibrary laden. Dann lädst du mit GetProcAddress die Funktion Direct3DCreate9 und rufst diese dann auf. Die Funktion Direct3DCreate9 ist der Einstieg in die DLL und all ihrere Komponenten.

2) Alle Funktionen müssen virtuell sein. Das hat den Grund weil es keine feste Bindung zwischen der DLL und der EXE oder einer anderen DLL gibt. Virtuelle-Methoden haben die Eigenschaft das sie zur Laufzeit gelinkt werden und nicht durch den Linker der IDE.

Darum sprach dot auch das Interface Design an. Denn dieses erfüllt Punkt 2. Alle Methoden eines Interfaces sind virtuelle Methoden.
Die eigentliche Implementation des Interfaces kann jedoch auch ganz normale Methode beitzten (also nicht virtuell).

Wenn du ein PlugIn-System aufbauen willst, ist es notwendig das dieses PlugIn eine Funktion nach den C Spezifikationen besitzt. Die sind einfacher zu laden ;) C++ Funktionen können natürlich auch über GetProcAddress geladen werden, man must aber erst den qualifizierten Funktionsnamen aus der DLL lesen.

In meiner Engine hab ich es so gemacht. Jedes PlugIn stellt eine solche Funktion bereit. Dazu gibt es ein PlugIn-Interface dessen konkrete Implementation in der DLL implementiert wird. Die PlugIn-Funktion wird dann mittels GetProcAddress geladen und aufgerufen. Die Funktion erzeugt eine Instanz von dem PlugIn-Interface (dessen Implementierung) und liefert diese als Ergebnis. Das PlugIn-Interface stellt dann alle möglichen Methoden zur verfügung. Wie z.B. für das abfragen der Version.


Zu den Headern:
Die anderen Source Codes die deine Klasse (aus der DLL) benutzen wollen brauch natürlich die Header. Aber nur die wo das Interface definiert ist und nicht die Implementation des Interface.
Wenn du nur mit Pointern Arbeitest brauchst du diese Header nicht einmal in irgendeine andere Header einfügen. Das kannst du mit der Klassen Deklaration umgehen ;) Die Source-Datei brauch natürlich die Header.
Wichtig! Ich übernehme keinerlei Verantwortung für eventl. Datenverlust oder Schäden am Rechner ;D

5

23.08.2004, 19:28

Amen, danke jetzt hab ich es fast restlos verstanden und ich wäre nie auf die Idee gekommen eine Einstiegsfunktion zu machen, da ich unbedingt eine Einstiegsklasse wollte. Aber gut, das stellt nicht das Problem dar.

Eine Frage noch, die ich nicht ganz verstanden habe:

Zitat

Wenn du nur mit Pointern Arbeitest brauchst du diese Header nicht einmal in irgendeine andere Header einfügen. Das kannst du mit der Klassen Deklaration umgehen Zwinker


Kannst du mir das noch ein bisschen näher erklären?

Ansonsten echt super erklärt, dass versteh sogar ich auf Anhieb.
Und nochmals sorry an Dot, dass ich erst an deiner Antwort gezweifelt habe. Aber jetzt ist mir das erst wirklich klar und du hattest natürlich auch schon Recht.

:roll:

6

23.08.2004, 19:50

Hier mal ein Beispiel:

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
class A
{
   void UseClassB(B* pBClass);
};

class B
{
   void UseClassA(A* pAClass);
};

Das ist eine Dummesituation, aber kommt sehr heufig vor. Das Problem ist das Klasse A die Klasse B brauch aber Klasse B für den Compiler noch nicht existiert. Wenn ich das ganze umdrehe hab ich das selbe Problem. Umgehen kann ich das mit der Klassen Deklaration.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
class B;

class A
{
   void UseClassB(B* pBClass);
};

class B
{
   void UseClassA(A* pAClass);
};

Da nur Zeiger verwendet werden, ist es dem Compiler ausreichend das er weis das es eine Klasse mit Namen "B" gibt. Da ein Zeiger immer 32Bit groß ist (in einem 32Bit OS), spielt die eigentliche Größe der Klasse und dessen Eigenschaften hier keine Rolle.

Das kannst du jetzt schnell auf dein Interface Design müntzen. Da du ja nicht willst das in einer Header myProgHeader.h die Header eines Interfaces eingefügt wird, kannst du statt der Header einfach die Klassen Deklaration benutzen.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
// MyProgHeader.h


class IPlugIn; // Das Interface, Ersatz für die IPlugIn.h Header


class MyProg
{
   void LoadPlugIn(IPlugIn** ppPlugIn);
   ...
};

In dem Modul ist es natürlich notwendig das du die IPlugIn.h einfügst. Da du hier ja gebrauch von der Klasse machst.
Wichtig! Ich übernehme keinerlei Verantwortung für eventl. Datenverlust oder Schäden am Rechner ;D

7

23.08.2004, 20:39

Ok, wenn das so geht ist das Praktisch.

Danke, ich denke jetzt gibt es keine Probleme mehr.
Ich werde später versuchen, dass Gelernte auf mein TestSystem anzuwenden.
Aber ich denke nach so einer guten (sehr guten) Erklärung sollte das kein Thema mehr sein.

:roll:

Anonymous

unregistriert

8

24.08.2004, 04:19

Mir fällt es wirklich schwer, aber ich hab immer noch ein Problem :(

Ich habe jetzt folgendes gemacht:
1.) Plugin Interface IBasePlugin
2.) Konkretes Plugin abgeleitet vom Interface
3.) Eine Funktion, die das Plugin Objekt erzeugt

C-/C++-Quelltext

1
2
3
4
    extern "C" _declspec(dllexport) void getPlugin(IBasePlugin** ppPlugin)
    {
        *ppPlugin = new TestPlugin;
    }

4.) In dem Hauptprogramm habe ich das Plugin geladen
5.) Ich habe einen Funktionszeiger erstellt, der durch GetProcAddress initialisiert wird

C-/C++-Quelltext

1
2
3
4
5
6
         typedef void (*GETPLUGIN)(IBasePlugin**); // In der Klassendeklaration

 
    GETPLUGIN pfnLoad = (GETPLUGIN)::GetProcAddress((*iter).second,           
                                                                                        "getPlugin");
    if(pfnLoad)
       pfnLoad(&m_pServer);

6.) Im Programm rufe ich die dazugehörige Methode auf
7.) Das Objekt wir durch 5. erzeugt und in der Member Variable m_pServer vom Typ des Plugin Interface gespeichert.

Jetzt mein Problem:
Die Adresse die ich an die Lade Funktion in der Plugin Klasse übergebe ist 0x00000000. Ist die Adresse übergeben hat der Parameter ppPlugin den Wert 0x00861004. Nach dem erzeugen des Objekts mit new verändert sich an dem Wert nichts.
Geht die Funktion zurück ist der Wert von m_pServer, aber nicht wie ich erwarten würde 0x00861004, sonder hat den Wert 0x008828a8.

Ich hab echt keine Ahnung warum? Ich nehm an dadurch kann ich auch keine Methoden aus der Klasse aufrufen, weil meine Member Variable die falsche Adresse hat!? :help:

9

24.08.2004, 04:22

Der letzte Beitrag war von mir!
Sorry, aber ich war schon wieder ausgeloggt :angel:
Kann man das irgendwie ausstellen :crying:

Klaus

Treue Seele

Beiträge: 245

Wohnort: Stuttgart

Beruf: Schüler

  • Private Nachricht senden

10

24.08.2004, 09:52

Hast du Cookies deaktiviert oder beim einloggen das Häkchen nicht gecheckt?
Ich bin Tag ein, Tag aus eingeloggt... völlig problemlos
Mozilla Firefox
The Browser - reloaded

Werbeanzeige