Walkthrough: Download assemblies on demand with the ClickOnce deployment API

By default, all of the assemblies included in a ClickOnce application are downloaded when the application is first run. However, you may have parts of your application that are used by a small set of your users. In this case, you want to download an assembly only when you create one of its types. The following walkthrough demonstrates how to mark certain assemblies in your application as "optional", and how to download them by using classes in the System.Deployment.Application namespace when the common language runtime (CLR) demands them.

Note

The ApplicationDeployment class and APIs in the System.Deployment.Application namespace are not supported in .NET Core and .NET 5 and later versions. In .NET 7, a new method of accessing application deployment properties is supported. For more information, see Access ClickOnce deployment properties in .NET. .NET 7 does not support the equivalent of ApplicationDeployment methods.

Note

Your application will have to run in full trust to use this procedure.

Prerequisites

You will need one of the following components to complete this walkthrough:

  • The Windows SDK. The Windows SDK can be downloaded from the Microsoft Download Center.

  • Visual Studio.

Create the projects

To create a project that uses an on-demand assembly

  1. Create a directory named ClickOnceOnDemand.

  2. Open the Windows SDK Command Prompt or the Visual Studio Command Prompt.

  3. Change to the ClickOnceOnDemand directory.

  4. Generate a public/private key pair using the following command:

    sn -k TestKey.snk
    
  5. Using Notepad or another text editor, define a class named DynamicClass with a single property named Message.

    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!");
                }
            }
        }
    }
    
  6. Save the text as a file named ClickOnceLibrary.cs or ClickOnceLibrary.vb, depending on the language you use, to the ClickOnceOnDemand directory.

  7. Compile the file into an assembly.

    csc /target:library /keyfile:TestKey.snk ClickOnceLibrary.cs
    
  8. To get the public key token for the assembly, use the following command:

    sn -T ClickOnceLibrary.dll
    
  9. Create a new file using your text editor and enter the following code. This code creates a Windows Forms application that downloads the ClickOnceLibrary assembly when it is required.

    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);
            }
        }
    }
    
  10. In the code, locate the call to LoadFile.

  11. SetPublicKeyToken to the value that you retrieved earlier.

  12. Save the file as either Form1.cs or Form1.vb.

  13. Compile it into an executable using the following command.

    csc /target:exe /reference:ClickOnceLibrary.dll Form1.cs
    

Mark assemblies as optional

To mark assemblies as optional in your ClickOnce application by using MageUI.exe

  1. Using MageUI.exe, create an application manifest as described in Walkthrough: Manually deploy a ClickOnce application. Use the following settings for the application manifest:

    • Name the application manifest ClickOnceOnDemand.

    • On the Files page, in the ClickOnceLibrary.dll row, set the File Type column to None.

    • On the Files page, in the ClickOnceLibrary.dll row, type ClickOnceLibrary.dll in the Group column.

  2. Using MageUI.exe, create a deployment manifest as described in Walkthrough: Manually deploy a ClickOnce application. Use the following settings for the deployment manifest:

    • Name the deployment manifest ClickOnceOnDemand.

Testing the new assembly

To test your on-demand assembly

  1. Upload your ClickOnce deployment to a Web server.

  2. Start your application deployed with ClickOnce from a Web browser by entering the URL to the deployment manifest. If you call your ClickOnce application ClickOnceOnDemand, and you upload it to the root directory of adatum.com, your URL would look like this:

    http://www.adatum.com/ClickOnceOnDemand/ClickOnceOnDemand.application
    
  3. When your main form appears, press the Button. You should see a string in a message box window that reads "Hello, World!".