Methodenzeiger bei Plugin-Interface



  • Hallo,

    ich bin gerade dabei und schreibe eine Software die Messwerte von verschiedenen Geräten grafisch darstellen können soll.

    Nun will ich für die Dateneingabe ein Plugin-Interface erstellen, damit für die verschiedenen Geräte einfach nur die entsprechende DLL geladen werden muss.

    Ich kann nun die DLL laden und entladen, Funktionen der dll aufrufen, und sogar inzwischen der DLL eine Callback-Funktion (adresse einer Methode) übergeben, so das die DLL eine funktion im Hauptprogramm aufruft.

    Das klappt auch soweit. Nur sobald ich auf die Daten meiner Hauptanwendung (Member der Klasse zu der die Callback-Funktion gehört) zugreifen möchte stürzt das Programm ab. Ich denke jetzt das liegt daran das die Funktion zwar an der Zeigerposition gefunden und aufgerufen wird. Allerdings nicht das Object (also die Instanz der Klasse in der Hauptanwendung).

    Ich müsste der DLL, denke ich, wohl 2 Adressen übergeben - Die des Objects und die der Methode.

    Und genau hier liegt der Hund begraben: Da es ne Standard-DLL werden soll, also mit C-Interface, habe ich jetzt ein struct innerhalb der DLL angelegt, in welches ich alle benötigten Daten aus meiner Hauptanwendung heraus reinschreiben möchte.
    Dafür erzeuge ich das gleiche struct innerhalb der Hauptanwendung, fülle es mit Daten und übergebe es dann einer init-function der DLL.

    typedef void (*AddDataFunc)(...);
    
    typedef struct _mod_info
    {
    	int version;
    	char *description;
    	HMODULE hModule;
    	HANDLE handle;
    	HWND hWnd;
    
    	AddDataFunc AddData;
    
    	int width;
    	int height;
    } mod_info;
    

    dieses struct kennt die DLL und die Hauptanwendung.

    CString strFunction = "init";
    	typedef mod_info* (*MYPROC)(...);
    	MYPROC proc = NULL;
    	proc = (MYPROC) GetProcAddress(m_Modules.GetAt(m_nCurrentModule).hModule, strFunction);
    
    		CErgoSoftDoc* edoc;
    		edoc = static_cast<CErgoSoftDoc*>(m_doc);
    
    		mod_info* pModInfo = NULL;
    		pModInfo->AddData = &CData::Add;
    		pModInfo->handle = m_doc;
    		pModInfo = proc(pModInfo);
    

    jetzt sagt er mir natürlich das er

    'void (__thiscall CData::* )(int,int,int,int,int,int,int,int,int)' kann nicht in 'AddDataFunc' konvertiert werden

    Logisch, weil es nen Methodenzeiger und nicht (nur) ein Funktionszeiger ist. Die DLL soll aber die Klasse CData nicht kennen, sondern einfach nur den Zeiger aufs Object und Funktion. Die Hauptanwendung soll das dann anhand der zeiger die im o.g. struct stehen, zurück-casten.

    - puh, ähh - alles klar? -



  • Was Du machen willst geht nur mit statischen Member-Funktionen (also mit "static" davor).
    I.d.R. wird dann immer noch ein Parameter mit übergeben, welcher PVOID ist. In diesen kann dann Dein Hauptprogramm was reinstecken was es will. Die DLL muss dann dieses auch genau der "Callback" als Parameter mitgeben. Damit kannst Du dann in Deinem Hauptprogramm (bzw. innerhalb Deiner Callback) die Instanz identifizieren (wenn Du diese natürlich mitgegeben hast).

    Das beste Beispiel für sowas sind diverse Callbacks in der Win32-API...



  • Hallo,

    warum machst Du das denn nicht in C++?
    Gerade das wäre doch die Stärke der Sprache.
    Dazu musst Du deiner Applikation nur die Basisklasse bekannt
    geben, und leitest die anderen Klassen davon ab. Das wird nur
    problematisch, wenn Du die Dll auch z. B. mit Borland verwenden
    wilsst (NameMangling).

    Im unten genannten Beispiel gibt es zwei bekannte
    Kommunikationslayer (CAppCom, CBasePlugin), von denen
    Du deine Applikation bzw. die Plugions ableitest und
    über die Du kommunizieren kannst. Du verwendest in diesem
    Fall mehrfachableitung. Das ist IMO neben inhertited
    templates die eleganteste Art zu kommunizieren.

    Beispiel:

    CAppCom
    {
    public: 
       CAppCom()  {}
       ~CAppCom() {}
       virtual bool OnNewData (CBasePlugin& plugIn, char *p_data) { return cbFALSE; }
    }
    
    CBasePlugin
    {
    public:
       CBasePlugin  (CMyApp& app) m_app (app) {}
       ~CBasePlugin () {}
    
       // Nachrichten zum Plugin
       virtual bool OnInit       () { return cbFALSE; }
       virtual bool OnBeforeExit () { return cbFALSE; }
    protected:
    CMyApp& m_app;
    }
    
    class CMyApp : public CFrameWnd, public CAppCom
    {
    public:
       // .....
       virtual bool OnNewData (CBasePlugin& plugIn, char *p_data) 
       {
           printf ("NewData received" );
       }
    }
    
    // Wenn ein Plugin nun m_app.OnNewData() aufruft, wird nicht
    // die Funktion des Basisklasse, sondern die deiner Applikation 
    // aufgerufen. So schaffst Du ein typsicheres Interface.
    

Anmelden zum Antworten