代码之家  ›  专栏  ›  技术社区  ›  Dan

在wix中,如何按名称选择IIS网站?

  •  12
  • Dan  · 技术社区  · 16 年前

    我要做的是向安装程序用户显示他们服务器上的网站列表,并允许他们选择其中一个(使用下面描述的方法: http://www.cmcrossroads.com/content/view/13160/120/ 现在好像坏了,看 here 对于核心代码)。然后安装程序将在所选网站中创建一个虚拟目录。

    然而,我的搜索似乎揭示了在wix中指定网站的唯一方法是通过IP、端口和头。要求这些不是很好的用户友好,所以我留下了写第二个自定义操作的想法,从一个网站名称获得这些细节。

    有更好的方法吗?

    顺便说一句,这需要在IIS6和IIS7中同时工作,以防影响答案。

    4 回复  |  直到 12 年前
        1
  •  9
  •   Dan    16 年前

    好的,这是可能的(在iis6或iis7中,与元数据库兼容),因为 this 发布到邮件列表,解释一下IIS:Website元素的工作方式有点奇怪。有用的部分是:

    Using a fragment like this and test with v3.0.5120.0:*
    
      <iis:WebSite Id="WebSite" Description="Default Web Site" SiteId="*">
        <iis:WebAddress Id="TestWebSite" Port="1" />
      </iis:WebSite>
    
    The following work:
    
    1. If WebSite/@SiteId="*" then a case sensitive match on WebSite/@Description happens.
    2. If WebSite/@SiteId matches the site id than WebSite/@Description is ignored and a match on site id happens.
    3. If WebSite/@SiteId has any value WebAddress/@Port is ignored (although the syntax requires it and it can't be 0).
    4. If WebSite/@SiteId is missing WebAddress/@Port is used and WebSite/@Description is ignored.
    5. Once a website is created and gets site id, you can rename it (therefore its site id is not the hash of its name), the WebSite/@SiteId="*" syntax will match on the WebSite/@Description.
    

    所以我的wix代码最终看起来像:

    <DirectoryRef Id="TARGETDIR">
      <Component Id="IisSetup" Guid="YOUR-GUID-HERE">
        <iis:WebVirtualDir Id="IisVirtualDir" Alias="[IIS_VIRTUALDIRNAME]" Directory="INSTALLLOCATION" WebSite="IisWebsite">
          <iis:WebApplication Id="IisWebApplication" Name="[IIS_VIRTUALDIRNAME]" WebAppPool="IisAppPool" Isolation="high"/>
        </iis:WebVirtualDir>
        <iis:WebAppPool Id="IisAppPool" Name="[IIS_APPPOOLNAME]" Identity="networkService"/>
      </Component>
    </DirectoryRef>
    
    <!-- Note that this entry should not be put under a component. If it is WiX
         will update the website on install and remove it on uninstall -->
    <iis:WebSite Id="IisWebsite" Description="[IIS_WEBSITENAME]" SiteId="*">
      <iis:WebAddress Id="IisWebAddress" Port="80" />
    </iis:WebSite>
    

    不应使用IIS:WebAddress元素,但它是项目编译所必需的。

        2
  •  3
  •   Cheeso    15 年前

    在我的安装程序中,我不想创建一个网站。我想允许用户选择 现有的 网站。我用JavaScript中的一个自定义操作和一个自定义UI面板来完成这项工作。


    自定义操作代码:

    //
    // CustomActions.js 
    // 
    // Custom Actions usable within WIX For IIS installations.
    // 
    // EnumerateWebSites_CA(): 
    //   Adds new UI to the MSI at runtime to allow the user to select a
    //   website, to which an ISAPI filter will be added.
    // 
    // UpdatePropsWithSelectedWebSite_CA():
    //   fills session with properties for the selected website. 
    //
    // SetAuthProps_CA():
    //   sets properties for the needed user and group that needs authorization to the created dir. 
    //
    // 
    // original idea from: 
    // http://blog.torresdal.net/2008/10/24/WiXAndDTFUsingACustomActionToListAvailableWebSitesOnIIS.aspx
    // 
    // Mon, 23 Nov 2009  10:54
    //
    // 
    // ===================================================================
    
    // http://msdn.microsoft.com/en-us/library/aa372516(VS.85).aspx
    
    var MsiViewModify = 
        {
            Refresh          : 0,
            Insert           : 1,
            Update           : 2,
            Assign           : 3,
            Replace          : 4,
            Merge            : 5,
            Delete           : 6,
            InsertTemporary  : 7,   // cannot permanently modify the MSI during install
            Validate         : 8,
            ValidateNew      : 9,
            ValidateField    : 10,
            ValidateDelete   : 11
        };
    
    
    // http://msdn.microsoft.com/en-us/library/sfw6660x(VS.85).aspx
    var Buttons = 
        {
            OkOnly           : 0,
            OkCancel         : 1,
            AbortRetryIgnore : 2,
            YesNoCancel      : 3
        };
    
    var Icons= 
        {
            Critical         : 16,
            Question         : 32,
            Exclamation      : 48,
            Information      : 64
        }
    
    var MsgKind =
        {
            Error            : 0x01000000,
            Warning          : 0x02000000,
            User             : 0x03000000,
            Log              : 0x04000000
        };
    
    // http://msdn.microsoft.com/en-us/library/aa371254(VS.85).aspx
    var MsiActionStatus = 
        {
            None             : 0,
            Ok               : 1, // success
            Cancel           : 2,
            Abort            : 3,
            Retry            : 4, // aka suspend?
            Ignore           : 5  // skip remaining actions; this is not an error.
        };
    
    //*****************************************************************************
    // Purpose: Custom action that enumerates the local websites, and stores their
    // properties in the ListBox and AvailableWebSites tables.
    // Effects: Fills the ListBox table and creates and fills the AvailableWebSites
    // tables.
    // Returns: MsiActionStatus.Ok  if the custom action executes without error.
    //          MsiActionStatus.Abort if error. 
    //*****************************************************************************
    function EnumerateWebSites_CA()
    {
        try 
        {
            LogMessage("function EnumerateWebSites_CA() ENTER");
    
            var c = 1;
            var serverBindings, aBindings;
    
            var listboxesView = Session.Database.OpenView("SELECT * FROM ListBox");
            listboxesView.Execute();
    
            var record = Session.Installer.CreateRecord(4);
            record.StringData(1) = "WEBSITE";     // Property
            record.IntegerData(2) = c++;          // display order
            record.StringData(3) = "Server";      // returned bby the selection
            record.StringData(4) = "Server-wide"; // displayed in the UI
            listboxesView.Modify(MsiViewModify.InsertTemporary, record);
    
            // Create this table dynamically.  We could also create this
            // custom table in the WiX .wxs file , but that's not necessary.
            //  old quote:  `````` 
            //  my quote:  '''''
    
    //        var createCmd = Session.Database.OpenView("CREATE TABLE 'AvailableWebSites' ('WebSiteNo' INT NOT NULL, 'WebSiteDescription' CHAR(50), 'WebSitePort' CHAR(50) NOT NULL, 'WebSiteIP' CHAR(50), 'WebSiteHeader' CHAR(50) PRIMARY KEY 'WebSiteNo')")
    
            var createCmd = Session.Database.OpenView("CREATE TABLE AvailableWebSites (Num INT NOT NULL, Name CHAR(64), Desc CHAR(64), Port CHAR(16) NOT NULL, IP CHAR(32), Hostname CHAR(80) PRIMARY KEY Num)")
            createCmd.Execute();
            createCmd.Close();
    
            LogMessage("Table 'AvailableWebSites' has been created");
    
            var websitesView = Session.Database.OpenView("SELECT * FROM AvailableWebSites");
            websitesView.Execute();
    
            LogMessage("Query from Table 'AvailableWebSites' has returned");
    
            var iis = GetObject("winmgmts://localhost/root/MicrosoftIISv2"); 
    
            // See the metabase hierarchy diagram here:
            //   http://msdn.microsoft.com/en-us/library/ms524661.aspx
    
            // http://msdn.microsoft.com/en-us/library/ms525545.aspx
            // list "virtual servers", which is the same as websites. 
            var query  = "SELECT * FROM IIsWebServerSetting" 
    
            // get the list of virtual servers
            var results = iis.ExecQuery(query);
    
            LogMessage("WMI Query completed.");
    
            LogMessage("WMI Query results : " + typeof results);
    
            for(var e = new Enumerator(results); !e.atEnd(); e.moveNext()) 
            { 
                var site = e.item();
                // site.Name                   // W3SVC/1, W3SVC/12378398, etc
                // site.Name.substr(6)         // 1, 12378398, etc
                // site.ServerComment)         // "Default Web Site", "Site2", etc
                // site.ServerBindings(0).Port // 80, 8080, etc
    
                LogMessage("Web site " + site.Name);
    
                LogMessage("listbox record");
                record = Session.Installer.CreateRecord(4);
                record.StringData(1) = "WEBSITE";
                record.IntegerData(2) = c++;
                record.StringData(3) = site.Name.substr(6); // site.Name;
                record.StringData(4) = site.ServerComment + " (" + site.Name + ")";
                listboxesView.Modify(MsiViewModify.InsertTemporary, record);
    
                LogMessage("websites record");
                LogMessage("website(" + site.Name + ") name(" + site.ServerComment + ") port(" + site.ServerBindings(0).Port + ")"); 
                record = Session.Installer.CreateRecord(6);
                record.IntegerData(1) = parseInt(site.Name.substr(6));  // WebSiteNo
                record.StringData(2) = site.Name;                       // name, like W3SVC/1
                record.StringData(3) = site.ServerComment;              // WebSiteDescription
                record.StringData(4) = site.ServerBindings(0).Port;     // WebSitePort
                record.StringData(5) = site.ServerBindings(0).Ip;       // WebSiteIP; maybe empty
                record.StringData(6) = site.ServerBindings(0).Hostname; // WebSiteHeader; maybe empty
                websitesView.Modify(MsiViewModify.InsertTemporary, record);
            }
            listboxesView.Close();
            websitesView.Close();
    
            LogMessage("function EnumerateWebSites_CA() EXIT");
        }
    
        catch (exc1)
        {
            Session.Property("CA_EXCEPTION") = exc1.message ;
            LogException(exc1);
            return MsiActionStatus.Abort;
        }
        return MsiActionStatus.Ok;
    }
    
    
    
    //*****************************************************************************
    // Purpose: Custom action that copies the selected website's properties from the
    // AvailableWebSites table to properties.
    // Effects: Fills the WEBSITE_DESCRIPTION, WEBSITE_PORT, WEBSITE_IP, WEBSITE_HEADER
    // properties.
    // Returns: MsiActionStatus.Ok  if the custom action executes without error.
    //          MsiActionStatus.Abort if error. 
    //*****************************************************************************
    function UpdatePropsWithSelectedWebSite_CA()
    {
        try 
        {
            LogMessage("function UpdatePropsWithSelectedWebSite_CA() ENTER");
            var selectedWebSiteId = Session.Property("WEBSITE");
    
            LogMessage("selectedWebSiteId(" + selectedWebSiteId + ") type(" + typeof selectedWebSiteId + ")");
    
            // check if the user selected anything
            if (selectedWebSiteId == "")
            {
                LogMessage("function UpdatePropsWithSelectedWebSite_CA() EXIT (None)");
                return MsiActionStatus.None;
            }
    
            if (selectedWebSiteId.toUpperCase() == "SERVER")
            {
                Session.Property("WEBSITE_NAME")        = "W3SVC";
                Session.Property("WEBSITE_DESCRIPTION") = "Server";
                Session.Property("WEBSITE_PORT")        = "";
                Session.Property("WEBSITE_IP")          = "";
                Session.Property("WEBSITE_HEADER")      = "";
                LogMessage("function UpdatePropsWithSelectedWebSite_CA() EXIT (Ok)");
                return MsiActionStatus.Ok;
            }
    
            var websitesView = Session.Database.OpenView("SELECT * FROM `AvailableWebSites` WHERE `Num`=" + selectedWebSiteId);
            websitesView.Execute();
            var record = websitesView.Fetch();
    
            LogMessage("website Fetch() complete");
    
            if (record.IntegerData(1) == parseInt(selectedWebSiteId))
            {
                Session.Property("WEBSITE_NAME")        = record.StringData(2);
                Session.Property("WEBSITE_DESCRIPTION") = record.StringData(3);
                Session.Property("WEBSITE_PORT")        = record.StringData(4);
                Session.Property("WEBSITE_IP")          = record.StringData(5);
                Session.Property("WEBSITE_HOSTNAME")    = record.StringData(6);
            }
            websitesView.Close();
    
            LogMessage("function UpdatePropsWithSelectedWebSite_CA() EXIT (Ok)");
        }
    
        catch (exc1)
        {
            Session.Property("CA_EXCEPTION") = exc1.message ;
            LogException(exc1);
            return MsiActionStatus.Abort;
        }
        return MsiActionStatus.Ok;
    }
    
    
    // Pop a message box.  also spool a message into the MSI log, if it is enabled. 
    function LogException(exc)
    {
        var record = Session.Installer.CreateRecord(0);
        record.StringData(0) = "IisEnumSites: Exception: 0x" + decimalToHexString(exc.number) + " : " + exc.message;
        Session.Message(MsgKind.Error + Icons.Critical + Buttons.btnOkOnly, record);
    }
    
    
    // spool an informational message into the MSI log, if it is enabled. 
    function LogMessage(msg)
    {
        var record = Session.Installer.CreateRecord(0);
        record.StringData(0) = "IisEnumSites: " + msg;
        Session.Message(MsgKind.Log, record);
    }
    
    
    
    function decimalToHexString(number)
    {
        if (number < 0)
        {
            number = 0xFFFFFFFF + number + 1;
        }    
        return number.toString(16).toUpperCase();
    }
    
    
    // Testing only
    function Test1_CA()
    {
        var record = Session.Installer.CreateRecord(0);
        record.StringData(0) = "Hello, this is an error message";
        Session.Message(msgKindUser + iconInformation + btnOk, record);
        return MsiActionStatus.Ok;
    }
    

    注册如下自定义操作:

    <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    
      <Fragment>
        <Binary Id="IisScript_CA" SourceFile="CustomActions.js" />
    
        <CustomAction Id="EnumerateWebSites"
                      BinaryKey="IisScript_CA"
                      JScriptCall="EnumerateWebSites_CA"
                      Execute="immediate"
                      Return="check" />
    
        <CustomAction Id="UpdatePropsWithSelectedWebSite"
                      BinaryKey="IisScript_CA"
                      JScriptCall="UpdatePropsWithSelectedWebSite_CA"
                      Execute="immediate"
                      Return="check" />
    
      </Fragment>
    
    </Wix>
    

    这是用于UI面板的.wxs:

    <Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
    
      <Fragment>
        <UI>
          <Dialog Id="SelectWebSiteDlg" Width="370" Height="270" Title="Select a Web Site">
            <Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Default="yes" Text="Next" />
            <Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Text="Back" />
            <Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Cancel="yes" Text="Cancel">
              <Publish Event="SpawnDialog" Value="CancelDlg">1</Publish>
            </Control>
            <Control Id="Description" Type="Text" X="25" Y="23" Width="280" Height="15" Transparent="yes" NoPrefix="yes" Text="Please select which web site you want to install to." />
            <Control Id="Title" Type="Text" X="15" Y="6" Width="200" Height="15" Transparent="yes" NoPrefix="yes" Text="Select a Web Site" />
            <Control Id="BannerBitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="44" TabSkip="no" Text="!(loc.InstallDirDlgBannerBitmap)" />
            <Control Id="BannerLine" Type="Line" X="0" Y="44" Width="370" Height="0" />
            <Control Id="BottomLine" Type="Line" X="0" Y="234" Width="370" Height="0" />
            <Control Id="SelectWebSiteLabel" Type="Text" X="20" Y="60" Width="290" Height="14" NoPrefix="yes" Text="Select the web site for the filter:" />
            <Control Id="SelectWebSiteCombo" Type="ListBox" X="20" Y="75" Width="200" Height="150" Property="WEBSITE" Sorted="yes" />
          </Dialog>
        </UI>
      </Fragment>
    </Wix>
    

    用户界面面板显示一个列表框,该列表框由列表框表中的元素和网站的第一个字段自动填充。此表在运行时由javascript中的自定义操作填充。

    要在正确的时间调用自定义操作,在主.wxs文件中需要类似这样的内容:

    <InstallUISequence>
      <Custom Action="EnumerateWebSites" After="CostFinalize" Overridable="yes">NOT Installed</Custom>
    </InstallUISequence>
    
        3
  •  0
  •   Dan    16 年前

    虽然这个问题和答案仍然有效,但我认为有必要问问你自己是否真的想使用这个网站名。我想存储它以便在卸载时使用,然后保存站点ID可能是一个更好的主意。在这种情况下,website元素变成:

    <iis:WebSite Id="IisWebsite" Description="Dummy" SiteId="[IIS_WEBSITEID]">
      <iis:WebAddress Id="IisWebAddress" Port="80" />
    </iis:WebSite>
    
        4
  •  0
  •   Yan Sklyarenko    12 年前

    答复 IisEnumSites:Exception: 0x80004005 : Modify, Mode, Record

    我有类似的经验,到目前为止我发现的是从 parseInt :

    record = Session.Installer.CreateRecord(6);
    record.IntegerData(1) = parseInt(site.Name.substr(6));  // WebSiteNo
    

    我有一个叫 W3SVC/1528550093 我怀疑 1528550093 太大了 AvailableWebSites 表。

    一旦我有了if语句来过滤掉这些大数字,脚本就可以正常工作了。

    希望这对其他人有帮助。