# Pluginschnittstelle



## fadade (27. Juni 2011)

Hey,

wie der Titel schon sagt, wollte ich mal wissen, ob jemand schonmal eine Pluginschnittstelle programmiert hat.
Bestes Beispiel momentan: *Minecraft* mit Server Mods

Die bestehende Serversoftware bietet einen Ordner "Plugins" an, in dem man Plugins einfach reinkopieren muss und beim nächsten Start werden sie mitgeladen. Die bereigestellten Funktionen haben oft SEHR GROßEN EINFLUSS auf das Spiel, scheinen also sehr tief im eigentlichen Spiel "verankert" zu sein.
Jetzt hat sich mir die Frage gestellt, wie man sowas macht (ich verweise hier mal auf den Thread _Programmierte Intelligenz_, wo man das z.B. auch gut anwenden könnte  )
*
Grundlage:*
- Ich habe eine selbst erstellte GUI mit ein paar Textfeldern, Menüs, Optionsdialogen etc.* und der Programmcode an sich ist sehr klein gehalten. 
- und nun soll er eine Pluginschnittstelle bekommen. Diese soll nach und nach (Textverarbeitungs-)Funktionen bereitstellen.
- die zusätzlichen Funktionen werden z.B. _bzw. am liebsten_ in eine DLL gepackt

*Probleme:*
- soweit ich weiß muss eine DLL im Hauptprogramm mit eingebunden sein, wobei im nachhinein eingefügte Plugins würden dann ja nicht erkannt 
- wie bekommt man im Hauptprogramm den kompletten Funktionsumfang zum laufen? Weil wenn das Hauptprogramm z.B. nicht mehr geupdatet wird, aber immer wieder neue Pluginversionen "installiert" werden, sind die neuen Methoden und klassen des Plugins im Hauptprogramm ja unbekannt.

*Ansatz am konkreten beispiel:*
- ein Fenster mit 1 Textbox und drei buttons existieren. ein button dient zum beenden des programms. die anderen beiden sollen einmal den textbox-string umkehren (aus hallo wird ollah) und einmal die ToUpper() bzw. ToLower-Methoden aufrufen.
- die Funktionen sind ja schnell gemacht und in eine DLL gepackt.
- aber wie geht es jetzt weiter? 


So, ich hoffe mal hier kann mir wer helfen, wenn ihr mich nicht verstanden habt, dann meckert bidde rum 


* Meinetwegen sowas wie der Windowseigenen Editor)


----------



## Fragile Heart (27. Juni 2011)

Ja habe ich schon mal. 

Das grundsätzliche Vorgehen ist im Prinzip sehr einfach. Du definierst eine Schnittstelle, im Fall von C++ würde sich eine basisklasse mit Abstrakten Funktionen anbieten und eine globale Funktionen mit der du eine Instanz erzeugen kannst (es gibt auch bessere Mehtoden, aber wenn du damit rum spielst wirst du merken, dass ist die einfachste). Dein Plugin implementiert diese Funktionen dann. 

Als nächstes gehst du im Hauptprogramm hin und durchst einen bestimmten Ordner, wohl dein PlugIn Ordner, nach alle infrage kommenden Funktionen und versuchst diese in den Speicher zu laden und die Funktion aufzurufen. Wenn das bei einer Datei gelingt, dann hast du schon dein Plugin geladen und kannst es verwenden. 

Natürlich gibt es hier viele Fragen die du dir im Detail noch überlegen musst, aber das ist das Grundprinzip.

Edit:


> wie bekommt man im Hauptprogramm den kompletten Funktionsumfang zum laufen? Weil wenn das Hauptprogramm z.B. nicht mehr geupdatet wird, aber immer wieder neue Pluginversionen "installiert" werden, sind die neuen Methoden und klassen des Plugins im Hauptprogramm ja unbekannt.


Nun, in diesen Fall sollte dein Plugin auch noch die GUI erweitern können.


----------



## bingo88 (27. Juni 2011)

Ein wenig Win32-Beispielcode 
(Aus dem Kopf, also mit Vorsicht genießen )


```
class IPlugin
{
public:
    virtual ~IPlugin()
    {
    }

    virtual void MachWasTolles(int param1) = 0;
};

// später im Programm...
#include <windows.h>

typedef IPlugin* (*PluginFactory)(void);

IPlugin* LoadPlugin()
{
    const char *szPluginPath = ...; // Mysteriöser Pfad zum Plugin
    IPlugin *plugin = NULL;

    HINSTANCE hPlugin = LoadLibrary(szPluginPath); // Plugin-DLL laden
    if (hPlugin != NULL)
    {
        PluginFactory pf = (PluginFactory) GetProcAddress(hPlugin, "createPluginInstance"); // Adresse der Factory-Funktion ermitteln
        if (pf != NULL)
        {
            plugin = pf(); // Funktion aufrufen und Plugin erzeugen
        }
        else
        {
            FreeLibrary(hPlugin); // Muss später auch für das Plugin aufgerufen werden, hab ich jetzt hier nicht gemacht.
        }
     }

     return plugin; // Instanz zurückgeben
}

// noch später...
...
IPlugin *plugin = LoadPlugin();
if (plugin != NULL)
    plugin->MachWasTolles(1337);

delete plugin; // wichtig!
...
```
Noch ein wenig Fehlerprüfungen rein und das sollte so funktionieren 

