Vereinfachen der Weitergabe und Lösen der "DLL Hell" mit dem .NET Framework

Veröffentlicht: 07. Nov 2000 | Aktualisiert: 16. Jun 2004
Von Steven Pratschner

In diesem Artikel wird das Baugruppenkonzept vorgestellt und beschrieben, wie das .NET Framework Baugruppen zum Lösen von Versions- und Weitergabeproblemen verwendet.

Auf dieser Seite

Einführung Einführung
 Problembeschreibung  Problembeschreibung
Lösungsmerkmale Lösungsmerkmale
 Baugruppen: Die Bausteine  Baugruppen: Die Bausteine
Versionsprüfung und gemeinsame Nutzung Versionsprüfung und gemeinsame Nutzung
Versionsrichtlinie Versionsrichtlinie
Weitergabe Weitergabe
Zusammenfassung Zusammenfassung

Einführung

Das Microsoft® .NET Framework bietet eine Reihe von neuen Features, die auf die Vereinfachung der Anwendungsweitergabe und die Lösung der "DLL Hell" abzielen. Sowohl Endbenutzer als auch Entwickler sind mit den Versions- und Weitergabeproblemen vertraut, die in den komponentenbasierten Systemen von heute auftreten können. So hat z.B. fast jeder Benutzer schon einmal eine neue Anwendung auf seinem Computer installiert, nur um feststellen zu müssen, dass eine vorhandene Anwendung aus unerfindlichen Gründen nicht mehr funktioniert. Darüber hinaus haben die meisten Entwickler bereits Zeit mit dem Registrierungseditor (Regedit) verbracht und versucht, die Konsistenz aller erforderlichen Registrierungseinträge sicherzustellen, um eine COM-Klasse zu aktivieren.

Die Entwurfsrichtlinien und Implementierungsverfahren, die im .NET Framework zur Lösung der "DLL Hell" verwendet werden, basieren auf der in Microsoft Windows® 2000 geleisteten Arbeit, wie von Rick Anderson in The End of DLL Hell (in Englisch) sowie von David D'Souza, BJ Whalen und Peter Wilson in Implementieren der gemeinsamen Nutzung von parallelen Komponenten in Anwendungen (Erweitert) beschrieben. Das .NET Framework bietet viele der Features, die in diesen beiden Artikeln beschrieben sind, einschließlich Anwendungsisolation und parallele Komponenten, für auf der .NET-Plattform erstellte Anwendungen. Künftig wird auf der .NET-Plattform auch Versionsunterstützung bereitgestellt, die eine noch weitreichendere Zusammenführung von systemeigenen Windows-Anwendungen ermöglicht.

In diesem Artikel wird das Baugruppenkonzept vorgestellt und beschrieben, wie das .NET Framework Baugruppen zum Lösen von Versions- und Weitergabeproblemen verwendet. Insbesondere werden die Struktur und Benennung von Baugruppen erörtert, und es wird beschrieben, wie Compiler und die gemeinsame Sprachlaufzeit Baugruppen für das Aufzeichnen und Erzwingen von Versionsabhängigkeiten zwischen Anwendungskomponenten verwenden. Darüber hinaus wird erläutert, wie Anwendungen und Administratoren das Versionsverhalten über die sog. Versionsrichtlinien anpassen können.

Nach der Einführung und Beschreibung der Baugruppen werden mehrere Weitergabeszenarios mit Beispielen für die verschiedenen Pack- und Verteilungsoptionen vorgestellt, die im .NET Framework verfügbar sind.

Problembeschreibung

Versionsprüfung

Aus Kundensicht ist das gängigste Versionsproblem die sog. "DLL Hell". Einfach ausgedrückt, umfasst der Begriff "DLL Hell" die Probleme, die entstehen, wenn mehrere Anwendungen eine Komponente, z.B. eine DLL oder eine COM-Klasse (Component Object Model), gemeinsam zu nutzen versuchen. Im häufigsten Fall installiert eine Anwendung eine neue Version der gemeinsam genutzten Komponente, die mit der Version, die bereits auf dem Computer installiert ist, nicht abwärtskompatibel ist. Die neu installierte Anwendung funktioniert zwar problemlos, vorhandene Anwendungen, die auf einer älteren Version der gemeinsam genutzten Komponente basieren, funktionieren jedoch u.U. nicht mehr. In einigen Fällen ist die Problemursache sogar noch subtiler. Nehmen wir als Beispiel ein Szenario, in dem ein Benutzer beim Besuch einer Website ein Microsoft ActiveX®-Steuerelement downloadet. Wenn das Steuerelement downgeloadet wird, ersetzt es alle auf dem Computer vorhandenen Versionen dieses Steuerelements. Wenn eine auf dem Computer installierte Anwendung zufällig dieses Steuerelement verwendet, funktioniert sie u.U. nicht mehr.

In vielen Fällen entdecken Benutzer erst mit großer Verzögerung, dass eine Anwendung nicht mehr funktioniert. Daher ist es häufig schwierig zu ermitteln, wann eine Änderung am Computer vorgenommen wurde, die sich auf die Anwendung ausgewirkt haben könnte. So erinnert sich ein Benutzer vielleicht, dass er vor einer Woche etwas installiert hat, doch es besteht kein offensichtlicher Zusammenhang zwischen dieser Installation und dem aufgetretenen Verhalten. Zu allem Überfluss stehen derzeit auch nur wenige Diagnosetools zur Verfügung, mit denen der Benutzer (oder der zur Hilfe gerufene Supportmitarbeiter) die Problemursache ermitteln kann.

Der Grund für diese Probleme liegt darin, dass Versionsinformationen über die unterschiedlichen Komponenten einer Anwendung nicht vom System aufgezeichnet oder erzwungen werden. Außerdem wirken sich Systemänderungen durch eine Anwendung i.d.R. auf alle Anwendungen auf diesem Computer aus – es ist heutzutage nicht einfach, Anwendungen zu erstellen, die völlig von Änderungen isoliert sind.

