Über Reflection kann man zur Laufzeit relativ einfach an alle Informationen eines Assembly herankommen. So ist es möglich, dass man von einem Assembly den Namen, Klassen, Methoden mit ihren Parametern, Eigenschaften und Rückgabewerte, usw. auslesen kann. Aber nicht nur das Auslesen ist möglich, sondern auch das Setzen.

Jetzt stellt sich für den einen oder anderen vielleicht die Frage, warum man zur Laufzeit auf diese Daten zugreifen kann. Jede Assembly verfügt über Module in denen sich die einzelnen Typen(Klassen) befinden. Für jedes Modul gibt es Metadaten, in denen die einzelnen Typen beschrieben werden. Die Assembly selbst verfügt noch über ein Manifest, in dem sich alle Informationen zum Assembly (z.B. Name, Version) befinden. Wenn man über Reflection auf Assemblies zugreift, dann werden diese Metadaten und das Manifest dafür herangezogen. Die Metadaten verfügen zwar noch über die Informationen, welche Modifizierer für Methoden, Properties, usw. verwendet wurden, aber der MSIL-Code (die Zwischensprache, die vom Endcompiler in die plattformspezifische Sprache übersetzt wird) nicht mehr. Die Modifizierer sind nur für den Compiler relevant und werden nach dem Kompilieren nicht mehr berücksichtigt. Somit ist es auch möglich über Reflection Properties, Methoden, usw. zu verwenden, die nicht öffentlich sind.

Für dieses Beispiel habe ich eine kleine Konsolen-Anwendung geschrieben (TestApplication), welche eine Referenz auf das Assembly TestProject.dll hat. Dieses Assembly hat zwei von mir erstellte Typen (Klassen) TestClass und SecondTestClass. In den beiden Klassen steckt keine Logik. Sie sind ausschließlich für dieses Beispiel erstellt worden, um ein paar Daten zu modifizieren und um zu zeigen wie die Informationen aus dem Assembly ausgelesen und verändert werden können und dass es möglich ist an nicht-öffentliche Methoden heranzukommen. weiter lesen…

Comments No Comments »

In diesem Blog-Post möchte ich kurz erläutern, wie ich mit dem Problem des dynamischen Ladens von Assemblies umgegangen bin.

Vor einiger Zeit stand ich vor dem Problem, dass ich zur Laufzeit Assemblies austauschen wollte. Grund dafür war, dass ich eine Host-Applikation hatte, die Plug-Ins verwendet. Jetzt wollte ich bestehende Plug-Ins während der Laufzeit austauschen oder neue Plug-Ins hinzufügen ohne die Host-Applikation zu beenden. Dazu hatte ich ein Verzeichnis in dem sich, außer den Plug-Ins, alle Assemblies befanden. Die Plug-Ins selbst befanden sich in einem eigenen Unterverzeichnis. Selbst wenn die Plug-Ins von der Host-Applikation nicht mehr verwendet wurden, war es nicht möglich, diese Assemblies zu löschen.

Im Normalfall ist es so, dass sobald eine Assembly von einer Applikation verwendet wird, eine Referenz auf diese existiert. Diese Referenz wird leider erst gelöscht, wenn die ganze Applikation beendet wird.

Bei dem dynamischen Laden von Assemblies wird die zu ladende Assembly geöffnet, ausgelesen und dann geschlossen. Aus den ausgelesenen Bytes wird dann mittels Reflection eine Assembly im Arbeitsspeicher erzeugt. Auf die lokale Assembly hängt somit keine Referenz und es ist möglich diese zu löschen.

Meine Umsetzung sieht folgendermaßen aus:

    2 public IList<Plugin> GetPlugins(string assemblyName) {
    3     Assembly assembly;
    4     IList<Plugin> pluginList = new IList<Plugin>();
    5     try {
    6         byte[] byteAssembly = File.ReadAllBytes(assemblyName);
    7         assembly = Assembly.Load(byteAssembly);
    8     } catch (Exception ex) {
    9         log.Error(ex);
   10     }
   11     try {
   12         if (assembly != null) {
   13             Type[] assemblyTypes = assembly.GetTypes();
   14             foreach (Type assemblyTyp in assemblyTypes) {
   15                 if (typeof(Plugin).IsAssignableFrom(assemblyTyp)) {
   16                     plugin = (Plugin)assembly.CreateInstance(assemblyTyp
   17                                                                 .FullName);
   18                     if (plugin != null) {
   19                         pluginList.Add(plugin);
   20                     }
   21                 }
   22             }
   23         }
   24     } catch (ReflectionTypeLoadException ex) {
   25         log.Error(ex);
   26     }
   27     return pluginList;
   28 }

Comments No Comments »