2009-08-28 93 views
12

我想要做的是向安裝程序用戶顯示其服務器上的網站列表,並允許他們選擇一個(使用此處描述的方法:http://www.cmcrossroads.com/content/view/13160/120/,現在看起來損壞,請參閱核心代碼here)。然後安裝程序會在所選網站中創建一個虛擬目錄。在WiX中,我怎樣才能按名稱選擇IIS網站?

但是,我的搜索似乎已經顯示,在WiX中指定網站的唯一方法是通過IP,端口和標頭。要求這些不是非常用戶友好的,所以我留下了編寫第二個自定義操作以從網站名稱獲取這些詳細信息的想法。

有沒有更好的方法?

順便說一句如果影響答案,這需要在IIS6和IIS7中工作。

回答

9

好吧有可能(在Metabase兼容性的IIS6或IIS7中),感謝this發佈到郵件列表,解釋了iis:網站元素工作的稍微離奇的方式。該有用的部分是:

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元素不應該被使用,但需要對項目進行編譯。

0

雖然這個問題和答案仍然有效,但我認爲值得問問自己是否真的想使用網站名稱。我希望將其存儲在卸載過程中使用,然後保存站點ID可能是一個更好的主意。在這種情況下,網站元素變爲:

<iis:WebSite Id="IisWebsite" Description="Dummy" SiteId="[IIS_WEBSITEID]"> 
    <iis:WebAddress Id="IisWebAddress" Port="80" /> 
</iis:WebSite> 
3

在我的安裝程序中,我不想創建網站。我想讓用戶選擇一個現有的網站。我在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> 

UI面板呈現列表框,其自動與從列表框表元素與網站的第一個字段中填充。該表在運行時由Javascript中的自定義操作填充。

要在正確的時間調用自定義動作,您需要在main中使用類似這樣的東西。WXS文件:

<InstallUISequence> 
    <Custom Action="EnumerateWebSites" After="CostFinalize" Overridable="yes">NOT Installed</Custom> 
</InstallUISequence> 
+0

任何想法爲什麼上面的腳本會拋出「IisEnumSites:異常:0x80004005:修改,模式,記錄」錯誤消息? – Aaron 2011-02-09 14:21:07

+0

啊,不,我不知道。 – Cheeso 2011-02-10 16:05:57

0

。對於IisEnumSites:Exception: 0x80004005 : Modify, Mode, Record

我也有類似的經驗,我發現迄今的答覆是,從parseInt提取站點ID:

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

我有一個網站用名稱如W3SVC/1528550093,我懷疑1528550093對於AvailableWebSites表太大。

一旦我有if語句篩選出這些大數字,並且腳本工作正常。

希望這有助於爲其他。