Ein Grund dafür, wieso das Erstellen einer isolierten Anwendung so schwierig ist, liegt darin, dass die aktuelle Laufzeitumgebung nur die Installation einer einzigen Version einer Komponente oder Anwendung zulässt. Diese Einschränkung bedeutet, dass Komponentenautoren ihren Code so schreiben müssen, dass er abwärtskompatibel ist, um nicht riskieren zu müssen, dass bei der Installation einer neuen Komponente vorhandene Anwendungen beschädigt werden. In der Praxis ist es extrem schwierig (wenn nicht sogar unmöglich), abwärtskompatiblen Code zu schreiben. Die Versionskonfliktlösung in .NET beruht auf dem Konzept der Parallelität: Parallelität ist die Möglichkeit, mehrere Versionen derselben Komponente gleichzeitig auf dem Computer zu installieren und auszuführen. Bei parallelen Komponenten sind Autoren nicht notwendigerweise an eine strikte Abwärtskompatibilität gebunden, da verschiedene Anwendungen unterschiedliche Versionen einer gemeinsam genutzten Komponente verwenden können.

Weitergabe und Installation

Die Installation einer Anwendung ist heutzutage ein Prozess, der sich aus mehreren Schritten zusammensetzt. I.d.R. umfasst die Installation einer Anwendung das Kopieren einer Reihe von Softwarekomponenten auf den Datenträger sowie das Erstellen einer Reihe von Registrierungseinträgen, die diese Komponenten für das System beschreiben.

Die Trennung zwischen den Registrierungseinträgen und den Datenträgerdateien gestaltet die Replikation und Deinstallation von Anwendungen äußerst schwierig. Außerdem ist die Beziehung zwischen den diversen Einträgen, die für die vollständige Beschreibung einer COM-Klasse in der Registrierung erforderlich sind, sehr flexibel. Diese Einträge umfassen häufig Einträge für Co-Klassen, Schnittstellen, Typbibliotheken und DCOM-Anwendungs-IDs, ganz zu schweigen von Einträgen, die für die Registrierung von Dokumenterweiterungen oder Komponentenkategorien erstellt werden. Häufig sind Sie gezwungen, diese manuell zu synchronisieren.

Diese Registrierungseinträge werden benötigt, um eine COM-Klasse zu aktivieren. Dies erschwert die Weitergabe von verteilten Anwendungen ganz erheblich, da auf jedem Clientcomputer die entsprechenden Registrierungseinträge erstellt werden müssen.

Ein weiteres gängiges Problem besteht darin, dass eine Anwendung nicht aktualisiert werden sollte, während sie ausgeführt wird. Dies ist v.a. ein Problem bei Webanwendungen, da der Webdienst angehalten und neu gestartet werden muss, um COM-Klassen zu aktualisieren, die von der Anwendung verwendet werden.

Diese Probleme entstehen hauptsächlich dadurch, dass die Beschreibung einer Komponente von der Komponente selbst getrennt ist. D.h., Anwendungen sind weder selbstbeschreibend noch unabhängig.

Lösungsmerkmale

Das .NET Framework muss für die Lösung der soeben beschriebenen Probleme die folgenden Funktionen bereitstellen:

  • Anwendungen müssen selbstbeschreibend sein. Bei selbstbeschreibenden Anwendungen entfällt die Abhängigkeit von der Registrierung. Es wird eine Installation ohne Auswirkungen auf andere Anwendungen ermöglicht, und die Deinstallation und Replikation werden vereinfacht.

  • Versionsinformationen müssen aufgezeichnet und erzwungen werden. Die Versionsunterstützung muss in die Plattform integriert sein, um sicherzustellen, dass zur Laufzeit die richtige Version einer Abhängigkeit geladen wird.

  • Muss sich an die letzten Komponenten "erinnern". Wenn eine Anwendung erfolgreich ausgeführt wird, muss die Anwendung sich "erinnern" können, welche Komponenten (einschließlich deren Versionen) zusammen verwendet wurden.

  • Muss parallele Komponenten unterstützen. Wenn mehrere Versionen einer Komponente parallel auf dem Computer installiert und ausgeführt werden können, wird Aufrufern keine unbekannte Version "aufgezwungen", sondern sie können angeben, welche Version sie laden möchten. Das .NET Framework führt die Parallelität noch einen Schritt weiter, indem mehrere Versionen des Frameworks selbst auf einem einzelnen Computer installiert sein können. Dadurch werden Upgrades entscheidend vereinfacht, da ein Administrator ggf. beschließen kann, verschiedene Anwendungen auf unterschiedlichen Versionen des .NET Frameworks auszuführen.

  • Muss Anwendungsisolation ermöglichen. Das .NET Framework muss das Schreiben von Anwendungen, die von Systemänderungen durch andere Anwendungen nicht beeinflusst werden, erleichtern bzw. zum Standard machen.

Baugruppen: Die Bausteine

Baugruppen sind die Bausteine, die vom .NET Framework für die Lösung der soeben beschriebenen Versions- und Weitergabeprobleme verwendet werden. Zudem sind Baugruppen die Weitergabeeinheit für Typen und Ressourcen. In vielerlei Hinsicht entspricht eine Baugruppe heute einer DLL: Baugruppen sind im Wesentlichen "logische DLLs".

Baugruppen sind aufgrund von Metadaten, die als "Manifest" bezeichnet werden, selbstbeschreibend. Das .NET Framework verwendet Metadaten nicht nur zur Beschreibung von Typen, sondern auch zur Beschreibung der Baugruppen, die die Typen enthalten.

Baugruppen sind jedoch nicht nur für die Weitergabe von Nutzen. So erfolgt die Versionsprüfung in .NET z.B. auf Baugruppenebene, d.h., die Version von kleineren Einheiten (z.B. Modul oder Typ) wird nicht überprüft. Baugruppen werden auch verwendet, um Codeanweisungen anwendungsübergreifend gemeinsam zu nutzen. Die Baugruppe, in der ein Typ enthalten ist, ist Bestandteil der ID dieses Typs.

Das Codezugriffs-Sicherheitssystem verwendet Baugruppen als Grundlage des Berechtigungsmodells. Der Autor einer Baugruppe zeichnet im Manifest die für das Ausführen des Codes erforderlichen Berechtigungen auf, und der Administrator gewährt Codeberechtigungen auf der Grundlage der Baugruppe, in der der Code enthalten ist.

