演练:为 ClickOnce 应用程序创建自定义安装程序

任何基于 .exe 文件的 ClickOnce 应用程序都可以由自定义安装程序进行无提示安装和更新。 自定义安装程序可以在安装过程中实现自定义用户体验,其中包括针对安全和维护操作的自定义对话框。 为了执行安装操作,自定义安装程序使用 InPlaceHostingManager 类。 本演练演示如何创建一个自定义安装程序,以无提示方式安装 ClickOnce 应用程序。

注意

.NET Core 和 .NET 5 及更高版本中不支持 System.Deployment.Application 命名空间中的 ApplicationDeployment 类和 API。 在 .NET 7 中,支持一种访问应用程序部署属性的新方法。 有关详细信息,请参阅访问 .NET 中的 ClickOnce 部署属性。 .NET 7 不支持等效的 ApplicationDeployment 方法。

先决条件

创建一个自定义 ClickOnce 应用程序安装程序

  1. 在 ClickOnce 应用程序中,添加对 System.Deployment 和 System.Windows.Forms 的引用。

  2. 向应用程序添加一个新类,并指定任意名称。 本演练使用名称 MyInstaller

  3. 将以下 Importsusing 指令添加到新类的顶部。

    using System.Deployment.Application;
    using System.Windows.Forms;
    
  4. 将以下方法添加到类。

    这些方法调用 InPlaceHostingManager 方法以下载部署清单,断言适当的权限,要求用户提供安装权限,然后将应用程序下载并安装到 ClickOnce 缓存中。 自定义安装程序可以指定 ClickOnce 应用程序是预信任的,也可以将信任决策推迟到 AssertApplicationRequirements 方法调用。 此代码预信任应用程序。

    注意

    通过预信任方式分配的权限不能超过自定义安装程序代码的权限范围。

    InPlaceHostingManager iphm = null;
    
    public void InstallApplication(string deployManifestUriStr)
    {
        try
        {
            Uri deploymentUri = new Uri(deployManifestUriStr);
            iphm = new InPlaceHostingManager(deploymentUri, false);
        }
        catch (UriFormatException uriEx)
        {
            MessageBox.Show("Cannot install the application: " + 
                "The deployment manifest URL supplied is not a valid URL. " +
                "Error: " + uriEx.Message);
            return;
        }
        catch (PlatformNotSupportedException platformEx)
        {
            MessageBox.Show("Cannot install the application: " + 
                "This program requires Windows XP or higher. " +
                "Error: " + platformEx.Message);
            return;
        }
        catch (ArgumentException argumentEx)
        {
            MessageBox.Show("Cannot install the application: " + 
                "The deployment manifest URL supplied is not a valid URL. " +
                "Error: " + argumentEx.Message);
            return;
        }
    
        iphm.GetManifestCompleted += new EventHandler<GetManifestCompletedEventArgs>(iphm_GetManifestCompleted);
        iphm.GetManifestAsync();
    }
    
    void iphm_GetManifestCompleted(object sender, GetManifestCompletedEventArgs e)
    {
        // Check for an error.
        if (e.Error != null)
        {
            // Cancel download and install.
            MessageBox.Show("Could not download manifest. Error: " + e.Error.Message);
            return;
        }
    
        // bool isFullTrust = CheckForFullTrust(e.ApplicationManifest);
    
        // Verify this application can be installed.
        try
        {
            // the true parameter allows InPlaceHostingManager
            // to grant the permissions requested in the applicaiton manifest.
            iphm.AssertApplicationRequirements(true) ; 
        }
        catch (Exception ex)
        {
            MessageBox.Show("An error occurred while verifying the application. " +
                "Error: " + ex.Message);
            return;
        }
    
        // Use the information from GetManifestCompleted() to confirm 
        // that the user wants to proceed.
        string appInfo = "Application Name: " + e.ProductName;
        appInfo += "\nVersion: " + e.Version;
        appInfo += "\nSupport/Help Requests: " + (e.SupportUri != null ?
            e.SupportUri.ToString() : "N/A");
        appInfo += "\n\nConfirmed that this application can run with its requested permissions.";
        // if (isFullTrust)
        // appInfo += "\n\nThis application requires full trust in order to run.";
        appInfo += "\n\nProceed with installation?";
    
        DialogResult dr = MessageBox.Show(appInfo, "Confirm Application Install",
            MessageBoxButtons.OKCancel, MessageBoxIcon.Question);
        if (dr != System.Windows.Forms.DialogResult.OK)
        {
            return;
        }
    
        // Download the deployment manifest. 
        iphm.DownloadProgressChanged += new EventHandler<DownloadProgressChangedEventArgs>(iphm_DownloadProgressChanged);
        iphm.DownloadApplicationCompleted += new EventHandler<DownloadApplicationCompletedEventArgs>(iphm_DownloadApplicationCompleted);
    
        try
        {
            // Usually this shouldn't throw an exception unless AssertApplicationRequirements() failed, 
            // or you did not call that method before calling this one.
            iphm.DownloadApplicationAsync();
        }
        catch (Exception downloadEx)
        {
            MessageBox.Show("Cannot initiate download of application. Error: " +
                downloadEx.Message);
            return;
        }
    }
    
    /*
    private bool CheckForFullTrust(XmlReader appManifest)
    {
        if (appManifest == null)
        {
            throw (new ArgumentNullException("appManifest cannot be null."));
        }
    
        XAttribute xaUnrestricted =
            XDocument.Load(appManifest)
                .Element("{urn:schemas-microsoft-com:asm.v1}assembly")
                .Element("{urn:schemas-microsoft-com:asm.v2}trustInfo")
                .Element("{urn:schemas-microsoft-com:asm.v2}security")
                .Element("{urn:schemas-microsoft-com:asm.v2}applicationRequestMinimum")
                .Element("{urn:schemas-microsoft-com:asm.v2}PermissionSet")
                .Attribute("Unrestricted"); // Attributes never have a namespace
    
        if (xaUnrestricted != null)
            if (xaUnrestricted.Value == "true")
                return true;
    
        return false;
    }
    */
    
    void iphm_DownloadApplicationCompleted(object sender, DownloadApplicationCompletedEventArgs e)
    {
        // Check for an error.
        if (e.Error != null)
        {
            // Cancel download and install.
            MessageBox.Show("Could not download and install application. Error: " + e.Error.Message);
            return;
        }
    
        // Inform the user that their application is ready for use. 
        MessageBox.Show("Application installed! You may now run it from the Start menu.");
    }
    
    void iphm_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
    {
        // you can show percentage of task completed using e.ProgressPercentage
    }
    
  5. 若要尝试从代码进行安装,请调用 InstallApplication 方法。 例如,如果你已将类命名为 MyInstaller,则可以按以下方式调用 InstallApplication

    MyInstaller installer = new MyInstaller();
    installer.InstallApplication(@"\\myServer\myShare\myApp.application");
    MessageBox.Show("Installer object created.");
    

后续步骤

ClickOnce 应用程序还可以添加自定义的更新逻辑,其中包括要在更新过程中显示的自定义用户界面。 有关详细信息,请参阅 UpdateCheckInfo。 ClickOnce 应用程序还可以通过使用 <customUX> 元素来取消标准的“开始”菜单条目、快捷方式和“添加或删除程序”条目。 有关详细信息,请参阅 <entryPoint> 元素ShortcutAppId