[gelöst] QLibrary / DLL in Klasse (Warning Multiple Declare)

Du bist neu in der Welt von C++? Dann schau hier herein!
Pixtar
Beiträge: 97
Registriert: 5. Mai 2010 15:32

[gelöst] QLibrary / DLL in Klasse (Warning Multiple Declare)

Beitrag von Pixtar »

Hallo Leute,

entweder hab ich ein Verständnisproblem oder ich stehe mächtig auf dem Schlauch!

Ich habe eine DLL mit Funktionen, die ich in mein QT-Projekt einbinden will:

Code: Alles auswählen

QLibrary myLib("dll/libCol.dll");    //DLL einbinden
    typedef void (*GetSens)();    //Namensraum der Funktion aus der DLL deklarieren
    GetSens mySensCount = (GetSens) myLib.resolve("SensCount");    //Funktion aus der DLL in den Namensraum auflösen
Den obige Code dürfte mir doch die Funktion mySensCount bereitstellen, die die Funktionsweise der 'SensCount'-Funktion aus der DLL besitzt, oder nicht?

Sollte das richtig sein?!, würde ich gerne wissen, wie ich diese Funktion in eine Klasse einbinde, denn würde ich den obigen Code in eine andere Datei als die main.cpp einbinden, erhalte ich die Warnung 'Multiple Declare'. Das kann ich natürlich übergehen indem ich in der main.cpp ein static auf die Funktionen setze, was aber Bullshit-Programmierung ist.

Code: Alles auswählen

-------mainW.h-------
typedef void (*GetSens)();
class MyClass{
public:
    MyClass();
    GetSens mySensCount;
};
----------------------
------mainW.cpp------
MyClass::MyClass(){
    QLibrary myLib("dll/libCol.dll");
    mySensCount = (GetSens) myLib.resolve("SensCount");  
}
Der obige Code ist intuitiv geschrieben und hat daher keine Richtigkeit, wäre nett wenn mir da mal jemand die Meinung sagen würde, wie ich DLL-Funktionen in einem Objekt/Klasse bereitstelle. Es sind da nämlich ca. 50 Funktionen drin, die will ich gebündelt in einer Klasse oder einem Struct haben, anstatt sie einzeln rumdümpeln zu lassen. (zu unübersichtlich)

Grüße Pixtar
Zuletzt geändert von Pixtar am 8. Juni 2010 12:50, insgesamt 1-mal geändert.
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

Muss die Library wirklich dynamisch geladen werden können?
Deine Idee geht nur solange die Funktion aus der DLL eine C-Funktion ist bzw. eine C-Aufrufkonvention benutzt.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
Pixtar
Beiträge: 97
Registriert: 5. Mai 2010 15:32

Beitrag von Pixtar »

Nein, sie kann auch statisch eingebunden werden!
Wie ist die Syntax denn bei einem statischen Aufruf?
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

Naja - wie man eine normale Lib (z.B. Qt) eben verwendet - Header inkludieren, Funktionen benutzen und dann gegen die Library linken.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
Pixtar
Beiträge: 97
Registriert: 5. Mai 2010 15:32

Beitrag von Pixtar »

Soooo .. also ich hab mich nun noch einmal informiert.
Ich habe lediglich die DLL und keine LIB.
Damit sich keiner aufregt, weil es kein QT mehr ist entschuldige ich mich hiermit schon mal, da dies im Forum ja nicht so gern gesehen ist. Würde mich dennoch über kurze Hilfe freuen. Ich habe die QLibrary nun nicht mehr verwendet, weil die Hilfe sagt, dass die DLL in C geschrieben sein muss mit dem Tag extern "C" __declspec(dllimport), was nicht der Fall ist.

<collection.h>

Code: Alles auswählen

#include <QString>
#include <windows.h>
class DLLFNC
{
private:
    typedef void (*CBGetSerial)(short* serial);
public:
    DLLFNC();
    CBGetSerial *inGetSerial;
};
<collection.cpp>

Code: Alles auswählen

#include "collection.h"
DLLFNC::DLLFNC()
{
    HINSTANCE hInstance;
    hInstance = ::LoadLibrary(L"dll/libCol.dll");
    inGetSerial  = (CBGetSerial*)::GetProcAddress((HMODULE)hInstance, "GetSerial");
}
<mainwidget.cpp>