Schließlich sind Baugruppen auch von grundlegender Bedeutung für das Typsystem und das Laufzeitsystem, indem sie eine Sichtbarkeitsgrenze für Typen einrichten und als Laufzeitbereich für das Auflösen von Verweisen auf Typen dienen.

Baugruppenmanifeste

Ein Manifest enthält v.a. die folgenden Daten zu einer Baugruppe:

  • ID. Die ID einer Baugruppe besteht aus drei Teilen: einem Namen, einer Versionsnummer und einer optionalen Kultur.

  • Dateiliste. Ein Manifest umfasst eine Liste aller Dateien, aus denen sich die Baugruppe zusammensetzt. Das Manifest zeichnet den Namen jeder Datei sowie ein verschlüsseltes Hash des Dateiinhalts zum Zeitpunkt der Manifesterstellung auf. Dieses Hash wird zur Laufzeit überprüft, um sicherzustellen, dass die Weitergabeeinheit konsistent ist.

  • Verweis auf Baugruppen. Abhängigkeiten zwischen Baugruppen werden im Manifest der aufrufenden Baugruppe gespeichert. Die Abhängigkeitsinformationen umfassen eine Versionsnummer, die zur Laufzeit verwendet wird, um sicherzustellen, dass die richtige Version der Abhängigkeit geladen wird.

  • Exportierte Typen und Ressourcen. Die für Typen und Ressourcen verfügbaren Sichtbarkeitsoptionen umfassen die Optionen "Nur in meiner Baugruppe sichtbar" und "Für Aufrufer außerhalb meiner Baugruppe sichtbar".

  • Berechtigungsanforderungen. Die Berechtigungsanforderungen für eine Baugruppe sind in drei Gruppen unterteilt: 1) die Berechtigungen, die für das Ausführen der Baugruppe erforderlich sind; 2) die Berechtigungen, die zwar erwünscht sind, bei deren Nichtgewährung aber dennoch Funktionen der Baugruppe verfügbar sind; und 3) die Berechtigungen, die der Baugruppe gemäß dem Autor nicht gewährt werden sollten.

Das SDK-Tool IL Disassembler (Ildasm) ist äußerst nützlich für das Anzeigen des Codes und der Metadaten in einer Baugruppe. In Abbildung 1 sehen Sie ein mit IL Disassembler angezeigtes Beispielmanifest. Die .assembly-Direktive identifiziert die Baugruppe, und die .assembly extern-Direktiven enthalten Informationen über andere Baugruppen, von der diese Baugruppe abhängt.

Bild01

Abbildung 1. Beispielmanifest, angezeigt mit IL Disassembler

Baugruppenstruktur

Bisher wurden Baugruppen primär als logisches Konzept beschrieben. In diesem Abschnitt werden Baugruppen jedoch konkretisiert, indem beschrieben wird, wie sie physisch dargestellt werden.

Im Allgemeinen bestehen Baugruppen aus vier Bestandteilen: den Baugruppenmetadaten (Manifest); Metadaten, die die Typen beschreiben; dem IL-Code (Intermediate Language), der die Typen implementiert, sowie einer Ressourcengruppe. Nicht alle diese Elemente sind in jeder Baugruppe enthalten. Nur das Manifest ist zwingend erforderlich, doch müssen entweder Typen oder Ressourcen für eine funktionsfähige Baugruppe vorhanden sein.

Es gibt mehrere Optionen für das "Packen" dieser vier Elemente. So sehen Sie in Abbildung 2 z.B. eine DLL, die die gesamte Baugruppe enthält: das Manifest, die Typmetadaten, den IL-Code und Ressourcen.

Bild02

Abbildung 2. DLL mit allen Baugruppenelementen

Wahlweise kann der Inhalt einer Baugruppe auch auf mehrere Dateien verteilt sein. In Abbildung 3 hat der Autor beschlossen, einen Teil des Dienstprogrammcodes in eine andere DLL zu verlagern und in der ursprünglichen Datei eine große Ressourcendatei (in diesem Fall eine GIF-Datei) zu speichern. Dies kann z.B. zugunsten einer Optimierung des Codedownloads ausgeführt werden. Das .NET Framework downloadet eine Datei nur, wenn darauf verwiesen wird. Wenn die Baugruppe jedoch Code oder Ressourcen enthält, auf die nur selten zugegriffen wird, kann das Verteilen dieser Komponenten auf einzelne Dateien die Downloadeffizienz steigern.

Bild03

Abbildung 3. Baugruppenelemente sind auf mehrere Dateien verteilt

Versionsprüfung und gemeinsame Nutzung

Eine der Hauptursachen der "DLL Hell" ist das Modell für die gemeinsame Nutzung, das derzeit in komponentenbasierten Systemen verwendet wird. Einzelne Softwarekomponenten werden standardmäßig von mehreren Anwendungen auf dem Computer gemeinsam genutzt. So hat z.B. jedes Mal, wenn ein Installationsprogramm eine DLL in das Systemverzeichnis kopiert oder eine Klasse in der COM-Registrierung registriert, der entsprechende Code potentielle Auswirkungen auf andere Anwendungen, die auf demselben Computer ausgeführt werden. Insbesondere wenn eine vorhandene Anwendung zuvor eine ältere Version dieser gemeinsam genutzten Komponente verwendet hat, wird sie nun automatisch unter Verwendung der neuen Version gestartet. Wenn die gemeinsam genutzte Komponente uneingeschränkt abwärtskompatibel ist, stellt dies zwar kein Problem dar, doch in vielen Fällen ist eine uneingeschränkte Abwärtskompatibilität nur schwierig, wenn nicht sogar unmöglich, zu erzielen. Wenn eine Abwärtskompatibilität nicht gegeben ist, werden häufig Anwendungen bei der Installation anderer Anwendungen beschädigt.

