Mit Shared Libs auf Funktionen im Hauptprogramm zugreifen?



  • Hallo allerseits.
    Ich hätte da eine Frage:
    Ist es möglich, von einer shared library aus auf Funktionen im aufrufenden Programm zuzugreifen?
    Ich frage aus folgendem Grund: Ich habe eine Mittelgroße Applikation, die durch shared libraries erweiterbar sein soll. Nun bekommt die library von der applikation zur laufzeit bestimmte objekte übergeben. Die library kennt die funktionen der hauptapplikation und damit auch der eingesetzten objekte ja zur linkzeit nicht und spuckt daher linker fehler aus. Ich könnte jetzt natürlich einen großteil meiner Applikation noch einmal als statische library machen und auf die dynamische linken, damit die Funktionen verfügbar sind, was aber die shared library ja nur sinnlos vergrößern würde (die funktionen sind zur laufzeit ja dann im hauptprogramm vorhanden).

    Ich hoffe, man versteht mein Anliegen 🙂

    Grüße
    Florian


  • Administrator

    Also in C/C++ hätte man die Möglichkeit Funktionspointer zu machen. Man könnte somit den Objekten aus der Library Funktionen als Pointer übergeben, welche die Objekte aus der Library dann aufrufen könnten.

    Zu Funktionspointer in C/C++:
    http://www.newty.de/fpt/index.html

    Grüssli



  • Unter Linux/Unix sollte dir das RTLD_GLOBAL-Flag bei man: dlopen(2) weiterhelfen.



  • hm, gibt es sowas auch unter Windows? und wie verhinder ich dann, dass der compiler beim kompilieren der .so/.dll über die fehlenden referenzen meckert?

    Ich will auch nochmal etwas näher in die Details gehen, dass ich noch etwas besser verstanden werde. (ich rede hier mal von dll statt shared library)

    Also, ich habe zum einen die Hauptanwendung und die DLL. Die Hauptanwendung hat ein Objekt vom der Klasse Client. Diese Klasse hat verschiedene Methoden und Attribute. Dann wird die DLL vom Hauptprogramm dynamisch zur laufzeit geladen und eine Funktion der DLL aufgerufen. Dieser Funktion wird ein Pointer auf das Client-Objekt übergeben. Die DLL soll jetzt auf die Methoden des Client-Objektes Zugreifen können, OHNE, dass die entsprechenden Methoden beim Kompilieren der DLL mit ringelinkt wurden.
    Das würde zum einen die DLL verkleinern, weil die Methoden dann nur einmal vorhanden sind (im Hauptprogramm), statt in beiden Teilen und würde auch die Wartung vereinfachen, weil man die Methode nur noch im Hauptprogramm ändern muss, aber nicht mehr in den DLLs.

    Mit Funktionspointern bzw. Funktors hab ich auch schon überlegt, aber das währe ein extremer Aufwand, wenn das Hauptprogramm viele Objekte mit vielen Methoden zur Verfügung stellt, gibt es da auch was komfortableres?



  • hallo, eigentlich sollte es reichen, wenn du die methoden von Client alle virtuell machst.

    du musst dann in der dll natürlich die header-datei includen.

    die implementierung der methoden kann dann aber in dem hauptprogramm liegen.

    wenigstens klappt das bei mir so, das geht dann auch zwischen dlls untereinander.

    ciao,
    jenz



  • So wie du eine dll laden kannst kannst du auch eine exe laden, kommt aufs gleiche raus, musst die Funktionen nur exportieren, dann kann man sie aus der dll heraus laden. Ob du eine exe statisch (im Sinne von __declspec( dllexport/dllimport ) zur dll linken kannst, kann ich dir jetzt nicht sagen, aber im Zeifelsfall kannst es ja dynamisch machen (ist eh besser, dann kannst du bessere Fehlermeldungen ausgeben).



  • Danke jenz, ich glaube, das war genau die lösung, die ich gesucht habe. Weis zwar noch nicht, ob das so funktioniert, wie ich es mir vorstelle, weil ich das erst morgen ausprobieren kann, aber es linkt jetzt zumindest auch ohne implementierung.

    Danke!



  • Du schreibst ne mittelgroße Applikation und weißt sowas nicht?
    Das wird was für thedailywtf.com



  • Eine für mich mittelgroße Applikation 🙂 (eigendlich sogar bis jetzt meine größte).

    Ich wusste bis jetzt durchaus, was virtuelle Methoden sind, aber ich wusste nicht, dass das auch geht ohne den code zu kennen, der später da rein kommt 🙂



  • Hm, irgendwie will das bei mir nicht 😞

    Ich habe jetzt die Methoden virtuell gemacht und die dll linkt auch, aber sobald ich aus der DLL heraus eine der Methoden aufrufen will stürzt mir das Programm ab 😞



  • zeig doch mal ein bisschen code,
    oder schick es mir zu ...

    eigentlich müsste das funktionieren.

    jenz



  • Hab den Fehler gefunden, hatte in paar virtual deklartionen im Hauptprogramm vergessen.



  • Was muss ich eigendlich bei der Programmierung mit virtuellen Methoden alles beachten, weil, die Header von Hauptprogramm und DLL scheinen ja exakt gleich sein zu müssen, damit das Funktioniert. Sprich, wenn ich die API im Hauptprogramm erweiter muss ich auch den Header der DLL anpassen, auch wenn die die neue Funktion gar nicht benutzt, oder seh ich das falsch?

    Wird vor allem später dann blöde sein, wenn ich DLLs/Plugins von Drittanbietern haben sollte, dann müssen die ja jedes mal neu Kompiliert werden, wenn sich etwas an der API ändert 😕



  • @FloFri
    Wenn du die API änderst, müssen alle Plugins neu kompiliert werden. Daher versuchen viele Projekte API Änderungen zu vermeiden und eine API Änderung ist oft ein Grund dafür die Major-Versionsnummer zu ändern.



  • Es geht mir ja nicht darum, die Signatur einer Funktion oder die Funktionsweise zu ändern.

    Ich meinte eher das folgende:
    Wenn wir mal von einem Socket-Objekt aus einer Netzwerk-Klasse ausgehen und sagen wir mal, das hat Funktionen zum connecten, senden und empfangen und wird von dem Plugin benutzt.
    Jetzt kommen aber noch Funktionen für einen Server, also zum beispiel listen dazu, dann müsste, wenn ich das richtig sehe, bei der vorgehensweise mit den virtuellen methoden, gleich alle plugins, die so einen socket benutzen neu kompiliert werden, auch wenn sie die listen-methode gar nicht verwenden.
    Wenn ich zum Beispiel funktionspointer benutzen würde, währe das nicht der fall, aber funktionspointer sind eben um einiges aufwendiger, als virtuelle methoden.



  • Sieht da noch jemand eine Möglichkeit das Problem auf eine einfache Art und Weise zu lösen oder muss ich mir doch einen komplexen Pluginhandler mit Funktionspointern/Funktoren basteln?



  • Hallo,

    warum nimmst du dann nicht einfach eine weitere "virtuelle plugin klasse" her?

    Und dann mit dynamischen Casts herausfinden, um welche Klasse es sich wirklich handelt...

    jenz



  • Hm, ich verstehe nicht ganz, wie du das meinst, also, jedes mal, wenn ich die API erweiter soll ich von der vorherigen Klasse eine neue ableiten und ergänzen?



  • Hallo,

    ableiten wäre wahrscheinlich am sinnvollsten.
    Wenn ich das jetzt richtig verstanden habe funzt es ja so, dass eine dll geladen und von ihr ein Objekt an das Hauptprogramm übergeben wird, oder?
    Oder funktioniert es andersrum? Egal, jeweils der der das Objekt empfängt muss halt im Rahmen seiner Kenntnis herausfinden, um was für einen Typ es sich handelt.
    Du übergibst ja bestimmt nur einen Pointer, oder?

    Zeig doch mal ein bisschen Code, wo dein Problem auftreten könnte. Das ist so luftleer einfach unklarer zu erklären...

    jenz



  • Ok, ich poste mal ein bisschen Beispielcode:

    Hier die DLL-Files

    main.cpp

    #include <iostream>
    
    #include <windows.h>
    #include "client.h"
    
    using namespace std;
    
    #define _DLL_FUNCTION extern "C" __declspec(dllexport)
    
    _DLL_FUNCTION void myFunc(Client *myclitest);
    
    // a sample exported function
    void myFunc(Client *myclitest)
    {
        cout << "Calling Client-Function" << endl;
        myclitest->ShowString();
    }
    
    BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
    {
        switch (fdwReason)
        {
            case DLL_PROCESS_ATTACH:
                // attach to process
                // return FALSE to fail DLL load
                break;
    
            case DLL_PROCESS_DETACH:
                // detach from process
                break;
    
            case DLL_THREAD_ATTACH:
                // attach to thread
                break;
    
            case DLL_THREAD_DETACH:
                // detach from thread
                break;
        }
        return TRUE; // succesful
    }
    

    client.h

    #ifndef CLIENT_H
    #define CLIENT_H
    
    #include <string>
    #include <iostream>
    
    using namespace std;
    
    class Client
    {
        public:
            Client(string initStr);
            virtual ~Client();
            virtual void ShowString();
    };
    
    #endif // CLIENT_H
    

    Hier das Hauptprogramm

    main.cpp

    #include <iostream>
    #include <windows.h>
    
    #include "client.h"
    
    using namespace std;
    
    typedef void (WINAPI * p_myFunc) (Client *myclitest);
    
    int main()
    {
        Client* myCli = new Client("ichbins");
    	cout << "Loading DLL" << endl;
    
        HINSTANCE dllInstance = LoadLibrary("testdll/testdll.dll");
    
        if (dllInstance == NULL)
        {
            cout << "Can not load!" << endl;
            return 1;
        }
    
        p_myFunc myFunc = (p_myFunc)GetProcAddress((HMODULE)dllInstance, "myFunc");
    
        if (myFunc == NULL)
        {
            cout << "Error" << endl;
            return 1;
        }
    
        cout << "Calling DLL-Function" << endl;
        myFunc(myCli);
    
    	return 0;
    }
    

    client.h

    #ifndef CLIENT_H
    #define CLIENT_H
    
    #include <string>
    #include <iostream>
    
    using namespace std;
    
    class Client
    {
        public:
            Client(string initStr);
            virtual ~Client();
            virtual void ShowString();
            void check();
        private:
            string myStr;
    };
    
    #endif // CLIENT_H
    

    client.cpp

    #include "client.h"
    
    Client::Client(string initStr)
    {
        myStr = initStr;
    }
    
    Client::~Client()
    {
    }
    
    void Client::ShowString()
    {
        cout << myStr << endl;
    }
    

    So. Soweit funktioniert das Beispielprogramm auch. Jetzt zu meinem Problem.
    Gehen wir mal davon aus, die DLL kommt von einem Drittanbieter und es gibt auch noch eine Reihe Anderer.

    Jetzt füge ich eine neue Funktion

    virtual void SetString(string newString);
    

    zu der Client-Klasse im Hauptprogramm hinzu, damit die Dlls bei Bedarf noch etwas mehr machen können.
    Jetzt muss ich aber alle Dlls neu kompilieren, bzw. kompilieren lassen, da die VMT der Klasse Client in dem Hauptprogramm und in den Dlls nicht mehr übereinstimmt.


Log in to reply