逐步解說:依 ClickOnce 部署 API 的要求下載組件
更新:2007 年 11 月
根據預設,在 ClickOnce 應用程式中包含的所有組件,都會在應用程式首次執行時下載。不過,您的應用程式可能有些部分是由一小群使用者使用。在此情況下,只有在建立一種類型的組件時,才需要下載組件。下列逐步解說會示範如何將應用程式中的某些組件標記為「選擇項」,以及如何在 Common Language Runtime (CLR) 要求這些組件時,使用 System.Deployment.Application 命名空間中的類別來下載這些組件。
注意事項: |
---|
您的應用程式需要在完全信任的狀態下執行,才能使用這個程序。 |
必要條件
您將需要下列其中一個元件才能完成這個逐步解說:
.NET Framework 2.0 SDK。.NET Framework 2.0 SDK 是隨 Visual Studio 2005 安裝程式提供的元件,或者可以從 Microsoft 下載中心 (英文) 下載。
適用於 Windows Vista 的 Windows SDK。適用於 Windows Vista 的 Windows SDK 可從 Microsoft 下載中心下載。
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 Form 應用程式,該應用程式會在需要時下載 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 應用程式中的組件標記為選擇項
使用 MageUI.exe,建立應用程式資訊清單,如逐步解說:手動部署 ClickOnce 應用程式中所述。請使用下列應用程式資訊清單的設定:
將應用程式資訊清單命名為 ClickOnceOnDemand。
在 [檔案] 頁面上的 [ClickOnceLibrary.dll] 資料列,將 [檔案類型] 欄設為 [無]。
在 [檔案] 頁面上 [ClickOnceLibrary.dll] 資料列的 [群組] 欄中,輸入 ClickOnceLibrary.dll。
使用 MageUI.exe,建立部署資訊清單,如逐步解說:手動部署 ClickOnce 應用程式中所述。請使用下列部署資訊清單的設定:
- 將部署資訊清單命名為 ClickOnceOnDemand。
測試新組件
若要測試視需要下載的組件
將您的 ClickOnce 部署上載至 Web 伺服器。
輸入部署資訊清單的 URL,以便從 Web 瀏覽器啟動以 ClickOnce 部署的應用程式。如果您呼叫 ClickOnce 應用程式 ClickOnceOnDemand,並將它上載至 adatum.com 的根目錄,您的 URL 會如下所示:
http://www.adatum.com/ClickOnceOnDemand/ClickOnceOnDemand.application
當您的主表單出現時,按下 Button。您應該會看到在訊息方塊視窗中顯示 "Hello, World!" 的字串。