Plugin-DLL:

```
class MeinPlugin : public IPlugin
{
public:
    virtual ~MeinPlugin()
    {
    }

    virtual void MachWasTolles(int param1)
    {
        cout << param1 << endl; // sehr sinnvoll ^^
    }
};

IPlugin *createPluginInstance()
{
    return new MeinPlugin;
}
```


----------



## Fragile Heart (27. Juni 2011)

Sieht nicht schlecht aus, nur das hier gefällt mir gerade nicht.


```
HINSTANCE hPlugin = LoadLibrary(szPluginPath); // Plugin-DLL laden
if (hPlugin != NULL)
{
    ...
    FreeLibrary(hPlugin);
}
```
Laut Beschreibung, funktioniert die FreeLibrary wie folgt



> This function decrements the reference count of the loaded DLL module.
> When the reference count reaches zero, the module is unmapped from the address space of the calling process and the handle is no longer valid.


 
Würde doch eigentlich bedeuten, dass die DLL entladen wird und deine Funktionszeiger ungültig werden.


----------



## bingo88 (27. Juni 2011)

Ich brauch den FP ja nur 1x um ne Instanz des Plugins zu laden. In meinem Beispiel gehe ich halt davon aus, dass das Plugin nur 1x geladen bzw. bei jedem Neuladen neu aus der DLL geladen wird.

Edit: Hmm... hab grad nochmal in die Doku gesehen und könnte evtl. wirklich problematisch sein. Das ist jetzt so ein Punkt, wo ich es ausm Kopf nicht mehr kann und es einfach mal ausprobieren müsste 

Edit2: Ja, man darf FreeLibrary nicht da aufrufen. D. h. man müsste das Handle irgendwo speichern und beim Entladen des Plugins schließen. Hab das mal in meinem Code geändert.


----------



## Fragile Heart (27. Juni 2011)

Ok, aber was ist mit der vtab des IPlugin Objektes? Die Funktionsadresse müsste doch eigentlich auf einen Ort im Speicherbereich der Dll verweisen und dürfte so nachdem Entladen nicht mehr gültig sein. Oder stehe ich da gerade auf den Schlauch?


----------



## bingo88 (27. Juni 2011)

Fragile Heart schrieb:


> Ok, aber was ist mit der vtab des IPlugin Objektes? Die Funktionsadresse müsste doch eigentlich auf einen Ort im Speicherbereich der Dll verweisen und dürfte so nachdem Entladen nicht mehr gültig sein. Oder stehe ich da gerade auf den Schlauch?


 hab meinen Post schon vorher editiert... meine letzte Pluginschnittstelle hab ich in C# geschrieben, da musste ich mich mit so low-level Zeugs net quälen


----------



## Fragile Heart (27. Juni 2011)

Naja, wir haben uns da irgendwie überschnitten.


----------



## fadade (27. Juni 2011)

hi,
danke schonmal ihr beiden für das ausführliche Feedback. Werde mir die Posts nach der Fahrstunde mal durchlesen und dann etwaige Fragen konkretisieren 

bd

PS: Hatte jetzt auch vor das mit C# zu machen ..... EInwände?


----------



## bingo88 (27. Juni 2011)

Nee, natürlich nicht. Vom Prinzip geht es da genauso, nur musst du dich nicht mit LoadLibrary & co. rumschlagen. Ich hatte da mal nen Tutorial zu erstellt, keine Ahnung, ob ich das noch finde...


----------



## Fragile Heart (27. Juni 2011)

Also wenn du das noch hast, hätte ich auch interesse.  Man weiß ja nie wozu man sowas noch braucht.


----------



## fadade (27. Juni 2011)

so, also nochmal danke für den Code, sieht (für mich!) jetzt zwar komplizierter aus, aber isses anscheinend nicht 
 verstanden hab ich ihn zwar nicht so ganz, aber das scheint mir auch nicht unbedingt C# zu sein oder? 

 Wäre an deinem Tutorial also auch interessiert 

 und @Fragile heart: Fragen zum Prinzip bekomm ich jetz nicht mehr hin  ... erstmal Bububettchen^^ und dann überleg ich weiter, aber thx schonmal  für deine kurzbeschreibung.
Hier mal, was mir spontan eingefallen ist:




Fragile Heart schrieb:


> Das grundsätzliche Vorgehen ist im Prinzip  sehr einfach. Du definierst eine Schnittstelle, im Fall von C++ würde  sich eine basisklasse mit Abstrakten Funktionen anbieten und eine  globale Funktionen mit der du eine Instanz erzeugen kannst (es gibt auch  bessere Mehtoden, aber wenn du damit rum spielst wirst du merken, dass  ist die einfachste). Dein Plugin implementiert diese Funktionen dann.


 
- Instanz erzeugen hätte ich mir irgendwie auch denken können
- was genau sind abstrakte Funktionen? _(der Code von bingo88 sieht z.B. ziemlich abstrakt aus  )_ -> wiki ist wie immer nicht so ... einfach zu verstehen^^
- "Dein Plugin implementiert diese Funktionen dann" .... bezieht sich  auf die Instanz, die dann diese "abstrakten Funktionen" enthält, die ich  aus meiner DLL lade?



