部署 Office 2016 VSTO 插件的最佳实践
Best practice for deploying Office 2016 VSTO Addins
我们已经为 Office 2016 开发了一些内部工具栏,并希望将它们部署到我们的所有用户。
目前,VSTO 构建为 EXE,我们可以使用组策略对其进行部署。安装后,插件会出现在“添加/删除程序”中 (Windows 10)。
但是,我们希望在未来证明这一点,以便我们在进行更新时可以推出更新版本的工具栏。
使用组策略方法,我们最终安装了多个版本的 EXE。 (应用程序中实际上只有一个,但您可以在“添加/删除程序”中看到两者)。
我们应该如何打包这些插件以便推出未来的版本(通过 GPO 或脚本)?我怀疑 MSI 可能是要走的路,但这对我们来说是新领域。
对于我的 VSTO 加载项,我通过 ClickOnce 进行部署。 ClickOnce 具有在用户打开相应的 Office 应用程序时自动检查更新的巨大优势。
当然,ClickOnce 很难通过组策略部署(尽管确实存在解决方案)。您可以查看的一件事是创建一个 ClickOnce 引导程序可执行文件(setup.exe 使用 MSBuild 生成),然后通过组策略部署它。
您可以通过以下两种方式之一生成引导程序;在 Visual Studio(项目属性、发布、先决条件、创建安装程序以安装先决条件组件)或以编程方式使用 Microsoft.Build 命名空间:
using System;
using System.IO;
using System.Reflection;
using Microsoft.Build.Construction;
using Microsoft.Build.Execution;
using Microsoft.Build.Framework;
using Microsoft.Build.Logging;
using Microsoft.Build.Tasks.Deployment.ManifestUtilities;
using Microsoft.Build.Utilities;
public class BootstrapperExample {
private string GenerateBootstrapper(string manifestFile, string applicationName, string applicationUrl, params string[] prerequisites) {
// root element
ProjectRootElement xml = ProjectRootElement.Create();
xml.ToolsVersion = "4.0";
xml.DefaultTargets = "BuildBootstrapper";
// build properties
var propertyGroup = xml.AddPropertyGroup();
propertyGroup.AddProperty("TargetFrameworkVersion", "v4.5");
propertyGroup.AddProperty("VisualStudioVersion", "11.0");
// prerequisites (product codes of each required package, e.g. ".NETFramework,Version=v4.5")
var itemGroup = xml.AddItemGroup();
foreach (string productCode in prerequisites) {
itemGroup.AddItem("BootstrapperFile", productCode);
}
// target
var target = xml.AddTarget("BuildBootstrapper");
var task = target.AddTask("GenerateBootstrapper");
task.SetParameter("ApplicationFile", Path.GetFileName(manifestFile));
task.SetParameter("ApplicationName", applicationName);
task.SetParameter("ApplicationUrl", applicationUrl);
task.SetParameter("BootstrapperItems", "@(BootstrapperFile)");
task.SetParameter("OutputPath", Path.GetDirectoryName(manifestFile));
task.SetParameter("Path", @"C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\Bootstrapper"); // replace with actual path
var proj = new ProjectInstance(xml);
var req = new BuildRequestData(proj, new string[] { "BuildBootstrapper" });
var parameters = new BuildParameters();
// optional logging of the build process
var logger = new FileLogger();
Uri codeBase = new Uri(Assembly.GetEntryAssembly().CodeBase);
logger.Parameters = "logfile=" + Path.Combine(Path.GetDirectoryName(codeBase.LocalPath), "msbuild.log");
parameters.Loggers = new ILogger[] { logger };
// build the bootstrapper executable (setup.exe)
var result = BuildManager.DefaultBuildManager.Build(parameters, req);
if (result.OverallResult == BuildResultCode.Failure) {
throw new InvalidOperationException("MSBuild task failed!", result.Exception);
}
// return path to the built setup.exe
return Path.Combine(Path.GetDirectoryName(manifestFile), "setup.exe");
}
}
我们已经为 Office 2016 开发了一些内部工具栏,并希望将它们部署到我们的所有用户。
目前,VSTO 构建为 EXE,我们可以使用组策略对其进行部署。安装后,插件会出现在“添加/删除程序”中 (Windows 10)。
但是,我们希望在未来证明这一点,以便我们在进行更新时可以推出更新版本的工具栏。
使用组策略方法,我们最终安装了多个版本的 EXE。 (应用程序中实际上只有一个,但您可以在“添加/删除程序”中看到两者)。
我们应该如何打包这些插件以便推出未来的版本(通过 GPO 或脚本)?我怀疑 MSI 可能是要走的路,但这对我们来说是新领域。
对于我的 VSTO 加载项,我通过 ClickOnce 进行部署。 ClickOnce 具有在用户打开相应的 Office 应用程序时自动检查更新的巨大优势。
当然,ClickOnce 很难通过组策略部署(尽管确实存在解决方案)。您可以查看的一件事是创建一个 ClickOnce 引导程序可执行文件(setup.exe 使用 MSBuild 生成),然后通过组策略部署它。
您可以通过以下两种方式之一生成引导程序;在 Visual Studio(项目属性、发布、先决条件、创建安装程序以安装先决条件组件)或以编程方式使用 Microsoft.Build 命名空间:
using System;
using System.IO;
using System.Reflection;
using Microsoft.Build.Construction;
using Microsoft.Build.Execution;
using Microsoft.Build.Framework;
using Microsoft.Build.Logging;
using Microsoft.Build.Tasks.Deployment.ManifestUtilities;
using Microsoft.Build.Utilities;
public class BootstrapperExample {
private string GenerateBootstrapper(string manifestFile, string applicationName, string applicationUrl, params string[] prerequisites) {
// root element
ProjectRootElement xml = ProjectRootElement.Create();
xml.ToolsVersion = "4.0";
xml.DefaultTargets = "BuildBootstrapper";
// build properties
var propertyGroup = xml.AddPropertyGroup();
propertyGroup.AddProperty("TargetFrameworkVersion", "v4.5");
propertyGroup.AddProperty("VisualStudioVersion", "11.0");
// prerequisites (product codes of each required package, e.g. ".NETFramework,Version=v4.5")
var itemGroup = xml.AddItemGroup();
foreach (string productCode in prerequisites) {
itemGroup.AddItem("BootstrapperFile", productCode);
}
// target
var target = xml.AddTarget("BuildBootstrapper");
var task = target.AddTask("GenerateBootstrapper");
task.SetParameter("ApplicationFile", Path.GetFileName(manifestFile));
task.SetParameter("ApplicationName", applicationName);
task.SetParameter("ApplicationUrl", applicationUrl);
task.SetParameter("BootstrapperItems", "@(BootstrapperFile)");
task.SetParameter("OutputPath", Path.GetDirectoryName(manifestFile));
task.SetParameter("Path", @"C:\Program Files (x86)\Microsoft SDKs\Windows\v8.0A\Bootstrapper"); // replace with actual path
var proj = new ProjectInstance(xml);
var req = new BuildRequestData(proj, new string[] { "BuildBootstrapper" });
var parameters = new BuildParameters();
// optional logging of the build process
var logger = new FileLogger();
Uri codeBase = new Uri(Assembly.GetEntryAssembly().CodeBase);
logger.Parameters = "logfile=" + Path.Combine(Path.GetDirectoryName(codeBase.LocalPath), "msbuild.log");
parameters.Loggers = new ILogger[] { logger };
// build the bootstrapper executable (setup.exe)
var result = BuildManager.DefaultBuildManager.Build(parameters, req);
if (result.OverallResult == BuildResultCode.Failure) {
throw new InvalidOperationException("MSBuild task failed!", result.Exception);
}
// return path to the built setup.exe
return Path.Combine(Path.GetDirectoryName(manifestFile), "setup.exe");
}
}