Code: Alles auswählen

#include "collection.h"
    DLLFNC *myCollection;
    myCollection->inGetSerial();
Was habe ich falsch gemacht, dass die Funktion inGetSerial() beim Aufruf nicht als Funktion erkannt wird?
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

Was habe ich falsch gemacht, dass die Funktion inGetSerial() beim Aufruf nicht als Funktion erkannt wird?
WIssen wir nicht, weil wir ja nicht wissen wie DLLFNC::inGetSerial deklariert ist !
Ich nehm mal an CBGetSerial soll nen typedef fuer den FUnktionspointer sein ? Wie iss der deklariert ?
Wahrscheinlich isses aber eher nicht die richtige FunktionsPointerSignatur !

btw:
dein code sieht sehr nach C aus ... klar wenn man ne C-Dll beschiessen will.
Aber besser wrappe das C Zeugs und bau dir generische(template) Funktoren um die Funktionspointer !
Das macht deine Aufrufe weitgehenst Typsicher (wenn der rahmen einmal steht) und du hasst nie wieder Aerger mit Dlls ... naja zumindest nicht mit dem Loadlibrary und GetProcAddress zeugs :-)

So sehen meine templates aus, z.B. fuer 3 parameter mit reuckgabewert
(DLLhandler iss ne Klasse die ne HInstance wrappt, versionschecks macht usw.)

Code: Alles auswählen

//////////////////////////////////////////////////////////////////////
// 3 Parameter, returnwert
//////////////////////////////////////////////////////////////////////
template<class P1,class P2, class P3,class T_RT,T_RT VReturn>
class Functor_3P_RT_CDECL
{
private:
	typedef T_RT (__cdecl T_func)(P1,P2,P3);
public:
	Functor_3P_RT_CDECL(DllHandler & rxDll,const char * FunctonName)
	{
		mp_func = static_cast<T_func * >(rxDll.GetProcAddress(FunctonName));
	}	
	~Functor_3P_RT_CDECL() // keine Vererbung !!! 
	{
	}
	// der Funktionsaufruf 
	T_RT operator () (P1 ap1,P2 ap2,P3 ap3)
	{
		T_RT ireturn = VReturn;
		if(mp_func)
		{
			ireturn = (*mp_func)(ap1,ap2,ap3);
		}
		return ireturn;
	};
private:
	T_func *		mp_func;
};
Ciao ...
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Pixtar hat geschrieben:Ich habe die QLibrary nun nicht mehr verwendet, weil die Hilfe sagt, dass die DLL in C geschrieben sein muss mit dem Tag extern "C" __declspec(dllimport), was nicht der Fall ist.
Versteh ich aber jetzt nicht wirklich. QLibrary macht keine eigene Zauberei, die verwenden auf Windows auch nur LoadLibrary und Konsorten. Wenn das Laden mit QLibrary nicht geht wirds auch mit LoadLibrary nicht gehen. Umgekehrt: Wenn es mit LoadLibrary geht, hets auch mit QLibrary, und sollte dann auch mit QLibrary gemacht werden, wenn du dich nicht auf nur Windows festlegen willst.

Und wenn etwas nicht funktioniert, wäre die Meldung, die der Compiler/Linker/Runtime dir geben nicht schlecht.
Pixtar
Beiträge: 97
Registriert: 5. Mai 2010 15:32

Beitrag von Pixtar »

Ich danke für eure Antworten, aber ich werde die DLL wohl nicht in mein Programm einbinden können. Weil:
  1. der DLL keine LIB beiliegt
  2. in der DLL kein

    Code: Alles auswählen

    __declspec(dllexport)
    
    definiert ist
  3. in der DLL kein

    Code: Alles auswählen

    extern "C"
    Block vorhanden ist
Denn in der Dokumentation steht folgendes:
The symbol must be exported as a C function from the library for resolve() to work. This means that the function must be wrapped in an extern "C" block if the library is compiled with a C++ compiler. On Windows, this also requires the use of a dllexport macro; see resolve() for the details of how this is done.
Also was sagen die Experten dazu :?: Ich für meinen Teil würde sagen es gibt keine Möglichkeit die DLL die ich habe einbinden zu können. Es sei denn die DLL würde nochmal mit den dazugehörigen Blöcken erstellt ..