Eine wichtige Entwurfsrichtlinie in .NET sind isolierte Komponenten (bzw. Baugruppen). Das Isolieren einer Baugruppe bedeutet, dass nur eine Anwendung auf eine Baugruppe zugreifen kann – sie wird also nicht von mehreren Anwendungen auf dem Computer gemeinsam genutzt, und Systemänderungen durch andere Anwendungen haben keine Auswirkungen. Die Isolation verleiht Entwicklern absolute Kontrolle über den von ihren Anwendungen verwendeten Code. Isolierte bzw. private Anwendungsbaugruppen stellen in .NET-Anwendungen den Standard dar. Der Trend zu isolierten Komponenten begann in Microsoft Windows® 2000 mit der Einführung der .local-Datei. Aufgrund dieser Datei konsultieren sowohl das Betriebssystem-Ladeprogramm als auch COM bei der Suche nach einer angeforderten Komponente zunächst das Anwendungsverzeichnis. (Weitere Informationen erhalten Sie im MSDN Information Center im Artikel Implementieren der gemeinsamen Nutzung von parallelen Komponenten in Anwendungen (Erweitert).)

Es gibt jedoch Fälle, in denen die gemeinsame Nutzung einer Baugruppe durch Anwendungen erforderlich ist. So würde es z.B. keinen Sinn machen, wenn jede Anwendung eine eigene Kopie von System.Winforms, System.ASP oder eines Web Forms-Steuerelements enthielte.

In .NET ist die gemeinsame Nutzung von Code zwischen Anwendungen eine explizite Entscheidung. Für Baugruppen, die gemeinsam genutzt werden, gelten einige zusätzliche Anforderungen. Insbesondere sollten gemeinsam genutzte Baugruppen Parallelität unterstützen, damit mehrere Versionen derselben Baugruppe gleichzeitig auf demselben Computer oder sogar innerhalb desselben Prozesses installiert und ausgeführt werden können. Darüber hinaus unterliegen Baugruppen mit gemeinsamer Nutzung strengeren Namenskonventionen. So muss eine gemeinsam genutzte Baugruppe z.B. einen global eindeutigen Namen aufweisen.

Anhand der Tatsache, dass sowohl Isolation als auch gemeinsame Nutzung erforderlich sein können, lassen sich Baugruppen in zwei "Arten" unterteilen. Dabei handelt es sich jedoch um eine flexible Kategorisierung, da es keine Strukturunterschiede zwischen den beiden Baugruppenarten gibt, sondern der Unterschied lediglich in ihrer Verwendung liegt: entweder privat für eine Anwendung oder gemeinsame Nutzung durch viele Anwendungen.

Private Anwendungsbaugruppen

Eine private Anwendungsbaugruppe ist eine Baugruppe, die nur für eine Anwendung sichtbar ist. Dies dürfte in .NET-Anwendungen der Normalfall sein, da das .NET Framework das Erstellen von Anwendungen erleichtert, die vor Systemänderungen durch andere Anwendungen isoliert sind.

Die Namensanforderungen für private Baugruppen sind einfach: Die Baugruppennamen müssen nur innerhalb der Anwendung eindeutig sein, ein global eindeutiger Name ist nicht erforderlich. Die Eindeutigkeit der Namen ist kein Problem, da der Anwendungsentwickler die absolute Kontrolle darüber hat, welche Baugruppen für die Anwendung isoliert werden.

Private Anwendungsbaugruppen werden innerhalb der Verzeichnisstruktur der Anwendung weitergegeben, in der sie verwendet werden. Private Baugruppen können direkt im Anwendungsverzeichnis (oder einem Unterverzeichnis) platziert werden. Die gemeinsame Sprachlaufzeit findet diese Baugruppen durch einen Prozess, der als Probing bezeichnet wird. Dabei handelt es sich einfach um die Zuordnung des Baugruppennamens zum Namen der Datei, die das Manifest enthält.

Die gemeinsame Sprachlaufzeit ruft den im Baugruppenverweis verzeichneten Namen der Baugruppe ab, hängt die Erweiterung .dll an und sucht im Anwendungsverzeichnis nach dieser Datei. Es gibt einige Variationen dieses Schemas, in denen die Laufzeit in Unterverzeichnissen sucht, die nach der Baugruppe oder der Kultur der Baugruppe benannt sind. So kann ein Entwickler z.B. entscheiden, die Baugruppe mit den deutschen Ressourcen in einem Unterverzeichnis mit der Bezeichnung de und die Baugruppe mit den spanischen Ressourcen im Verzeichnis es weiterzugeben.

Wie soeben beschrieben, enthält jedes Baugruppenmanifest Versionsinformationen über die entsprechenden Abhängigkeiten. Diese Versionsinformationen werden für private Baugruppen nicht erzwungen, da der Entwickler absolute Kontrolle über die Baugruppen hat, die an das Anwendungsverzeichnis weitergegeben werden.

Gemeinsam genutzte Baugruppen

Das .NET Framework unterstützt außerdem das Konzept einer gemeinsam genutzten Baugruppe. Eine gemeinsam genutzte Baugruppe ist eine Baugruppe, die von mehreren Anwendungen auf dem Computer verwendet wird. Die gemeinsame Nutzung von Code durch mehrere Anwendungen ist in .NET eine bewusste Entscheidung. Für gemeinsam genutzte Baugruppen gelten einige zusätzliche Anforderungen, die darauf abzielen, die bei der gemeinsamen Nutzung auftretenden Probleme zu lösen. Neben der bereits beschriebenen Parallelitätsunterstützung unterliegen gemeinsam genutzte Baugruppen wesentlich strengeren Namensanforderungen. So muss eine gemeinsam genutzte Baugruppe z.B. einen global eindeutigen Namen haben. Außerdem muss das System den "Namensschutz" sicherstellen, d.h. verhindern, dass ein Benutzer den Namen einer anderen Baugruppe verwendet. Nehmen wir einmal an, Sie erstellen Tabellensteuerelemente und haben gerade Version 1 Ihrer Baugruppe veröffentlicht. Als Autor brauchen Sie die Gewissheit, dass keine andere Person eine Baugruppe veröffentlichen und diese als Version 2 Ihres Tabellensteuerelements ausgeben kann. Das .NET Framework unterstützt diese Namensanforderungen durch ein Verfahren, das als gemeinsam genutzte Namen bezeichnet und im nächsten Abschnitt genauer beschrieben wird.

