Mit diesem Workshop möchten wir Ihnen zeigen, wie man seine eigenenen Programme um sogenannte Plugins in Form von DLL-Dateien erweitern kann. Ein Tutorial von Fabian Stern ( http://www.smart-coding.com ) Die drei Projekte zu den hier angesprochenen Codes sind im Download enthalten. Vorwort Und darum geht es auch gleich los mit… 1. Funktionsweise von Plugins in .NET durch ein Interface Das Interface ist folgendermaßen aufgebaut: (clsPluginLoader) Public Interface IPlugin Function GetName() As String Sub Load() Sub Unload() Sub Run() Sub SetHost(ByVal mHost As IHost) End Interface Public Interface IHost Function myLabel() As System.Windows.Forms.Label End Interface Das Plugin besitzt also beispielsweise die Funktion GetName, die eine eindeutige Kennung des Plugins zurückliefern sollte, damit man es später identifizieren und zuordnen kann. In dem Assembly clsPluginSample ist der eindeutige Name einfach "MyPlugin by Fabian Stern". Unsere Anwendung hingegen soll lediglich den Zugriff auf ein Label ermöglichen, in das wir vom Plugin aus hineinschreiben möchten. Wie wir sehen, ist also ein Zwei-Wege-Zugriff durchaus erwünscht und möglich. Nun gilt ein Spezialfall: Die Interfaces müssen von Plugin und Anwendung identisch sein, daher erstellen wir die Interfaces in einem extra Assembly (clsPluginLoader.dll) und fügen sowohl beim Plugin als auch bei der Anwendung einen Verweis auf diese Datei hinzu. Damit haben wir Schritt 1 von 3 getan und können gleich weitergehen zur... 2. Erstellung des Plugins mit vorgegebenen Funktionen vom Interface Ist das Interface erstellt und der Verweis auf den Loader vollzogen, müssen grundlegende Dinge beachtet werden:
Zunächst jedoch müssen wir der CLR (Common Language Runtime) erlauben, das Plugin zur Laufzeit zu erstellen. Dies geschieht durch einfache Serialisierung der Klasse: <Serializable()> Public Class clsPlugin ' Make it creatable on Runtime Hiermit erlauben wir das Kapseln der Funktionen mit unserem Programm: Inherits MarshalByRefObject ' AppDomain Compatibility Es folgt ein Beispiel einer gekapselten Funktion: Public Function GetName() As String Implements clsPluginLoader.IPlugin.GetName ' Get the plugin name Return "MyPlugin by Fabian Stern" End Function Zunächst haben wir also eine ganz normale Funktion, die durch Implements an das Interface und die Funktion GetName im Interface gekoppelt ist. Funktionsname und Interface-Funktionsname können sich durchaus unterscheiden. Hier wird auch der Code festgelegt, der beim Aufruf ausgeführt werden soll – die Rückgabe des eindeutigen Plugin-Namen. Um innerhalb des Plugins einen Zugriff auf die Hauptanwendung zu erhalten, muss die Hauptanwendung selbst die Objekte zum Plugin leiten, dann kann das Plugin darauf zugreifen: Dim myHost As clsPluginLoader.IHost und Public Sub SetHost(ByVal mHost As clsPluginLoader.IHost) _ Implements clsPluginLoader.IPlugin.SetHost myHost = mHost ' Set a link to objects of the main-application End Sub Hierbei ist myHost unsere Zugriffsvariable auf die von der Hauptanwendung freigegebenen Objekte und Funktionen. Die Hauptanwendung ruft SetHost im Plugin auf und übergibt uns "sich selbst", damit wir darauf zugreifen können. Fortan haben wir myHost.myLabel als zugreifbares Objekt (also das lblAccess in wappPluginSample) von unserem Plugin aus. Da nun alles Wichtige zur Laufzeit, Serialisierung und Zugreifbarkeit gesagt wurde, geht es weiter zu 3. Erstellen einer AppDomain und Laden der Plugins in der Hauptanwendung Durch die Funktion InitPlugins werden alle Plugins eingeladen, die sich im Anwendungsverzeichnis-Ordner "Plugins" befinden (also momentan nur plugins\clsPluginSample.dll). Unsere Klasse erbt die Funktionen des Formulars, da es sich hier um eine Windows-Forms-Anwendung handelt. Wir müssen auch noch unser IHost-Interface an die Form-Klasse Form1 koppeln: Implements clsPluginLoader.IHost Hier folgen nun unsere wichtigsten Elemente zur Plugin-Funktionalität: Dim mySetup As New AppDomainSetup Dim myAppDomain As AppDomain Dim myEvidence As System.Security.Policy.Evidence = AppDomain.CurrentDomain.Evidence Dim myPlugin() As clsPluginLoader.IPlugin AppDomainSetup: Wir wollen hier lediglich die ApplicationBase festlegen, die den Hauptpfad unserer Anwendung und Plugins beinhaltet. AppDomain: Die wichtigste Variable, da wir in diese Domain (etwas, das unsere Plugins verwaltet) unsere Plugins packen, und damit ermöglichen, alle Plugins zu einer späteren Zeit wieder zu entladen. Die Entlademöglichkeit ist sehr wichtig, wenn man beispielsweise sein Programm nicht beenden möchte, um neue Plugins hinzuzufügen. Trotzdem ist das manchmal erforderlich, wenn man das Interface ändert oder weitere Objekte hinzugefügt hat, die nun allen Plugins zur Verfügung stehen sollen. Hierbei gelten folgende Regeln:
Evidence: Der Evidence (Nachweis) ist im Grunde zur Sicherheit geschaffen worden. Damit verhindert man, dass ungewollter Code in das Hauptprogramm eingeschleust werden kann. Da es hier unzählige Informationen wie zum Beispiel Sponsoren oder Gültigkeitsbereiche gibt, verweise ich auf die MSDN. ( http://msdn.microsoft.com ) Evidence und Setup werden nun benötigt, um die AppDomain zu erstellen: mySetup.ApplicationBase = System.Environment.CurrentDirectory myAppDomain = AppDomain.CreateDomain("AppBase", myEvidence, mySetup) Nun geht alles vergleichsweise einfach: myPlugin(myPI) = myAppDomain.CreateInstanceFromAndUnwrap(myFile, "clsPluginSample.clsPlugin") ' Add Plugin to myPlugin variable myPlugin(myPI).SetHost(Me) Die AppDomain soll das Plugin mit dem eindeutigen Namespace und der eindeutigen Klasse aus dem Plugins-Ordner einladen und zur Ausführung bereitstellen (CreateInstanceFromAndUnwrap). Als nächstes wird im Plugin die Funktion SetHost aufgerufen, bei der wir alle von der Hauptdomain freigegebenen Objekte, Funktionen, etc. durch Me übergeben. Das Plugin kennt an dieser Stelle durch das Interface schon alle benötigten Informationen. Da hier die Möglichkeit gegeben ist, mehrere Plugins einzuladen, haben wir ein Array myPlugin(), welches uns alle Plugins zur Verfügung stellt. An dieser Stelle möchte ich noch einmal darauf hinweisen, dass alle Plugins dieselben Objekte, Funktion, etc. haben müssen, die im Interface definiert sind. Das wird jetzt auch klar, denn (es folgt ein nicht machbares Beispiel): PluginA hat die Funktionen GetSong und GetName. Im Interface steht jedoch nur GetSong, nicht aber GetLyrics. Wenn wir nun myPlugin(myPI). (der Punkt dahinter ist wichtig) im Code-Editor eingeben, sähen wir in der Quickhilfe lediglich die Funktion GetSong. Somit ist PluginB ungültig und kann nicht verwendet werden. Man kann dieses Problem unter einer Ausnahme umgehen: Die Funktion muss Identische Parameter sowie Rückgabetypen haben, dann kann man im Code des Plugin- und Hauptassemblies Implements clsPluginSample.GetSong bei der Funktion GetLyrics angeben, da - wie vorhin schon erwähnt - Funktionsname vom Interfacefunktionsnamen unterschiedlich sein kann. 4. "Last but not least" Sind nun Plugin, Loader und Hauptanwendung auf dieselben Funktionen abgestimmt, steht der Benutzung des Plugins nichts mehr im Wege. Einmal eingeladene Plugins können mit dem hier beschriebenen Weg nicht einzeln wieder verworfen werden. Dazu müssen alle Plugins mit dem Verwerfen der AppDomain ent-laden, und danach alle wieder ge-laden werden: For I As Int32 = 0 To myPlugin.GetUpperBound(0) If Not myPlugin(I) Is Nothing Then myPlugin(I).Unload() myPlugin(I) = Nothing End If Next If bLoaded Then AppDomain.Unload(myAppDomain) : bLoaded = False Beachtet werden muss auch, dass eine AppDomain nur einmal entladen werden kann. Andernfalls folgt ein Ausnahmefehler, da die AppDomain ja nicht mehr existiert. 5. Letzte Verdeutlichung zur Funktionsweise von Plugins
Bei der Anfertigung sollten also folgende Dinge zuerst erledigt werden:
6. Schlusswort Falls während des Durcharbeitens Fehler oder Probleme entstanden sind, könnt ihr eine Mail an webmaster@smart-coding.com mit Betreff: Plugin-Tutorial Frage senden. Ich antworte so schnell wie möglich. Bei der Gelegenheit möchte ich auch auf meine Homepage www.smart-coding.com verweisen, bei der ihr eure Sources hochladen, in Foren und an Votes teilnehmen und eure eigene mySQL Datenbank mit externem Zugriff haben könnt. Ich freue mich über eure Anmeldung und ein paar neue Dateien/Projekte oder Links. Weiterhin habt ihr die Möglichkeit, mir "Live" im IRC-Chat von irc.cumZ.de (Raum #cumZ) Fragen zu stellen. Dieser Workshop wurde bereits 32.538 mal aufgerufen.
Anzeige
Diesen und auch alle anderen Workshops finden Sie auch auf unserer aktuellen vb@rchiv Vol.6 (einschl. Beispielprojekt!) Ein absolutes Muss - Geballtes Wissen aus mehr als 8 Jahren vb@rchiv! - nahezu alle Tipps & Tricks und Workshops mit Beispielprojekten - Symbol-Galerie mit mehr als 3.200 Icons im modernen Look Weitere Infos - 4 Entwickler-Vollversionen (u.a. sevFTP für .NET), Online-Update-Funktion u.v.m. |
sevISDN 1.0 Überwachung aller eingehender Anrufe! Die DLL erkennt alle über die CAPI-Schnittstelle eingehenden Anrufe und teilt Ihnen sogar mit, aus welchem Ortsbereich der Anruf stammt. Weitere Highlights: Online-Rufident, Erkennung der Anrufbehandlung u.v.m. Tipp des Monats Dezemeber 2024 Roland Wutzke MultiSort im ListView-Control Dieses Beispiel zeigt, wie sich verschiedene Sortierfunktionen für ein ListView Control realisieren lassen. Neu! sevDTA 3.0 Pro SEPA mit Kontonummernprüfung Erstellen von SEPA-Dateien mit integriertem BIC-Verzeichnis und Konto- nummern-Prüfverfahren, so dass ungültige Bankdaten bereits im Vorfeld ermittelt werden können. |
|||||||||||||
Microsoft, Windows und Visual Basic sind entweder eingetragene Marken oder Marken der Microsoft Corporation in den USA und/oder anderen Ländern. Weitere auf dieser Homepage aufgeführten Produkt- und Firmennamen können geschützte Marken ihrer jeweiligen Inhaber sein. |