Exemplarische Vorgehensweise: Herunterladen von Assemblys bei Bedarf mit der API für die ClickOnce-Bereitstellung
Standardmäßig werden alle in einer ClickOnce-Anwendung enthaltenen Assemblys beim ersten Ausführen der Anwendung heruntergeladen. Möglicherweise werden aber bestimmte Teile der Anwendung nur von wenigen Benutzern verwendet. In diesem Fall ist es sinnvoll, eine Assembly erst dann herunterzuladen, wenn Sie einen der zugehörigen Typen erstellen. Die folgende exemplarische Vorgehensweise zeigt, wie Sie bestimmte Assemblys einer Anwendung als "optional" markieren und wie Sie diese unter Verwendung von Klassen im System.Deployment.Application-Namespace herunterladen, wenn sie von der Common Language Runtime (CLR) angefordert werden.
Tipp
Die Anwendung muss mit voller Vertrauenswürdigkeit ausgeführt werden, um dieses Verfahren zu verwenden.
Vorbereitungsmaßnahmen
Zum Durchführen dieser exemplarischen Vorgehensweise benötigen Sie eine der folgenden Komponenten:
Windows SDK. Das Windows SDK kann aus dem Microsoft Download Center heruntergeladen werden.
Visual Studio
Erstellen der Projekte
So erstellen Sie ein Projekt mit einer bedarfsgesteuerten Assembly
Erstellen Sie ein Verzeichnis mit dem Namen ClickOnceOnDemand.
Öffnen Sie die Windows SDK-Eingabeaufforderung oder die Visual Studio-Eingabeaufforderung.
Wechseln Sie zum ClickOnceOnDemand-Verzeichnis.
Generieren Sie mit dem folgenden Befehl ein Schlüsselpaar aus öffentlichem und privatem Schlüssel:
sn -k TestKey.snk
Definieren Sie in Editor oder einem anderen Text-Editor eine Klasse mit dem Namen DynamicClass und einer einzelnen Eigenschaft mit dem Namen Message.
Public Class DynamicClass Sub New() End Sub Public ReadOnly Property Message() As String Get Message = "Hello, world!" End Get End Property End Class
using System; using System.Collections.Generic; using System.Text; namespace Microsoft.Samples.ClickOnceOnDemand { public class DynamicClass { public DynamicClass() {} public string Message { get { return ("Hello, world!"); } } } }
Speichern Sie den Text je nach verwendeter Sprache als Datei mit dem Namen ClickOnceLibrary.cs oder ClickOnceLibrary.vb im ClickOnceOnDemand-Verzeichnis.
Kompilieren Sie die Datei in eine Assembly.
csc /target:library /keyfile:TestKey.snk ClickOnceLibrary.cs
vbc /target:library /keyfile:TestKey.snk ClickOnceLibrary.vb
Um das öffentliche Schlüsseltoken für die Assembly abzurufen, verwenden Sie den folgenden Befehl:
sn -T ClickOnceLibrary.dll
Erstellen Sie eine neue Datei im Text-Editor, und geben Sie den folgenden Code ein. Durch diesen Code wird eine Windows Forms-Anwendung erstellt, durch die die ClickOnceLibrary-Assembly, falls angefordert, heruntergeladen wird.
Imports System Imports System.Windows.Forms Imports System.Deployment.Application Imports System.Drawing Imports System.Reflection Imports System.Collections.Generic Imports Microsoft.Samples.ClickOnceOnDemand Namespace Microsoft.Samples.ClickOnceOnDemand <System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand, Unrestricted:=true)> _ Class Form1 Inherits Form ' Maintain a dictionary mapping DLL names to download file groups. This is trivial for this sample, ' but will be important in real-world applications where a feature is spread across multiple DLLs, ' and you want to download all DLLs for that feature in one shot. Dim DllMapping as Dictionary(Of String, String) = new Dictionary(of String, String)() Public Sub New() ' Add button to form. Dim GetAssemblyButton As New Button() GetAssemblyButton.Location = New Point(100, 100) GetAssemblyButton.Text = "Get assembly on demand" AddHandler GetAssemblyButton.Click, AddressOf GetAssemblyButton_Click Me.Controls.Add(GetAssemblyButton) DllMapping("ClickOnceLibrary") = "ClickOnceLibrary" AddHandler AppDomain.CurrentDomain.AssemblyResolve, AddressOf CurrentDomain_AssemblyResolve End Sub <STAThread()> _ Shared Sub Main() Application.EnableVisualStyles() Application.Run(New Form1()) End Sub Private Function CurrentDomain_AssemblyResolve(ByVal sender As Object, ByVal args As ResolveEventArgs) As Assembly If ApplicationDeployment.IsNetworkDeployed Then Dim deploy As ApplicationDeployment = ApplicationDeployment.CurrentDeployment ' Get the DLL name from the Name argument. Dim nameParts() as String = args.Name.Split(",") Dim dllName as String = nameParts(0) Dim downloadGroupName as String = DllMapping(dllName) Try deploy.DownloadFileGroup(downloadGroupName) Catch de As DeploymentException End Try ' Load the assembly. Dim newAssembly As Assembly = Nothing Try newAssembly = Assembly.LoadFile(Application.StartupPath & "\\" & dllName & ".dll," & _ "Version=1.0.0.0, Culture=en, PublicKeyToken=03689116d3a4ae33") Catch ex As Exception MessageBox.Show("Could not download assembly on demand.") End Try CurrentDomain_AssemblyResolve = newAssembly Else CurrentDomain_AssemblyResolve = Nothing End If End Function Private Sub GetAssemblyButton_Click(ByVal sender As Object, ByVal e As EventArgs) Dim ourClass As New DynamicClass() MessageBox.Show("DynamicClass string is: " + ourClass.Message) End Sub End Class End Namespace
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Reflection; using System.Deployment.Application; using Microsoft.Samples.ClickOnceOnDemand; namespace ClickOnceOnDemand { [System.Security.Permissions.SecurityPermission(System.Security.Permissions.SecurityAction.Demand, Unrestricted=true)] public class Form1 : Form { // Maintain a dictionary mapping DLL names to download file groups. This is trivial for this sample, // but will be important in real-world applications where a feature is spread across multiple DLLs, // and you want to download all DLLs for that feature in one shot. Dictionary<String, String> DllMapping = new Dictionary<String, String>(); public static void Main() { Form1 NewForm = new Form1(); Application.Run(NewForm); } public Form1() { // Configure form. this.Size = new Size(500, 200); Button getAssemblyButton = new Button(); getAssemblyButton.Size = new Size(130, getAssemblyButton.Size.Height); getAssemblyButton.Text = "Test Assembly"; getAssemblyButton.Location = new Point(50, 50); this.Controls.Add(getAssemblyButton); getAssemblyButton.Click += new EventHandler(getAssemblyButton_Click); DllMapping["ClickOnceLibrary"] = "ClickOnceLibrary"; AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); } /* * Use ClickOnce APIs to download the assembly on demand. */ private Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { Assembly newAssembly = null; if (ApplicationDeployment.IsNetworkDeployed) { ApplicationDeployment deploy = ApplicationDeployment.CurrentDeployment; // Get the DLL name from the Name argument. string[] nameParts = args.Name.Split(','); string dllName = nameParts[0]; string downloadGroupName = DllMapping[dllName]; try { deploy.DownloadFileGroup(downloadGroupName); } catch (DeploymentException de) { MessageBox.Show("Downloading file group failed. Group name: " + downloadGroupName + "; DLL name: " + args.Name); throw (de); } // Load the assembly. // Assembly.Load() doesn't work here, as the previous failure to load the assembly // is cached by the CLR. LoadFrom() is not recommended. Use LoadFile() instead. try { newAssembly = Assembly.LoadFile(Application.StartupPath + @"\" + dllName + ".dll," + "Version=1.0.0.0, Culture=en, PublicKeyToken=03689116d3a4ae33"); } catch (Exception e) { throw (e); } } else { //Major error - not running under ClickOnce, but missing assembly. Don't know how to recover. throw (new Exception("Cannot load assemblies dynamically - application is not deployed using ClickOnce.")); } return (newAssembly); } private void getAssemblyButton_Click(object sender, EventArgs e) { DynamicClass dc = new DynamicClass(); MessageBox.Show("Message: " + dc.Message); } } }
Suchen Sie im Code den Aufruf von LoadFile.
Legen Sie PublicKeyToken auf den zuvor abgerufenen Wert fest.
Speichern Sie die Datei entweder als Form1.cs oder Form1.vb.
Kompilieren Sie sie mit dem folgenden Befehl in eine ausführbare Datei.
csc /target:exe /reference:ClickOnceLibrary.dll Form1.cs
vbc /target:exe /reference:ClickOnceLibrary.dll Form1.vb
Markieren von Assemblys als optional
So markieren Sie Assemblys in der ClickOnce-Anwendung mit MageUI.exe als optional
Erstellen Sie unter Verwendung von MageUI.exe ein Anwendungsmanifest, wie in Exemplarische Vorgehensweise: Manuelles Bereitstellen einer ClickOnce-Anwendung beschrieben. Verwenden Sie die folgenden Einstellungen für das Anwendungsmanifest:
Nennen Sie das Anwendungsmanifest ClickOnceOnDemand.
Legen Sie auf der Seite Dateien in der Zeile für die ClickOnceLibrary.dll die Spalte Dateityp auf Keine fest.
Geben Sie auf der Seite Dateien in der Zeile für die ClickOnceLibrary.dll in der Spalte Gruppe den Namen ClickOnceLibrary.dll ein.
Verwenden Sie MageUI.exe, um ein Bereitstellungsmanifest zu erstellen, wie in Exemplarische Vorgehensweise: Manuelles Bereitstellen einer ClickOnce-Anwendung beschrieben. Verwenden Sie die folgenden Einstellungen für das Bereitstellungsmanifest:
- Nennen Sie das Bereitstellungsmanifest ClickOnceOnDemand.
Testen der neuen Assembly
So testen Sie die bedarfsabhängige Assembly
Laden Sie die ClickOnce-Bereitstellung auf einen Webserver herauf.
Starten Sie die mit ClickOnce bereitgestellte Anwendung über einen Webbrowser, indem Sie die URL zum Bereitstellungsmanifest eingeben. Wenn Sie der ClickOnce-Anwendung den Namen ClickOnceOnDemand geben und sie in das Stammverzeichnis von adatum.com heraufladen, sieht die URL folgendermaßen aus:
http://www.adatum.com/ClickOnceOnDemand/ClickOnceOnDemand.application
Wenn das Hauptformular angezeigt wird, klicken Sie auf den Button. Nun sollte in einem Meldungsfenster die Zeichenfolge "Hello, World!" angezeigt werden.