In der Regel besitzt der Ersteller einer Anwendung nicht dasselbe Niveau an Kontrolle über die gemeinsam genutzten Baugruppen, die von der Anwendung verwendet werden. Folglich werden die Versionsinformationen bei jedem Verweis auf eine gemeinsam genutzte Baugruppe überprüft. Außerdem ermöglicht das .NET Framework es Anwendungen und Administratoren, anhand von Versionsrichtlinien festzulegen, dass die Anwendung eine andere Version einer Baugruppe verwendet.

Gemeinsam genutzte Baugruppen werden i.d.R. an den globalen Baugruppenspeicher weitergegeben. Der globale Baugruppenspeicher ist ein computerübergreifender Speicher für Baugruppen, die von mehreren Anwendungen verwendet werden. Die Verwendung dieses Speichers ist zwar nicht zwingend erforderlich, bietet jedoch eine Reihe von Vorteilen. So wird z.B. automatisch eine parallele Speicherung von mehreren Versionen einer Baugruppe bereitgestellt. Darüber hinaus können Administratoren den Speicher verwenden, um Problemkorrekturen oder Sicherheitspatches weiterzugeben, die von jeder Anwendung auf dem Computer verwendet werden sollen. In diesem Sinn kann die Weitergabe einer Baugruppe an den globalen Baugruppenspeicher Auswirkungen auf mehrere Anwendungen auf dem Computer haben. Das .NET Framework verwendet das Konzept der Versionsrichtlinien (wie im Folgenden beschrieben), um die Probleme zu vermeiden, die heute in Zusammenhang mit freigegebenen Systembereichen auftreten, z.B. %windir%\system32.

Das Hinzufügen einer Baugruppe zum Speicher erfordert einen expliziten Administratoreingriff: der Prozess, der die Installation durchführt, muss über "Administratorrechte" verfügen. Baugruppen werden beim Ausführen einer Anwendung nicht automatisch im globalen Baugruppenspeicher platziert, und es erfolgt gegenwärtig auch keine Zwischenspeicherung von gemeinsam genutzten Baugruppen. Im Rahmen von Visual Studio.NET wird Windows Installer so aktualisiert, dass ein systemeigenes Verständnis für Baugruppen und den Baugruppenspeicher gegeben ist. D.h., Sie können alle Features von Windows Installer, z.B. Bedarfsinstallationen und Anwendungsreparaturen, in Ihren .NET-Anwendungen nutzen.

Das .NET Framework SDK enthält zwei Tools für das Arbeiten mit dem Baugruppenspeicher. Das erste Tool trägt den Namen AL und ermöglicht es Ihnen, dem Speicher Baugruppen hinzuzufügen. AL ist äußerst nützlich in Entwicklungs- und Testszenarios, wenn Sie kein Windows Installer-Paket erstellen möchten, um dem Speicher eine Baugruppe hinzuzufügen. Verwenden Sie den /install-Schalter, um dem Speicher eine Baugruppe hinzuzufügen:

Al /install:myassembly.dll 

Das zweite Tool ist eine Windows-Shellerweiterung, die es Ihnen ermöglicht, den Speicher unter Verwendung von Windows-Explorer zu bearbeiten. In Abbildung 4 sehen Sie eine Ansicht des globalen Baugruppenspeichers.

Bild04

Abbildung 4. Globaler Baugruppenspeicher

Gemeinsam genutzte Namen

Gemeinsam genutzte Namen werden verwendet, um die strengeren Namensanforderungen für gemeinsam genutzte Baugruppen zu ermöglichen. Gemeinsam genutzte Namen verfolgen drei Ziele:

  • Eindeutigkeit des Namens. Die Namen von gemeinsam genutzten Baugruppen müssen global eindeutig sein.

  • Verhindern des Imitierens von Namen. Als Entwickler möchten Sie nicht, dass keine andere Person eine Folgeversion einer Ihrer Baugruppen veröffentlicht und fälschlicherweise (entweder versehentlich oder bewusst) behauptet, diese stamme von Ihnen.

  • Bereitstellen von IDs bei Verweisen. Beim Auflösen eines Verweises auf eine Baugruppe werden gemeinsam genutzte Namen verwendet, um sicherzustellen, dass die geladene Baugruppe vom erwarteten Verleger stammt.

Gemeinsam genutzte Namen werden unter Verwendung der Standardkryptografie für öffentliche Schlüssel implementiert. Im Allgemeinen verläuft der Prozess wie folgt: Der Autor einer Baugruppe erstellt ein Schlüsselpaar (oder verwendet ein vorhandenes), signiert die Datei, die das Manifest enthält, mit dem privaten Schlüssel und stellt den öffentlichen Schlüssel Aufrufern zur Verfügung. Wenn Verweise auf die Baugruppe erfolgen, zeichnet der Aufrufer den öffentlichen Schlüssel gemäß dem privaten Schlüssel auf, der für das Erzeugen des strikten Namens verwendet wird. In Abbildung 5 sehen Sie eine Übersicht über die Funktionsweise dieses Prozesses zur Laufzeit, einschließlich der Speicherung von Schlüsseln in den Metadaten und der Generierung der Signatur.

Das Szenario ist eine Baugruppe mit dem Namen Main, die auf eine Baugruppe mit dem Namen MyLib verweist. MyLib verfügt über einen gemeinsam genutzten Namen. Die wichtigen Schritte werden im Folgenden beschrieben.

Bild05

Abbildung 5. Prozess für das Implementieren von gemeinsam genutzten Namen

  1. Der Entwickler ruft einen Compiler auf, an den ein Schlüsselpaar sowie die Quelldateien für die Baugruppe übergeben wurden. Das Schlüsselpaar wird i.d.R. mit einem SDK-Tool namens SN erstellt. So erstellt z.B. der folgende Befehl ein neues Schlüsselpaar und speichert dieses in einer Datei:

    Sn –k MyKey.snk
    

    Die meisten Compiler signieren die Baugruppe während der Kompilierung. Im Folgenden sehen Sie eine C#-Beispielbefehlszeile, die das Schlüsselpaar akzeptiert und die Baugruppe signiert:

    Csc /t:library math.cs /a.keyfile:MyKey.snk /a.version:1.0.0.0
    
  2. Wenn der Compiler die Baugruppe ausgibt, wird der öffentliche Schlüssel als Bestandteil der Baugruppen-ID im Manifest aufgezeichnet. Durch die Verwendung des öffentlichen Schlüssels als Bestandteil der ID erhält die Baugruppe einen global eindeutigen Namen.

  3. Nach der Ausgabe der Baugruppe wird die Datei, die das Manifest enthält, mit dem privaten Schlüssel signiert. Die entstandene Signatur wird in der Datei gespeichert.

  4. Wenn Main vom Compiler generiert wird, wird der öffentliche Schlüssel von MyLib als Bestandteil des Verweises auf MyLib im Main-Manifest gespeichert.

