17.2 Assemblys
17.2.1 Ein Überblick über das Konzept der Assemblys
Entwickeln Sie eine Konsolen-, Windows- oder Windows-Dienst-Anwendung, wird eine EXE-Datei erzeugt. Ist das Projekt zum Beispiel vom Typ Klassenbibliothek, wird eine DLL-Datei generiert. Die Kompilate werden, abhängig von der Konfigurationseinstellung, im Ordner /bin/Debug bzw. obj/Debug unterhalb des Projektordners gespeichert.
Für etwas Verwirrung kann die Endung DLL (Dynamic Link Library) sorgen. DLLs waren ursprünglich als reine Funktionssammlungen gedacht (denken Sie beispielsweise an die Betriebssystemfunktionen der Win32-API oder die Funktionen in der ODBC-API), später wurde dieselbe Dateierweiterung aber auch für COM-basierte InProc-Server benutzt. Unter .NET haben wir es mit einem weiteren Typus zu tun, der auf der Common Language Runtime (CLR) basiert. Zumindest eines ist allen DLL-Typen gemeinsam: Sie sind, um sie ausführen zu können, immer auf die Unterstützung einer ausführbaren EXE-Datei angewiesen.
Die Softwarekomponenten allgemein verfügbar zu machen, ist ein Ziel, das viele Hersteller anstreben. Microsoft hat in den 90er-Jahren dazu eine Technologie entwickelt, die sich über viele Jahre hinweg auf den Windows-Plattformen etablierte und im Laufe der Zeit immer mächtiger und komplexer wurde: COM und DCOM. Diese schnittstellenorientierten Technologien beschreiben, wie Softwarekomponenten miteinander kommunizieren, auch wenn sie in unterschiedlichen Programmiersprachen realisiert worden sind. Der Datenaustausch musste dabei über ein COM-spezifisches, von den Programmiersprachen unabhängiges Typsystem abgewickelt werden.
Das war aber nicht das einzige Problem, das COM/DCOM bereitete. Ein ganz wesentlicher Nachteil war die Trennung des Programmcodes von seiner Selbstbeschreibung, die in der Typbibliothek der Registrierungsdatenbank zu finden ist. Zudem war es nicht möglich, mehrere versionsunterschiedliche COM-Komponenten gleichzeitig auf einem Rechner zu installieren. Das führte in der Vergangenheit häufig dazu, dass Programme, die für den Zugriff auf eine ältere Komponentenversion geschrieben waren, ihren Dienst quittierten, wenn mit einem anderen Programm eine neuere Komponentenversion installiert wurde.
Um diesen Kreislauf aufzubrechen, wurde ein völlig neues Konzept spezifiziert, das die folgenden Anforderungen definierte:
- Eine Anwendung muss ihre Dienste selbst beschreiben können, ohne von anderen Systemdiensten wie der Registrierungsdatenbank abhängig zu sein. Dementsprechend müssen Code und Selbstbeschreibung einer Komponente eine kompakte Einheit bilden.
- Zur Vermeidung von Versionskonflikten müssen mehrere Versionen einer Komponente parallel installiert werden können. Damit wird gewährleistet, dass eine Anwendung, die sich der Dienste einer Komponente bedient, nicht durch die Installation einer neuen, jedoch inkompatiblen Komponente in das laufzeittechnische Nirwana befördert wird.
- Die verschiedenen Versionen einer Softwarekomponente müssen gleichzeitig ausführbar sein. Da eine neu zu verteilende Komponentenversion durchaus auch nur das Ziel haben kann, einen bekannt gewordenen Fehler zu beseitigen, sollte eine von dieser Komponente abhängige Anwendung in der Lage sein, aus einer Vielzahl gleicher, jedoch versionsverschiedener Komponenten diejenige zu finden, mit der problemlos zusammengearbeitet werden kann.
- Es muss sichergestellt werden, dass die von einer Anwendung geforderte, richtige Version der Komponente geladen und ausgeführt wird.
Bei allen genannten Punkten setzt das Konzept der Assemblys an. Mehrere Versionen derselben Softwarekomponente dürfen auf einem Rechner installiert sein – mehr noch, sie dürfen sogar gleichzeitig ausgeführt werden. Damit wird zwar einerseits das Prinzip der Abwärtskompatibilität aufgegeben, das unter COM eine elementare Forderung war, andererseits ist Abwärtskompatibilität auch nicht mehr notwendig, weil an eine Anwendung ebenfalls Forderungen hinsichtlich des Komponentenzugriffs gestellt werden.
Die Selbstbeschreibung einer COM-Komponente erfolgt in der Typbibliothek, die als eigenständige Einheit getrennt vom Binärcode existiert. Die Beschreibung einer Assembly samt ihren internen Komponenten hingegen erfolgt in einem Block, der als Manifest bezeichnet wird und mit dem Code unzertrennlich verbunden ist.
Eine Assembly lässt sich nicht nur als die Baugruppe einer Anwendung verstehen. Sie bildet auch gleichzeitig die Einheit, die verteilt wird, beschreibt Sicherheitsrichtlinien und ist die Basis der Versionierung.
17.2.2 Allgemeine Beschreibung privater und globaler Assemblys
Die Frage, die bei jeder Anwendungsentwicklung neu gestellt werden muss, ist, ob der Code in einer .NET-DLL (Assembly) nur einer Anwendung zugänglich sein soll oder ob auch andere Programme darauf zugreifen dürfen. Aus der Fragestellung, wie und von wem eine Assemblierung genutzt werden darf, folgt die Definition zweier unterschiedlicher Assembly-Typen:
- Private Assemblys, die nur von einer Anwendung genutzt werden können
- Gemeinsame Assemblys (globale Assemblys), die allen Anwendungen gleichermaßen ihre Dienste offenlegen
Eine private Assembly zu entwickeln, ist denkbar einfach: Man muss nichts Besonderes dafür tun, private Assemblys sind der Standard. Genauso verhält es sich auch mit der weiter oben bereitgestellten Klassenbibliothek GeometricObjects. Sie gehört zur Anwendung TestApplication. EXE- und DLL-Dateien müssen bei der Verteilung gemeinsam installiert werden. Dabei muss die Klassenbibliothek im gleichen Verzeichnis liegen wie die darauf zugreifende Anwendung.
Nutzen mehrere Anwendungen die gleiche Klassenbibliothek, gilt diese Regel für jede Installation. Im Extremfall kann das dazu führen, dass ein und dieselbe Klassenbibliothek mehrfach auf einem Rechner vorliegt. Das ist zwar grundsätzlich ein Nachteil, weil dadurch Speicherressourcen verschwendet werden, andererseits relativiert sich dieser im Zeitalter der TByte-Festplatten. Gravierender ist jedoch die Auswirkung, wenn die Assembly geändert wird, beispielsweise aufgrund eines Bugs. Das hätte zur Folge, dass man jede einzelne Assembly suchen und austauschen müsste.
Hier betreten die globalen Assemblys die Bühne, die an einem zentralen Ort gespeichert sind und von jeder Anwendung gleichermaßen genutzt werden können. Dieser zentrale Ort ist nicht, wie vielleicht zu vermuten wäre, die Registry, sondern der Global Assembly Cache, kurz GAC genannt. 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.
17.2.3 Die Struktur einer Assembly
Allgemeine Beschreibung
Eine Assembly muss vielen Anforderungen gerecht werden, um die gesteckten Ziele einer einfachen und sicheren Versionierung und Verteilung zu erreichen. Der wesentlichste Punkt ist die Zusammenfassung von Code und Selbstbeschreibung. Überlegen wir, was zu einer Selbstbeschreibung alles gehört:
- der Name, um die Assembly zu identifizieren
- Informationen, um anderen Assemblys mitzuteilen, ob die vorliegende Assembly das ursprüngliche Original oder eine neuere Version ist
- Informationen darüber, von welchen anderen Komponenten sie abhängt; dazu gehören unter anderem der Name und die Versionsnummer der von ihr referenzierten Assemblys
- Informationen über die von der Assembly exportierten Typen
- die Bezeichner aller Methoden, einschließlich der Parameternamen und -typen, sowie der Typ des Rückgabewertes
Diese Punkte lassen sich in zwei logische Kategorien zusammenfassen:
- Metadaten, die eine Assembly ganzheitlich beschreiben und als Manifest der Assembly bezeichnet werden
- Daten zur Beschreibung des IL-Codes, die Typmetadaten
Metadaten sind Daten, die andere Daten beschreiben. Wenn Ihnen das zu abstrakt ist, denken Sie an die Tabelle einer Datenbank. Die Entitäten (Spalten bzw. Felder) der Tabelle werden ebenfalls über Metadaten beschrieben. Dazu gehören beispielsweise die Typdefinition eines Feldes, Gültigkeitsregeln, Standardwerte usw.
Selbst die einfachste Assemblierung setzt sich damit konsequenterweise aus drei Blöcken zusammen:
- aus den Metadaten, die die Assembly allgemein beschreiben (das Manifest)
- aus den Typmetadaten, die die öffentlichen Typen beschreiben
- aus dem IL-Code
Assemblys können nicht nur Module für Typen enthalten, oft gehören auch Ressourcen dazu, beispielsweise BMP-, JPEG- und HTML-Dateien, die von der Assembly zur Laufzeit benötigt werden. Diese Dateien sind dann ebenfalls Bestandteil einer Assembly. Man kann sich auch eine Assembly vorstellen, die weder Code noch Ressourcen enthält – ob eine solche Assembly allerdings noch einen Sinn ergibt, lassen wir dahingestellt.
Manifest und Metadaten
Metadaten sind binäre Informationen, die beim Kompilieren einer Datei, sei es in eine DLL- oder EXE-Datei, hinzugefügt werden und die Daten ganzheitlich beschreiben. Jeder Typ, den man innerhalb einer Anwendung definiert oder einbindet, wird von den Metadaten erfasst. Zur Laufzeit werden die Metadaten in den Speicher geladen und von der Common Language Runtime dazu benutzt, die benötigten Informationen zu beziehen, die zur Erstellung und Verwendung eines Objekts erforderlich sind.
Der Informationsgehalt der Metadaten ist vielseitiger Natur und lässt sich in zwei Gruppen einteilen:
- das Manifest, das die Struktur einer Assembly beschreibt. Zu dem Informationsgehalt eines Manifests
gehören:
- der Typname
- die Versionsnummer
- der öffentliche Schlüssel
- die Liste aller Dateien, aus denen sich die Assembly zusammensetzt
- die Liste aller weiteren Assemblys, die statisch an die aktuelle Assembly gebunden sind
- Sicherheitsrichtlinien, die die Berechtigungen an der Assembly steuern
- Typmetadaten, die die Typen innerhalb einer Komponente beschreiben. Das schließt den Namen des Typs, seine Sichtbarkeit, seine Basisklassen und die von ihm implementierten Schnittstellen ein.
Mit dem Manifest und den Typmetadaten verfügt die Common Language Runtime über genügend Informationen, um Klassen aus einer Datei zu laden, Objekte zu erstellen, Methodenaufrufe aufzulösen und auf Objektdaten zuzugreifen.
Es spielt keine Rolle, in welcher Sprache eine Assembly entwickelt worden ist. Das Manifest verwischt die Spuren des zugrunde liegenden Quellcodes. Unter COM war zur binären Bindung zweier Komponenten noch Bindecode notwendig (IDL – Interface Definition Language), um eine gemeinsame Basis für die Kommunikation und den Datenaustausch beider Komponenten zu schaffen. Die Intermediate Language (IL) und die Common Language Runtime (CLR) schaffen mit Hilfe des Manifests die Voraussetzung für den problemlosen Austausch, ohne dass man solche Behelfsbrücken bauen muss.
Der IL-Disassembler
Sie können sich die Metadaten einer Komponente ansehen, wenn Sie das mit Visual Studio 2012 gelieferte Tool ildasm.exe, den sogenannten IL-Disassembler, an der Konsole aufrufen. Sie finden diese Datei in einem Unterordner der Visual-Studio-Installation. Haben Sie die Standardvorgaben bei der Installation übernommen, wird es sich um
..\Programme\Microsoft SDKs\Windows\v6.0A\Bin
handeln. Optional geben Sie beim Aufruf des Tools den Pfad zu einer Anwendung bzw. Assembly an, beispielsweise:
ildasm C:\MeineProjekte\MyFirstAssembly.exe
Wenn Sie das Tool ohne Dateiangabe starten, können Sie über das Menü Datei • Öffnen die zu inspizierende Assembly wählen.
Wir wollen uns nun mit Hilfe des ILDASM-Tools das Manifest einer Konsolenanwendung ansehen, die neben dem Ausgabecode der Methode Main in derselben Quellcodedatei noch die Definition der Klasse ClassA enthält. In einer zweiten Quellcodedatei des Projekts ist die Klasse ClassB definiert. Um den Informationsgehalt im IL-Tool zu verdeutlichen, enthält der Typ ClassB insgesamt drei Variablendeklarationen mit unterschiedlichen Sichtbarkeiten.
// die Klassen ClassA und ClassB sind in der Quellcodedatei ClassA.cs definiert
using System;
using System.Data;
namespace MyAssembly {
class Program {
static void Main(string[] args) {
DataColumn col = new DataColumn();
Console.WriteLine("Hallo Welt.");
Console.ReadLine();
}
}
public class ClassA {
public int intVar;
}
}
// Klasse ClassB ist in der Quellcodedatei ClassB.cs definiert
public class ClassB {
public int intVar;
private long lngVar;
protected string strText;
}
Der Name der Assembly sei MyAssembly. Beachten Sie bitte, dass in Main zu Demonstrationszwecken ein Objekt vom Typ DataColumn erstellt wird, also auf die standardmäßig eingebundene Datei System.Data.dll zugegriffen wird. Sehen wir uns jetzt an, was uns das IL-Tool liefert, ohne dabei allzu sehr in die Details zu gehen (siehe Abbildung 17.1).
Abbildung 17.1 Die Anzeige des ILDASM-Tools
Unterhalb des Wurzelknotens, der den Pfad zu der Assemblierung angibt, ist – mit einem roten Dreieck gekennzeichnet – das Manifest angeführt. Darunter befindet sich der Knoten MyAssembly, der in der Abbildung bereits vollständig geöffnet ist. Das blaue Rechteck, das mit seinen drei nach rechts weisenden Linien an einen Stecker erinnert, symbolisiert Klassendefinitionen.
Neben den Typmetadaten listet das Tool alle Variablen und Klassenmethoden auf – in unserem Beispiel nur die statische Methode Main aus Program 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.
Werfen wir jetzt einen Blick auf das Manifest dieser Assemblierung. Ein Doppelklick auf den Manifest-Eintrag des Disassemblers öffnet ein weiteres Fenster, das die Metadaten der Assemblierung wiedergibt (siehe Abbildung 17.2).
Der Reihe nach werden alle externen Assemblys aufgelistet, von denen die aktuelle Anwendung abhängt. Dazu gehört die wichtigste aller Assemblys, die mscorlib (beschrieben durch die Datei mscorlib.dll). Ihr folgt die Assembly, die aufgrund der Erzeugung des Objekts vom Typ DataColumn ebenfalls von der Anwendung benutzt wird: System.Data (bzw. die Datei System.Data.dll). Da in der Assemblierung MyAssembly keine weitere externe Assembly eine Rolle spielt, werden auch keine anderen im Manifest aufgeführt. Der Liste der externen Assemblierungen schließt sich im Block
.assembly MyAssembly
eine Liste diverser Attribute an, mit denen die Assemblierung beschrieben wird. Die Attribute können in der Datei AssemblyInfo.cs der Entwicklungsumgebung festgelegt werden. Sehen wir uns nun die Angaben zu einer externen Assembly genauer an, die beispielsweise folgendermaßen lautet:
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 )
.ver 1:0:3300:0
}
Abbildung 17.2 Das Manifest einer Assembly
Weiter oben wurde bereits ein wesentlicher Unterschied zwischen einer privaten und einer gemeinsam genutzten, externen Assembly beschrieben: Eine gemeinsam genutzte, globale Assembly verfügt über einen öffentlichen Schlüssel. Im Manifest wird dieser Schlüssel hinter dem Attribut .publickeytoken in geschweiften Klammern angegeben. Neben dem Namen einer Assemblierung trägt unter anderem auch der Schlüssel zur eindeutigen Identifizierung der Assembly bei und sichert gleichzeitig die Identität des Komponentenentwicklers. Auf dieses Thema werden wir gleich zurückkommen.
Unter .NET dürfen mehrere gleichnamige, allerdings versionsunterschiedliche Assemblierungen nebeneinander existieren, ohne damit Konflikte zu verursachen. Die Versionsinformationen sind in einem standardisierten Format dargestellt, das im Manifest der referenzierenden Assemblierung mit dem Attribut .ver gekennzeichnet ist.
17.2.4 Globale Assemblys
Eine globale Assembly stellt ihre Dienste allen .NET-Anwendungen des Systems gleichermaßen zur Verfügung. Das beste Beispiel globaler Assemblys sind die Klassen des .NET Frameworks. Die Entscheidung, ob eine Assembly global zur Verfügung stehen soll, muss schon vor der Kompilierung berücksichtigt werden, weil standardmäßig immer eine private Assembly erzeugt wird. Globale Assemblys werden in einem speziellen Verzeichnis installiert: dem Global Assembly Cache (GAC). Der GAC ist ein Speicherort, in dem sogar mehrere unterschiedliche Versionen derselben Assembly installiert werden dürfen.
Es gibt seit der Version .NET 4.0 zwei verschiedene Lokalitäten für den GAC. Bis einschließlich der Version .NET 3.5 befindet sich der GAC im Verzeichnis
\<Betriebssystemordner>\assembly
Ab der Version 4.0 wurde eine neue Lokalität definiert. Nunmehr ist der GAC unter
\<Betriebssystemordner>\Microsoft.NET\assembly
zu finden.
In Abbildung 17.3 sehen Sie die Anzeige des alten GAC im Explorer. Dabei verhindert zwar ein Plug-in den Einblick in die Struktur des Dateisystems, aber bietet andererseits einen besseren Überblick.
Abbildung 17.3 Der Global Assembly Cache (GAC) bis einschließlich .NET 3.5
Der GAC ist so strukturiert, dass für jede eingetragene Assembly ein auf dem Dateibezeichner basierender Unterordner angelegt wird. Bei diesem Unterordner wird auf die Dateiextension verzichtet. Dieser Ordner enthält seinerseits weitere Unterordner, deren Bezeichner sich aus der Versionsnummer, der Kultur (das bedeutet, der Sprachversion) und dem öffentlichen Schlüssel der Assembly zusammensetzt. Erst im letztgenannten Unterordner findet sich die entsprechende DLL. Das gilt sowohl für den GAC bis Version 3.5 als auch für den GAC ab Version 4.0.
Doch nach welchen Kriterien wird nun eine von einer Anwendung benötigte Assemblierung gesucht? Das Prinzip ist sehr einfach. Die Common Language Runtime wertet beim Starten einer Anwendung das Manifest aus. Wie Sie wissen, enthält das Manifest alle Angaben zu den benötigten externen Assemblierungen (siehe dazu auch Abbildung 17.2). 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 fündig, wird die entsprechende Komponente geladen. War die Suche erfolglos, wird im Verzeichnis der Anwendung weitergesucht, weil die CLR dann davon ausgehen muss, dass es sich um eine private Assembly handelt.
Streng genommen geht die Suche sogar noch weiter, weil unter Umständen auch noch mögliche Vorgaben in den Konfigurationsdateien eine wichtige Rolle spielen. Auf diese Gesichtspunkte und auf die Konfigurationsdateien komme ich in Abschnitt 17.3 noch zu sprechen.
Versionierung von Assemblys
Zur Laufzeit ermittelt die Common Language Runtime anhand der Versionsnummer, welche Version einer Assembly von einer Anwendung benutzt werden soll. Standardmäßig wird die Version geladen, die im Manifest der Anwendung angegeben ist.
Die Beschreibung einer Version folgt nach einer festgelegten Spezifikation. Jede Baugruppe hat eine Versionsnummer, die sich aus vier Elementen zusammensetzt, beispielsweise:
1.0.2.2
Die ersten beiden Zahlen beschreiben die Haupt- und Nebenversion. Werden an einer Komponente Änderungen vorgenommen, die inkompatibel zu der Vorgängerversion dieser Komponente sind (beispielsweise durch die Änderung der Parameterliste einer Methode), müssen sich die beiden Komponenten in der Haupt- oder Nebenversionsangabe unterscheiden, z. B.:
2.0.2.2
Eine abwärtskompatible Änderung wird durch die Elemente Build und Revision beschrieben. Änderungen, die über diese beiden Elemente bekannt gegeben werden, sind nur Korrekturen oder Fehlerbeseitigungen im Programmcode, die auf den Client keinen Einfluss ausüben – zumindest nicht im negativen Sinne, denn normalerweise dürfte ein Client von solchen Änderungen nur profitieren.
Abbildung 17.4 Das Schema der Versionierung
Die Versionsnummer wird vor der Kompilierung einer Assembly als Attribut in der Datei AssemblyInfo.cs festgelegt.
[Assembly: AssemblyVersion("1.0.1.0")]
Alternativ können Sie die Version auch im Eigenschaftsfenster des Projekts festlegen. Klicken Sie dazu auf den Knoten Properties im Projektmappen-Explorer, und vergewissern Sie sich, dass die Registerkarte Anwendung angezeigt wird. In dieser finden Sie die Schaltfläche Assemblyinformationen. Darüber gelangen Sie zu einem Dialog, wie er in Abbildung 17.5 zu sehen ist. Hier können Sie übrigens nicht nur die Version festlegen, sondern auch eine Reihe weiterer allgemeiner Informationen, die nach dem Schließen des Dialogs in die Datei AssemblyInfo.cs eingetragen werden.
Abbildung 17.5 Festlegen der Assemblyversion
Die Schlüsseldatei erzeugen
Globale Assemblys sind dadurch gekennzeichnet, dass sie mit einem binären Schlüsselpaar signiert sind, das aus einem öffentlichen und einem privaten Schlüssel besteht. Beide kryptografischen Schlüssel dienen einerseits zur Identifizierung einer Assembly und gewährleisten andererseits bei einer Änderung, dass der Autor der neuen Version derselbe ist wie der der alten Version. Nur der Entwickler der Ursprungsversion ist im Besitz von beiden Schlüsseln.
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. Der öffentliche Schlüssel ist ein Teil der Informationen, die eine Clientanwendung zur eindeutigen Identifikation einer bestimmten Assembly benötigt. Der private Schlüssel ist für den Aufrufer bedeutungslos, er sichert aber die Arbeit des Komponentenentwicklers und schützt gleichzeitig vor unbefugter oder gar böswilliger Änderung einer globalen Assemblierung. Privater und öffentlicher Schlüssel korrespondieren miteinander, mit anderen Worten: Zu einem öffentlichen Schlüssel gehört auch ein bestimmter privater – das ist ein wichtiger Aspekt, der in seiner Bedeutung nicht hoch genug eingeschätzt werden darf.
Der öffentliche und der private 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 und die notwendigen Optionsschalter setzen. Eleganter und einfacher ist es jedoch, wenn Sie dazu Visual Studio 2012 nutzen. Öffnen Sie dazu das Projekteigenschaftsfenster, und wählen Sie die Lasche Signierung. Markieren Sie anschließend die Auswahlbox Assembly signieren. Daraufhin wird die Auswahlliste aktiviert, die die Suche nach einer bereits vorhandenen Schlüsseldatei oder das Erstellen einer neuen ermöglicht. Wenn Sie sich für letztgenannte Alternative entscheiden, geben Sie in einem zusätzlichen Dialogfenster den Schlüsseldateinamen an. Darüber hinaus können Sie die Schlüsseldatei auch mit einem Kennwort schützen.
Abbildung 17.6 Eine Schlüsseldatei mit Visual Studio 2012 erzeugen
Beim Signieren einer Assembly haben Sie möglicherweise nicht immer Zugriff auf den privaten Schlüssel. So kann ein Unternehmen beispielsweise ein stark gesichertes Schlüsselpaar haben, auf das die Entwickler nicht täglich zugreifen können. In diesem Fall müssen Sie eine verzögerte Signierung vornehmen, um zunächst nur den öffentlichen Schlüssel verfügbar zu machen. Markieren Sie hierzu in der Registerkarte Signierung den Optionsschalter Nur verzögerte Signierung. Das Hinzufügen des privaten Schlüssels wird auf den Zeitpunkt verschoben, zu dem die Assembly bereitgestellt wird.
Die Installation einer Assembly im GAC mit dem Tool gacutil.exe
Eine Assembly global bereitzustellen erfordert zwei Arbeitsgänge:
- Der Quellcode wird kompiliert, und dabei wird eine vorher erzeugte oder bereits vorhandene Schlüsseldatei eingebunden. Damit ist die Assembly zur gemeinsamen Nutzung vorbereitet.
- Das Kompilat muss im GAC installiert werden.
Wie der erste Punkt erfüllt wird, habe ich Ihnen im Abschnitt zuvor gezeigt. Zur Erfüllung des zweiten bieten sich zwei Alternativen:
- das Kommandozeilenprogramm gacutil.exe des .NET Frameworks
- eine Installationsroutine mit dem Microsoft Windows Installer
Widmen wir uns zunächst dem Kommandozeilentool gacutil.exe. Wie alle anderen Tools findet man gacutil.exe unter:
\Programme\Microsoft SDKs\Windows\V6.0A\bin
Die allgemeine Aufrufsyntax lautet:
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, z. B.:
gacutil /i MyGlobalAssembly.dll
beziehungsweise:
gacutil /u MyGlobalAssembly
Der Vorteil des Global Assembly Caches ist, dass andere Anwendungen, die ebenfalls auf die Dienste von GeometricObjects zurückgreifen, sich derselben Komponente bedienen können, weil sie zentral registriert ist.
Eine neue Version der globalen Assembly installieren
Nicht ungewöhnlich für eine allgemein zur Verfügung stehende Komponente ist, dass eine neue Version von ihr verteilt wird – sei es, um Bugs zu beseitigen oder um die alte Komponentenversion um neue Fähigkeiten zu erweitern. Sie müssen nur dafür sorgen, dass Sie eine neuere Versionsnummer vor dem Kompilieren angeben. Diese stellen Sie im Dialog Assemblyinformationen ein, der in Abbildung 17.5 zu sehen ist.
Eine Anwendung, die mit der alten Version kompiliert worden ist, wird auch weiterhin diese benutzen, weil es dafür den entsprechenden Eintrag im Manifest gibt. Wie Sie dennoch die Anwendung dazu bringen, die neuere Assemblyversion zu benutzen, wird in Abschnitt 17.4 erläutert.
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.