Zum Inhalt wechseln

Als Gast hast du nur eingeschränkten Zugriff!


Anmelden 

Benutzerkonto erstellen

Du bist nicht angemeldet und hast somit nur einen sehr eingeschränkten Zugriff auf die Features unserer Community.
Um vollen Zugriff zu erlangen musst du dir einen Account erstellen. Der Vorgang sollte nicht länger als 1 Minute dauern.

  • Antworte auf Themen oder erstelle deine eigenen.
  • Schalte dir alle Downloads mit Highspeed & ohne Wartezeit frei.
  • Erhalte Zugriff auf alle Bereiche und entdecke interessante Inhalte.
  • Tausche dich mich anderen Usern in der Shoutbox oder via PN aus.
 

   

Foto

[TUT] Klassenbasiertes Plugin-System in C++

- - - - -

  • Bitte melde dich an um zu Antworten
Keine Antworten in diesem Thema

#1
TgZero

TgZero

    Noob

  • Members
  • PIPPIP
  • Likes
    16
  • 4 Beiträge
  • 0 Bedankt
Klassenbasiertes Plugin-System in C++

  Tool Beschreibung
Das ganze wurde gestern von mir auf coderz veröffentlicht. Da die Seite atm down ist, poste ich das ganze mal hier. Dies Tutorial zeigt kurz und knapp wie man ein Pluginsystem in C++ schreiben kann, welches mit Klassen/Objekten arbeitet.


#Vorwort#
Dieses Tutorial soll beschreiben, wie ihr ein klassenbasiertes Plugin-System für eure Programme schreiben könnt, um diese während der Laufzeit
um Funktionalität zu erweitern. Klassenbasiertes bedeutet hier, dass, neben ein paar anderen Funktionen zur Verwaltung, zwei Funktionen für das
Erstellen und für das Löschen des jeweiligen Plugins aus einer dynamischen Bibliothek importiert werden.
Mit dynamischer Bibliothek ist hier eine "shared Library" (unter Linux/unix-: *.so, unter Windows: *.dll) gemeint.

#Das Laden einer dynamischen Bibliothek#
Sowohl unter Windows, als auch unter Linux werden uns API/Syscalls zur verfügung gestellt zum Laden,Entladen und Importieren von Funktionen.
Dabei werden sogenannte Funktionszeiger erstellt, welche dann zur Laufzeit auf die Funktionen der dynamischen Bibliotheken zeigen und so aufgerufen
werden können.

Ein gängiger Ablauf wäre der Folgende:
  • Beim Starten unseres Programms wird ein Verzeichnis durchsucht und alle dynamischen Bibliotheken einzeln geladen
  • Beim Laden wird die jeweilige API-Function/Syscall aufgerufen und anschließend überprüft, ob die Bibliothek über die nötigen Funktionen verfügt.
  • Falls dies der Fall ist, wird diese Bibliothek in dem Speicher gehalten und ein Plugin-Objekt wird im System hinterlegt.
  • Beim Beenden unseres Programms werden alle Plugin-Objekt zerstört und die Bibliotheken werden aus dem Speicher geladen.
Da ich im folgenden wxDynamicLibrary für das Laden und Halten einer dynamischen Bibliothek benutze werde ich hier nicht weiter auf die Funktionen
eingehen. Ein Beitrag dürfte sich bei CoderZ.cc befinden, oder ihr guckt selbst folgende Funktionen nach:
Windows: LoadLibrary, GetProcAddress, FreeLibrary
Linux: dlopen, dlsym, dlclose

#Funktionszeiger#
Im Vorwort wurde kurz auf Funktionszeiger hingewiesen, doch wofür genau braucht man diese?
Sowohl GetProcAddress,dlsym, als auch wxDynamicLibrary geben uns für eine gesuchte Funktion nur eine Adresse zurück.
Wie wir die Funktion nun aufrufen ist uns überlassen, wobei wir immer die Aufrufkonvention einhalten sollten.
Angenommen wir möchten in unserer dynamischen Bibliothek eine Funktion "double __cdecl GetSdkVersion()" haben,
dann erstellen wir uns einen Funktionszeiger mit double(* _cdecl ptrVersion)() und lassen diesen auf die Adresse, welche
wir von unserer Adress-Funktion bekommen haben, zeigen. Nun können wir diesen Zeiger bequem aufrufen.


