良い質問。
現在のプロジェクトで WixSharp を使用していますが、とても満足しています。すべてを C# 構文で行うことにより、XML ファイルの作成を回避できるのは素晴らしいことです。ところで、私はあなたが車輪の再発明をしているとは思いません... あなたは WixSharp で車輪をスピードアップしています.
WixSharp バージョン 1.9.6 では WebAppPool が関連付けられた Web サイトを作成できないため、CustomWebSite.cs ファイルを作成して作成しました。このようにして、次のコードを使用して Web アプリケーションを作成しました。
...
var project = new ManagedProject("My Project",
new InstallDir(@"c:\my_tool",
new Dir("my_frontend",
new Files($"{frontendDir}\\app\\*.*"),
new CustomWebSite("GateKeeper", "*:31515")
{
WebApplication = new CustomWebApplication("DemoApp")
{
WebAppPool = new WebAppPool("DemoApp", "ManagedPipelineMode=Integrated;Identity=applicationPoolIdentity"),
},
InstallWebSite = true
}
)
),
...
これは、1 つの Web サイトを作成するためにのみ使用した CustomWebSite.cs ファイルです。
using System;
using System.Collections.Generic;
using System.Xml.Linq;
using WixSharp;
using WixSharp.CommonTasks;
using static WixSharp.WebSite;
namespace ToolBox.WixSharp
{
/// <summary>
/// Defines the WebSite element to be created associated to a Dir element.
/// </summary>
///<example>The following is an example of associating a CustomWebSite to a Dir element.
///
///<code>
/// var project =
/// new Project("My Product",
/// new Dir(@"%ProgramFiles%\My Company\My Product",
/// new Dir(@"some_dir",
/// new CustomWebSite("MyApp", "*:81")
/// {
/// WebApplication = new CustomWebApplication("DemoApp")
/// {
/// WebAppPool = new WebAppPool("DemoApp", "ManagedPipelineMode=Integrated;Identity=applicationPoolIdentity")
/// }
/// InstallWebSite = true
/// }
/// ...
///
/// Compiler.BuildMsi(project);
///</code>
///
/// This code will generate something like this:
///<code>
/// <Component Id="DemoApp_WebSite" Guid="a6896bba-1818-43e0-824f-9c585b3e366b" KeyPath="yes" Win64="yes">
/// <iis:WebSite Id = "DemoApp_WebSite" Description="DemoApp_WebSite" Directory="INSTALLDIR.some_dir">
/// <iis:WebAddress Id = "WebSite_Address1" IP="*" Port="31515" />
/// <iis:WebApplication Id = "DemoApp_WebApplication" Name="DemoApp" WebAppPool="DemoApp_AppPool"/>
/// </iis:WebSite>
/// <iis:WebAppPool Id = "DemoApp_AppPool" Name="DemoApp" ManagedPipelineMode="Integrated" Identity="applicationPoolIdentity" />
///
/// <CreateFolder />
/// <RemoveFolder Id = "INSTALLDIR.some_dir" On="uninstall" />
/// </Component>
/// </code>
/// </example>
public class CustomWebSite : WixEntity, IGenericEntity
{
/// <summary>
/// Indicates if the WebSite is to be installed (created on IIS) or existing WebSite should be used to install the corresponding
/// WebApplication. The default <see cref="InstallWebSite"/> value is <c>false</c>
/// <para>Developers should be aware of the WebSite installation model imposed by WiX/MSI and use <see cref="InstallWebSite"/> carefully.</para>
/// <para>If <see cref="InstallWebSite"/> value is set to <c>false</c> the parent WebApplication (<see cref="T:WixSharp.IISVirtualDir"/>)
/// will be installed in the brand new (freshly created) WebSite or in the existing one if a site with the same address/port combination already exists
/// on IIS). The undesirable side affect of this deployment scenario is that if the existing WebSite was used to install the WebApplication it will be
/// deleted on IIS during uninstallation even if this WebSite has other WebApplications installed.</para>
/// <para>The "safer" option is to set <see cref="InstallWebSite"/> value to <c>true</c> (default value). In this case the WebApplication will
/// be installed in an existing WebSite with matching address/port. If the match is not found the installation will fail. During the uninstallation
/// only installed WebApplication will be removed from IIS.</para>
/// </summary>
public bool InstallWebSite = false;
/// <summary>
/// Initializes a new instance of the <see cref="WebSite" /> class.
/// </summary>
public CustomWebSite()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="CustomWebSite"/> class.
/// </summary>
/// <param name="description">The description of the web site (as it shows up in the IIS manager console).</param>
/// <param name="addressDefinition">The address definition.</param>
public CustomWebSite(string description, string addressDefinition)
{
this.Id = $"{description}_WebSite";
this.Description = description;
this.AddressesDefinition = addressDefinition;
}
/// <summary>
/// Initializes a new instance of the <see cref="CustomWebSite"/> class.
/// </summary>
/// <param name="id">The id</param>
/// <param name="description">The description of the web site (as it shows up in the IIS manager console).</param>
/// <param name="addressDefinition">The address definition.</param>
public CustomWebSite(Id id, string description, string addressDefinition)
{
this.Id = id;
this.Description = description;
this.AddressesDefinition = addressDefinition;
}
internal void ProcessAddressesDefinition()
{
if (!AddressesDefinition.IsEmpty())
{
List<WebAddress> addressesToAdd = new List<WebAddress>();
foreach (string addressDef in AddressesDefinition.Split(";".ToCharArray(), StringSplitOptions.RemoveEmptyEntries))
{
try
{
string[] tokens = addressDef.Split(":".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
string address = tokens[0];
string port = tokens[1];
if (tokens[1].ContainsWixConstants())
{
addressesToAdd.Add(new WebAddress { Address = address, AttributesDefinition = "Port=" + port });
}
else
{
addressesToAdd.Add(new WebAddress { Address = address, Port = Convert.ToInt32(port) });
}
}
catch (Exception e)
{
throw new Exception("Invalid AddressesDefinition", e);
}
}
this.addresses = addressesToAdd.ToArray();
}
}
/// <summary>
/// References a WebAppPool instance to use as the application pool for this application in IIS 6 applications.
/// </summary>
public string WebAppPool; //WebApplication element attribute
/// <summary>
/// Specification for auto-generating the <see cref="T:WebSite.WebAddresses"/> collection.
/// <para>If <see cref="AddressesDefinition"/> is specified, the existing content of <see cref="Addresses"/> will be ignored
/// and replaced with the auto-generated one at compile time.</para>
/// </summary>
/// <example>
/// <c>webSite.AddressesDefinition = "*:80;*90";</c> will be parsed and converted to an array of <see cref="T:WixSharp.WebSite.WebAddress"/> as follows:
/// <code>
/// ...
/// webSite.Addresses = new []
/// {
/// new WebSite.WebAddress
/// {
/// Address = "*",
/// Port = 80
/// },
/// new WebSite.WebAddress
/// {
/// Address = "*",
/// Port = 80
/// }
/// }
/// </code>
/// </example>
public string AddressesDefinition = "";
//// The iis:WebSite/@Directory attribute must be specified when the element has a Component as an ancestor..
//public string Directory = "";
/// <summary>
/// Reference to a WebApplication that is to be installed as part of this web site.
/// </summary>
public CustomWebApplication WebApplication = null;
/// <summary>
/// Collection of <see cref="T:WebSite.WebAddresses"/> associated with website.
/// <para>
/// The user specified values of <see cref="Addresses"/> will be ignored and replaced with the
/// auto-generated addresses if <see cref="AddressesDefinition"/> is specified either directly or via appropriate <see cref="WebSite"/> constructor.
/// </para>
/// </summary>
public WebAddress[] Addresses
{
get
{
ProcessAddressesDefinition();
return addresses;
}
set
{
addresses = value;
}
}
/// <summary>
/// This class defines WebAppPool WiX element. It is used to specify the application pool for this application in IIS 6 applications.
/// </summary>
public partial class CustomWebApplication : WixEntity
{
/// <summary>
/// References a WebAppPool instance to use as the application pool for this application in IIS 6 applications.
/// </summary>
public WebAppPool WebAppPool; //WebApplication element attribute
/// <summary>
/// Initializes a new instance of the <see cref="WebApplication"/> class.
/// </summary>
/// <param name="name">The name.</param>
/// <param name="attributesDefinition">The attributes definition. This parameter is used to set encapsulated <see cref="T:WixSharp.WixEntity.AttributesDefinition"/>.</param>
public CustomWebApplication(string name, string attributesDefinition)
{
base.Id = $"{name}_WebApplication";
base.Name = name;
base.AttributesDefinition = attributesDefinition;
}
/// <summary>
/// Initializes a new instance of the <see cref="WebAppPool"/> class.
/// </summary>
/// <param name="name">The name.</param>
public CustomWebApplication(string name)
{
base.Id = $"{name}_WebApplication";
base.Name = name;
}
/// <summary>
/// Initializes a new instance of the <see cref="WebAppPool"/> class.
/// </summary>
public CustomWebApplication()
{
}
}
WebAddress[] addresses = new WebAddress[0];
/// <summary>
/// Primary key used to identify this particular entry.
/// </summary>
[Xml]
public new string Id
{
get
{
return base.Id;
}
set
{
base.Id = value;
}
}
/// <summary>
/// The value to set into the environment variable. If this attribute is not set, the environment variable is removed
/// during installation if it exists on the machine.
/// </summary>
[Xml]
public string Description;
/// <summary>
/// Defines the installation <see cref="Condition"/>, which is to be checked during the installation to
/// determine if the registry value should be created on the target system.
/// </summary>
public Condition Condition;
/// <summary>
/// Adds itself as an XML content into the WiX source being generated from the <see cref="WixSharp.Project"/>.
/// See 'Wix#/samples/Extensions' sample for the details on how to implement this interface correctly.
/// </summary>
/// <param name="context">The context.</param>
public void Process(ProcessingContext context)
{
// IIS namespace
XNamespace ns = WixExtension.IIs.ToXNamespace();
XElement component = this.CreateAndInsertParentComponent(context);
component.Add(this.ToXElement(ns + "WebSite"));
XElement webSiteElement = component.FindAll("WebSite")[0];
if (webSiteElement.Parent.Name == "Component" && webSiteElement.Parent.Parent.Name == "Directory")
{
// Add attributes for WebSite element
webSiteElement.AddAttributes($"Directory={webSiteElement.Parent.Parent.Attribute("Id").Value}");
}
if (Addresses != null)
{
int index = 1;
// Generates the XML fragment for WebAddress element
foreach (WebAddress address in Addresses)
{
webSiteElement.AddElement(new XElement(ns + "WebAddress",
new XAttribute("Id", $"WebSite_Address{index}"),
new XAttribute("IP", "*"),
new XAttribute("Port", address.Port)));
index++;
}
}
if (WebApplication != null)
{
// Generates the XML fragment for WebApplication element
XElement webApplicationElement = new XElement(ns + "WebApplication",
new XAttribute("Id", WebApplication.Id),
new XAttribute("Name", this.WebApplication.Name));
webSiteElement.AddElement(webApplicationElement);
if (WebApplication.WebAppPool != null)
{
WebApplication.WebAppPool.Id = $"{WebApplication.WebAppPool.Name}_WebAppPool";
webApplicationElement.SetAttribute($"WebAppPool={WebApplication.WebAppPool.Id}");
// Generates the XML fragment for WebAppPool element
webSiteElement.Parent.AddElement(new XElement(ns + "WebAppPool",
new XAttribute("Id", WebApplication.WebAppPool.Id),
new XAttribute("Name", WebApplication.WebAppPool.Name),
new XAttribute("ManagedPipelineMode", "Integrated"),
new XAttribute("Identity", "applicationPoolIdentity")));
}
}
if (Condition != null)
{
component.AddElement(new XElement("Condition", Condition.ToXValue())
.AddAttributes(Condition.Attributes));
}
}
}
}
また、 WebSite 定義に従って WIX xml ファイルを生成し、WixSourceGenerated イベントをサブスクライブできるXMLInjection を使用したWixSharp IIS サンプル に示されているように XML インジェクションを使用することによって、問題を解決する別の方法もあります 。
project.WixSourceGenerated += Compiler_WixSourceGenerated;
WixSharp は Wix XML 定義ファイルを生成し、WixSourceGenerated イベントの後にこの XML ファイルを変更できることに注意してください。