演练:使用 ClickOnce 部署 API 按需下载程序集
更新:2007 年 11 月
默认情况下,首次运行 ClickOnce 应用程序时,会下载该应用程序中包含的所有程序集。但是,少数用户可能仅使用应用程序的某些部分。在这种情况下,您可能希望只有在创建某一类型的程序集时,才下载一个该类型的程序集。下面的演练演示如何将应用程序中的某些程序集标记为“可选”,以及如何在公共语言运行库 (CLR) 需要这些程序集时,通过使用 System.Deployment.Application 命名空间中的类来下载这些程序集。
说明: |
---|
要使用此过程,应用程序必须以完全信任的方式运行。 |
先决条件
若要完成本演练,您将需要下列组件之一:
.NET Framework 2.0 SDK。.NET Framework 2.0 SDK 可以作为 Visual Studio 2005 安装程序的一个组件提供,或者可从 Microsoft Download Center(Microsoft 下载中心)下载。
用于 Windows Vista 的 Windows SDK。可以从 Microsoft 下载中心下载用于 Windows Vista 的 Windows SDK。
Microsoft Visual Studio 2005 或更高版本。
创建项目
创建使用按需程序集的项目
创建一个名为 ClickOnceOnDemand 的目录。
打开 .NET Framework SDK 命令提示或 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 窗体应用程序,在需要 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
将程序集标记为可选
使用 MageUI.exe 在 ClickOnce 应用程序中将程序集标记为可选
按照演练:手动部署 ClickOnce 应用程序中的描述,使用 MageUI.exe 创建一个应用程序清单。请对该应用程序清单使用下列设置:
将该应用程序清单命名为 ClickOnceOnDemand。
在“文件”页上的 ClickOnceLibrary.dll 行中,将“文件类型”列设置为“无”。
在“文件”页上的 ClickOnceLibrary.dll 行中,在“组”列中键入 ClickOnceLibrary.dll。
按照演练:手动部署 ClickOnce 应用程序中的描述,使用 MageUI.exe 创建一个部署清单。请对该部署清单使用下列设置:
- 将该部署清单命名为 ClickOnceOnDemand。
测试新的程序集
测试按需程序集
将 ClickOnce 部署上载到 Web 服务器。
通过在 Web 浏览器中输入部署清单的 URL 来启动使用 ClickOnce 部署的应用程序。如果调用 ClickOnce 应用程序 ClickOnceOnDemand,并将其上载到 adatum.com 的根目录下,则该 URL 可能如下所示:
http://www.adatum.com/ClickOnceOnDemand/ClickOnceOnDemand.application
显示主窗体时,按 Button。此时,消息框窗口中应该显示“Hello, World!”这样的字符串。