Zur Laufzeit führt das .NET Framework zwei Schritte aus, um sicherzustellen, dass gemeinsam genutzte Namen dem Entwickler die gewünschten Vorteile verleihen. Zunächst wird die gemeinsam genutzte Namenssignatur von MyLib überprüft, wenn die Baugruppe im globalen Baugruppenspeicher installiert wird (es sind auch Optionen für das Überprüfen der Signatur verfügbar, die nicht an den Speicher weitergegeben werden). Mit der Überprüfung der Signatur wird sichergestellt, dass der Inhalt von MyLib seit der Erstellung der Baugruppe nicht geändert wurde. Der zweite Schritt besteht darin zu überprüfen, ob der öffentliche Schlüssel, der als Bestandteil des Main-Verweises auf MyLib gespeichert ist, mit dem öffentlichen Schlüssel übereinstimmt, der Bestandteil der ID von MyLib ist. Wenn diese Schlüssel identisch sind, kann der Autor von Main sicher sein, dass die geladene Version von MyLib von demselben Verleger stammt wie die Version von MyLib, mit der Main erstellt wurde. Diese Prüfung der Schlüsselübereinstimmung erfolgt zur Laufzeit, wenn der Verweis von Main auf MyLib aufgelöst wird.

Der Begriff "Signieren" erinnert zwar häufig an Microsoft Authenticode®, gemeinsam genutzte Namen und Authenticode haben jedoch nichts miteinander zu tun. Diese beiden Verfahren haben unterschiedliche Ziele. Insbesondere setzt Authenticode ein bestimmtes Vertrauen gegenüber einem Verleger voraus, das bei gemeinsam genutzten Namen nicht gegeben ist. Es gibt keine Zertifikate oder sonstigen Signaturautoritäten für verstärkte Namen. Außerdem erfolgt die Signatur gemeinsam genutzter Namen häufig direkt über den Compiler im Rahmen des Erstellungsprozesses. Es gibt jedoch auch Dienstprogramme für das Signieren im SDK.

Eine weitere Erwägung, die zu berücksichtigen ist, ist der "Testsignatur"-Prozess. Es ist häufig der Fall, dass der Autor einer Baugruppe keinen Zugriff auf den privaten Schlüssel hat, der für die volle Signatur erforderlich ist. Die meisten Unternehmen speichern diese Schlüssel in geschützten Speichern, auf die nur wenige Personen zugreifen können. Folglich bietet das .NET Framework verschiedene Verfahren für die "Testsignatur" während der Entwicklung sowie die spätere tatsächliche Signatur.

Versionsrichtlinie

Wie soeben beschrieben, zeichnet jedes Baugruppenmanifest Informationen zur Version jeder Abhängigkeit auf, für die es erstellt wurde. Es gibt jedoch einige Szenarios, in denen der Anwendungsautor oder -administrator u.U. zur Laufzeit eine andere Version einer Abhängigkeit ausführen möchte. So sollten Anwendungsadministratoren z.B. in der Lage sein, Problemkorrekturversionen weiterzugeben, ohne dass alle Anwendung für die Korrektur neu kompiliert werden müssen. Außerdem müssen Administratoren festlegen können, dass eine bestimmte Version einer Baugruppe nie verwendet wird, falls eine Sicherheitslücke oder ein anderer schwerer Fehler gefunden werden. Das .NET Framework ermöglicht diese Flexibilität bei der Versionsbindung durch Versionsrichtlinien.

Baugruppenversionsnummern

Jede Baugruppe verfügt über eine vierteilige Versionsnummer als Bestandteil ihrer ID (d.h. Version 1.0.0.0 einer Baugruppe und Version 2.1.0.2 sind in Bezug auf das Klassenladeprogramm völlig unterschiedliche IDs). Die Verwendung der Version als Bestandteil der ID ist von entscheidender Bedeutung, um verschiedene Versionen einer Baugruppe für Parallelitätszwecke unterscheiden zu können.

Sowohl Entwickler als auch Administratoren müssen die Struktur der Versionsnummer verstehen, da dies für das Erzwingen von Versionsabhängigkeiten zwischen Baugruppen durch die gemeinsame Sprachlaufzeit von grundlegender Bedeutung ist.

Bild06

Abbildung 6. Die vier Elemente der Baugruppenversionsnummer

Die Versionsnummer besteht aus den folgenden vier Elementen: Hauptversion, Nebenversion, Build und Revision. Änderungen an der Haupt- oder Nebenversionsnummer gelten als inkompatible Änderungen. In diesem Fall hat der Entwickler z.B. die Typen einiger Methodenparameter geändert oder einige Typen völlig entfernt. Das Klassenladeprogramm verwendet diese Informationen, so dass inkompatible Versionen einer abhängigen Baugruppe standardmäßig nie geladen werden.

Andererseits gelten Änderungen an den Build- und Revisionselementen der Versionsnummer als kompatibel. Diese Änderungen sind i.d.R. Problemkorrekturen oder Sicherheitspatches, die dahingehend kompatibel sind, dass bei der Änderung der Typdefinitionen keine Beschädigung der Aufrufer erfolgt. Diese kompatiblen Änderungen werden oft als QFE-Fixes (Quick Fix Engineering) bzw. Hotfixes bezeichnet.

Standardversionsrichtlinie