#Das Design der Plugins#
So nun gehts los mit dem Designen unserer Klassen. Da wir Plugins verwalten, dynamische Bibliothek laden und erst bei der Destruktion wieder
freigeben. Hört sich für mich nach einem typischen Manager und einer Liste/Vektor mit shared_ptr an.
Da wir mehrere Plugintypen erlauben möchten, erstellen wir uns am besten eine Basisklasse, hier PluginBase, und erben von dieser für jeden neuen
Plugintyp,oder von einem bereits vorhandenem Plugintyp. So haben wir für jeden Plugintyp eine eigene PluginType-Klasse mit rein virtuellen Methoden.
Wenn wir ein Plugin schreiben möchten, müssen wir nurnoch von der jeweiligen PluginType-Klasse erben und die virtuellen Methoden implementieren.
Angenommen wir möchten unser Programm mit den Plugintypen "Serialisierung","GUI","Dialog","Frame" ausstatten,
dann würden "Serialisierung" und "GUI" direkt von "PluginBase" erben, "Dialog" und "Frame" jedoch von "GUI".
Alles in allem hätten wir so 6 PluginType-Klassen die wir benutzen könnten.
Falls ein PDK (Plugin Developer Kit) gewünscht ist, müssen natürlich alle PluginType-Klassen für die Plugintypen in diesem enthalten sein.
Am besten ihr geht für jede Funktion eurer Klassen eine Aufrufkonvention an, sodass die Kompatibilität unter verschiedenen Kompilern erhalten bleibt.
Doch wer erstellt eine neue Instanz der Plugins?
Dazu enthält die dynamische Bibliothek zwei Funktionen "PluginBase* GetNewPluginInstance()" und "void DeletePluginInstance(PluginBase* instance)", die
für das Erzeugen und Löschen zuständig sind.

#Fertige Version#
Ich werde hier ein kleines Beispiel zeigen, welches sehr minimal aufgebaut ist. Als PDK muss lediglich PluginBase, PluginTypes und die jeweiligen
PluginType-Klassen, sowie PluginExportHeader mitgeliefert werden. Für ein Plugin muss dann eine neue Plugin-Klasse erstellt werden,
welche "IMPLEMENT_PLUGIN(name)" und "SET_PLUGIN_TYPE(type)" benutzt. In diesem Beispiel verzichte ich auf eine PluginType-Klasse, sondern erbe
direkt von PluginBase mit dem Plugintype "Base".

#Die Programm-Sicht#

PluginBase.h
#ifndef PLUGINBASE_H
#define PLUGINBASE_H

#include <string>

class PluginBase
{
public:
		 PluginBase();
	 virtual ~PluginBase();
	 virtual unsigned long __cdecl getPluginType()=0;
	 virtual std::string __cdecl getAuthor() = 0;
	 virtual std::string __cdecl getInformation()=0;
};

#endif // PLUGINBASE_H

PluginBase.cpp
#include "PluginBase.h"
PluginBase::PluginBase()
{
//ctor
}

PluginBase::~PluginBase()
{
//dtor
}

PluginTypes.h
#ifndef PLUGINTYPES
#define PLUGINTYPES

enum{
	 PluginType_Base = 0
};

#endif // PLUGINTYPES

PluginManager.h
#ifndef PLUGINMANAGER_H
#define PLUGINMANAGER_H

#include <vector>
#include <wx/dynlib.h>
#include <PluginBase.h>
#include <PluginTypes.h>

class PluginManager
{
public:
	 PluginManager();
	 virtual ~PluginManager();

