Herzlich Willkommen, lieber Gast!
  Sie befinden sich hier:

  Forum » C / C++ (WinAPI, Konsole) » Klasse per DLL einbinden

Forum | Hilfe | Team | Links | Impressum | > Suche < | Mitglieder | Registrieren | Einloggen
  Quicklinks: MSDN-Online || STL || clib Reference Grundlagen || Literatur || E-Books || Zubehör || > F.A.Q. < || Downloads   

Autor Thread - Seiten: > 1 < [ 2 ]
000
30.05.2006, 09:47 Uhr
~DiplWI_BA
Gast


Hallo,

ich plane ein Projekt, welches aus einem C++/CLI Frontend und einem Ansi-C Backend besteht. Grund: Backend muss eine C-DLL zur Maschinensteuerung implementieren, für die Windows Oberfläche würde ich gerne Windows-Forms verwenden (die MFC habe ich nie so wirklich lieb gewonnen). Dohc ich scheitere schon an der ersten Hürde.

Ich habe eine Klasse für die Datenhaltung in unmanaged C++ erstellt. Diese möchte ich eine DLL packen (auf die dann sowohl die Klasse, welche das Ansi-C Backend kapselt, alsauch die C++/CLI Windows-Forms Anwendung für das Frontend zugriff haben.

Ich habe also die komplette Klasse in Visual Studio 2005 Standard als Win32 Konsolenanwendung geschrieben udn getestet. Die Komplette Klasse befindet sich in einer Header Datei (.h). Dann habe ich eine neue Win32 Konsolenanwendung angelegt und diesesmal als Option "dll" angekreuzt, die Header Datei dort hinein kopiert und in der beim anlegen erstellten cpp Datei includiert.
(Die Dateien sehen jetzt so aus


C++:
//Datenspeicher.h
//diverse Klassen die von der exportierten Klasse gneutzt werden
//....

class __declspec(dllexport) C_Daten
{
    protected:
        ....

    public:
        ....
};
C_Daten::C_Daten()
{    
    this->O_Liste = new C_VerketteteListeLaraObj();
}
//diverse weitere implementierungen von Funktionen aus C_Daten
//...





C++:
// Datenspeicher.cpp : Definiert den Einstiegspunkt für die DLL-Anwendung.
//

#include "stdafx.h"
#include "Datenspeicher.h"

/*#ifdef _MANAGED
#pragma managed(push, off)
#endif
*/


BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    return TRUE;
}
/*
#ifdef _MANAGED
#pragma managed(pop)
#endif
*/




Das ganze kompiliert auch und soweit scheint alles ist in Butter.


Versuche ich aber diese zum test in einer anderen Win32 Konsolenanwendung zu verwenden funktioniert das nicht.


Ich habe die kompilierte lib und die dll in das Verzeichnis meines neuen Win32 Konsolenanwendungsprojektes kopiert und bei Projekteigenschaften->Konfigurationseigenschaften->Linker->Eingabe im Feld "Zusätzliche Abhängigkeiten" den Namen der lib Datei (inkl Endung ".lib") eingegeben.
Dann habe ich (wie es in den wenigen Tutorials, die ich zu dem Thema fand geschrieben stand) die Header Datei aus der DLL includiert, als wäre sie Teil des aktuellen Projekts


C++:
// dll-test.cpp : Definiert den Einstiegspunkt für die DLL-Anwendung.
//

#include "stdafx.h"
#include "Datenspeicher.h"

/*#ifdef _MANAGED
#pragma managed(push, off)
#endif
*/


BOOL APIENTRY DllMain( HMODULE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    return TRUE;
}
/*
#ifdef _MANAGED
#pragma managed(pop)
#endif
*/




Das Ergebnis ist ein Compilerfehler


Code:

------ Erstellen gestartet: Projekt: DLL-Test, Konfiguration: Debug Win32 ------
Kompilieren...
DLL-Test.cpp
c:\daten\visual studio 2005\projects\dll-test\dll-test\dll-test.cpp(5) : fatal error C1083: Datei (Include) kann nicht geöffnet werden: "Datenspeicher.h": No such file or directory
Das Buildprotokoll wurde unter "file://c:\Daten\Visual Studio 2005\Projects\DLL-Test\DLL-Test\Debug\BuildLog.htm" gespeichert.
DLL-Test - 1 Fehler, 0 Warnung(en)
========== Erstellen: 0 erfolgreich, Fehler bei 1, 0 aktuell, 0 übersprungen ==========



Was mache ich falsch?

 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
001
30.05.2006, 09:51 Uhr
Guybrush Threepwood
Gefürchteter Pirat
(Operator)


Du musst die Klasse in der Anwendung die sie aus der DLL importieren will auch micht dllimport anstatt mit dllexport bekannt machen.

Dieser Post wurde am 30.05.2006 um 09:52 Uhr von Guybrush Threepwood editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
002
30.05.2006, 09:54 Uhr
~DiplWI_BA
Gast


Jetzt bin ich etwas durcheinander. Heißt dass ich muss die Klasse in der importierenden Anwendung nochmals deklarieren?
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
003
30.05.2006, 10:04 Uhr
Guybrush Threepwood
Gefürchteter Pirat
(Operator)


Ja hast du doch sowieso in dem du die Datenspeicher.h eingebunden hast. Du musst halt das dllexport in ein dllimport ändern.

Die Funkrionsdefinitionen, also das was die Funktion genau macht, brauchst du natürlich nur in der DLL. Aber deine Anwendung muss doch wissen wie die Klasse aussiehbt und das sie aus der DLL kommt.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
004
30.05.2006, 11:05 Uhr
~DiplWI_BA
Gast


Bin ich heute nicht gut drauf, oder einfach nur von Natur aus zu blöd?

Ich hab das ganze nach Deinem hinweis mal in zwei getrennte Header für import und export aufgebrochen.
(in der Datenspeicher DLL gibt es jetzt die DLL Datenspeicher.h mit sämtlichen Deklaration, wobei vorsorglich ALLE Klassen ein __declspec(dllexport) haben). Alle Defintionen habe ich nach Datenspeicher.cpp verschoben.

Im Projekt DLL-Test habe ich eine Kopie der Datenspeicher.h bei der alle __declspec(dllexport) durch __declspec(dllimport) ersetzt sind.

Das sollte doch eigentlich eien 100% sichere Lösung sein, oder?

Ist es aber elider nicht. Ich erhalte folgenden Laufeztifehler, der mich im Debugger in die Datei "free.c" schickt.


Code:

Windows hat einen Haltepunkt in DLL-Test.exe ausgelöst.

Dies kann auf eine Beschädigung des Heaps zurückzuführen sein und weist auf ein Problem in DLL-Test.exe oder in einer der geladenen DLLs hin.

Weitere Analyseinformationen finden Sie möglicherweise im Ausgabefenster.



Klingt irgendwie nach einem Pointer-Fehler. Das interessante ist: Kopiere ich auch die Datenspeicher.cpp in das Programm DLL-Test und lösche alle DLL Spezifika (also wenn ich die Selben Klassen mit den Selben Implementierungen "direkt" statt über DLL verwende) taucht dieser Fehler nicht auf.

Hat Irgendjemand eien Idee dazu?
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
005
30.05.2006, 12:49 Uhr
Guybrush Threepwood
Gefürchteter Pirat
(Operator)


Hast du in deiner Anwendung die komplette Klassendefinition?

Der Fehler könnte evtl dadurch hervorgerufen werden das deine Klasse in der DLL so:


C++:
class __declspec(dllexport) TEST
public:
   TEST();
private:
   int i;
   char c;



und in deiner Anwendung so:

C++:
class __declspec(dllimport) TEST
public:
   TEST();
private:
   int i;


deklariert hast.
Das würde nämlich beudeuten das der Compiler mit einer zu kleinen Größe für eine Instanz der Klasse rechnet da er die char Variable nicht kennt.

Das Definitionen jetzt in der .cpp Datei der DLL stehen ist ok und schön so.

Wenn das oben nicht das Problem ist solltest du deine Anwendung mal schrittweise debuggen. Dann siehst du wo genau das Problem auftritt.

Dieser Post wurde am 30.05.2006 um 12:50 Uhr von Guybrush Threepwood editiert.
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
006
30.05.2006, 13:18 Uhr
~DiplWi_BA
Gast


Nein, ich habe die Kompletten Klassendeklarationen genutzt, dass war es also nicht. Aber mit dme schrittweise Debuggen der DLL habe ich schon begonnen und ich glaube ich habe es gefunden.

Es scheint, dass die Übergabe eines std::string an eine DLL nicht ganz trivial ist. Das hat mir die Funktionen in der DLL dann durcheinandergebracht. Eine Umstellung auf die Übergabe von string* hat nicht geholfen (es scheint also irrelevant zu sein ob ein Objekt oder ein Pointer übergeben wird).
Ich habe das Problem zwar noch nicht 100% verstanden, aber konnte es erfolgreich umgehen indem ich einen char* zur Übergabe des Textes nutze.

Trotzdem würde ich gerne noch verstehen was da los ist.
Hier nochmal etwas Source Code aus der DLL-Test (die DLL wurde natürlich entsprechend angepasst):

-------------------------------------------------------------------
Funktioniert nicht

C++:
// DLL-Test.cpp : Definiert den Einstiegspunkt für die Konsolenanwendung.
//

#include "stdafx.h"


#include <fstream>    // FileStreams
#include <sstream>    // Stringstreams
#include <vector>    
#include <iostream>
using namespace std;

#include "Datenspeicher.h"
#include "outputtest.h"

int _tmain(int argc, _TCHAR* argv[])
{
    int x;
    cin>>x;

        C_Daten* O_Daten= new C_Daten();
        string* V_file = new string ("C:\\Daten\\Visual Studio 2005\\Projects\\Lara_Datenschicht_Performancetest\\Testdaten\\Test_small.lara");
        if(O_Daten->setToDataLayer(V_file))
        {
            givetoRTC3(O_Daten);
        }
        delete(V_file);
        delete(O_Daten);

    cin>>x;

    return 0;
}




C++:
//Datenspeicher.h
//....
class __declspec(dllimport) C_Daten {
    protected:
        C_VerketteteListeObj* O_Liste;

    public:
        bool setToDataLayer(string* filename);
        C_VerketteteListeObj* getFromDataLayer();
        C_Daten();
        ~C_Daten();
};



-------------------------------------------------------------------
Funktioniert auch nicht

C++:
// DLL-Test.cpp : Definiert den Einstiegspunkt für die Konsolenanwendung.
//

#include "stdafx.h"


#include <fstream>    // FileStreams
#include <sstream>    // Stringstreams
#include <vector>    
#include <iostream>
using namespace std;

#include "Datenspeicher.h"
#include "outputtest.h"

int _tmain(int argc, _TCHAR* argv[])
{
    int x;
    cin>>x;

        C_Daten* O_Daten= new C_Daten();
                          if(O_Daten->setToDataLayer("C:\\Daten\\Visual Studio 2005\\Projects\\Lara_Datenschicht_Performancetest\\Testdaten\\Test_small.lara"))
        {
            givetoRTC3(O_Daten);
        }
        delete(V_file);
        delete(O_Daten);

    cin>>x;

    return 0;
}





C++:
//Datenspeicher.h
//....
class __declspec(dllimport) C_Daten {
    protected:
        C_VerketteteListeObj* O_Liste;

    public:
        bool setToDataLayer(string filename);
        C_VerketteteListeObj* getFromDataLayer();
        C_Daten();
        ~C_Daten();
};



-------------------------------------------------------------------
Funktioniert


C++:
// DLL-Test.cpp : Definiert den Einstiegspunkt für die Konsolenanwendung.
//

#include "stdafx.h"


#include <fstream>    // FileStreams
#include <sstream>    // Stringstreams
#include <vector>    
#include <iostream>
using namespace std;

#include "Datenspeicher.h"
#include "outputtest.h"

int _tmain(int argc, _TCHAR* argv[])
{
    int x;
    cin>>x;

        C_Daten* O_Daten= new C_Daten();
                          if(O_Daten->setToDataLayer("C:\\Daten\\Visual Studio 2005\\Projects\\Lara_Datenschicht_Performancetest\\Testdaten\\Test_small.lara"))
        {
            givetoRTC3(O_Daten);
        }
        delete(V_file);
        delete(O_Daten);

    cin>>x;

    return 0;
}




C++:
//Datenspeicher.h
//....
class __declspec(dllimport) C_Daten {
    protected:
        C_VerketteteListeObj* O_Liste;

    public:
        bool setToDataLayer(char* filename);
        C_VerketteteListeObj* getFromDataLayer();
        C_Daten();
        ~C_Daten();
};

 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
007
30.05.2006, 13:41 Uhr
Guybrush Threepwood
Gefürchteter Pirat
(Operator)


hmm das sollte nicht so sein. Ich mal schnell was zusammengestellt was bei mir funktioniert:

Testanwendung:

C++:
#include <stdio.h>
#include <iostream>

#pragma comment (lib,"testdll.lib")

class __declspec(dllimport) CTestdll {
public:
    CTestdll(void);
    bool test(std::string str);
};


int main()
{
    CTestdll a;
    std::string t = "Test";

    if (a.test(t))
        printf ("OK");
    else
        printf ("Nee");
    return 0;
}



Dll:

C++:
#include <windows.h>
#include <iostream>
#include <string>
#include "stdafx.h"

// Diese Klasse ist aus testdll.dll exportiert
class __declspec(dllexport) CTestdll {
public:
    CTestdll(void);
    bool test(std::string str);
};


BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
    }
    return TRUE;
}



