Пошаговое руководство. Загрузка сборок по требованию с помощью API развертывания ClickOnce
Обновлен: Ноябрь 2007
По умолчанию все сборки, содержащиеся в приложении ClickOnce, загружаются при первом запуске приложения. Однако могут иметься части приложения, которые используются небольшим числом пользователей. В этом случае необходимо, чтобы сборка загружалась только при создании одного из ее типов. В следующем разборе примера демонстрируется маркировка определенных сборок в приложении как "необязательных" и их загрузка с помощью классов в пространстве имен System.Deployment.Application по запросам общеязыковой среды выполнения.
Примечание. |
---|
Чтобы использовать эту процедуру, приложение должно выполняться с полным доверием. |
Обязательные компоненты
Чтобы выполнить данный разбор примера, потребуется один из следующих компонентов:
Набор инструментальных средств разработки (SDK) .NET Framework 2.0. Набор инструментальных средств разработки (SDK) .NET Framework 2.0 доступен как компонент установки Visual Studio 2005 или может быть загружен с веб-узла Центр загрузки Майкрософт.
Набор инструментальных средств разработки (SDK) Windows для Windows Vista. Набор инструментальных средств разработки (SDK) Windows для Windows Vista может быть загружен с веб-узла "Центр загрузки Майкрософт".
Microsoft Visual Studio 2005 или более поздняя версия.
Создание проектов
Чтобы создать проект, использующий сборку по требованию
Создайте каталог с именем ClickOnceOnDemand.
Откройте окно командной строки SDK .NET Framework или окно командной строки Visual Studio.
Измените каталог на ClickOnceOnDemand.
Создайте пару открытого и закрытого ключей, используя следующую команду:
sn -k TestKey.snk
Используя Блокнот или другой текстовый редактор, определите класс с именем DynamicClass, имеющий единственное свойство с именем 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!"); } } } }
В зависимости от используемого языка сохраните текст как файл с именем ClickOnceLibrary.cs или ClickOnceLibrary.vb в каталог ClickOnceOnDemand.
Скомпилируйте файл в сборку.
csc /target:library /keyfile:TestKey.snk ClickOnceLibrary.cs
vbc /target:library /keyfile:TestKey.snk ClickOnceLibrary.vb
Чтобы получить для сборки маркер открытого ключа, выполните следующую команду:
sn -T ClickOnceLibrary.dll
Создайте новый файл, используя текстовый редактор или введя следующий код. Этот код позволяет создать приложение Windows Forms, которое загружает сборку ClickOnceLibrary, когда она необходима.
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); } } }
Найдите в коде вызов метода LoadFile.
Установите для PublicKeyToken значение, которое было извлечено раньше.
Сохраните файл как Form1.cs или Form1.vb.
Скомпилируйте его в исполняемый файл, воспользовавшись следующей командой.
csc /target:exe /reference:ClickOnceLibrary.dll Form1.cs
vbc /target:exe /reference:ClickOnceLibrary.dll Form1.vb
Пометка сборок как необязательных
Чтобы пометить сборки как необязательные в приложении ClickOnce с помощью средства MageUI.exe
Используя MageUI.exe, создайте манифест приложения, как описано в Пошаговое руководство. Развертывание приложения ClickOnce вручную. Для манифеста приложения используйте следующие параметры:
Назовите манифест приложения как ClickOnceOnDemand.
На странице Файлы в строке ClickOnceLibrary.dll установите для столбца Тип файла значение Нет.
На странице Файлы в строке ClickOnceLibrary.dll введите ClickOnceLibrary.dll в столбец Группа.
Используя MageUI.exe, создайте манифест развертывания, как описано в Пошаговое руководство. Развертывание приложения ClickOnce вручную. Для манифеста развертывания используйте следующие параметры:
- Назовите манифест развертывания как ClickOnceOnDemand.
Проверка новой сборки
Чтобы проверить сборку по требованию
Передайте развертывание ClickOnce на веб-сервер.
Запустите приложение, развернутое с помощью ClickOnce, из веб-обозревателя, введя URL-адрес в манифест развертывания. Если вызывается приложение ClickOnce с именем ClickOnceOnDemand, и выполняется его передача в корневой каталог веб-узла adatum.com, URL-адрес выглядел бы следующим образом:
http://www.adatum.com/ClickOnceOnDemand/ClickOnceOnDemand.application
Когда появится главная форма, нажмите кнопку Button. В окне сообщения должна быть видна строка "Hello, World!".