@franzf: Danke für die Aufklärung über die Prozedur von QLibrary. Man lernt nie aus. ;)
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

pexports als Tool zeigt Dir an welche Funktionen exportiert sind. Aber sowas sollte auch in der Doku zur DLL stehen.
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

aber ich werde die DLL wohl nicht in mein Programm einbinden können.
Naja was Du zitiert hasst, gilt fuer QLibrary, bzw den darauf aufsetzenden Methoden.

Wenn man einige M$ Spezifische dinge beachtet, kann man aber low level sonst was anbinden, man muss dann halt nur einiges "wissen"
Mit etwas Übung bekommt man funktionssignaturen über den dependency walker auch ganz gut raus ...
Wenn man selber noch binaerkompatibel zu dem compiler ist, mit dem die dll erstellt wurde, und grob weiss, mit welchen flags sie befeuert wurde, brauch man auch kein C-Interface.
- also man braucht keine lib, wenn man die soweiso 100% dynamisch anzieht
- Man braucht kein extern C, wenn dll und exe das gleiche namemergling, aka gleichen compiler verwenden. Sobald man ein klassensymbol exportiert, wars das eh mit extern C
- symbole in die Dll exporttabelle pappen kann man auch mit ner definitionsdatei, aka man braucht kein __declspec(dllexport) Man muss nur wissen mit welchen konventionen die exportierten funktionen erzeugt wurden (ueber compiler flags als standard, oder explizit ueber schluesselwoerter(stdcall, cdecl,pascal,winapi ... ) vor der definition)

Wenn du recht haettest, wuerde keines meiner Plugins funktionieren ^^

Ciao ...
Pixtar
Beiträge: 97
Registriert: 5. Mai 2010 15:32

Beitrag von Pixtar »

Tja, der der die DLL geschrieben hat, hat es leider nicht so mit dem dokumentieren. Also im CPPForum hab ich folgenden Beitrag gefunden, der aus einer *.dll eine *.def erstellt. Ob mich das nun wirklich weiter bringt weiß ich leider nicht. Aufjedenfall stehen in der *.def nun alle Funktionen der DLL:

Code: Alles auswählen

LIBRARY libCol.dll
EXPORTS
GetSerial
...
@Christian, du sagst das mir das Tool anzeigt welche Funktionen exportiert sind - demnach alle Funktionen. Dann frag ich mich doch ehrlich ob ich zu dämlich bin. Asche auf mein Haupt. :evil:

Hier mal der komplette SourceCode:
<dllCol.h>

Code: Alles auswählen

#ifndef DLLCOL_H
#define DLLCOL_H
#include <windows.h>
#include <stdio.h>
#include <QMessageBox>
#include <QLibrary>
#include <QFile>
class DLLFNC
{
private:
    typedef int (*CBGetSerial)();
public:
    DLLFNC();
    CBGetSerial inGetSerial;
};
#endif // DLLCOL_H
<dllCol.cpp>

Code: Alles auswählen

#include "dllCol.h"