Die gemeinsame Sprachlaufzeit legt fest, welche Version einer abhängigen Baugruppe geladen werden soll, wenn im Code ein Verweis auf diese Baugruppe enthalten ist. Standardmäßig muss die Baugruppe, die zum Auflösen des Verweises geladen wird, dieselben Haupt- und Nebenversionsnummern wie der Verweis haben. Wenn sich diese Nummern unterscheiden, gilt die Baugruppe als inkompatibel und wird standardmäßig nicht geladen. Im Gegensatz dazu wählt die gemeinsame Sprachlaufzeit den QFE-Fix (bzw. Hotfix) mit der höchsten Nummer.

Wenn Main z.B. mit Version 1.0.0.0 von MyLib erstellt wurde, zur Laufzeit jedoch Version 1.0.1.1 von MyLib erkannt wurde, wird Version 1.0.1.1 geladen.

Diese Vorgehensweise, bei der stets die aktuellste Build- und Revisionsversion verwendet wird, wird als "Automatische QFE-Richtlinie" bezeichnet. Der Hauptzweck dieser Richtlinie besteht darin, es Administratoren zu ermöglichen, Problemkorrekturversionen weiterzugeben, ohne dass alle Anwendungen neu erstellt werden müssen.

Benutzerdefinierte Versionsrichtlinie

Gelegentlich entspricht die beschriebene Standardrichtlinie jedoch einfach nicht Ihren Anforderungen. So wurde eine Anwendung u.U. versehentlich durch einen installierten QFE-Fix beschädigt.

Die Standardversionsrichtlinie kann unter Verwendung der XML-Konfigurationsdateien geändert werden. Hinsichtlich der Versionsprüfung gibt es zwei von diesen Dateien: eine anwendungsspezifische Datei und eine computerübergreifende Datei bzw. Administratordatei. Die anwendungsspezifische Datei befindet sich im selben Verzeichnis wie die Anwendung, und Richtlinienanweisungen in dieser Datei wirken sich lediglich auf diese Anwendungen aus. Die computerübergreifende Richtliniendatei hingegen befindet sich im Windows-Verzeichnis. Diese Datei wird von Administratoren verwendet, um Richtlinienanweisungen zu erstellen, die sich auf alle Anwendungen auf dem Computer auswirken. So hat ein Administrator ggf. festgestellt, dass eine bestimmte Version einer Baugruppe eine Sicherheitslücke bewirkt, und möchte sicherstellen, dass diese Baugruppe nicht verwendet wird.

Beispiele für benutzerdefinierte Richtlinien

Bindung an eine bestimmte Version

Gelegentlich ist es erforderlich, eine Bindung an eine völlig andere als die im Manifest aufgezeichnete Version einer Baugruppe herzustellen. Die Unterstützung für dieses Szenario wird über das <BindingPolicy>-Tag in den Konfigurationsdateien bereitgestellt. Diese Richtlinie kann verwendet werden, um Verweise entweder einer bestimmten Version einer Abhängigkeit oder aber allen Versionen einer Abhängigkeit zuzuordnen.

Im folgenden Beispiel werden Verweise auf alle Versionen einer Baugruppe mit dem Namen Calcr Version 6.0.0.0 zugeordnet:

<BindingPolicy> 
<BindingRedir Name="Calcr" 
Originator="32ab4ba45e0a69a1" 
Version="*" VersionNew="6.0.0.0" 
UseLatestBuildRevision="yes"/> 
</BindingPolicy> 

Automatische QFE-Richtlinie deaktivieren

Diese Richtlinienanweisung ermöglicht es Ihnen, die automatische QFE-Richtlinie für Verweise auf eine bestimmte Baugruppe zu deaktivieren. Das <BindingPolicy>-Tag wird dafür ebenfalls verwendet, doch das UseLatestBuildRevision-Attribut ist, wie im folgenden Beispiel, auf No gesetzt:

<BindingPolicy> 
<BindingRedir Name="Calcr" 
Originator="32ab4ba45e0a69a1" 
Version="*" VersionNew="6.1.1212.14" 
UseLatestBuildRevision="no"/> 
</BindingPolicy> 

Sicherer Modus

Die "Sicherer Modus"-Richtlinie wird verwendet, um die voreingestellte Konfiguration wiederherzustellen. Bei Aktivierung dieser Richtlinie lädt die gemeinsame Sprachlaufzeit genau die Versionen der Abhängigkeiten, die im Manifest aufgezeichnet wurden. Vermutlich hat die Anwendung beim Erstellen, Testen und ersten Weitergeben funktioniert. Der sichere Modus dient quasi als Sicherheitsnetz für die Wiederherstellung dieses Status. Die folgende XML-Codeanweisung aktiviert den sicheren Modus für eine bestimmte Anwendung:

<BindingMode> 
<AppBindingMode Mode="safe"/> 
</BindingMode> 

Sowohl "Sicherer Modus" als auch "Automatische QFE-Richtlinie deaktivieren" werden verwendet, um die Anwendung wieder in einen funktionsfähigen Status zu versetzen, falls eine bestimmte Abhängigkeit die Versionsregeln nicht befolgt oder versehentlich ein Softwareproblem verursacht hat.

Stadien der Richtlinienauflösung

In diesem Artikel wurden mehrere Versionsprüfungs- und Weitergabekonzepte vorgestellt, einschließlich privater Anwendungsbaugruppen, gemeinsam genutzter Baugruppen, des globalen Baugruppenspeichers und der Verwendung von XML-Dateien für das Angeben von Versionsrichtlinien. In diesem Abschnitt werden diese Konzepte zusammengeführt, indem die einzelnen Stadien beschrieben werden, die die gemeinsame Sprachlaufzeit durchläuft, um Baugruppen zu suchen und Versionsrichtlinien anzuwenden.