Fragile Heart schrieb:


> Als nächstes gehst du im Hauptprogramm  hin und durchst einen bestimmten Ordner, wohl dein PlugIn Ordner, nach  alle infrage kommenden Funktionen und versuchst diese in den Speicher zu  laden


hmm... also z.B. ein array mit den "paths" der plugins/DLLs bekomme ich  hin. aber das mit dem "in den speicher laden" ... wie macht man das (in  C#)?  



Fragile Heart schrieb:


> und die Funktion aufzurufen. Wenn das bei einer Datei gelingt, dann hast  du schon dein Plugin geladen und kannst es verwenden.


Heißt ich müsste die "speicherorte" (?) der gefundenen Funktionen wieder durchlaufen und testen _(geht ja jetzt in meinem beispiel noch einfach)_




Fragile Heart schrieb:


> Nun, in diesen Fall sollte dein Plugin auch noch die GUI erweitern können.



Nu ma aber langsam erstmal 
Da muss ich mich dann ja in der klasse irgendwie auf WinFOrm1 oder so beziehen und dann da ein label erstellen lassen o.ä.

Also wenn ihr jetzt nicht so die Lust habt euch mit so einem Anfängergeschwafel von mir abzugeben, dann is das auch okay 
Ich schmeiß morgen auch mal google an und poste hier auch gefundene Ergebnisse

_Pre-PS: Ihr habt echt Ahnung _


----------



## Fragile Heart (27. Juni 2011)

fadade schrieb:


> Aber das scheint mir auch nicht unbedingt C# zu sein oder?


Nein das ist C++, wie das in C# geht kann ich dir im Moment auch nicht sagen, hab ich mich noch nie mit beschäftigt. Aber ich denke Bingo kann uns da aufklären.



> - was genau sind abstrakte Funktionen? _(der Code von bingo88 sieht z.B. ziemlich abstrakt aus  )_ -> wiki ist wie immer nicht so ... einfach zu verstehen^^


Also eine Abstrakte Funktion ist eine Funktion in einer Basis Klasse die nicht implementiert ist. Damit kannst du beschreiben wie eine Funktion aussehen muss und der Compiler stellt dann für dich sicher, dass du sie in den abgeleiteten Klassen auch implementiert hast, da er sonst kein Objekt erzeugung zu lässt.

Ist in Bingos Programmteil enthalten im IPlugin bei der Funktion MachWasTolles() 



> "Dein Plugin implementiert diese Funktionen dann" .... bezieht sich auf die Instanz, die dann diese "abstrakten Funktionen" enthält, die ich aus meiner DLL lade?


Ja, genau.



> Heißt ich müsste die "speicherorte" (?) der gefundenen Funktionen wieder durchlaufen und testen _(geht ja jetzt in meinem beispiel noch einfach)_


Ja, auch richtig verstanden. Allerdings suchst du nach Dateien und schaust dann ob die Funktion dadrin exportiert wurde.



> Nu ma aber langsam erstmal
> Da muss ich mich dann ja in der klasse irgendwie auf WinFOrm1 oder so beziehen und dann da ein label erstellen lassen o.ä.


Naja, das ist recht einfach wenn du das Prinzip verstanden hast.


----------



## bingo88 (28. Juni 2011)

Ich hab es doch tatsächlich noch auf meinem Server gefunden 

Das habe ich 2010 erstellt, ist allerdings auf Englisch (da meine Website auf Englisch ist). Ich hab nochmal reingeschaut, ob es auch funktioniert und mir sind keine groben Schnitzer aufgefallen. Das Design ist vermutlich nicht das beste, sollte aber ausreichend sein, um das Vorgehen zu demonstrieren. Bei Fragen einfach melden.


----------



## Fragile Heart (28. Juni 2011)

Danke sehr! Werde ich mir nachher mal in aller Ruhe ansehen.


----------



## fadade (29. Juni 2011)

Hi,

jop, sowas habe ich gesuch 
Auch wenn es, wie du schreibst, nicht unbedingt tauglich ist für ernste Projekte, aber es ist schonmal ein Anfang. *BIG THX* dafür^^

Hab auch mal ein bisschen rumgesucht, aber wie man sowas selber macht hab ich nicht gefunden, nur wie man bestehende Schnittstellen und Frameworks nutzt


----------



## bingo88 (29. Juni 2011)

Du musst im Prinzip "nur" die Interfaces IPlugin und IHost selbst designen und evtl. nen bisschen Verwaltungslogik hinzupacken. Mehr ist das eigentlich nicht mehr.


----------



## Fragile Heart (30. Juni 2011)

Wenn du das wirklich ernst nutzen willst, denk bitte daran, dass du so eventuell gefährlichen Code ausführst, denke also immer darüber nach wie du die Userdaten schützen kannst.


----------