	 void loadFromDirectory(std::wstring directory);
	 unsigned long getPluginCount() const;
PluginBase* getPlugin(unsigned long index) const;

private:
	 double minVersion;
	 double maxVersion;
	 std::vector<std::pair<std::shared_ptr<wxDynamicLibrary>,PluginBase*>> vecPlugins;
};

#endif // PLUGINMANAGER_H

PluginManager.cpp
#include "PluginManager.h"
#include <wx/dir.h>

const wxString strGetNewPluginInstace = "GetNewPluginInstance";
const wxString strDeletePluginInstance = "DeletePluginInstance";
const wxString strGetPdkVersion = "GetPdkVersion";
const wxString strGetPluginType = "GetPluginType";

typedef double (*__cdecl ptrGetPdkVersion)();
typedef PluginBase* (* __cdecl ptrGetNewPluginInstance)();
typedef void (* __cdecl ptrDeletePluginInstance)(PluginBase* instance);
typedef unsigned long (* __cdecl ptrGetPluginType)();

PluginManager::PluginManager()
{
this->minVersion = 1.0;
this->maxVersion = 1.0;
}

PluginManager::~PluginManager()
{
ptrDeletePluginInstance funcDeletePluginInstance;
for(auto iter : vecPlugins)
{
funcDeletePluginInstance = iter->first->GetSymbol(strDeletePluginInstance);
funcDeletePluginInstance(iter->second);
}
vecPlugins.clear();
}

void PluginManager::loadFromDirectory(std::wstring directory)
{
wxArrayString files;
wxString spec;
double pdkversion;
bool hasSymbols;
ptrGetPdkVersion funcGetPdkVersion;
ptrGetNewPluginInstance funcGetNewPluginInstance;
ptrDeletePluginInstance funcDeletePluginInstance;
ptrGetPluginType funcGetPluginType;

#ifdef __WIN32__
spec = "*.dll";
#else
spec = "*.so";
#endif // __WIN32__

if(wxDirExists(directory))
{
	 wxDir::GetAllFiles(directory,&files,spec);
	 for(unsigned long i=0; i<files.Count(); i++)
	 {
		 std::shared_ptr<wxDynamicLibrary> lib(new wxDynamicLibrary(files[i]));
		 if(lib->IsLoaded())
		 {
			 hasSymbols = true;
			 hasSymbols = hasSymbols && lib->HasSymbol(strGetNewPluginInstace);
			 hasSymbols = hasSymbols && lib->HasSymbol(strDeletePluginInstance);
			 hasSymbols = hasSymbols && lib->HasSymbol(strGetPdkVersion);
			 hasSymbols = hasSymbols && lib->HasSymbol(strGetPluginType);


			 if(hasSymbols == true)
			 {
				 funcGetNewPluginInstance = (ptrGetNewPluginInstance) lib->GetSymbol(strGetNewPluginInstace);
				 funcGetPdkVersion = (ptrGetPdkVersion) lib->GetSymbol(strGetPdkVersion);
				 funcGetPluginType = (ptrGetPluginType) lib->GetSymbol(strGetPluginType);

				 pdkversion = funcGetPdkVersion();
				 if(pdkversion <= this->maxVersion && pdkversion >= this->minVersion)
				 {
					 std::pair<std::shared_ptr<wxDynamicLibrary>,PluginBase*> pa = std::make_pair(lib,funcGetNewPluginInstance());

					 switch(funcGetPluginType())
					 {
					 case PluginType_Base:
						 this->vecPlugins.push_back(pa);
						 break;

					 default:
						 break;
					 }
				 }
			 }
		 }
	 }
}
}

unsigned long PluginManager::getPluginCount() const
{
return this->vecPlugins.size();
}

PluginBase* PluginManager::getPlugin(unsigned long index) const
{
return vecPlugins[index].second;
}


#Die PDK-Sicht"

PluginExportHeader.h
#ifndef PLUGINEXPORTHEADER
#define PLUGINEXPORTHEADER