Das Laden einer Baugruppe beginnt, wenn die gemeinsame Sprachlaufzeit auf einen Verweis auf eine andere Baugruppe stößt, die in Metadaten gespeichert ist. Anhand dieses Verweises legen die folgenden Schritte fest, welche Version dieser Baugruppe geladen werden soll:

  1. Prüfen Sie in der anwendungsspezifischen Konfigurationsdatei, ob Richtlinien festgelegt sind. Wenn dies der Fall ist, aktualisieren Sie den ursprünglichen Verweis mit Informationen zur Richtlinie. Wenn der Verweis z.B. Version 1.0.0.0 festlegt, die anwendungsspezifische Richtliniendatei jedoch Version 2.0.0.0, verhält sich die gemeinsame Sprachlaufzeit, als wäre ursprünglich Version 2.0.0.0 angefordert worden.

  2. Suchen Sie im Anwendungsverzeichnis (und den Unterverzeichnissen) nach einer entsprechenden Baugruppe. (Diese Suche wird als Probing bezeichnet.) Dabei müssen die Haupt- und Nebenversionen exakt übereinstimmen (es sei denn, die QFE-Richtlinie wurde deaktiviert).

  3. Unabhängig davon, ob beim Probing eine Entsprechung gefunden wurde, wird der globale Baugruppenspeicher auf QFE-Fixes überprüft. Dies ermöglicht dem Administratorhook die Weitergabe von Problemkorrekturen, die global verwendet werden sollten.

  4. Schließlich wird die Administratorrichtliniendatei überprüft. Diese Datei wird als letzte konsultiert, da der Administrator entscheidet, welche Version letztendlich geladen wird.

Weitergabe

Die Weitergabe umfasst mindestens zwei unterschiedliche Aspekte: das Packen des Codes und das Verteilen der Pakete an die verschiedenen Clients und Server, auf denen die Anwendung ausgeführt wird. Ein Hauptziel des .NET Frameworks besteht darin, die Weitergabe (v.a. den Verteilungsaspekt) zu vereinfachen, indem Installationen ohne Auswirkungen und xcopy-Weitergabe ermöglicht werden. Da Baugruppen selbstbeschreibend sind, kann unsere Abhängigkeit aus der Registrierung entfernt werden, wodurch Installationen, Deinstallationen und Replikationen erheblich vereinfacht werden. Es gibt jedoch Szenarios, in denen xcopy als Verteilungsmechanismus nicht ausreicht. In diesen Fällen bietet das .NET Framework umfangreiche Downloaddienste für den Code sowie eine Integration in Windows Installer.

Packen

In der ersten Version des .NET Frameworks sind drei Packoptionen verfügbar:

  • As-Built (DLLs und EXEs). In vielen Szenarios ist kein spezielles Packen erforderlich. Eine Anwendung kann in dem Format weitergegeben werden, das vom Entwicklungstool erzeugt wird, d.h. in Form von DLLs und EXEs.

  • CAB-Dateien. CAB-Dateien können verwendet werden, um Ihre Anwendung für effizientere Downloads zu komprimieren.

  • Windows Installer-Pakete. Microsoft Visual Studio.NET und andere Installationstools ermöglichen Ihnen das Erstellen von Windows Installer-Paketen (MSI-Dateien). Mit Windows Installer können Sie Anwendungsreparaturen, Bedarfsinstallationen und andere Microsoft Windows 2000-Anwendungsverwaltungsfeatures nutzen.

Verteilungsszenarios

.NET-Anwendungen können auf viele Arten verteilt werden, einschließlich xcopy, Codedownload und über Windows Installer.

In vielen Anwendungen, einschließlich Webanwendungen und Webdiensten, ist die Weitergabe genauso einfach wie das Kopieren von Dateien auf einen Datenträger und das anschließende Ausführen. Die Deinstallation und Replikation ist ebenfalls ein Kinderspiel: Löschen oder kopieren Sie die Dateien einfach.

Das .NET Framework bietet umfassende Unterstützung für Codedownloads unter Verwendung eines Webbrowsers. In diesem Bereich wurden u.a. folgende Verbesserungen vorgenommen:

  • Keine Beeinträchtigungen. Auf dem Computer werden keine Registrierungseinträge erstellt.

  • Inkrementeller Download. Teile einer Baugruppe werden nur downgeloadet, wenn auf sie verwiesen wird.

  • Isolierter Download für die Anwendung. Code, der für eine Anwendung downgeloadet wurde, hat keine Auswirkungen auf andere Anwendungen auf dem Computer. Das Hauptziel unseres Codedownloads besteht darin zu verhindern, dass der Download einer neuen Version einer gemeinsam genutzten Komponente von einer Website negative Auswirkungen auf andere Anwendungen hat.

  • Keine Authenticode-Dialogfelder. Das Codezugriffs-Sicherheitssystem wird verwendet, um das Ausführen von mobilem Code mit eingeschränkter Vertrauenswürdigkeit zu ermöglichen. Benutzern werden keine Dialogfelder angezeigt, in denen sie gebeten werden zu entscheiden, ob der Code vertrauenswürdig ist.

Und schließlich ist .NET voll in Windows Installer und die Anwendungsverwaltungsfeatures von Windows 2000 integriert.

Zusammenfassung

Das .NET Framework ermöglicht Installationen ohne Auswirkungen auf andere Anwendungen und löst somit das Problem der "DLL Hell". Baugruppen sind die selbstbeschreibenden, versionierbaren Weitergabeeinheiten, die diese Features ermöglichen.

Das Erstellen von isolierten Anwendungen ist von entscheidender Bedeutung, da Systemänderungen durch andere Anwendungen keine Auswirkungen auf diese Anwendungen haben. Das .NET Framework fördert diesen Anwendungstyp durch private Anwendungsbaugruppen, die innerhalb der Verzeichnisstruktur der Anwendung weitergegeben werden.

Parallelität ist ein wesentlicher Bestanteil der gemeinsamen Nutzung und Versionsprüfung in .NET. Parallelität ermöglicht die gleichzeitige Installation und Ausführung von mehreren Versionen einer Baugruppe auf einem Computer und das Anfordern einer bestimmten Version dieser Baugruppe durch die einzelnen Anwendungen.

Die gemeinsame Sprachlaufzeit zeichnet Versionsinformationen zwischen den Komponenten einer Anwendung auf und verwendet diese Informationen zur Laufzeit, um sicherzustellen, dass die richtige Version einer Abhängigkeit geladen wird. Versionsrichtlinien können sowohl von Anwendungsentwicklern als auch von Administratoren verwendet werden, um bei der Auswahl der zu ladenden Version einer Baugruppe eine gewisse Flexibilität zu ermöglichen.

Das .NET Framework stellt verschiedene Pack- und Verteilungsoptionen bereit, einschließlich Windows Installer, Codedownload und xcopy.


Anzeigen: