Archive for the ‘Visual Studio’ Category
Ein Einfaches Addin Framework
Seit kurzem beschäftige ich mich etwas mit Reflection und möchte euch helfen ein paar Fehler zu vermeiden welche ich am Anfang gemacht habe. Darum werde ich ein Einfaches Addin Modul erstellen, welches es ermöglicht Addins dynamisch in eine Applikation zu laden.
Der Code
Es werden Mehrere Klassen in unterschiedlichen Assembly’s benötigt um die Funktionsweiße zu demonstrieren. Ich Fange mit den Zentralen Klassen des Frameworks an.
Das Loader Assembly
Wir benötigen ein Attribut welches uns mitteilt welche Klassen ein Addin sind, dazu definieren wir folgende Klasse:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace org.wirsin.blogs.addinframework
{
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public class AddinIdentifier : Attribute
{
private string className;
public string ClassName
{
get
{
return className;
}
private set
{
className = value;
}
}
public AddinIdentifier(string className)
{
this.className = className;
}
}
}
Desweiteren benötigen wir eine Klasse welche das erzeugen einer neuen Addin Instanz übernehmen kann, und unter Umständen auch noch zusätzliche Informationen über die Addins zur Verfügung stellen kann. Diese Informationen sollten über Attribute zur Verfügung gestellt werden. Diese Klasse ist Generisch da wir ja wenn wir uns eine Instanz des Addins erzeugen lassen das Addin Interface als Rückgabewert erwarten.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace org.wirsin.blogs.addinframework
{
public class Addin
{
private Type addinType;
public Addin(Type addinType)
{
this.addinType = addinType;
}
public AddinType Instance
{
get
{
return (AddinType)Activator.CreateInstance(addinType);
}
}
}
}
Die letzte Klasse welche im AddinFramework Assembly beheimatet ist ist der AddinLoader. Diese Klasse dient dazu Addins aus Assembly’s zu laden. Dies ist der “langsamste” Teil des Ladens da jäh nach Vorgehensweise mehrere Typen geladen werden müssen welche unter umständen später nicht mehr verwendet werden. Darum wurde für dieses Beispiel der Ansatz mit Assembly Attributen gewählt. Die Implementation sieht wie folgt aus:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace org.wirsin.blogs.addinframework
{
public class AddinLoader where PluginIdentifier : AddinIdentifier
{
private Type pluginIdentifier;
private Type pluginType;
private List> addins = new List>();
public List> Addins
{
get
{
return addins;
}
private set
{
addins = value;
}
}
public AddinLoader()
{
pluginIdentifier = typeof(PluginIdentifier);
pluginType = typeof(PluginType);
}
public void LoadAddinsFromFile(string fileName)
{
Assembly currentAssembly = Assembly.LoadFrom(fileName);
LoadAddinsFromAssembly(currentAssembly);
}
public void LoadAddinsFromAssembly(Assembly assembly)
{
PluginIdentifier[] Attributes = (PluginIdentifier[])assembly.GetCustomAttributes(pluginIdentifier, false);
foreach (PluginIdentifier i in Attributes)
{
addins.Add(new Addin(assembly.GetType(i.ClassName)));
}
}
}
}
Diese Klasse ist wiederum Generisch definiert. Dadurch ist es Möglich Addins zu laden welche nicht ausschließlich mittels des in diesem Assembly definierten AddinIdentifier geladen werden können sondern mittels Attributen versehen werden können welche von AddinIdentifier erben. Die Zentrale Methode und gleichzeitig auch die Methode mit dem höchsten Einsparungs potential ist die LoadAddinsFromAssembly Methode. Durch die Implementierung die gewählt wurde sind Teilweise riesige Einsparungsmöglichkeiten gegeben. Man könnte diese Methode auch Implementieren indem man jeden Typ im Assembly daraufhin überprüft ob er z.B.: ein spezielles Interface implementiert oder ob die Klasse mit einem Speziellen Attribut gekennzeichnet ist. Dies können in großen Assemblies mehrere Hundert Iterationen sein um festzustellen das nur ein Addin im Assembly definiert ist. Mit der hier implementieren Methode werden wirklich nur diese Typen aus dem Assembly geladen welche als Addin dienen.
Der Client
In unserer Implementierung ist der Client eine einfache Konsolen Application welche Addins verwendet um beliebigen Inhalt auf die Console zu schreiben.
Als erstes benötigen wir ein Interface welches ein Addin repräsentiert.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace org.wirsin.blogs.addinframework
{
public interface IAddin
{
void WriteSomething();
}
}
Und den Client welcher die AddinLoader Klasse verwendet um Addins aus einem Anderen Assembly nachzuladen.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace org.wirsin.blogs.addinframework
{
class Program
{
static void Main(string[] args)
{
AddinLoader loader = new AddinLoader();
loader.LoadAddinsFromFile("Addins.dll");
foreach (Addin i in loader.Addins)
{
i.Instance.WriteSomething();
}
Console.ReadLine();
}
}
}
Die Addins
Alle Addins werden in einem Assembly definiert. Diese Definition kann auch gerne in mehreren Addins erfolgen. Die Iteration über den AddinFolder muss dann aber vom Client durchgeführt werden.
Nun zur Implementierung, In diesem Assembly benötigen wir eine Reference auf die Client Application und auf das AddinFramework Assembly. Wenn die Referenzen gesetzt sind implementieren wir folgende Klassen.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
[assembly: org.wirsin.blogs.addinframework.AddinIdentifier("org.wirsin.blogs.addinframework.FirstAddin")]
namespace org.wirsin.blogs.addinframework
{
public class FirstAddin : IAddin
{
#region IAddin Members
public void WriteSomething()
{
Console.WriteLine("FirstAddin Loaded.");
}
#endregion
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
[assembly: org.wirsin.blogs.addinframework.AddinIdentifier("org.wirsin.blogs.addinframework.SecondAddin")]
namespace org.wirsin.blogs.addinframework
{
public class SecondAddin : IAddin
{
#region IAddin Members
public void WriteSomething()
{
Console.WriteLine("SecondAddin loaded");
}
#endregion
}
}
Beide Addins Implementieren das IAddin Interface und werden mittels eines Assembly Attribute als Addin Identifiziert.
Die Demo
Der Output des Clients sieht wie folgt aus. Auf die Reihenfolge in welcher die AddIns ausgeführt werden nehmen wir keinen Einfluss.

Demo Application für den AddinLoader
Solltet ihr noch fragen haben schreibt einfach einen Kommentar und lasst es mich wissen.
Freundliche Assemblies und geheime Klassen
Kürzlich hatte ich die Anforderung eine Klasse mit einigen Members nur gewissen Assemblies zugänglich zu machen. Nach kurzem Nachdenken und einer schnellen Suche stellte sich heraus das dieses Unterfangen am einfachsten mit Friendly Assemblies zu bewerkstelligen ist.
Bloß wie definiert man Friendly Assemblies und wie Verwendet man selbige? Genau das werde ich hier nun erklären.
Ich starte mit einer Blank Solution um alle Schritte so genau wie Möglich zu beschreiben.

Erstellen einer Empty Solution
In dieser Solution legen wir nun 2 Projekte an zum einen das Assembly mit den geheimen Klassen und Membern und zum anderen das Assembly welches auf diese geheimen Klassen und Member zugreifen soll.

Erstellen einer Class Library

Erstellen einer Console Application
Als nächstes generiere ich ein KeyPair zum Signieren der Assemblies.
Dazu öffne ich einen Visual Studio 2008 Command Prompt und führe folgende Schritte aus.

Erstellen eines KeyPair zum Signieren von Assemblies
sn -k MeinName.snk sn -p MeinName.snk MeinName.Public.snk sn -tp MeinName.Public.snk > publickey.txt
Nun da ich ein KeyPair und einen daraus resultierenden Public Key habe kann ich beide Assemblies Signieren.
Wenn dies geschehen ist kann ich mich dem Implementieren einer geheimen Klasse zuwenden.
using System;
namespace SecretDemo
{
internal class SecretDog
{
internal SecretDog()
{
Console.WriteLine("SecretDog created.");
}
internal void Bark()
{
Console.WriteLine("Bark...");
}
}
}
Nun muss ich in meinem Assembly noch definieren wer seine Freunde sind. Dies geschieht über eine Direktive in der Datei AssemblyInfo.cs. Die Direktive lautet:
[assembly: InternalsVisibleTo("UserDemo, PublicKey=0024000004800000940000000602000000240000525341310004000001000100e34a70830f261f8133ec72c3b49047f2a41970d2750b92aed7bb94daabe9883f88a736b1bef5ad7c4462776a9780e8a927a61c329249f0e0973463f134dc54cb1048e000d5a1ace7a9d97bd9e5cf5a3ced78edae726b5656b61ce4340214ceee2936838b73183909657bc7a56e17a8559b1869cd6d9497dbb7e6b12f4d96dcc7")]
Diese Direktive folgt dem Schema „AssemblyName, PublicKey=PublicKey“. Den PublicKey entnimmt man der Textdatei welche im Zuge der Erstellung des KeyPairs entstanden ist. Da Dies nun geschehen ist kann ich diese Klasse in meiner UserDemo Assembly verwenden. Dazu füge ich eine Referenz auf das SecretDemo Projekt in das UserDemo Projekt ein und erstelle den Code der die Geheime Klasse verwendet.
using System;
using SecretDemo;
namespace UserDemo
{
static class Program
{
static void Main(string[] args)
{
SecretDog dog = new SecretDog();
dog.Bark();
Console.ReadLine();
}
}
}
Wenn alles gutgegangen ist sollte eine lauffähige Applikation entstanden sein welche den SecretDog verwenden kann. Und das Ergebnis sollte wie folgt aussehen.

Laufende Demo Applikation welche die geheime Klasse SecretDog verwendet.
You are currently browsing the archives for the Visual Studio category.