#ifdef __WIN32__
#include <windows.h>
#endif

#ifdef BUILD_DLL
#define DLL_EXPORT __declspec(dllexport)
#else
#define DLL_EXPORT __declspec(dllimport)
#endif

#include "PluginBase.h"
#define IMPLEMENT_PLUGIN(name) DLL_EXPORT PluginBase* __cdecl GetNewPluginInstance(){return (PluginBase*) new RealPlugin();}
#define SET_PLUGIN_TYPE(type) DLL_EXPORT unsigned long __cdecl GetPluginType(){return type;}

#ifdef __cplusplus
extern "C"
{
#endif
DLL_EXPORT double					 __cdecl GetPdkVersion();
DLL_EXPORT void					 __cdecl DeletePluginInstance(PluginBase* instance);
#ifdef __cplusplus
}
#endif

#endif // PLUGINEXPORTHEADER

PluginExportHeader.cpp
#include "PluginExportHeader.h"

#define PdkVersion 1.0

#ifdef __WIN32__
extern "C" DLL_EXPORT BOOL APIENTRY 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
}
#endif

DLL_EXPORT double __cdecl GetPdkVersion()
{
return PdkVersion;
}

DLL_EXPORT void __cdecl DeletePluginInstance(PluginBase* instance)
{
delete instance;
}

RealPlugin.h
#ifndef REALPLUGIN_H
#define REALPLUGIN_H

#include "PluginExportHeader.h"
#include "PluginTypes.h"
#include "PluginBase.h"

#include <string>

class RealPlugin : PluginBase
{
public:
	 RealPlugin();
	 virtual ~RealPlugin();
	 virtual std::string __cdecl getAuthor();
	 virtual std::string __cdecl getInformation();
	 virtual unsigned long __cdecl getPluginType();
};

#endif // REALPLUGIN_H

RealPlugin.cpp
#include "RealPlugin.h"

#ifdef __cplusplus
extern "C"
{
#endif
IMPLEMENT_PLUGIN(RealPlugin)
SET_PLUGIN_TYPE(PluginType_Base)
#ifdef __cplusplus
}
#endif

RealPlugin::RealPlugin()
: PluginBase()
{
//ctor
}

RealPlugin::~RealPlugin()
{
//dtor
}

std::string __cdecl RealPlugin::getAuthor()
{
return "Ich bin der Autor";
}

std::string __cdecl RealPlugin::getInformation()
{
return "Ein Plugin fuer Testzwecke";
}

unsigned long __cdecl RealPlugin::getPluginType()
{
return PluginType_Base;
}

#Nachwort#
Das ganze stammt aus meinem aktuellem Projekt und wurde noch um einige Plugintypen erweitert.
Der obige SRC wurde dafür leicht abgeändert, sodass er nur das Nötigste enthält und nicht verwirrt.
Falls noch Fragen offen sind, beantworte ich diese gerne innerhalb der Kommentare/Posts. Falls es zu Problemen während des Kompiliervorgangs kommt, schreit einfach laut auf :D

mfg
euer TgZero



  Thema Forum Themenstarter Statistik Letzter Beitrag

Besucher die dieses Thema lesen:

Mitglieder: , Gäste: , unsichtbare Mitglieder:


This topic has been visited by 36 user(s)


    acidvirus23, b.st0rm, Born2Hack, Bot4ng, Botmopp, BurningWay, Cube, Dope, Download305, easysurfer, HellYeah, hikhack7, ice, Imperial, Iron, Izon, jerexdlv, lolorollo, lynx, MasterGee, mr.blue, nong_dan, pdr0, peterslow, Psykoon303, R3s1stanc3, S1lent, shuffle, smc2014, speedfreak, Stanley, TgZero, thaJack, trooperhdx, Unkiii, Xenio
Die besten Hacking Tools zum downloaden : Released, Leaked, Cracked. Größte deutschsprachige Hacker Sammlung.