연습: ClickOnce 배포 API에서 요청 시 어셈블리 다운로드
기본적으로 ClickOnce 응용 프로그램에 포함된 모든 어셈블리는 응용 프로그램을 처음 실행할 때 다운로드됩니다. 그러나 응용 프로그램의 특정 부분은 그 사용자가 많지 않을 수 있습니다. 이 경우 해당 형식 중 하나를 만들 때만 어셈블리를 다운로드하도록 설정할 수도 있습니다. 다음 연습에서는 응용 프로그램의 특정 어셈블리를 "선택적"인 것으로 표시하는 방법과 CLR(공용 언어 런타임)에서 요청할 때 System.Deployment.Application 네임스페이스의 클래스를 사용하여 이러한 어셈블리를 다운로드하는 방법을 보여 줍니다.
참고
이 절차를 사용하려면 응용 프로그램을 완전 신뢰 수준에서 실행해야 합니다.
사전 요구 사항
이 연습을 완료하려면 다음 구성 요소 중 하나가 필요합니다.
Windows SDK입니다. Microsoft 다운로드 센터에서 Windows SDK를 다운로드할 수 있습니다.
Visual Studio
프로젝트 만들기
요청 시 어셈블리를 사용하는 프로젝트를 만들려면
이름이 ClickOnceOnDemand인 디렉터리를 만듭니다.
Windows SDK 명령 프롬프트나 Visual Studio 명령 프롬프트를 엽니다.
ClickOnceOnDemand 디렉터리로 이동합니다.
다음 명령을 사용하여 공개/개인 키 쌍을 생성합니다.
sn -k TestKey.snk
메모장이나 기타 텍스트 편집기를 사용하여 Message라는 속성 하나가 있는 DynamicClass라는 클래스를 정의합니다.
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
텍스트 편집기에서 새 파일을 만들고 다음 코드를 입력합니다. 이 코드는 필요할 때 ClickOnceLibrary 어셈블리를 다운로드하는 Windows Forms 응용 프로그램을 만듭니다.
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 배포를 웹 서버에 업로드합니다.
웹 브라우저에 배포 매니페스트의 URL을 입력하여 ClickOnce를 통해 배포된 응용 프로그램을 시작합니다. ClickOnce 응용 프로그램 ClickOnceOnDemand를 호출하고 adatum.com의 루트 디렉터리에 이를 업로드하는 경우 URL은 다음과 같습니다.
http://www.adatum.com/ClickOnceOnDemand/ClickOnceOnDemand.application
주 폼이 나타나면 Button을 누릅니다. "Hello, World!"라는 문자열이 메시지 상자 창에 나타나야 합니다.