DLLFNC::DLLFNC()
{
    QMessageBox msgBox;
    QFile file("dll/libCol.dll");
    if(file.exists()==true){
        inGetSerial = (CBGetSerial) QLibrary::resolve("dll/libCol.dll", "GetSerial");
        if (inGetSerial ){
            inGetSerial();
            msgBox.setText("Die Funktion GetSerial wurde erfolgreich geladen!");
            msgBox.exec();
        }else{
            msgBox.setText("Die Funktion GetSerial konnte nicht geladen werden!");
            msgBox.exec();
        }
    }else{
        msgBox.setText("Die DLL libCol.dll existiert nicht an dem angegebenen Ort!");
        msgBox.exec();
    }
<widget.h>

Code: Alles auswählen

#include <QWidget>
class MainWin : public QWidget{
    Q_OBJECT
public:
    MainWin(QWidget *parent = 0);
private:
    DLLFNC *dllCollection;
}
<widget.cpp>

Code: Alles auswählen

#include "widget.h"
MainWin::MainWin(QWidget *parent)
    : QWidget(parent)
{
    dllCollection = new DLLFNC();
}
<main.cpp>

Code: Alles auswählen

#include "widget.h"

int main(int argc, char *argv[])
{
    QApplication app(argc, argv);
    MainWin window;
    window.show();
    return app.exec();
}
<output>
"Die Funktion GetSerial konnte nicht geladen werden!"
@RHBaum: Die DLL wurde mit dem Visual C++ 6 erstellt, und mein Programm wird über QT4.6 erstellt - also mingw/qmake. Compiler sind somit unterschiedlich ...
franzf
Beiträge: 3114
Registriert: 31. Mai 2006 11:15

Beitrag von franzf »

Also :D
Note that fileName should not include the platform-specific file suffix;
->Name ändern: .dll weg!
Ich hab aber kein Windows, drum kann ich das nicht überprüfen.

Dann nimm mal NICHT die statische Funktion, sondern erstell eine QLibrary-Objekt. Mach ein load() und überprüf auf "isLoaded()" und gib evtl. den errorString() aus. Falls geladen, selbes Spielchen mit resolve(). Vielleicht kommst du dem Fehler so besser auf die Schliche.
Ist denn die DLL tatsächlich mit nem c++-Compiler übersetzt worden?
Christian81
Beiträge: 7319
Registriert: 26. August 2004 14:11
Wohnort: Bremen
Kontaktdaten:

Beitrag von Christian81 »

Ich würde erstmal schauen ob QLibrary die DLL überhaupt laden kann. Dann bekommt man auch bessere Fehlermeldungen...
MfG Christian

'Funktioniert nicht' ist keine Fehlerbeschreibung
Pixtar
Beiträge: 97
Registriert: 5. Mai 2010 15:32

Beitrag von Pixtar »

Hier der neue Code, den ich anfertigen sollte:
<Quelltext des Konstruktors (dllCol.cpp)>

Code: Alles auswählen

    QMessageBox msgBox;
    QFile file("libCol.dll");
    if(file.exists()==true){
        QLibrary myLib("libCol");
        if(myLib.load()==true){
            if(myLib.isLoaded()==true){
                inGetSerial= (CBGetSerial) myLib.resolve("GetSerial");
                if (inGetSerial){
                    inGetSerial();
                    msgBox.setText("Die Funktion GetSerial wurde erfolgreich geladen!");
                    msgBox.exec();
                }else{
                    msgBox.setText("Die Funktion GetSerial konnte nicht geladen werden!\n"+myLib.errorString());
                    msgBox.exec();
                }
            }else{
                msgBox.setText("Die DLL libCol wurde nicht geladen!\n"+myLib.errorString());
                msgBox.exec();
            }
        }else{
            msgBox.setText("Die DLL libCol konnte nicht geladen werden!\n"+myLib.errorString());
            msgBox.exec();
        }
    }else{
        msgBox.setText("Die DLL libCol.dll existiert nicht an dem angegebenen Ort!");
        msgBox.exec();
    }
<Output>
Die DLL libCol konnte nicht geladen werden!
Connot load library libCol: Das angegebene Modul wurde nicht gefunden!
@franzf: Habe gerade die Bestätigung erhalten, dass die DLL mit einem C++ Compiler kompiliert wurde. (Visual C++ 6.0)
RHBaum
Beiträge: 1436
Registriert: 17. Juni 2005 09:58

Beitrag von RHBaum »

DU scheinst den Programmierer zu kennen ....

kommst du an eine Impl oder an eine h ran, wo die exportierten funktionen aufgefuehrt sind ?
Die namen allein nuetzen dir nix, du brauchst auch die signaturen !


Die DLL wurde mit dem Visual C++ 6 erstellt, und mein Programm wird über QT4.6 erstellt - also mingw/qmake
Dann hasst du wirklich schlechte karten, wenn es kein C Interface ist ....
Das heisst der vc6 hat die symbolnamen soweiso zermatscht ... weiss ned ob es da tools zum anpassen gibt.
Sobald in dem interface ne klasse auftaucht, hasst eh verloren, da die beiden compiler nicht nur die symbolnamen nach anderen Schemen erzeugen, sondern bei klassen die methodenpointer und parameterausrichtung eh anders machen.

der sauberste weg waere dann, sich nen vc6 zu krallen, und ne zwischendll schreiben, die die funktionen der orginalen dll auf eine saubere C-Schnittstelle umlegt ...

Ciao ...
Antworten