如何使用WixSharp安装网站并关联AppPool

时间:2010-03-16 10:25:51

标签: msbuild wix wixsharp

我正在尝试查找如何使用WixSharp(WiX的托管代码接口)来安装网站并关联AppPool的示例。

我想要实现的步骤是:

  1. 如果网站存在于IIS 6中,请将其删除。
  2. 如果IIS 6中存在AppPool,请将其删除。
  3. 从目标目录中删除应用程序工件。
  4. 将新的应用程序工件复制到目标目录。
  5. 创建AppPool。
  6. 创建网站,将其链接到AppPool。
  7. 我在MSBuild中实现了这一点,但是它没有MSI那么有用。因此,我试图在WixSharp语法中“重写”上述内容。

    WixSharp显然支持WIXIISExtension,但Google尚未提供任何示例。

    我如何在WixSharp中编写上述代码?

2 个答案:

答案 0 :(得分:1)

我正在使用WIX用于同一目的。我尝试部署的应用程序大约300 MB,我需要为同一个应用程序池等创建虚拟目录。

我认为你的要求是一样的。

我建议WIX对此非常好。您可以拥有询问用户虚拟目录名称,应用程序池等的屏幕

WIX代码适用于IIS 5.1,6,7。对于7.5,您需要创建一个习惯。因此,如果安装了IIS 6兼容模式,即使对于IIS 7.5,也可以使用wix创建虚拟目录。

现在提升我使用WIX部署Web应用程序时没有遇到任何错误。

答案 1 :(得分:0)

好问题。

我正在为当前项目使用WixSharp,对此我感到非常满意。如何避免使用C#语法编写XML文件真是太棒了。顺便说一句,我认为您不是在重新发明轮子……而是通过WixSharp加快轮子的工作。

由于WixSharp 1.9.6版不提供使用关联的WebAppPool创建WebSite的功能,因此我通过创建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
                                    }
                                )
                            ),
...

这是我的CustomWebSite.cs文件,仅用于创建一个WebSite,我相信它会更好:

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 definitions之后生成WIX xml文件并按照WixSharp IIS Sample with XMLInjection中的指示使用XML注入,您还可以通过其他方式来解决问题,您可以在其中订阅WixSourceGenerated事件。

project.WixSourceGenerated += Compiler_WixSourceGenerated;

请记住,WixSharp会生成Wix XML定义文件,并且您可以在WixSourceGenerated事件之后修改此XML文件。