// Dies ist der Konstruktor einer Klasse, die exportiert wurde.
// see testdll.h for the class definition
CTestdll::CTestdll()
{
    return;
}

bool CTestdll::test(std::string str)
{
    if (str == "Test")
        return true;
    return false;
}


Nich besonders schön, aber vielleicht hilfts dir ja
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
008
30.05.2006, 14:36 Uhr
~DiplWi_BA
Gast


Jetzt wirds interessant. Dein Beispiel funzt nämlich auch nicht. Ich erhalte zur Laufzeit die Fehlermeldung


Code:

Debug Error!
Program: ...
Module: ...debug\DLL-HelloWorld.exe
File:

Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention.






zu diesem Zeitpunkt steht schon groß und fett NEE auf der Konsole

Gehe ich in den Debugger sagt er mir


Code:

DLL-HelloWorld.exe hat einen Haltepunkt ausgelöst.


und verweist auf die schließnede Klammer von "int main()"

Auch musste ich den Quellcode anpassen, damit er überhaupt kompiliert.
Mein Programm sieht nun so aus:

C++:
// DLL-HelloWorld.cpp : Definiert den Einstiegspunkt für die Konsolenanwendung.
//
#include "stdafx.h" //NEW***

#include <stdio.h>
#include <iostream>

#pragma comment (lib,"string_dll.lib")

