G++ LoadLibrary + DLL = Laufzeitproblem



  • Hallo miteinander,

    habe folgendes Problem:
    Ich habe eine DLL mit VC2005 erstellt, die u.a. MFC Code enthält. Ziel ist es die Klasse, die diese DLL enthält in (theoretisch) allen C++-Windows Compilern verwendbar zu machen. Um dies zu ermöglichen, wurde diese Klasse von einer Struktur abgeleitet, die abstrakte, virtuelle Methoden enthält (Auszug):

    struct ISerialCommunication
    {
      virtual void Config(int* cfg) = 0;
    };
    

    Die DLL enthält eine exportierte Funktion, die einen Zeiger auf ein neu instanziiertes Objekt der Klasse liefert:

    extern "C" __declspec(dllexport) ISerialCommunication* __stdcall GetInstance();
    

    die wie folgt implementiert ist:

    ISerialCommunication* APIENTRY GetInstance()
    {	
     return new CSerialCommunication;
    }
    

    Das Laden der DLL und ausführen der Funktion ist wie folgt implementiert:

    typedef ISerialCommunication* (*ComPointer)();
    int main()
    {
    	HMODULE hmod = LoadLibrary("dcl.dll"); //DLL laden
    	ComPointer ptr = (ComPointer)GetProcAddress(hmod, "GetInstance"); //Funktionspointer holen
    	ISerialCommunication* ptrx = ptr(); //Zeiger auf Instanz holen
    	int val = 3;
    	printf("%d\n", val);
    	ptrx->Config(&val); //Funktion ausführen
    	printf("%d", val);
    	getchar();
    	return 0;
    }
    

    So und nun fängt das Problem an. Wollte dies mit dem VC6 Compiler probieren, da klappt dieser Code einwandfrei (Config(int*) ändert den int einfach auf 9). Nun habe ich mir den G++ Compiler (MinGW) runtergeladen und es kompiliert (g++ main.cpp -o main.exe) auch einwandfrei. Das Problem ist aber das irgendwo der Wurm drin ist. Wenn ich die DLL debugge, dann erzeugt er erfolgreich das Objekt, die Funktion Config(int*); wird auch aufgerufen, aber der this-Zeiger steht irgendwo im Nirvana und es geht einfach nichts. Wieso? Ich weiß leider auch nicht wo der Fehler liegt, ob mein Code falsch ist oder Compiler/Linkereinstellungen falsch sind. Vielleicht kann mir ja jemand helfen?

    Dankeschön

    Ergänzung, hab noch zwei weitere Compiler getestet und wie ich erwartet habe. VC2005 geht, Borland C++ 5.5 geht nicht.



  • Also nach stundenlangem suchen & analysieren bin ich wohl auf die Lösung gestoßen. Der Hund liegt in der calling convention begraben. Wenn ich __stdcall hinzufüge:

    struct ISerialCommunication
    {
      virtual void __stdcall Config(int* cfg) = 0;
    };
    

    dann klappts. Wenn ich das jetzt richtig verstanden habe, dann gibt diese calling convention nur an, wie eine Funktion aufgerufen wird, also wie der Stack auf- bzw. abgebaut wird? Gibt es noch irgendwelche Sachen, die man hierbei unbedingt beachten muss?



  • Soweit ich weiß, ist die konkrete Implementierung von virtuellen Funktionen usw leider nicht standartisiert. Evtl implementiert der G++ virtuelle Funktionen anders als der MS-Compiler, dann hast du afaik keine Chance das zum Laufen zu kriegen. Nur, wenn du die Bibliothek mit demselben Compiler (auch selbe Version) benutzt, mit dem du sie erstellt hast, bist du auf der sicheren Seite 😕



  • Doch, das sollte "dank" der Marktmacht Microsofts funktionieren.

    The short explanation is: because COM technology works with other compilers. Now, for the long explanation. Actually, using a member-less abstract class as an interface between modules is exactly what COM does in order to expose COM interfaces. The notion of a virtual table, as we know it in the C++ language, fits nicely into the specification of the COM standard. This is not a coincidence. The C++ language, being the mainstream development language for at least over a decade now, has been used extensively with COM programming. It is thanks to natural support for object oriented programming in the C++ language. It is not surprising at all that Microsoft has considered the C++ language as the main heavy-duty instrument for industrial COM development. Being the owner of the COM technology, Microsoft has ensured that the COM binary standard and their own C++ object model implementation in the Visual C++ compiler do match, with as little overhead as possible.

    No wonder that other C++ compiler vendors jumped on the bandwagon and implemented the virtual table layout in their compilers in the same way as Microsoft did. After all, everybody wanted to support COM technology, and to be compatible with the existing solution from Microsoft. A hypothetical C++ compiler that fails to support COM efficiently is doomed to oblivion in the Windows market. That is why ,nowadays, exposing a C++ class from a DLL via an abstract interface will work reliably with every decent C++ compiler on the Windows platform.

    [url]
    http://www.codeproject.com/KB/cpp/howto_export_cpp_classes.aspx#CppMatureApproach
    [/url]



  • Oh cool! Wieder was gelernt, danke 🙂


Log in to reply