8.2 Assemblies
Wenn Sie eine Konsolen-, Windows- oder Windows-Dienst-Anwendung entwickeln, wird eine EXE-Datei erzeugt. Ist das Projekt zum Beispiel vom Typ Klassenbibliothek, wird eine nicht eigenständig ausführbare DLL-Datei generiert. Die Kompilate werden, abhängig von der Konfigurationseinstellung, im Ordner /bin/Debug bzw. obj/Debug unterhalb des Projektordners gespeichert.
Die Interoperabilität von (D)COM basiert auf einem eigenen Typsystem und der Orientierung an Schnittstellen und nicht an Implementierungen. Die Schnittstellenbeschreibungen sind in der Registrierungsdatenbank abgelegt, der Programmcode getrennt davon im Dateisystem. Das Typsystem erlaubt keine unterschiedlichen Versionen derselben Komponente: Das ist die sogenannte DLL-Hölle. Wegen dieser Probleme führte .NET ein neues Konzept ein:
- Code und Selbstbeschreibung einer Komponente bilden eine Einheit (Registry unnötig).
- Verschiedene Versionen einer Komponente können parallel installiert und auch parallel ausgeführt werden (keine Abwärtskompatibilität).
- Eine Anwendung muss selber wissen, welche Komponentenversionen sie nutzen kann. Die Laufzeitumgebung muss dementsprechend versionsrichtige Komponenten laden.
Diese Komponenten werden Assembly genannt. Ihre Selbstbeschreibung (inklusive Version und Sicherheitsrichtlinien) und die Beschreibung benötigter Komponenten (inklusive Version) erfolgt als ein Teil des Codes, der als Manifest bezeichnet wird. Es gibt zwei Assembly-Typen:
- Private Assemblies die nur von einer Anwendung genutzt werden können und im selben Verzeichnis liegen müssen.
- Gemeinsame Assemblies (globale Assemblies), die von allen Anwendungen genutzt werden können und im Global Assembly Cache (GAC) liegen.
-
- Für eine Veröffentlichung im GAC ist ein kryptografischer Schlüssel Voraussetzung. Dieser gewährleistet, dass eine Assembly von einer anderen, zufälligerweise gleichnamigen Assembly eines anderen Entwicklers eindeutig unterschieden werden kann.
8.2.1 Struktur einer Assembly
Um eine einfache und sichere Versionierung und Verteilung zu erreichen, muss die Selbstbeschreibung einer Assembly einige Informationen enthalten:
- der Name, um die Assembly zu identifizieren
- eine Angabe darüber, ob die vorliegende Assembly das ursprüngliche Original oder eine neuere Version ist
- Abhängigkeiten von anderen Komponenten, inklusive deren Namen und Versionsnummern
- Informationen über die von der Assembly exportierten Typen
- Bezeichner aller Methoden, inklusive Parameternamen und -typen, und den Typ des Rückgabewertes
Diese Metadaten sind Daten, die andere Daten beschreiben. Beispiele dafür sind Datentypen und Standardwerte von Tabellenspalten. Eine Assemblierung hat damit drei Blöcke:
- Metadaten, die die Assembly allgemein beschreiben (das Manifest)
- Typmetadaten, die die öffentlichen Typen beschreiben
- den IL-Code inklusive Ressourcen (z. B. Bilder, Tabellen für Fremdsprachenübersetzung)
Eine Assembly muss Metadaten enthalten, selbst der Code und die Ressourcen sind optional.
Manifest und Metadaten
Metadaten sind binär in der DLL- bzw. EXE-Datei (der Assembly) gespeichert. Sie werden von der Common Language Runtime benötigt, um Datentypen und Objekte überhaupt verwenden zu können. Sie lassen sich in zwei Gruppen einteilen:
- Das Manifest, das die Struktur einer Assembly beschreibt, enthält unter anderem:
- Typname
- Versionsnummer
- öffentliche Schlüssel
- Liste aller Dateien, aus denen sich die Assembly zusammensetzt
- Liste aller weiteren Assemblies, die an die aktuelle Assembly statisch gebunden sind
- Sicherheitsrichtlinien, die die Berechtigungen an der Assembly steuern
- Typmetadaten, die die Typen des IL-Codes innerhalb einer Komponente beschreiben. Das schließt den Namen des Typs, seine Sichtbarkeit, seine Basisklassen und die von ihm implementierten Schnittstellen ein.
Es spielt keine Rolle, in welcher Sprache eine Assembly entwickelt worden ist. Das Manifest verwischt die Spuren des zugrunde liegenden Quellcodes. Die Intermediate Language (IL) und die Common Language Runtime (CLR) schaffen mittels des Manifests die Voraussetzung für den problemlosen Austausch, ohne weitere Informationen zu benötigen.
IL-Disassembler
Das mit Visual Studio gelieferte Tool ildasm.exe, der sogenannten IL-Disassembler, macht die Metadaten sichtbar. In der Standardinstallation befindet er sich im Verzeichnis ...\Programme\Microsoft SDKs\Windows\v6.0A\Bin. Dieser Pfad ist in der Eingabeaufforderung bereits gesetzt, die Sie über den Startmenüpunkt Start • Alle Programme • Microsoft Visual Studio 2008 • Visual Studio Tools • Visual Studio 2008 Eingabeaufforderung öffnen. Entweder geben Sie hinter ildasm einen Dateipfad zu einer Assembly an, oder Sie öffnen die grafische Oberfläche durch Weglassen des Dateipfades. Zum Beispiel:
ildasm C:\MeineProjekte\MyFirstAssembly.exe
Sehen wir uns mit dem ILDASM-Tool das Manifest der folgenden Konsolenanwendung an, die in der Datei ClassA.vb die Main-Methode und eine Klasse ClassA enthält und in der Datei ClassB.vb die Klasse ClassB. Um den Informationsgehalt im Disassembler zu verdeutlichen, enthält der Typ ClassB insgesamt drei Variablendeklarationen mit unterschiedlichen Sichtbarkeiten.
'...\Applikation\ILdasm\ClassA.vb |
Imports System.Data
Namespace MyAssembly
Module Start
Sub Main()
Dim col As DataColumn = New DataColumn()
Console.WriteLine("Hallo Welt.")
Console.ReadLine()
End Sub
End Module
Public Class ClassA
Public intVar As Integer
End Class
End Namespace
'...\Applikation\ILdasm\ClassB.vb |
Public Class ClassB Public intVar As Integer Private lngVar As Long Protected strText As String End Class
Der Name der Assembly ist ILdasm. Sehen wir uns jetzt an, was uns das ILDASM-Tool liefert, ohne dabei allzu sehr in die Details zu gehen (siehe Abbildung 8.2).
Abbildung 8.2 Anzeige des ILDASM-Tools
Unterhalb des Wurzelknotens, der den Pfad zu der Assemblierung angibt, steht das Manifest. Darunter ist der Knoten ILdasm, der in der Abbildung bis auf den Namensraum My vollständig geöffnet ist. Neben den Typmetadaten listet das Tool alle Variablen und Klassenmethoden auf – in unserem Beispiel nur die statische Methode Main aus Start sowie die mit .ctor bezeichneten Konstruktoren – und gibt den Sichtbarkeitsbereich der Variablen an. Der Rückgabewert der Methoden wird, getrennt durch einen Doppelpunkt, hinter dem Methodennamen angeführt.
Das Manifest sehen Sie nach einem Doppelklick auf den Manifest-Eintrag in einem neuen Fenster. Der Reihe nach werden zuerst alle externen Assemblies aufgelistet, von denen die aktuelle Anwendung abhängt, unter anderem auch System.Data. Die wichtigste Assembly mscorlib aus mscorlib.dll macht den Anfang.
.assembly extern mscorlib { .publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4.. .ver 2:0:0:0 }
Jede gemeinsam genutzte Assembly im GAC hat einen öffentlichen Schlüssel. Im Manifest steht er hinter dem Attribut .publickeytoken in geschweiften Klammern. Neben dem Namen einer Assemblierung ist unter anderem auch der Schlüssel Teil ihrer Identität. Er sichert gleichzeitig die Identität des Komponentenentwicklers (siehe unten). Ein weiterer Teil der Identität ist die Version, die im Manifest im Attribut .ver gespeichert ist. Dadurch sind gleichnamige Assemblierungen verschiedener Versionen unterschiedlich und können nebeneinander existieren. Der Liste der externen Assemblierungen schließt sich im Block
.assembly ILdasm
eine Liste diverser Attribute an, mit denen die Assemblierung beschrieben wird. Die Attribute werden in den Projekteigenschaften oder der Datei AssemblyInfo.vb festgelegt.
8.2.2 Globale Assemblies
Eine globale Assembly steht allen .NET-Anwendungen zur Verfügung, ein Beispiel sind die Klassen des .NET Frameworks. Bereits vor der Kompilierung entscheiden Sie, ob eine Assembly privat oder öffentlich wird. Global wird sie durch die Installation in einem speziellen Verzeichnis: dem Global Assembly Cache (GAC). Der GAC ist ein Speicherort, an dem sogar mehrere unterschiedliche Versionen derselben Assembly installiert werden dürfen. Zu finden ist der GAC in folgendem Verzeichnis:
\<Betriebssystemordner>\assembly
Der Windows Explorer lässt uns nicht in das Verzeichnis assembly »hineinschauen«. Sie können jedoch an der Eingabekonsole die Struktur des GAC erkunden. Die Bibliotheksnamen werden unter Berücksichtigung der Metadaten auf die Verzeichnisstruktur abgebildet.
<Betriebssystemordner>\assembly\GAC_MSIL\ <Bibliothek ohne Endung>\<Version>_<Kultur>_<Schlüssel>\Bibliothek.dll |
Zum Beispiel finden Sie die Bibliothek System.Drawing.dll der Version 2.0.0.0 mit dem Schlüssel b03f5f7f11d50a3a und keiner Kulturangabe (neutrale Kultur) in folgendem Verzeichnis
C:\WINDOWS\assembly\GAC_MSIL\System.Drawing\2.0.0.0__b03f5f7f11d50a3a
Mit den Informationen zu Dateiname, Version, Kultur und öffentlichem Schlüssel aus dem Manifest der Anwendung sucht die CLR die passende Assembly im GAC. Wird die CLR nicht fündig, geht sie von einer privaten Assembly aus und sucht im Verzeichnis der Anwendung. Konfigurationsdateien beeinflussen diesen Suchprozess (siehe unten).
Hinweis |
Wegen der eindeutigen Identifizierbarkeit werden die Namen von Assemblies mit einem Schlüssel auch starke Namen genannt. |
Versionierung von Assemblies
Die Version einer Assembly besteht aus vier durch jeweils einen Punkt getrennten Zahlen:
- Hauptversion
- Nebenversion
- Build
- Revision
Versionen, die sich in einer der beiden ersten Zahlen unterscheiden, sind inkompatibel. Die beiden letzten Ziffern erfassen kompatible Korrekturen (siehe Abbildung 8.3).
Abbildung 8.3 Schema der Versionierung
Das Manifest einer Anwendung spezifiziert die Versionen benötigter Assemblies. Findet die CLR zur Laufzeit diese nicht, weicht sie auf kompatible Versionen aus (gegebenenfalls einer anderen Kultur). Scheitert dies auch, kann die Assembly nicht geladen werden.
Die Versionsnummer ist ein Attribut der Assembly und steht in der Datei AssemblyInfo.vb.
<Assembly: AssemblyVersion("1.0.1.0")>
Sie tragen sie entweder dort ein oder in dem Dialog aus Abbildung 8.4, den Sie über die Schaltfläche Assemblyinformationen auf der Karteikarte Anwendung der Projekteigenschaften finden. Nach dem Schließen des Dialogs werden die Informationen in die Datei AssemblyInfo.vb eingetragen.
Abbildung 8.4 Festlegen der Assemblyversion
Schlüsseldatei erzeugen
Globale Assemblies sind gekennzeichnet durch die Signierung mit einem binären Schlüsselpaar, das aus einem öffentlichen und einem privaten Schlüssel besteht. Nur der Besitzer des privaten Schlüssels kann eine neue »schlüsselrichtige« Version der Assembly erstellen.
Beim Kompiliervorgang wird ein Teil des öffentlichen Schlüssels (Token) in das Manifest geschrieben und die Datei, die das Manifest enthält, mit dem privaten Schlüssel signiert. Nutzer der Assembly brauchen nur den öffentlichen Schlüssel, der private Schlüssel dient »nur« zur Authentifizierung. Er sichert Ihre Arbeit – verwahren Sie ihn gut.
Öffentlicher und privater Schlüssel werden durch eine Schlüsseldatei beschrieben, die mit dem Tool sn.exe erzeugt wird. Sie können dieses Tool an der Kommandozeile aufrufen oder einfacher über die Auswahlbox Assembly signieren auf der Karteikarte Signierung der Projekteigenschaften. Über die darunterliegende Auswahlliste können Sie dann eine vorhandenen Schlüsseldatei wählen oder eine neue erstellen. Bei der Neuerstellung geben Sie einen Dateinamen an und können die Datei mit einem Kennwort schützen (siehe Abbildung 8.5).
Abbildung 8.5 Schlüsseldatei mit Visual Studio 2008 erzeugen
Beim Signieren einer Assembly haben Sie möglicherweise nicht immer sofort Zugriff auf den privaten Schlüssel. In diesem Fall nehmen Sie eine verzögerte Signierung vor, die zunächst nur den öffentlichen Schlüssel verfügbar macht. Markieren Sie hierzu den Optionsschalter Nur verzögerte Signierung. Das Hinzufügen des privaten Schlüssels wird auf den Zeitpunkt der Bereitstellung der Assembly verschoben.
Installation im GAC mit dem Tool gacutil.exe
Nachdem nun eine Schlüsseldatei in die Assembly eingebunden ist, kann das Kompilat im GAC installiert werden. Dazu haben Sie zwei Möglichkeiten:
- das Kommandozeilenprogramm gacutil.exe des .NET Frameworks.
- eine Installationsroutine mit dem Micosoft Windows Installer
Fangen wir mit der ersten Variante an. Wie alle anderen Tools findet man gacutil.exe unter:
\Programme\Microsoft SDKs\Windows\V6.0A\bin
Die allgemeine Aufrufsyntax lautet:
gacutil [Optionen] [Assemblyname] |
Aus der Liste der Optionen ragen zwei besonders heraus: der Schalter /i, um die darauf folgend angegebene Assembly im GAC zu installieren, und der Schalter /u, um eine gemeinsam genutzte Assembly zu deinstallieren, zum Beispiel so:
gacutil /i MyGlobalAssembly.dll gacutil /u MyGlobalAssembly
Installation im GAC mit einer Setup-Routine
Visual Studio bietet die Projektvorlage Setup-Projekt an, die eine automatisch ablaufende Installationsroutine erzeugt. Die Projektdetails übergehe ich hier und lege den Fokus auf die Installation im GAC. Als Beispiel dienen eine winzige Bibliothek und eine Testanwendung. Die Projekteinstellungen der Bibliothek verwenden einen leeren Stamm-Namespace.
'...\Applikation\GacBibliothek\GacKlasse.vb |
Namespace GacTest Public Class GacKlasse Public Function Version() As String Return "Erste Version" End Function End Class End Namespace
'...\Applikation\GacAnwendung\Programm.vb |
Module Programm
Sub Main(ByVal args() As String)
Dim g As New GacTest.GacKlasse()
For no As Integer = 1 To args.Length
Console.WriteLine("{0}: {1}", no, args(no – 1))
Next
Console.WriteLine(g.Version())
Console.ReadLine()
End Sub
End Module
Wie beschrieben, ergänzen wir eine Schlüsseldatei, die im Projektmappen-Explorer zu sehen ist (siehe Abbildung 8.6).
Abbildung 8.6 Projektmappe mit Schlüsseldatei
Danach sollten Sie nicht vergessen, das Projekt noch einmal zu kompilieren. Der Projektmappe fügen Sie anschließend über ihren Kontextmenüpunkt Hinzufügen • Neues Projekt ein Setup-Projekt hinzu (siehe Abbildung 8.7).
Abbildung 8.7 Auswahl des Setup-Projekts
Nach dem Klick auf OK wird im Codeeditor der Dateisystemeditor des Setup-Projekts angezeigt (siehe Abbildung 8.8). Er fasst unter anderem alle zu installierenden kompilierten Dateien zusammen.
Abbildung 8.8 Ansicht des Dateisystem-Editors
Im Kontextmenü des linken Knotens Anwendungsordner wählen Sie zuerst Hinzufügen und danach Datei. Sie wählen die kompilierte EXE-Datei der Anwendung GacAnwendung. Danach ist diese sowie die Bibliothek als benötigte Komponente im rechten Teil zu sehen.
Im Kontextmenü des obersten Knoten Dateisystem auf Zielcomputer fügen Sie mit Speziellen Ordner hinzufügen das Verzeichnis Cacheordner für globale Assembly hinzu. Verschieben Sie die Bibliothek aus dem Anwendungsordner in diesen Ordner. Nach der Kompilation ist eine setup.exe-Datei im Ausgabeordner des Setup-Projekts. Installieren Sie nun durch deren Aufruf das Programm, oder nutzen Sie dazu den Punkt Installieren im Kontextmenü des Setup-Projekts (Sie brauchen Administratorrechte). Die Abbildung 8.9 zeigt, dass die Biblio-thek im GAC ist.
Abbildung 8.9 Klassenbibliothek »GacBibliothek« im GAC
Der Vorteil des Global Assembly Caches ist, dass andere Anwendungen ebenfalls dieselbe zentral registrierte GacBibliothek nutzen können. Beim Starten von GacAnwendung wird ein GacKlasse-Objekt erzeugt und die Methode Version aufgerufen. Erwartungsgemäß wird »Erste Version« ausgegeben.
Neue Version der globalen Assembly installieren
Angenommen, die erste Version von GacBibliothek sei bereits verteilt (unter anderem über GacAnwendung). Nun soll eine überarbeitete Klasse GacKlasse verteilt werden, hier simuliert durch die Rückgabe einer anderen Version:
Public Function Version() As String
Return "Zweite Version"
End Function
Entweder Sie fügen einer neuen Projektmappe die beiden Projekte hinzu, oder Sie heben die installierte EXE-Datei zur späteren Verwendung auf. Nachdem Sie die Rückgabe geändert haben, setzen Sie die Version der Assembly GacBibliothek auf 2.0.0.0. Fügen Sie der Projektmappe ein neues Setup-Projekt hinzu, dem Sie nur GacBibliothek im Knoten Cacheordner für globale Assembly hinzufügen, nicht GacAnwendung.
Auch die neue Setup-Routine muss ausgeführt werden, damit sich die zweite Version von GeometricsObject in den GAC einträgt (siehe Abbildung 8.10).
Abbildung 8.10 Zwei versionsverschiedene Einträge im GAC
Die bereits installierte GacAnwendung nutzt weiterhin die erste Version der Bibliothek. Damit sie die zweite Version nutzt, muss eine Konfigurationsdatei geändert werden.
Ihre Meinung
Wie hat Ihnen das Openbook gefallen? Wir freuen uns immer über Ihre Rückmeldung. Schreiben Sie uns gerne Ihr Feedback als E-Mail an kommunikation@rheinwerk-verlag.de.