<?xml version="1.0" encoding="UTF-8"?><rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>erweiterung Archive - Robert Skibbe</title>
	<atom:link href="https://robbelroot.de/blog/tag/erweiterung/feed/" rel="self" type="application/rss+xml" />
	<link></link>
	<description>alias RobbelRoot – Freelance Full Stack Developer .NET</description>
	<lastBuildDate>Thu, 26 Mar 2026 14:53:25 +0000</lastBuildDate>
	<language>de</language>
	<sy:updatePeriod>
	hourly	</sy:updatePeriod>
	<sy:updateFrequency>
	1	</sy:updateFrequency>
	<generator>https://wordpress.org/?v=6.9.4</generator>

<image>
	<url>https://robbelroot.de/wp-content/uploads/2020/12/cropped-favicon-32x32.png</url>
	<title>erweiterung Archive - Robert Skibbe</title>
	<link></link>
	<width>32</width>
	<height>32</height>
</image> 
	<item>
		<title>Dein VB NET Plugin-System im Eigenbau – DAS ultimative Beispiel!</title>
		<link>https://robbelroot.de/blog/dein-vb-net-plugin-system-im-eigenbau-das-ultimative-beispiel/</link>
					<comments>https://robbelroot.de/blog/dein-vb-net-plugin-system-im-eigenbau-das-ultimative-beispiel/#comments</comments>
		
		<dc:creator><![CDATA[Robert Skibbe]]></dc:creator>
		<pubDate>Fri, 11 Feb 2022 03:11:05 +0000</pubDate>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[Tutorials]]></category>
		<category><![CDATA[Visual Basic .NET]]></category>
		<category><![CDATA[Visual Basic .NET Problemlösungen]]></category>
		<category><![CDATA[code]]></category>
		<category><![CDATA[eigene]]></category>
		<category><![CDATA[erweiterung]]></category>
		<category><![CDATA[erweiterungen]]></category>
		<category><![CDATA[net]]></category>
		<category><![CDATA[plugin]]></category>
		<category><![CDATA[plugins]]></category>
		<category><![CDATA[system]]></category>
		<category><![CDATA[vb]]></category>
		<category><![CDATA[vb.net]]></category>
		<category><![CDATA[vbnet]]></category>
		<category><![CDATA[visual basic]]></category>
		<guid isPermaLink="false">https://robbelroot.de/?p=8577</guid>

					<description><![CDATA[<p>In diesem Beitrag baust Du von Grund auf ein eigenes VB.NET Plugin-System &#x1f449; ohne externe Frameworks, dafür mit vollem Verständnis dafür, was unter der Haube passiert. Du lernst, wie Du mit Assembly.LoadFrom und Reflection externe DLLs zur Laufzeit lädst, ein Interface-Konzept für Deine Plugins definierst und einen PluginManager samt EventAggregator implementierst. Plugins &#8230;</p>
<p>Der Beitrag <a href="https://robbelroot.de/blog/dein-vb-net-plugin-system-im-eigenbau-das-ultimative-beispiel/">Dein VB NET Plugin-System im Eigenbau – DAS ultimative Beispiel!</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></description>
										<content:encoded><![CDATA[




<p>In diesem Beitrag baust Du von Grund auf ein eigenes VB.NET Plugin-System &#x1f449; ohne externe Frameworks, dafür mit vollem Verständnis dafür, was unter der Haube passiert. Du lernst, wie Du mit <code>Assembly.LoadFrom</code> und Reflection externe DLLs zur Laufzeit lädst, ein Interface-Konzept für Deine Plugins definierst und einen PluginManager samt EventAggregator implementierst.</p>



<p>Plugins sind kein Luxus, sondern ein handfestes Architekturprinzip: Sie erlauben es Dir, Dein Programm durch Dritte erweiterbar zu machen, ohne den Kern anzufassen. Ob Du das aus WordPress, Visual Studio oder anderen Tools kennst — das dahinterliegende Konzept ist überall das gleiche, und genau das setzen wir hier gemeinsam um.</p>



<div style="
  box-sizing: border-box;
  margin: 36px 0;
  border-radius: 12px;
  overflow: hidden;
  background: #1a1a2e;
  position: relative;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
">

  <!-- Subtiles Grid-Muster (kein extra HTTP-Request) -->
  <div style="
    position: absolute;
    inset: 0;
    background-image:
      linear-gradient(rgba(255,255,255,0.03) 1px, transparent 1px),
      linear-gradient(90deg, rgba(255,255,255,0.03) 1px, transparent 1px);
    background-size: 40px 40px;
    pointer-events: none;
  "></div>

  <!-- Oranger Glow-Akzent -->
  <div style="
    position: absolute;
    top: -60px;
    right: -60px;
    width: 240px;
    height: 240px;
    background: radial-gradient(circle, rgba(230,126,34,0.18) 0%, transparent 70%);
    pointer-events: none;
  "></div>

  <!-- Inhalt -->
  <div style="
    position: relative;
    padding: 36px 40px;
  ">

    <!-- Label -->
    <div style="
      display: inline-block;
      font-size: 11px;
      font-weight: 700;
      letter-spacing: 2.5px;
      text-transform: uppercase;
      color: #e67e22;
      margin-bottom: 14px;
      border: 1px solid rgba(230,126,34,0.35);
      border-radius: 4px;
      padding: 3px 10px;
    ">Für Dein Projekt?</div>

    <!-- Überschrift -->
    <p style="
      margin: 0 0 10px 0;
      font-size: clamp(17px, 4vw, 21px);
      font-weight: 700;
      color: #ffffff;
      line-height: 1.35;
    ">Zu komplex, um es alleine anzugehen?</p>

    <!-- Subtext -->
    <p style="
      margin: 0 0 24px 0;
      font-size: 14px;
      color: rgba(255,255,255,0.65);
      line-height: 1.7;
      max-width: 540px;
    ">
      Plugin-Architekturen, Dependency Injection, Event-Systeme –
      ich entwickle solche Strukturen täglich professionell in VB.NET und C#.
      Schreib mir, ich melde mich schnell zurück.
    </p>

    <!-- Buttons -->
    <div style="
      display: flex;
      flex-wrap: wrap;
      gap: 12px;
      align-items: center;
    ">
      <a href="https://robbelroot.de/kontakt/"
         style="
           display: inline-block;
           padding: 12px 24px;
           background: #e67e22;
           color: #ffffff;
           font-size: 14px;
           font-weight: 700;
           text-decoration: none;
           border-radius: 7px;
           letter-spacing: 0.3px;
           transition: background 0.2s ease;
           white-space: nowrap;
         "
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Projekt anfragen</a>

      <a href="https://robbelroot.de/net-programmierung/"
         style="
           display: inline-block;
           padding: 12px 24px;
           background: transparent;
           color: rgba(255,255,255,0.75);
           font-size: 14px;
           font-weight: 600;
           text-decoration: none;
           border-radius: 7px;
           border: 1px solid rgba(255,255,255,0.2);
           letter-spacing: 0.3px;
           transition: border-color 0.2s ease, color 0.2s ease;
           white-space: nowrap;
         "
         onmouseover="this.style.borderColor='rgba(230,126,34,0.6)';this.style.color='#e67e22'"
         onmouseout="this.style.borderColor='rgba(255,255,255,0.2)';this.style.color='rgba(255,255,255,0.75)'"
      >Leistungen ansehen</a>
    </div>

  </div>
</div>






<h2 class="wp-block-heading" id="als-youtube-video"><strong>VB.NET Plugin-System – Das komplette Video-Tutorial</strong></h2>



<p>Schaue Dir meinen Beitrag gern in visueller Form auf YouTube an. Das Video führt Dich durch den kompletten Aufbau: Von der ersten Assembly bis zum fertigen UsageWidget-Plugin. Ideal als Ergänzung zum Text oder wenn Du den Code lieber beim Zuschauen abtippst.</p>


<div class="async-youtube" data-embed="hcJQgWUJVT8" data-alt="">
    <div class="play-button"></div>      
  </div>



<h2 class="wp-block-heading" id="hintergrundgeschichte-von-plugins-plug-play">Hintergrundgeschichte von Plugins – Plug &amp; Play</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/02/VB-NET-Plugin-System-Hintergrund.png"><img fetchpriority="high" decoding="async" width="1200" height="628" src="https://robbelroot.de/wp-content/uploads/2022/02/VB-NET-Plugin-System-Hintergrund.png" alt="VB NET Plugin-System Hintergrund" class="wp-image-8595" title="VB NET Plugin-System Hintergrund"/></a><figcaption class="wp-element-caption">VB NET Plugin-System Hintergrund</figcaption></figure>



<p>Man hat zu diesem Zeitpunkt ein vermutlich schon bestehendes Programm und möchte einen Schritt weiter gehen. Die bisherige Funktionalität ist eventuell schon ausgereift und das Programm erhält gute Resonanz. Download-Zahlen und Nutzer-Statistiken stimmen und immer mehr Vorschläge prasseln auf Dich als Entwickler ein.</p>



<p>Ich denke, dass jeder Entwickler irgendwann mal an diesen Punkt kommt:</p>



<p>&#8222;Jetzt wäre es cool, ein eigenes Plugin-System zu haben, aber wie!?&#8220;. Mit Visual Basic NET gibt es dafür zum Glück einige von Haus aus zur Verfügung gestellte Tools. Diese ermöglichen uns die kinderleichte Umsetzung eigener <a href="https://de.wikipedia.org/wiki/Plug-in" target="_blank" rel="noreferrer noopener"><strong>Plugins</strong></a>.</p>



<p>Somit kann unsere Anwendung schön durch die Kreativität anderer Softwareentwickler erweitert werden. Neben dieser gerade erwähnten Standard-Tools vom NET Framework, gibt es natürlich weitere Alternativen. Die Einen sind vermutlich einfacher, weniger komplex, aber auch eventuell für den Lernprozess hilfreicher.</p>



<p>Ganz nach dem Motto &#8222;back to the roots&#8220; werden wir uns zuerst mit der Eigenbau-Variante beschäftigen. Ich denke, es hilft vor allem, die Hintergrund-Prozesse eines solchen Plugin-Systems zu verstehen &#8211; und gerade diesen Anspruch habe ich immer z. B. an mich selbst.</p>



<h2 class="wp-block-heading" id="vb-net-plugin-system-marke-eigenbau-first">VB NET Plugin-System – Marke Eigenbau &#8222;first&#8220;</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/02/VB-NET-Plugin-System-Marke-Eigenbau.png"><img decoding="async" width="1200" height="628" src="https://robbelroot.de/wp-content/uploads/2022/02/VB-NET-Plugin-System-Marke-Eigenbau.png" alt="VB NET Plugin-System Marke Eigenbau" class="wp-image-8612" title="VB NET Plugin-System Marke Eigenbau"/></a><figcaption class="wp-element-caption">VB NET Plugin-System Marke Eigenbau</figcaption></figure>



<p>Um die eventuellen ersten Schritte im Bereich der Plugin-Entwicklung so rudimentär wie möglich zu halten, starten wir bei 0. Ich denke, dass wir zuerst einen Blick in die allgemeine Struktur einer Anwendung werfen müssen. Damit können wir dann genauer verstehen, wie man dann eventuelle weitere Bestandteile laden kann.</p>



<p>Diese Bestandteile müssen sich anhand gewisser Regeln in das Gesamtbild der Anwendung einfügen. Natürlich kann ich hier nicht zu tief ins Detail gehen, da wir sonst den bekannten Rahmen sprengen würden..</p>



<h3 class="wp-block-heading" id="net-assembly-exkurs">NET Assembly Exkurs</h3>



<p>Grundsätzlich kann man folgende &#8222;Quick-Facts&#8220; festhalten:</p>



<ul class="wp-block-list">
<li>.NET Anwendungen setzen sich aus Bausteinen zusammen</li>



<li>Dies gilt natürlich auch für dynamische Programmbibliotheken &#8211; DLLs</li>



<li>Die genannten Bausteine bezeichnet man als Assemblies (Singular Assembly)</li>



<li>&#8222;To assemble&#8220; bedeutet aus dem Englischen nichts anderes als &#8222;zusammensetzen&#8220;</li>
</ul>



<p>Ein NET Programm besteht also aus einer oder mehreren Assemblies, Welche somit wiederum als Bausteine betitelt werden können. Diese Bausteine sind mehr oder weniger &#8222;Brocken&#8220;, versehen mit Informationen für die CLR (Common Language Runtime). Die Brocken bestehen aus für die CLR vorkompiliertem Code und darin enthaltenen Metadaten.</p>



<p>Mit diesen Metadaten weiß die CLR, wie Sie mit der Anwendung und deren Bestandteilen verfahren kann/muss. Wie gesagt könnte man hier noch tiefer tauchen, aber ich denke das reicht für einen kleinen Exkurs.</p>



<h3 class="wp-block-heading" id="eine-externe-assembly-laden">Eine &#8222;externe&#8220; Assembly laden</h3>



<p>Wir können aus diesem Exkurs also folgende Schlussfolgerung ziehen:</p>



<p>Um unser Programm dynamisch erweitern zu können, müssen wir zusätzliche Assemblies laden. Mit diesen zusätzlichen Assemblies findet dann weiterer, strukturierter Code seinen Weg in unser Programm. Auch hierzu bietet uns das NET Framework natürlich von Haus aus komfortable Möglichkeiten.</p>



<p>Eine der dazu verwendeten Methoden befindet sich im &#8222;<strong><a href="https://learn.microsoft.com/de-de/dotnet/api/system.reflection?view=net-9.0" target="_blank" rel="noreferrer noopener">System.Reflection</a></strong>&#8222;-Namespace. Genauer genommen, spreche ich von der statischen &#8222;<strong><a href="https://learn.microsoft.com/de-de/dotnet/api/system.reflection.assembly.loadfrom?view=net-9.0" target="_blank" rel="noreferrer noopener">Assembly.LoadFrom</a></strong>&#8222;-Funktion. Die Funktion wird durch die &#8222;System.Runtime&#8220;-Programmbibliothek bereitgestellt – also einer Assembly.</p>



<p>Laden wir nun mit Hilfe der &#8222;LoadFrom&#8220;-Funktion einmal eine Assembly. Der Einfachheit halber habe ich ich die DLL einfach auf den Desktop gelegt.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Dim pathToDll = "C:\Users\Anwender\Desktop\MyPlugin.dll"
Dim assembly = Assembly.LoadFrom(pathToDll)</pre>



<h2 class="wp-block-heading" id="die-projektmappe-fur-unser-vb-net-plugin-system-vorbereiten">Die Projektmappe für unser VB NET Plugin-System vorbereiten</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/02/VB-NET-Plugin-System-Projektmappe-vorbereiten.png"><img decoding="async" width="1200" height="628" src="https://robbelroot.de/wp-content/uploads/2022/02/VB-NET-Plugin-System-Projektmappe-vorbereiten.png" alt="VB NET Plugin-System Projektmappe vorbereiten" class="wp-image-8678" title="VB NET Plugin-System Projektmappe vorbereiten"/></a><figcaption class="wp-element-caption">VB NET Plugin-System Projektmappe vorbereiten</figcaption></figure>



<p>Für unser Beispiel werden wir 2-3 Projekte benötigen:</p>



<ul class="wp-block-list">
<li>Das eine Projekt wird das Skelett, bzw. der Bauplan für unser Plugin sein. Ebenso wird dort &#8222;die Anwendung&#8220; sein.</li>



<li>Im zweiten Projekt werden wir den Plugin-Bauplan umsetzen, also eine Plugin-Klasse bauen, Welche den Bauplan implementiert.</li>
</ul>



<p>Die Projekte werde ich bewusst in der gleichen Projektmappe unterbringen, damit es einfacher ist. Man könnte natürlich auch alternativverschiedene Projektmappen verwenden.</p>



<h3 class="wp-block-heading" id="neue-projektmappe-fur-das-vb-net-plugin-system-erstellen">Neue Projektmappe für das VB NET Plugin-System erstellen</h3>



<p>Nun beginnen wir mit der Erstellung der Projektmappe, wo dann später die anderen Projekte folgen werden.</p>



<p>Öffne dazu wie gewohnt Visual Studio und wähle &#8222;Neues Projekt erstellen&#8220;:</p>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/02/Visual-Studio-neues-Projekt-anlegen-Startbildschirm.png"><img loading="lazy" decoding="async" width="1029" height="682" src="https://robbelroot.de/wp-content/uploads/2022/02/Visual-Studio-neues-Projekt-anlegen-Startbildschirm.png" alt="Neues Visual Studio Projekt erstellen" class="wp-image-8277" title="Neues Visual Studio Projekt erstellen"/></a><figcaption class="wp-element-caption">Neues Visual Studio Projekt erstellen</figcaption></figure>



<p>Danach kannst Du in dem Suchfeld oben nach dem Projekt-Typ &#8222;projektmappe&#8220; suchen. Wähle dann einfach den passenden Eintrag aus der rechten Auflistung. Wir wählen hier bewusst die leere Projektmappe aus, da die anderen Projekte gleich noch folgen:</p>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/02/Eine-neue-leere-Projektmappe-in-Visual-Studio-anlegen.png"><img loading="lazy" decoding="async" width="1025" height="681" src="https://robbelroot.de/wp-content/uploads/2022/02/Eine-neue-leere-Projektmappe-in-Visual-Studio-anlegen.png" alt="Eine neue leere Projektmappe in Visual Studio anlegen" class="wp-image-8673" title="Eine neue leere Projektmappe in Visual Studio anlegen"/></a><figcaption class="wp-element-caption">Eine neue leere Projektmappe in Visual Studio anlegen</figcaption></figure>



<p>Im nächsten Fenster kannst Du Dich dann für einen sinnvollen Namen der Projektmappe entscheiden. Damit das nicht zu kompliziert wird (also was Namespacing, Hersteller, usw. betrifft), machen wir&#8217;s hier ganz einfach. Verwende daher eventuell einfach den Namen &#8222;PluginBeispiel&#8220;.</p>



<p>Klicke final auf den &#8222;Erstellen&#8220;-Knopf und wir sind bereit loszulegen.</p>



<h2 class="wp-block-heading" id="beispiel-anwendung-fur-das-vb-net-plugin-system">Beispiel-Anwendung für das VB NET Plugin-System</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/02/Eine-durch-Plugins-erweiterbare-Beispiel-Anwendung.png"><img loading="lazy" decoding="async" width="1200" height="628" src="https://robbelroot.de/wp-content/uploads/2022/02/Eine-durch-Plugins-erweiterbare-Beispiel-Anwendung.png" alt="Eine durch Plugins erweiterbare Beispiel-Anwendung" class="wp-image-8687" title="Eine durch Plugins erweiterbare Beispiel-Anwendung"/></a><figcaption class="wp-element-caption">Eine durch Plugins erweiterbare Beispiel-Anwendung</figcaption></figure>



<p>Nun sind wir an dem Punkt angelangt, wo wir eine Anwendung benötigen. Dieses Anwendung soll dann durch unsere Plugin-Infrastruktur erweitert werden können. Damit das funktioniert, können andere Entwickler dann unsere Plugin-Baupläne implementieren und ein Plugin umsetzen.</p>



<p>Unsere Anwendung muss dann nur noch dafür Sorgen, dass die jeweiligen Plugins geladen werden. Dabei spielen natürlich Abhängigkeiten, Zeitpunkte und zugreifbare Kontexte eine Rolle. Möchte ich Plugins z. B. nur beim Start Gelegenheit geben in das Programm-Geschehen einzugreifen?</p>



<p>Oder sollen Plugins auch mitten in der Ausführung zum Beispiel auf die Datenbank zugreifen können. Wie Du siehst, verlangt so ein Plugin-System einiges an Planung ab, da wir uns diese Fragen zuerst beantworten müssen.</p>



<h3 class="wp-block-heading" id="der-projektmappe-ein-windows-forms-projekt-hinzufugen">Der Projektmappe ein Windows Forms Projekt hinzufügen</h3>



<p>Erstelle also nun für den Anfang eine neue Windows Forms App, Welche Du als neues Projekt innerhalb der Projektmappe anlegst. Das kannst Du oben rechts, durch einen Rechtsklick auf die Projektmappe im Projektmappen-Explorer erledigen.</p>



<p>Ich werde das Projekt für unser Beispiel als &#8222;MyCRM&#8220; betiteln.</p>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/02/Projekt-zu-Projektmappe-in-Visual-Studio-hinzufuegen.png"><img loading="lazy" decoding="async" width="948" height="433" src="https://robbelroot.de/wp-content/uploads/2022/02/Projekt-zu-Projektmappe-in-Visual-Studio-hinzufuegen.png" alt="Projekt zu Projektmappe in Visual Studio hinzufügen" class="wp-image-8697" title="Projekt zu Projektmappe in Visual Studio hinzufügen"/></a><figcaption class="wp-element-caption">Projekt zu Projektmappe in Visual Studio hinzufügen</figcaption></figure>



<p>Nachdem Du diesen Schritt erledigt hast, springen wir auch direkt zum nächsten Punkt. Ich habe mir für dieses Tutorial etwas Besonderes überlegt, da ich die üblichen Beispiele aus dem Netz zu langweilig finde. Wir werden also nicht nur ein VB NET Plugin-System an sich schreiben, sondern auch ein wenig Konfiguration einführen.</p>



<p>Aus der Anwendung heraus soll es möglich sein, gewisse <strong>Plugins aktivieren und deaktivieren</strong> zu können. Ich denke, dass diese Variante durchaus eher an der Praxis orientiert ist, als die üblichen Beispiele. Wäre ja auch langweilig, wenn ich wie jeder das: &#8222;Hey ich geb&#8216; zig mal einen String aus&#8220;-Beispiel bringe, oder!?</p>



<h2 class="wp-block-heading" id="ein-erweiterbares-dashboard-anfang-der-anwendung">Ein erweiterbares Dashboard – Anfang der Anwendung</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/02/Beispiel-VB-NET-Plugin-System.png"><img loading="lazy" decoding="async" width="798" height="478" src="https://robbelroot.de/wp-content/uploads/2022/02/Beispiel-VB-NET-Plugin-System.png" alt="Beispiel VB NET Plugin System" class="wp-image-8709" title="Beispiel VB NET Plugin System"/></a><figcaption class="wp-element-caption">Beispiel VB NET Plugin System</figcaption></figure>



<p>Natürlich versuche ich auch hier (wie immer) den Mittelweg zwischen &#8222;anschauliches Beispiel&#8220; und &#8222;einfach zu verstehen&#8220; zu wählen. Das gelingt mir glaube ich meistens ganz gut, daher werde ich auch hier einfach mal loslegen. Im ersten Schritt bezüglich unserer Anwendung, werden wir die Haupt-Funktionalität umsetzen.</p>



<p>Dabei denke ich in erster Linie natürlich (wie oben im Bild zu sehen) an Tabs wie &#8222;Kunden&#8220;, &#8222;Rechnungen&#8220;, etc. Zusätzlich kam mir ein kleines Dashboard in den Sinn, was man ja auch aus den verschiedensten Anwendungen kennen sollte. Dort können z. B. verschiedene Informationen durch die verfügbaren Widgets dargestellt werden.</p>



<p>Je nach Kreativität des jeweiligen Entwicklers können weitere Elemente dynamisch hinzugefügt werden. An dieser Stelle ist der Vorstellung kaum eine Grenze gesetzt, außer der Grenzen, die wir selbst bestimmen. Wir selbst definieren die Bereiche der Anwendung, worauf die Plugins letztendlich Zugriff haben.</p>



<p>Erste Einfälle für Plugins, Welche auch als Dashboard-Widgets fungieren könnten, sind Folgende:</p>



<ul class="wp-block-list">
<li>Das <strong>Wetter </strong>(relevant für Tourenplanung, etc.)</li>



<li>Die letzten 5 <strong>Rechnungen</strong></li>



<li>Informationen über den <strong>letzten Anruf</strong></li>



<li>und beliebig <strong>viel mehr</strong>..</li>
</ul>



<p>Lass uns allerdings nun im nächsten Abschnitt erstmal eine kleine Basis an Plugin-Infrastruktur bauen.</p>



<h2 class="wp-block-heading" id="ein-plugin-manager-als-infrastruktur-fur-das-vb-net-plugin-system">Ein Plugin-Manager als Infrastruktur für das VB NET Plugin-System</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/02/Eine-eigene-Plugin-Manager-Klasse.png"><img loading="lazy" decoding="async" width="1200" height="628" src="https://robbelroot.de/wp-content/uploads/2022/02/Eine-eigene-Plugin-Manager-Klasse.png" alt="Eine eigene Plugin-Manager Klasse" class="wp-image-8739" title="Eine eigene Plugin-Manager Klasse"/></a><figcaption class="wp-element-caption">Eine eigene Plugin-Manager Klasse</figcaption></figure>



<p>Nun gehen wir in unserem VB NET Plugin-System den nächsten Schritt, indem wir eine Infrastruktur bauen. Wie ich auch im Code kommentiert habe/kommentieren werde, hasse ich eigentlich diese typischen &#8222;Manager&#8220;-Klassen. Diese verstoßen meistens gegen das <a href="https://de.wikipedia.org/wiki/Single-Responsibility-Prinzip" target="_blank" rel="noreferrer noopener"><strong>&#8222;Single Responsibility&#8220;-Prinzip&#8220;</strong></a> und sind daher alles Andere als sauber.</p>



<p>In diesem Beispiel dürfte es aber kontextual dennoch passen und es soll ja auch nur als Basis dienen. Als Basis für Deine eigenen, künftigen, kreativen Gedanken das System zu erweitern. Beachte hier bitte erneut den Hinweis, dass ich nur die essentiellen Themen behandeln kann.</p>



<p>Sonst müsste ich zusätzlich noch Themen wie <strong>&#8222;<a href="https://de.wikipedia.org/wiki/Dependency_Injection" target="_blank" rel="noreferrer noopener">Dependency Injection</a>&#8222;</strong> und und und abarbeiten. Dies mache ich aber in getrennten Videos und Beiträgen, wo ich dann mit mehr Zeit darauf eingehen kann.</p>



<h3 class="wp-block-heading" id="das-ipluginmanager-interface">Das IPluginManager-Interface</h3>



<p>Zuerst schauen wir uns an, was ein Plugin-Manager in unserem Kontext können muss. Erneut, dies ist nur ein Beispiel, Welches ich in kurzer Zeit zusammengeschustert habe. Daher wird es keine &#8222;non mega plus ultra&#8220;-Lösung sein, sondern ein möglicher Startpunkt.</p>



<p>Wir verwenden jedoch natürlich im ersten Schritt ein Interface, um ein grobes Funktions-Skelett zu definieren:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Imports System.IO

Namespace Contracts

    Public Interface IPluginManager

        ReadOnly Property Directory As DirectoryInfo

        ReadOnly Property Plugins As List(Of IPlugin)

        ReadOnly Property ActivePlugins As List(Of IPlugin)

        ReadOnly Property InactivePlugins As List(Of IPlugin)

        Sub EnsurePluginFolder()

        Sub LoadActivePluginsList()

        Sub LoadPlugins()

        Sub ActivatePlugin(plugin As IPlugin)

        Sub DeactivatePlugin(plugin As IPlugin)

        Function IsPluginActive(plugin As IPlugin) As Boolean

        Function IsPluginActive(pluginId As String) As Boolean

    End Interface

End Namespace
</pre>



<h4 class="wp-block-heading" id="eigenschaften">Eigenschaften</h4>



<h5 class="wp-block-heading" id="directory">Directory</h5>



<p>Die Directory-Eigenschaft beinhaltet eine Instanz der <a href="https://learn.microsoft.com/de-de/dotnet/api/system.io.directory?view=net-9.0" target="_blank" rel="noreferrer noopener">Directory-Klasse</a>. Damit können wir ganz einfach die Assembly-Dateien finden.</p>



<h5 class="wp-block-heading" id="plugins">Plugins</h5>



<p>Hier speichern wir eine Auflistung aller Plugins, Welche wir beim Start der Anwendung sammeln.</p>



<h5 class="wp-block-heading" id="activeplugins">ActivePlugins</h5>



<p>Wie der Name suggeriert, eine Auflistung, worin sich alle aktiven Plugins befinden.</p>



<h5 class="wp-block-heading" id="inactiveplugins">InactivePlugins</h5>



<p>Das Gegenstück zur vorherigen Eigenschaft, hier befinden sich alle inaktiven Plugins.</p>



<h4 class="wp-block-heading" id="methoden">Methoden</h4>



<h5 class="wp-block-heading" id="ensurepluginfolder">EnsurePluginFolder</h5>



<p>Eine Hilfs-Methode, um sicherzustellen, dass der Plugin-Ordner existiert. Könnte man eventuell auch drauf verzichten..</p>



<h5 class="wp-block-heading" id="loadactivepluginslist">LoadActivePluginsList</h5>



<p>Diese Methode lädt die durch Konfiguration aktivierten Plugins in eine vorgesehene ID-Liste.</p>



<h5 class="wp-block-heading" id="loadplugins">LoadPlugins</h5>



<p>Die Methode lädt die vorhandenen Plugin-Typen und instanziiert Diese. Anschließend könnten wir nur gewissen Instanzen dann eventuelle Abhängigkeiten geben. Mit Sicherheit gibt es hier noch bessere Varianten, ich beschreibe aber später, Welche ich verwendet habe. Nach Bestückung der Abhängigkeiten, packen wir die Plugins in jeweilige Listen oben (allgemein, aktiv, inaktiv).</p>



<h5 class="wp-block-heading" id="activateplugin">ActivatePlugin</h5>



<p>Wie der Name dieser Methode schon vermuten lässt, aktiviert Sie ein jeweiliges Plugin. Dazu zählt einerseits die Verwaltung durch den Manager selbst und andererseits die Aktivierung des Plugins selbst. Mehr dazu weiter unten!</p>



<h5 class="wp-block-heading" id="deactivateplugin">DeactivatePlugin</h5>



<p>Das Gegenstück zur vorherigen Methode, schaue am besten dort :)!</p>



<h5 class="wp-block-heading" id="ispluginactive">IsPluginActive</h5>



<p>Diese Hilfs-Funktion unterstützt uns beim Check, ob ein jeweiliges Plugin aktiviert ist. Dazu gibt es zwei Überladungen, einmal mit dem Plugin und einmal mit der Plugin-ID als Parameter.</p>



<div style="
  box-sizing: border-box;
  margin: 36px 0;
  border-radius: 12px;
  overflow: hidden;
  background: #1a1a2e;
  position: relative;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
">

  <!-- Subtiles Grid-Muster (kein extra HTTP-Request) -->
  <div style="
    position: absolute;
    inset: 0;
    background-image:
      linear-gradient(rgba(255,255,255,0.03) 1px, transparent 1px),
      linear-gradient(90deg, rgba(255,255,255,0.03) 1px, transparent 1px);
    background-size: 40px 40px;
    pointer-events: none;
  "></div>

  <!-- Oranger Glow-Akzent -->
  <div style="
    position: absolute;
    top: -60px;
    right: -60px;
    width: 240px;
    height: 240px;
    background: radial-gradient(circle, rgba(230,126,34,0.18) 0%, transparent 70%);
    pointer-events: none;
  "></div>

  <!-- Inhalt -->
  <div style="
    position: relative;
    padding: 32px 28px;
  ">

    <!-- Label -->
    <div style="
      display: inline-block;
      font-size: 11px;
      font-weight: 700;
      letter-spacing: 2.5px;
      text-transform: uppercase;
      color: #e67e22;
      margin-bottom: 14px;
      border: 1px solid rgba(230,126,34,0.35);
      border-radius: 4px;
      padding: 3px 10px;
    ">Brauchst Du sowas für Dein Projekt?</div>

    <!-- Überschrift -->
    <p style="
      margin: 0 0 10px 0;
      font-size: clamp(17px, 4vw, 21px);
      font-weight: 700;
      color: #ffffff;
      line-height: 1.35;
    ">Zu komplex, um es alleine anzugehen?</p>

    <!-- Subtext -->
    <p style="
      margin: 0 0 24px 0;
      font-size: 14px;
      color: rgba(255,255,255,0.65);
      line-height: 1.7;
      max-width: 540px;
    ">
      Plugin-Architekturen, Dependency Injection, Event-Systeme –
      ich entwickle solche Strukturen täglich professionell in VB.NET und C#.
      Schreib mir, ich melde mich schnell zurück.
    </p>

    <!-- Buttons -->
    <div style="
      display: flex;
      flex-wrap: wrap;
      gap: 12px;
      align-items: center;
    ">
      <a href="https://robbelroot.de/kontakt/"
         style="
           display: inline-block;
           padding: 12px 24px;
           background: #e67e22;
           color: #ffffff;
           font-size: 14px;
           font-weight: 700;
           text-decoration: none;
           border-radius: 7px;
           letter-spacing: 0.3px;
           transition: background 0.2s ease;
           white-space: nowrap;
         "
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Projekt anfragen</a>

      <a href="https://robbelroot.de/net-programmierung/"
         style="
           display: inline-block;
           padding: 12px 24px;
           background: transparent;
           color: rgba(255,255,255,0.75);
           font-size: 14px;
           font-weight: 600;
           text-decoration: none;
           border-radius: 7px;
           border: 1px solid rgba(255,255,255,0.2);
           letter-spacing: 0.3px;
           transition: border-color 0.2s ease, color 0.2s ease;
           white-space: nowrap;
         "
         onmouseover="this.style.borderColor='rgba(230,126,34,0.6)';this.style.color='#e67e22'"
         onmouseout="this.style.borderColor='rgba(255,255,255,0.2)';this.style.color='rgba(255,255,255,0.75)'"
      >Leistungen ansehen</a>
    </div>

  </div>
</div>



<h3 class="wp-block-heading" id="pluginmanager-implementierung">PluginManager-Implementierung</h3>



<p>Schauen wir uns im nächsten Schritt den Code zur Implementierung des obigen Interfaces an.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Imports System.IO
Imports System.Reflection
Imports Autofac
Imports MyCRM.Contracts

' usually i hate these typical "manager" classes
' but in this case it's usage context is limited so..
' could also be composed of sub-components like loader, settings, whatever..
Public Class PluginManager
    Implements IPluginManager

    ' should be configurable whatever..
    Public ACTIVE_PLUGINS_CONFIG_FILE As String = Path.Combine(Application.StartupPath, "plugins_active.txt")

    Public ReadOnly Property Directory As DirectoryInfo Implements IPluginManager.Directory

    Public ReadOnly Property Plugins As List(Of IPlugin) Implements IPluginManager.Plugins

    Public ReadOnly Property ActivePlugins As List(Of IPlugin) Implements IPluginManager.ActivePlugins

    Public ReadOnly Property InactivePlugins As List(Of IPlugin) Implements IPluginManager.InactivePlugins

    Protected PluginTypes As List(Of Type)

    Protected ReadOnly ActivePluginIds As List(Of String)

    Sub New(directory As String)
        Me.ActivePluginIds = New List(Of String)
        Me.Directory = New DirectoryInfo(directory)
        Me.Plugins = New List(Of IPlugin)
        Me.ActivePlugins = New List(Of IPlugin)
        Me.InactivePlugins = New List(Of IPlugin)
        Me.PluginTypes = New List(Of Type)
    End Sub

    Public Sub EnsurePluginFolder() Implements IPluginManager.EnsurePluginFolder
        If Not Directory.Exists Then
            Directory.Create()
        End If
    End Sub

    Public Sub LoadPluginTypes()
        Dim libraryFiles = Directory.GetFiles("*.dll", SearchOption.AllDirectories)
        Dim assemblies = libraryFiles.Select(Function(libraryFile) Assembly.LoadFrom(libraryFile.FullName))
        Dim assemblyTypes = assemblies.SelectMany(Function(assemblyType) assemblyType.GetTypes())
        ' or use IsAssignableFrom..
        Dim pluginTypes = assemblyTypes.Where(Function(x) x.GetInterface(NameOf(IPlugin)) &lt;> Nothing)
        Me.PluginTypes.AddRange(pluginTypes)
    End Sub

    Public Sub LoadActivePluginsList() Implements IPluginManager.LoadActivePluginsList
        If Not File.Exists(ACTIVE_PLUGINS_CONFIG_FILE) Then
            File.Create(ACTIVE_PLUGINS_CONFIG_FILE)
        Else
            Dim activePluginIds = File.ReadAllLines(ACTIVE_PLUGINS_CONFIG_FILE)
            Me.ActivePluginIds.AddRange(activePluginIds)
        End If
    End Sub

    ' maybe use some kind of caching mechanism..
    Public Sub LoadPlugins() Implements IPluginManager.LoadPlugins
        Dim pluginInstances = PluginTypes.Select(Function(x) CType(Activator.CreateInstance(x), IPlugin)).ToList()
        If pluginInstances.Count = 0 Then
            Return
        End If

        Dim eventAggregator = Program.Container.Resolve(Of IEventAggregator)

        Plugins.AddRange(pluginInstances)
        For Each plugin In Plugins
            AttachEventAggregatorIfAware(plugin, eventAggregator)
            If ActivePluginIds.Contains(plugin.GetId()) Then
                ActivePlugins.Add(plugin)
                plugin.Load()
            Else
                InactivePlugins.Add(plugin)
            End If
        Next
    End Sub

    ''' &lt;remarks>could be done different, dont know.. not flexible but well..&lt;/remarks>
    Private Sub AttachEventAggregatorIfAware(plugin As IPlugin, eventAggregator As IEventAggregator)
        Dim pluginType = plugin.GetType()
        Dim awareInterface = pluginType.GetInterface(NameOf(IEventAggregatorAware))
        Dim isAware = awareInterface IsNot Nothing
        If isAware Then
            DirectCast(plugin, IEventAggregatorAware).EventAggregator = eventAggregator
        End If
    End Sub

    Protected Async Function AddPluginToActiveFileListAsync(plugin As IPlugin) As Task
        Dim lines = (Await File.ReadAllLinesAsync(ACTIVE_PLUGINS_CONFIG_FILE)).ToList()
        If Not lines.Contains(plugin.GetId()) Then
            lines.Add(plugin.GetId())
            Await File.WriteAllLinesAsync(ACTIVE_PLUGINS_CONFIG_FILE, lines)
        End If
    End Function

    Protected Async Function RemovePluginFromActiveFileListAsync(plugin As IPlugin) As Task
        Dim lines = (Await File.ReadAllLinesAsync(ACTIVE_PLUGINS_CONFIG_FILE)).ToList()
        If lines.Contains(plugin.GetId()) Then
            lines.Remove(plugin.GetId())
            Await File.WriteAllLinesAsync(ACTIVE_PLUGINS_CONFIG_FILE, lines)
        End If
    End Function

    ' could be made as task..
    Public Async Sub ActivatePlugin(plugin As IPlugin) Implements IPluginManager.ActivatePlugin
        ' maybe check if inactive before ??
        plugin.Activate()
        Await AddPluginToActiveFileListAsync(plugin)
        InactivePlugins.Remove(plugin)
        ActivePlugins.Add(plugin)
    End Sub

    Public Async Sub DeactivatePlugin(plugin As IPlugin) Implements IPluginManager.DeactivatePlugin
        ' maybe check if active before ??
        plugin.Deactivate()
        Await RemovePluginFromActiveFileListAsync(plugin)
        ActivePlugins.Remove(plugin)
        InactivePlugins.Add(plugin)
    End Sub

    Public Function IsPluginActive(plugin As IPlugin) As Boolean Implements IPluginManager.IsPluginActive
        Return IsPluginActive(plugin.GetId())
    End Function

    Public Function IsPluginActive(pluginId As String) As Boolean Implements IPluginManager.IsPluginActive
        Return ActivePlugins.SingleOrDefault(Function(x) x.GetId() = pluginId) IsNot Nothing
    End Function

End Class</pre>



<p>Neben den im Interface definierten Dinge, gibt es noch weitere Sachen, die ich zur Implementierung hinzugenommen habe.</p>



<h4 class="wp-block-heading" id="wo-die-aktivierten-plugins-gemerkt-werden">Wo die aktivierten Plugins &#8222;gemerkt&#8220; werden</h4>



<p>Das erste ins Auge fallende &#8222;Ding&#8220; ist dabei die &#8222;Konstante&#8220; namens &#8222;ACTIVE_PLUGINS_CONFIG_FILE&#8220;. Zugegebenermaßen handelt es sich nicht um eine richtige Konstante, da Sie eben nicht die <a href="https://learn.microsoft.com/de-de/dotnet/visual-basic/language-reference/statements/const-statement" target="_blank" rel="noreferrer noopener"><strong>&#8222;Const&#8220;-Anweisung</strong></a> verwendet. Das liegt daran, dass ich den Pfad so gesehen als &#8222;fix&#8220; ansehe, Ihn aber mehr oder weniger dennoch dynamisch ziehe.</p>



<p>Ich verwende die <a href="https://learn.microsoft.com/de-de/dotnet/api/system.io.path.combine?view=net-9.0" target="_blank" rel="noreferrer noopener"><strong>&#8222;Path.Combine&#8220;</strong>-Funktion</a> um den Pfad zur Konfiguration der aktiven Plugins zu setzen.</p>



<h4 class="wp-block-heading" id="plugintypes">PluginTypes</h4>



<p>In dieser Auflistung speichern wir die rohen Typen der Plugins, um Diese dann später zu nutzen. Ist praktisch nur als Zwischenspeichern vorgesehen.</p>



<h4 class="wp-block-heading" id="activepluginids">ActivePluginIds</h4>



<p>Hier befinden sich die Plugin-IDs der aktiven Plugins, auch als Zwischenspeicher gedacht. Könnte man sich ggf. auch sparen, aber für den Anfang passt es so.</p>



<h4 class="wp-block-heading" id="attacheventaggregatorifaware">AttachEventAggregatorIfAware</h4>



<p>Diese Helfer-Funktion soll uns beim &#8222;Anfügen&#8220; des &#8222;EventAggregators&#8220; an jeweilige Plugins helfen. Dies passiert nur, wenn ein jeweiliges Plugin Gebrauch vom gleich noch folgenden &#8222;IEventAggregatorAware&#8220;-Interface macht. Somit kann das Plugin signalisieren, was es braucht. Auf die Schnelle ist mir nichts Anderes eingefallen, da wir in unserem Fall nicht den Konstruktor verwenden können. Das würde heißen, dass das jeweilige Plugin vom DI-Container &#8222;resolved&#8220; werden müsste, wofür es registriert sein muss. Eben das geht leider nicht, da wir ja nicht wissen, welche Plugins unser Programm erwartet. Vielleicht fällt Dir, mir, oder wem auch immer noch was anderes ein :)! Wer das &#8222;EventAggregator-Pattern&#8220; nicht kennt: Es ist eine Möglichkeit zur <strong>losgelösten Kommunikation zwischen verschiedenen Objekten</strong>.</p>



<h4 class="wp-block-heading" id="addplugintoactivefilelistasync">AddPluginToActiveFileListAsync</h4>



<p>Diese Funktion hilft bei der Verwaltung der aktiven Plugins, indem Sie die jeweilige Plugin-ID zur passenden Datei hinzufügt.</p>



<h4 class="wp-block-heading" id="removepluginfromactivefilelistasync">RemovePluginFromActiveFileListAsync</h4>



<p>Die Funktion macht das Gegenteil der Vorherigen: Sie entfernt die jeweilige Plugin-ID wieder von der Auflistung der aktivierten Plugins.</p>



<h2 class="wp-block-heading" id="das-plugin-selbst-die-vb-net-plugin-system-basis">Das Plugin selbst – Die VB NET Plugin-System Basis</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/02/Eine-VB-NET-Plugin-Schnittstelle.png"><img loading="lazy" decoding="async" width="1200" height="628" src="https://robbelroot.de/wp-content/uploads/2022/02/Eine-VB-NET-Plugin-Schnittstelle.png" alt="Eine VB NET Plugin Schnittstelle" class="wp-image-8778" title="Eine VB NET Plugin Schnittstelle"/></a><figcaption class="wp-element-caption">Eine VB NET Plugin Schnittstelle</figcaption></figure>



<p>Oben haben wir schonmal den Plugin-Manager besprochen, das bringt uns aber bei fehlenden Plugins wenig. Daher besprechen wir nun das Skelett und die Implementierung eines jeweiligen Plugins selbst.</p>



<p>Schaue Dir also einmal folgende Plugin-Schnittstelle namens &#8222;IPlugin&#8220; an:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Namespace Contracts

    Public Interface IPlugin

        ''' &lt;summary>An id like 'vendor-plugin-some_number' which shouldn't change over time to uniquely identify the plugin&lt;/summary>
        Function GetId() As String

        ''' &lt;summary>A valid Image filepath or empty string/null if not having an image&lt;/summary>
        Function GetIcon() As String

        ''' &lt;summary>The displayable plugin name&lt;/summary>
        Function GetName() As String

        ''' &lt;summary>The displayable plugin description&lt;/summary>
        Function GetDescription() As String

        ' could be Version Class Type depending on use case..
        Function GetVersion() As String

        ''' &lt;summary>Process internal handling of activation&lt;/summary>
        Sub Activate()

        ''' &lt;summary>Process internal handling of deactivation&lt;/summary>
        Sub Deactivate()

        ''' &lt;summary>If plugin is activated, this will get for example executed at app start&lt;/summary>
        Sub Load()

        Event Activated As EventHandler

        Event Deactivated As EventHandler

    End Interface

End Namespace</pre>



<h3 class="wp-block-heading" id="methoden-1">Methoden</h3>



<p>Du wirst sicherlich festgestellt haben, dass das Plugin-Interface keine Eigenschaften besitzt &#8211; aus gutem Grund. Dazu folgt gleich eine Erklärung, wirf nun den Blick auf die Interface-Methoden:</p>



<h4 class="wp-block-heading" id="getid">GetId</h4>



<p>Diese Funktion dient der eindeutigen Identifikation des Plugins. Am besten setzt Sie sich aus dem Hersteller-Namen &amp; einer Art Plugin-Kennung zusammen. Ein Beispiel dafür, findest Du weiter unten in der Beispiel-Implementierung namens &#8222;UsageWidget&#8220;.</p>



<h4 class="wp-block-heading" id="geticon">GetIcon</h4>



<p>Mit dieser Funktion kann ein Pfad zu einem lokalen Bild wiedergegeben werden. Dieses wird dann zum Beispiel für die Plugin-Auflistung im Programm verwendet. Falls kein Bild benötigt wird, kann man hier einfach &#8222;Nothing&#8220;, oder einen leeren String zurückgeben.</p>



<h4 class="wp-block-heading" id="getname">GetName</h4>



<p>Die Implementierung und spätere Überschreibung dieser Methode sollte den Namen des Plugins in lesbarer Form wiedergeben. Dieser Name wird ebenfalls in der Plugin-Auflistung zur Darstellung verwendet.</p>



<h4 class="wp-block-heading" id="getdescription">GetDescription</h4>



<p>Hiermit kann man dem Plugin eine für den Nutzer lesbare Beschreibung bereitstellen. Eventuell könnte man hier auch eine HTML-Variante verwenden und die durch einen Browser darstellen. Damit hätte man dann sogar die Möglichkeit auf die Seite des Autors zu verweisen.</p>



<h4 class="wp-block-heading" id="getversion">GetVersion</h4>



<p>Hier geben wir die aktuell erstellte Version des Plugins wieder. Man könnte natürlich auch die spezielle <a href="https://learn.microsoft.com/de-de/dotnet/api/system.version?view=net-9.0" target="_blank" rel="noreferrer noopener"><strong>Version-Klasse</strong></a> verwenden. Die anderen Methoden sehen wir in der Implementierung der gleich folgenden, abstrakten Klasse.</p>



<h3 class="wp-block-heading" id="die-pluginbase-eine-abstrakte-iplugin-implementierung">Die PluginBase – Eine abstrakte IPlugin-Implementierung</h3>



<p>Bevor wir mit der abstrakten Klasse fortfahren, erinnere Dich, dass wir Methoden, statt Eigenschaften verwenden. Die Plugin-Daten sollen zur Laufzeit nicht verändert werden können, da nur der Entwickler Sie festlegt. Beim Programmieren des Plugins werden die Methoden in der implementierenden Klasse überschrieben.</p>



<p>Doch schauen wir uns zuerst einmal die dafür notwendige, abstrakte Basis-Klasse an. Dort deklarieren wir die Funktionen als <strong>&#8222;<a href="https://learn.microsoft.com/de-de/dotnet/visual-basic/language-reference/modifiers/mustoverride" target="_blank" rel="noreferrer noopener">MustOverride</a>&#8222;</strong>, damit wir darauf bestehen, dass erbende Klassen diese überschreiben müssen. Zusätzlich haben wir die beiden Basis-Implementierungen der &#8222;Activate&#8220;- &amp; &#8222;Deactivate&#8220;-Funktionen.</p>



<p>Diese rufen die Überschreibungs-pflichtigen &#8222;OnActivate&#8220; &amp; &#8222;OnDeactivate&#8220; Methoden auf. Danach lösen Sie das &#8222;Activated&#8220;- &amp; &#8222;Deactivated&#8220;-Ereignis aus. Die beiden Ereignisse habe ich erstmal nur so eingefügt, aktiv werden Sie in dem Projekt noch nicht verwendet.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Imports MyCRM.Contracts

Public MustInherit Class PluginBase
    Implements IPlugin

    Sub New()

    End Sub

    MustOverride Function GetId() As String Implements IPlugin.GetId

    MustOverride Function GetIcon() As String Implements IPlugin.GetIcon

    MustOverride Function GetName() As String Implements IPlugin.GetName

    MustOverride Function GetDescription() As String Implements IPlugin.GetDescription

    MustOverride Function GetVersion() As String Implements IPlugin.GetVersion

    Public Sub Activate() Implements IPlugin.Activate
        OnActivate()
        RaiseEvent Activated(Me, EventArgs.Empty)
    End Sub

    MustOverride Sub OnActivate()

    Public Sub Deactivate() Implements IPlugin.Deactivate
        OnDeactivate()
        RaiseEvent Deactivated(Me, EventArgs.Empty)
    End Sub

    MustOverride Sub OnDeactivate()

    MustOverride Sub Load() Implements IPlugin.Load

    Public Event Activated As EventHandler Implements IPlugin.Activated

    Public Event Deactivated As EventHandler Implements IPlugin.Deactivated

End Class
</pre>



<div style="
  box-sizing: border-box;
  margin: 36px 0;
  border-radius: 12px;
  overflow: hidden;
  background: #1a1a2e;
  position: relative;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
">

  <!-- Subtiles Grid-Muster (kein extra HTTP-Request) -->
  <div style="
    position: absolute;
    inset: 0;
    background-image:
      linear-gradient(rgba(255,255,255,0.03) 1px, transparent 1px),
      linear-gradient(90deg, rgba(255,255,255,0.03) 1px, transparent 1px);
    background-size: 40px 40px;
    pointer-events: none;
  "></div>

  <!-- Oranger Glow-Akzent -->
  <div style="
    position: absolute;
    top: -60px;
    right: -60px;
    width: 240px;
    height: 240px;
    background: radial-gradient(circle, rgba(230,126,34,0.18) 0%, transparent 70%);
    pointer-events: none;
  "></div>

  <!-- Inhalt -->
  <div style="
    position: relative;
    padding: 32px 28px;
  ">

    <!-- Label -->
    <div style="
      display: inline-block;
      font-size: 11px;
      font-weight: 700;
      letter-spacing: 2.5px;
      text-transform: uppercase;
      color: #e67e22;
      margin-bottom: 14px;
      border: 1px solid rgba(230,126,34,0.35);
      border-radius: 4px;
      padding: 3px 10px;
    ">Brauchst Du sowas für Dein Projekt?</div>

    <!-- Überschrift -->
    <p style="
      margin: 0 0 10px 0;
      font-size: clamp(17px, 4vw, 21px);
      font-weight: 700;
      color: #ffffff;
      line-height: 1.35;
    ">Zu komplex, um es alleine anzugehen?</p>

    <!-- Subtext -->
    <p style="
      margin: 0 0 24px 0;
      font-size: 14px;
      color: rgba(255,255,255,0.65);
      line-height: 1.7;
      max-width: 540px;
    ">
      Plugin-Architekturen, Dependency Injection, Event-Systeme –
      ich entwickle solche Strukturen täglich professionell in VB.NET und C#.
      Schreib mir, ich melde mich schnell zurück.
    </p>

    <!-- Buttons -->
    <div style="
      display: flex;
      flex-wrap: wrap;
      gap: 12px;
      align-items: center;
    ">
      <a href="https://robbelroot.de/kontakt/"
         style="
           display: inline-block;
           padding: 12px 24px;
           background: #e67e22;
           color: #ffffff;
           font-size: 14px;
           font-weight: 700;
           text-decoration: none;
           border-radius: 7px;
           letter-spacing: 0.3px;
           transition: background 0.2s ease;
           white-space: nowrap;
         "
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Projekt anfragen</a>

      <a href="https://robbelroot.de/net-programmierung/"
         style="
           display: inline-block;
           padding: 12px 24px;
           background: transparent;
           color: rgba(255,255,255,0.75);
           font-size: 14px;
           font-weight: 600;
           text-decoration: none;
           border-radius: 7px;
           border: 1px solid rgba(255,255,255,0.2);
           letter-spacing: 0.3px;
           transition: border-color 0.2s ease, color 0.2s ease;
           white-space: nowrap;
         "
         onmouseover="this.style.borderColor='rgba(230,126,34,0.6)';this.style.color='#e67e22'"
         onmouseout="this.style.borderColor='rgba(255,255,255,0.2)';this.style.color='rgba(255,255,255,0.75)'"
      >Leistungen ansehen</a>
    </div>

  </div>
</div>



<h2 class="wp-block-heading" id="ein-usagewidget-plugin-fur-unsere-anwendung">Ein UsageWidget-Plugin für unsere Anwendung</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/02/Die-Plugin-Implementierung-ein-UsageWidget.png"><img loading="lazy" decoding="async" width="1200" height="628" src="https://robbelroot.de/wp-content/uploads/2022/02/Die-Plugin-Implementierung-ein-UsageWidget.png" alt="Die Plugin Implementierung - ein UsageWidget" class="wp-image-8800" title="Die Plugin Implementierung - ein UsageWidget"/></a><figcaption class="wp-element-caption">Die Plugin Implementierung &#8211; ein UsageWidget</figcaption></figure>



<p>Nachdem wir uns nun die ganze Basis des VB NET Plugin-Systems angeschaut haben, gehen wir zu einem konkreten Beispiel. Es handelt sich bei unserem konkreten Plugin um eine Art &#8222;Nutzungs-Tracking&#8220;. Das Plugin benötigt eine Referenz zum oben erwähnten &#8222;EventAggregator&#8220;, Welcher durch den &#8222;PluginManager&#8220; injiziert wird.</p>



<h3 class="wp-block-heading" id="die-implementierung">Die Implementierung</h3>



<p>Hier siehst Du die beispielhafte Implementierung der abstrakten &#8222;PluginBase&#8220;-Klasse. Die einzelnen Funktionen hatte ich ja bereits oben erklärt. Daher werde ich nun im nächsten Schritt die Plugin-spezifischen Eigenheiten erklären.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Imports MyCRM

' to expand this class like in a real life situation:
' - develop things here..
' - build this project
' - take the produced dll file into a separate folder like usage-widget and
' - put it inside the plugins folder of the MyCRM folder

' NOTE: I put this project inside the same folder as the other project for ease..
' for sure it's possible and recommended in real life situations
' to start developing widgets in separate projects anyways, because as extension developer
' you dont have access to the original code anyways.. - which is the point of plugins..

Public Class UsageWidget
    Inherits PluginBase
    Implements IEventAggregatorAware

    Public Const VendorName As String = "RobbelRoot"

    Public Const PluginName As String = NameOf(UsageWidget)

    Public Property EventAggregator As IEventAggregator Implements IEventAggregatorAware.EventAggregator

    Sub New()

    End Sub

    Public Overrides Function GetId() As String
        Return $"{VendorName}-{PluginName}"
    End Function

    Public Overrides Function GetIcon() As String
        Return Nothing
    End Function

    Public Overrides Function GetName() As String
        Return PluginName
    End Function

    Public Overrides Function GetDescription() As String
        Return "A simple plugin for managing some kind of application usage data"
    End Function

    Public Overrides Function GetVersion() As String
        Return "1.0.0"
    End Function

    Public Overrides Sub OnActivate()
        Debug.WriteLine($"[{NameOf(UsageWidget)}] Yay I got activated!")
        EventAggregator.Subscribe("app.booted", AddressOf OnAppBooted)
    End Sub

    Public Overrides Sub OnDeactivate()
        Debug.WriteLine($"[{NameOf(UsageWidget)}] Ney :(, I got deactivated!")
        EventAggregator.Unsubscribe("app.booted", AddressOf OnAppBooted)
    End Sub

    Public Overrides Sub Load()
        EventAggregator.Subscribe("app.booted", AddressOf OnAppBooted)
    End Sub

    Private Sub OnAppBooted(parameter As Object)
        Debug.WriteLine($"[{PluginName}] {NameOf(OnAppBooted)}")
    End Sub

End Class
</pre>



<h4 class="wp-block-heading" id="onactivate">OnActivate</h4>



<p>Die &#8222;OnActivate&#8220;-Methode wird entsprechend aufgerufen, wenn das Plugin aktiviert wird. Das passiert für gewöhnlich durch den &#8222;PluginManager&#8220; im Plugin-Konfigurationsbildschirm. In unserem Beispiel abonnieren wir via &#8222;EvenAggregator&#8220; das &#8222;app.booted&#8220;-Ereignis. Dadurch führen wir die im Plugin definierte Methode namens &#8222;OnAppBooted&#8220; aus.</p>



<h4 class="wp-block-heading" id="ondeactivate">OnDeactivate</h4>



<p>Diese Methode ist das Gegenstück zur &#8222;OnActivate&#8220;-Methode, Welche beim Deaktivieren des Plugins aufgerufen wird. Hier deabonnieren wir das vorherig erwähnte Ereignis.</p>



<h4 class="wp-block-heading" id="load">Load</h4>



<p>Die Load-Methode wird beim Start der Anwendung aufgerufen, falls das Plugin aktiviert ist. Prinzipiell unterscheidet Sie sich nicht arg vom &#8222;OnActivate&#8220;, ist nur praktisch Logik mäßig notwendig. Ein Aktivieren des Plugins ist in unserem Kontext hier zumindest kein Laden beim Start..</p>



<h2 class="wp-block-heading" id="der-eventaggregator-losgeloste-kommunikation">Der EventAggregator – losgelöste Kommunikation</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/02/Das-EventAggregator-Entwurfsmuster.png"><img loading="lazy" decoding="async" width="1200" height="628" src="https://robbelroot.de/wp-content/uploads/2022/02/Das-EventAggregator-Entwurfsmuster.png" alt="Das EventAggregator Entwurfsmuster" class="wp-image-8811" title="Das EventAggregator Entwurfsmuster"/></a><figcaption class="wp-element-caption">Das EventAggregator Entwurfsmuster</figcaption></figure>



<p>Wie oben bereits angesprochen, ist das EventAggregator-Pattern (auch als EventBus bekannt) eine Art Nachrichten- bzw. Ereignis-System. Es ermöglicht die losgelöste Kommunikation zwischen verschiedenen Objekten, ohne dass diese eine direkte oder streng typisierte Abhängigkeit voneinander haben.</p>



<p>Es gibt bereitsBibliotheken, Welche dies auch implementieren, allerdings habe ich hier einen schnellen Eigenbau produziert. Ganz einfach, weil ich weitere gedownloadete Abhängigkeiten vermeiden wollte. Natürlich ist der Eigenbau hier nicht so ausführlich wie andere Implementierungen.</p>



<p>Das betrifft zum Beispiel den Umgang mit unterschiedlichen Threads, etc. &#8211; er reicht allerdings hier aus.</p>



<h3 class="wp-block-heading" id="das-ieventaggregator-interface">Das IEventAggregator-Interface</h3>



<p>Das &#8222;EventAggregator&#8220;-Interface und die Erklärung zu den Methoden findet Ihr hier drunter.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Namespace Contracts
    Public Interface IEventAggregator

        Sub Subscribe(eventName As String, action As Action(Of Object))

        Sub Unsubscribe(eventName As String, action As Action(Of Object))

        Sub Publish(eventName As String, Optional parameter As Object = Nothing)

    End Interface
End Namespace</pre>



<h3 class="wp-block-heading" id="methoden-2">Methoden</h3>



<h4 class="wp-block-heading" id="subscribe">Subscribe</h4>



<p>Diese Methode dient zum Abonnieren von hiesigen Ereignissen. Dabei erwartet die Methode als ersten Parameter den jeweiligen Namen des Ereignisses. Anschließend haben wir die optionale Möglichkeit ein Objekt mit gewissen Daten mitzuliefern.</p>



<h4 class="wp-block-heading" id="unsubscribe">Unsubscribe</h4>



<p>Mit der nächsten Methode machen wir das Abonnement wieder rückgängig.</p>



<h4 class="wp-block-heading" id="publish">Publish</h4>



<p>Hiermit können wir ein jeweiliges Ereignis auslösen, damit alle Abonnenten mit Ihren Handlern benachrichtigt werden. Wie erwähnt kann ein optionaler Parameter für die Ereignisdaten mitgeliefert werden.</p>



<h3 class="wp-block-heading" id="die-implementierung-des-eventaggregators">Die Implementierung des EventAggregators</h3>



<p>Hier findest Du meine beispielhafte Implementierung des &#8222;IEventAggregator&#8220;-Interfaces. Ich verwende ein <strong>&#8222;<a href="https://learn.microsoft.com/de-de/dotnet/api/system.collections.generic.dictionary-2?view=net-9.0" target="_blank" rel="noreferrer noopener">Dictionary</a>&#8222;</strong> um einem Ereignis die jeweiligen Handler zuzuweisen. Je nach Aktion wird ein zugeordneter Handler erstellt/hinzugefügt, oder eben wieder entfernt.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Imports MyCRM.Contracts

Public Class EventAggregator
    Implements IEventAggregator

    Protected eventNamesToActions As IDictionary(Of String, List(Of Action(Of Object)))

    Sub New()
        eventNamesToActions = New Dictionary(Of String, List(Of Action(Of Object)))
    End Sub

    Public Sub Subscribe(eventName As String, action As Action(Of Object)) Implements IEventAggregator.Subscribe
        If Not eventNamesToActions.ContainsKey(eventName) Then
            eventNamesToActions.Add(eventName, New List(Of Action(Of Object)))
        End If
        eventNamesToActions(eventName).Add(action)
    End Sub

    Public Sub Unsubscribe(eventName As String, action As Action(Of Object)) Implements IEventAggregator.Unsubscribe
        If eventNamesToActions.ContainsKey(eventName) Then
            Dim list = eventNamesToActions(eventName)
            list.Clear()
            eventNamesToActions.Remove(eventName)
        End If
    End Sub

    Public Sub Publish(eventName As String, Optional parameter As Object = Nothing) Implements IEventAggregator.Publish
        If eventNamesToActions.ContainsKey(eventName) Then
            Dim handlers = eventNamesToActions(eventName)
            For Each handler In handlers
                handler(parameter)
            Next
        End If
    End Sub

End Class
</pre>



<h2 class="wp-block-heading" id="der-einstiegspunkt-fur-unser-programm">Der Einstiegspunkt für unser Programm</h2>



<p>Da wir in unserem Beispiel hier am besten die &#8222;Dependency-Injection&#8220; verwenden, hier ein kleines Beispiel. Installiere dafür die &#8222;autofac&#8220;-Bibliothek, z. B. über das entsprechende <a href="https://www.nuget.org/packages/Autofac/" target="_blank" rel="noreferrer noopener"><strong>NuGet-Paket</strong></a>. Lege diese Klasse ins Wurzelverzeichnis Deines Windows Forms Projekts:</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Imports System.IO
Imports System.Reflection
Imports Autofac
Imports MyCRM.Contracts

Public Class Program

    Public Shared Container As IContainer

    Public Shared Sub Main(args As String())
        PrepareDI()
        RunApp()
    End Sub

    Private Shared Sub PrepareDI()
        Dim builder = New ContainerBuilder()

        builder.RegisterType(Of frmMain)
        builder.RegisterType(Of PluginListItem)
        builder.RegisterType(Of EventAggregator).As(Of IEventAggregator)().SingleInstance()

        builder.Register(Function(c)
                             Dim pluginDir = Path.Combine(Application.StartupPath, "plugins")
                             Dim pluginManager = New PluginManager(pluginDir)
                             pluginManager.EnsurePluginFolder()
                             pluginManager.LoadPluginTypes()
                             pluginManager.LoadActivePluginsList()
                             pluginManager.LoadPlugins()
                             Return pluginManager
                         End Function).As(Of IPluginManager)().SingleInstance()

        RegisterViews(builder)

        Container = builder.Build()
    End Sub

    Private Shared Sub RegisterViews(builder As ContainerBuilder)
        Dim asm = Assembly.Load(NameOf(MyCRM))
        Dim isInViewsNamespace = Function(x) x.Namespace = $"{NameOf(MyCRM)}.{NameOf(Views)}"
        builder.RegisterAssemblyTypes(asm).Where(isInViewsNamespace)
    End Sub

    Private Shared Sub RunApp()
        Dim frm = Container.Resolve(Of frmMain)
        Application.Run(frm)
    End Sub

End Class
</pre>



<p>Schaue Dir für weitere Details am besten das Programm selbst an, dafür kannst Du es wie immer herunterladen. Sonst würde das Tutorial vermutlich noch 2000 weitere Wörter bekommen!</p>



<h2 class="wp-block-heading" id="das-hauptformular">Das Hauptformular</h2>



<p>Nun kommt das Hauptformular, wo wir dann über den &#8222;EventAggregator&#8220; mit dem Plugin &#8222;telefonieren&#8220;. Beachte, dass ich den &#8222;PluginManager&#8220; hier als Abhängigkeit laden lasse, damit dessen Konstruktion ausgeführt wird. Das könnte man natürlich/vermutlich auch anders lösen, aber da darfst Du dann gerne selbst Deine kreative Hand anlegen.</p>



<p>Im &#8222;Load&#8220;-Ereignishandler kommuniziere ich dann das &#8222;app.booted&#8220; Ereignis an alle Abonnenten. Unsere Plugin-Methode wird dies natürlich durch das Abonnement mitbekommen und entsprechend verarbeiten.</p>



<pre class="EnlighterJSRAW" data-enlighter-language="visualbasic" data-enlighter-theme="" data-enlighter-highlight="" data-enlighter-linenumbers="" data-enlighter-lineoffset="" data-enlighter-title="" data-enlighter-group="">Imports Autofac
Imports MyCRM.Contracts
Imports MyCRM.Views

Public Class frmMain

    Private _pluginManager As IPluginManager

    Private _eventAggregator As IEventAggregator

    Sub New(pluginManager As IPluginManager, eventAggregator As IEventAggregator)
        InitializeComponent()
        _pluginManager = pluginManager
        _eventAggregator = eventAggregator
    End Sub

    Private Sub frmMain_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        ' maybe display some kind of loading screen, depending on needed time later
        _eventAggregator.Publish("app.booted")
        tcMain.SelectedTab = tpSettings
    End Sub

    Private Sub btnPluginSettings_Click(sender As Object, e As EventArgs) Handles btnPluginSettings.Click
        ShowPluginSettings()
    End Sub

    ' as method, cuz other menu items could trigger displaying it..
    Private Sub ShowPluginSettings()
        splcSettings.Panel2.Controls.Clear()
        Dim pluginSettings = Program.Container.Resolve(Of PluginSettingsView)()
        splcSettings.Panel2.Controls.Add(pluginSettings)
    End Sub

    Private Sub tcMain_Selected(sender As Object, e As TabControlEventArgs) Handles tcMain.Selected
        If e.TabPage Is tpSettings Then
            ShowPluginSettings()
        End If
    End Sub

    Private Sub btnLogout_Click(sender As Object, e As EventArgs) Handles btnLogout.Click
        Application.Exit()
    End Sub

    Private Sub btnExit_Click(sender As Object, e As EventArgs) Handles btnExit.Click
        Application.Exit()
    End Sub

End Class
</pre>



<h3 class="wp-block-heading" id="das-plugin-final-laden-lassen">Das Plugin final laden lassen</h3>



<p>Lege nun das durch Visual Studio erstellte Plugin (die fertige DLL) in das passende Verzeichnis. Zur Erinnerung: Es befindet sich im Ordner der Anwendung unter &#8222;plugins&#8220;. Dort kannst Du noch einen weiteren Ordner wie z. B. &#8222;usage-widget&#8220; erstellen.</p>



<p>Packe die DLL dann anschließend in diesen Ordner und es wird beim nächsten Programmstart verfügbar.</p>



<h2 class="wp-block-heading">Moderne Alternativen: MEF &amp; Plugin-Frameworks (.NET 6+)</h2>



<p>Das Tutorial oben zeigt den <strong>Eigenbau von Grund auf</strong> &#x1f449; und genau so lernst du, was unter der Haube passiert. In der Praxis gibt es aber auch fertige Frameworks, die dir Arbeit abnehmen. Hier ein Vergleich:</p>



<figure class="wp-block-table"><table class="has-fixed-layout"><thead><tr><th>Ansatz</th><th>Vorteile</th><th>Nachteile</th><th>Geeignet für</th></tr></thead><tbody><tr><td><strong>Eigenbau</strong> (dieses Tutorial)</td><td>Volle Kontrolle, maximales Lernpotential, keine externen Abhängigkeiten</td><td>Mehr Code, Security &amp; Isolation selbst umsetzen</td><td>Lernprojekte, kleine bis mittlere Apps</td></tr><tr><td><strong><a href="https://learn.microsoft.com/de-de/dotnet/framework/mef/" target="_blank" rel="noreferrer noopener">MEF</a> </strong>(Managed Extensibility Framework)</td><td>Teil von .NET, Attribut-basierte Discovery, kein NuGet nötig</td><td>Weniger flexibel bei komplexen Szenarien, Debugging schwieriger</td><td>Mittelgroße Projekte mit standardisierter Plugin-Erkennung</td></tr><tr><td><strong><a href="https://github.com/natemcmaster/DotNetCorePlugins" target="_blank" rel="noreferrer noopener">McMaster.NETCore.Plugins</a></strong></td><td>Assembly-Isolation via AssemblyLoadContext, NuGet-Versionskonflikt-frei</td><td>Nur .NET Core / .NET 5+, zusätzliche NuGet-Abhängigkeit</td><td>Produktions-Apps mit strikter Plugin-Isolation</td></tr><tr><td><strong>Autofac Module</strong></td><td>Nahtlose DI-Integration (nutzen wir bereits!), sauber testbar</td><td>Setzt Autofac als DI-Container voraus</td><td>Projekte, die bereits Autofac verwenden</td></tr></tbody></table></figure>



<p><strong>Meine Empfehlung:</strong> Starte mit dem Eigenbau (wie oben), um die Konzepte zu verstehen. Für Produktions-Projekte unter .NET 6+ lohnt sich ein Blick auf <strong>McMaster.NETCore.Plugins</strong>, dort bekommst du echte Assembly-Isolation, sodass Plugins ihre eigenen NuGet-Abhängigkeiten mitbringen können, ohne Versionskonflikte mit deiner Hauptanwendung.</p>



<p>Ich persönlich bevorzuge jedoch die absolute Kontrolle &#8211; daher verwende ich in meinen Projekten zu 98% die Marke Eigenbau &#x1f601;.</p>



<h2 class="wp-block-heading" id="fazit-vb-net-plugin-system">Fazit – VB NET Plugin-System</h2>



<figure class="wp-block-image size-full"><a href="https://robbelroot.de/wp-content/uploads/2022/02/VB-NET-Tutorial-1-Fazit.jpg"><img loading="lazy" decoding="async" width="1200" height="628" src="https://robbelroot.de/wp-content/uploads/2022/02/VB-NET-Tutorial-1-Fazit.jpg" alt="VB NET Plugin-System Fazit" class="wp-image-8236" title="VB NET Plugin-System Fazit"/></a><figcaption class="wp-element-caption">VB NET Plugin-System Fazit</figcaption></figure>



<p>Am Ende des Tutorials angekommen hoffe ich, dass ich Dir ein eigenes Plugin-System näher bringen konnte.</p>



<p>Dabei habe ich es so gut wie möglich versucht, verständlich und schnell zu sein, allerdings wurde doch noch ein Riesen-Beitrag draus – ups.</p>



<p>Lass&#8216; Dich final nicht verwirren und lies Dir die einzelnen Passagen aufmerksam und zur Not mehrfach durch.</p>



<p>Lade Dir jedoch vor allem das Beispielprojekt herunter, wo Du dann alles zusammen und im Einsatz sehen kannst.</p>



<p>Da das Tutorial doch so lang geworden ist, werde ich die Umsetzung des Dashboard-Widgets wohl in einem zweiten Teil erläutern.</p>



<div style="
  box-sizing: border-box;
  margin: 36px 0;
  border-radius: 12px;
  overflow: hidden;
  background: #1a1a2e;
  position: relative;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
">

  <!-- Subtiles Grid-Muster (kein extra HTTP-Request) -->
  <div style="
    position: absolute;
    inset: 0;
    background-image:
      linear-gradient(rgba(255,255,255,0.03) 1px, transparent 1px),
      linear-gradient(90deg, rgba(255,255,255,0.03) 1px, transparent 1px);
    background-size: 40px 40px;
    pointer-events: none;
  "></div>

  <!-- Oranger Glow-Akzent -->
  <div style="
    position: absolute;
    top: -60px;
    right: -60px;
    width: 240px;
    height: 240px;
    background: radial-gradient(circle, rgba(230,126,34,0.18) 0%, transparent 70%);
    pointer-events: none;
  "></div>

  <!-- Inhalt -->
  <div style="
    position: relative;
    padding: 32px 28px;
  ">

    <!-- Label -->
    <div style="
      display: inline-block;
      font-size: 11px;
      font-weight: 700;
      letter-spacing: 2.5px;
      text-transform: uppercase;
      color: #e67e22;
      margin-bottom: 14px;
      border: 1px solid rgba(230,126,34,0.35);
      border-radius: 4px;
      padding: 3px 10px;
    ">Brauchst Du sowas für Dein Projekt?</div>

    <!-- Überschrift -->
    <p style="
      margin: 0 0 10px 0;
      font-size: clamp(17px, 4vw, 21px);
      font-weight: 700;
      color: #ffffff;
      line-height: 1.35;
    ">Zu komplex, um es alleine anzugehen?</p>

    <!-- Subtext -->
    <p style="
      margin: 0 0 24px 0;
      font-size: 14px;
      color: rgba(255,255,255,0.65);
      line-height: 1.7;
      max-width: 540px;
    ">
      Plugin-Architekturen, Dependency Injection, Event-Systeme –
      ich entwickle solche Strukturen täglich professionell in VB.NET und C#.
      Schreib mir, ich melde mich schnell zurück.
    </p>

    <!-- Buttons -->
    <div style="
      display: flex;
      flex-wrap: wrap;
      gap: 12px;
      align-items: center;
    ">
      <a href="https://robbelroot.de/kontakt/"
         style="
           display: inline-block;
           padding: 12px 24px;
           background: #e67e22;
           color: #ffffff;
           font-size: 14px;
           font-weight: 700;
           text-decoration: none;
           border-radius: 7px;
           letter-spacing: 0.3px;
           transition: background 0.2s ease;
           white-space: nowrap;
         "
         onmouseover="this.style.background='#cf6d17'"
         onmouseout="this.style.background='#e67e22'"
      >→ Projekt anfragen</a>

      <a href="https://robbelroot.de/net-programmierung/"
         style="
           display: inline-block;
           padding: 12px 24px;
           background: transparent;
           color: rgba(255,255,255,0.75);
           font-size: 14px;
           font-weight: 600;
           text-decoration: none;
           border-radius: 7px;
           border: 1px solid rgba(255,255,255,0.2);
           letter-spacing: 0.3px;
           transition: border-color 0.2s ease, color 0.2s ease;
           white-space: nowrap;
         "
         onmouseover="this.style.borderColor='rgba(230,126,34,0.6)';this.style.color='#e67e22'"
         onmouseout="this.style.borderColor='rgba(255,255,255,0.2)';this.style.color='rgba(255,255,255,0.75)'"
      >Leistungen ansehen</a>
    </div>

  </div>
</div>



<h2 class="wp-block-heading">FAQ</h2>



<div class="schema-faq wp-block-yoast-faq-block"><div class="schema-faq-section" id="faq-question-1774438440665"><strong class="schema-faq-question">Kann ich das Plugin-System auch in C# verwenden?</strong> <p class="schema-faq-answer">Ja. VB.NET und C# sind beides .NET-Sprachen und teilen die gleichen APIs (<code>System.Reflection</code>, <code>Assembly.LoadFrom</code>, Interfaces). Die Logik bleibt identisch, nur die Syntax ändert sich. Die Interfaces (<code>IPlugin</code>, <code>IPluginManager</code>) können sogar sprachübergreifend genutzt werden.</p> </div> <div class="schema-faq-section" id="faq-question-1774438466758"><strong class="schema-faq-question">Wie lade ich Plugins sicher, ohne Sicherheitsrisiken?</strong> <p class="schema-faq-answer">Lade Assemblies nur aus einem dedizierten Plugin-Ordner, validiere den Typ per Interface-Check (<code>GetInterface</code>) und verwende nie <code>Assembly.LoadFrom</code> mit Benutzereingaben direkt. Für Produktivumgebungen empfiehlt sich zusätzlich eine Signatur-Prüfung der DLLs.</p> </div> <div class="schema-faq-section" id="faq-question-1774438487662"><strong class="schema-faq-question">Was ist der Unterschied zwischen MEF und einem eigenen Plugin-System?</strong> <p class="schema-faq-answer">MEF (Managed Extensibility Framework) ist Microsofts eingebaute Lösung für Plugin-Architekturen. Der Vorteil eines Eigenbaus, wie in diesem Guide, ist volle Kontrolle über Laden, Konfiguration und Abhängigkeiten, besonders wenn du bereits einen DI-Container wie Autofac nutzt.</p> </div> <div class="schema-faq-section" id="faq-question-1774528831021"><strong class="schema-faq-question"><strong>Wie finde ich alle Plugin-DLLs in einem Ordner automatisch?</strong></strong> <p class="schema-faq-answer">Nutze <code>Directory.GetFiles(pluginFolder, "*.dll")</code> um alle DLL-Dateien im Plugin-Verzeichnis zu finden. Anschließend lädst Du jede DLL mit <code>Assembly.LoadFrom()</code> und prüfst per Reflection, ob sie Dein Plugin-Interface implementiert. Im Beispielprojekt übernimmt der PluginManager diese Logik in der <code>LoadPlugins</code>-Methode.</p> </div> <div class="schema-faq-section" id="faq-question-1774528840634"><strong class="schema-faq-question">K<strong>ann ich Plugins zur Laufzeit aktivieren und deaktivieren?</strong></strong> <p class="schema-faq-answer">Ja. Im Tutorial implementiert der PluginManager die Methoden <code>ActivatePlugin</code> und <code>DeactivatePlugin</code>, die Plugins zur Laufzeit ein- und ausschalten. Der Aktivierungsstatus wird in einer Textdatei (<code>plugins_active.txt</code>) persistiert und beim nächsten Start automatisch wiederhergestellt.</p> </div> <div class="schema-faq-section" id="faq-question-1774528857285"><strong class="schema-faq-question"><strong>Brauche ich Dependency Injection für ein Plugin-System?</strong></strong> <p class="schema-faq-answer">Nein, DI ist nicht zwingend erforderlich. Das Tutorial nutzt Autofac, um Abhängigkeiten wie den EventAggregator sauber an Plugins zu übergeben. Für einfachere Szenarien kannst Du Abhängigkeiten auch manuell über Properties oder Methoden-Parameter injizieren — der Eigenbau funktioniert auch ohne DI-Container.</p> </div> </div>



<h2 class="wp-block-heading" id="downloads">Downloads</h2>



<div class="wp-block-buttons is-layout-flex wp-block-buttons-is-layout-flex">
<div class="wp-block-button"><a class="wp-block-button__link wp-element-button" href="https://robbelroot.de/downloads/vbnet/VBPluginSystemExample.zip" target="_blank" rel="noreferrer noopener">VBPluginSystemExample.zip</a></div>
</div>



<h2 class="wp-block-heading" id="weiterfuhrende-links">Weiterführende Links</h2>



<ul class="wp-block-list">
<li><strong><a href="https://robbelroot.de/blog/vb-net-webbrowser-alternative/" target="_blank" rel="noreferrer noopener">VB NET Webbrowser Alternative</a></strong></li>



<li><strong><a href="https://robbelroot.de/blog/backgroundworker-beispiel-in-vb-net/" target="_blank" rel="noreferrer noopener">Backgroundworker Beispiel</a></strong></li>



<li><strong><a href="https://robbelroot.de/blog/tutorials-vbnet-1-variablen/" target="_blank" rel="noreferrer noopener">Visual Basic NET von Grund auf lernen</a></strong></li>
</ul>
<p>Der Beitrag <a href="https://robbelroot.de/blog/dein-vb-net-plugin-system-im-eigenbau-das-ultimative-beispiel/">Dein VB NET Plugin-System im Eigenbau – DAS ultimative Beispiel!</a> erschien zuerst auf <a href="https://robbelroot.de">Robert Skibbe</a>.</p>
]]></content:encoded>
					
					<wfw:commentRss>https://robbelroot.de/blog/dein-vb-net-plugin-system-im-eigenbau-das-ultimative-beispiel/feed/</wfw:commentRss>
			<slash:comments>6</slash:comments>
		
		
			</item>
	</channel>
</rss>