class __declspec(dllimport) CTestdll {
public:
    CTestdll(void);
    bool test(std::string str);
};


int main()
{
    CTestdll a;
    std::string t = "Test";

    if (a.test(t))
        printf ("OK");
    else
        printf ("Nee");
    return 0;
}



Die DLL so:

C++:
#include <windows.h>
#include <iostream>
#include <string>
#include "stdafx.h"

#include <sstream>    // Stringstreams //NEW***

using namespace std;

// Diese Klasse ist aus testdll.dll exportiert
class __declspec(dllexport) CTestdll {
public:
    CTestdll(void);
    bool test(std::string str);
};


BOOL APIENTRY DllMain( HANDLE hModule,
                       DWORD  ul_reason_for_call,
                       LPVOID lpReserved
                     )
{
    switch (ul_reason_for_call)
    {
        case DLL_PROCESS_ATTACH:
        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
    }
    return TRUE;
}



// Dies ist der Konstruktor einer Klasse, die exportiert wurde.
// see testdll.h for the class definition
CTestdll::CTestdll()
{
    return;
}

bool CTestdll::test(std::string str)
{
    if (str == "Test")
        return true;
    return false;
}

 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
009
30.05.2006, 14:37 Uhr
~DiplWi_BA
Gast


Vielleicht etwas in Punkto Compiler Standardeinstellungen? Was nutzt Du?
Ich nutze wie geagt: Visual Studio 2005 Standard
 
Profil || Private Message || Suche Download || Zitatantwort || Editieren || Löschen || IP
Seiten: > 1 < [ 2 ]     [ C / C++ (WinAPI, Konsole) ]  


ThWBoard 2.73 FloSoft-Edition
© by Paul Baecher & Felix Gonschorek (www.thwboard.de)

Anpassungen des Forums
© by Flo-Soft (www.flo-soft.de)

Sie sind Besucher: