Custom Actions for IIS installation - ComboxBox Empty Issue
I'm following a number of different tutorials in an attempt to make a generic IIS WIX installer which I can then split up as 开发者_JAVA百科appropriate into different libraries.
Tutorials
creating a web application installer with wix 3.5 and visual studio 2010 (Paul Reynolds blog)
Creating WIX Installer for ASP.NET Web Application (Code Project)
Web Application Installer in WiX (John Robbins' blog)
So far, I've got an installation that seems to work when run with the /q flag. However in interactive mode, the custom action which is supposed to populate the combo box isn't working.
I've tried to debug but I've come into the following problems:
- Session.Log commands don't seem to output anywhere
- Attaching to msiexec and calling Debugger.Launch or MmsiBreak seem to just exit straight away (even with breakpoints enabled).
Starting to get frustrating as it shouldn't be this difficult to take values from a custom action and pass them into a combo box.
Anybody have any ideas what could be the root cause, I'm expecting a lot of people have either used this code or something similar to it:
using System;
using System.Diagnostics;
using System.DirectoryServices;
using System.Globalization;
using System.Linq;
using Microsoft.Deployment.WindowsInstaller;
using Microsoft.Web.Administration;
using Microsoft.Win32;
namespace MyNameSpace
{
/// <summary>
/// Static class for the IIS Installer Custom Action.
/// </summary>
public static class CustomActions
{
#region Private Constants
private const string IISEntry = "IIS://localhost/W3SVC";
private const string SessionEntry = "WEBSITE";
private const string ServerComment = "ServerComment";
private const string CustomActionException = "CustomActionException: ";
private const string IISRegKey = @"Software\Microsoft\InetStp";
private const string MajorVersion = "MajorVersion";
private const string IISWebServer = "iiswebserver";
private const string GetComboContent = "select * from ComboBox";
private const string AvailableSites = "select * from AvailableWebSites";
private const string SpecificSite = "select * from AvailableWebSites where WebSiteID=";
#endregion
#region Custom Action Methods
[CustomAction]
public static ActionResult GetWebSites(Session session)
{
ActionResult result = ActionResult.Failure;
try
{
if (session == null) { throw new ArgumentNullException("session"); }
View comboBoxView = session.Database.OpenView(GetComboContent);
View availableWSView = session.Database.OpenView(AvailableSites);
if (IsIIS7Upwards)
{
GetWebSitesViaWebAdministration(comboBoxView, availableWSView);
}
else
{
GetWebSitesViaMetabase(comboBoxView, availableWSView);
}
result = ActionResult.Success;
}
catch (Exception ex)
{
if (session != null)
{
session.Log(CustomActionException + ex);
}
}
return result;
}
[CustomAction]
public static ActionResult UpdatePropsWithSelectedWebSite(Session session)
{
Debugger.Break();
ActionResult result = ActionResult.Failure;
try
{
if (session == null) { throw new ArgumentNullException("session"); }
string selectedWebSiteId = session[SessionEntry];
session.Log("CA: Found web site id: " + selectedWebSiteId);
using (View availableWebSitesView = session.Database.OpenView(
SpecificSite + selectedWebSiteId))
{
availableWebSitesView.Execute();
using (Record record = availableWebSitesView.Fetch())
{
if ((record[1].ToString()) == selectedWebSiteId)
{
session["WEBSITE_ID"] = selectedWebSiteId;
session["WEBSITE_DESCRIPTION"] = (string)record[2];
session["WEBSITE_PATH"] = (string)record[3];
}
}
}
result = ActionResult.Success;
}
catch (Exception ex)
{
if (session != null)
{
session.Log(CustomActionException + ex);
}
}
return result;
}
#endregion
#region Private Helper Methods
private static void GetWebSitesViaWebAdministration(View comboView,
View availableView)
{
using (ServerManager iisManager = new ServerManager())
{
int order = 1;
foreach (Site webSite in iisManager.Sites)
{
string id = webSite.Id.ToString(CultureInfo.InvariantCulture);
string name = webSite.Name;
string path = webSite.PhysicalPath();
StoreSiteDataInComboBoxTable(id, name, path, order++, comboView);
StoreSiteDataInAvailableSitesTable(id, name, path, availableView);
}
}
}
private static void GetWebSitesViaMetabase(View comboView, View availableView)
{
using (DirectoryEntry iisRoot = new DirectoryEntry(IISEntry))
{
int order = 1;
foreach (DirectoryEntry webSite in iisRoot.Children)
{
if (webSite.SchemaClassName.ToLower(CultureInfo.InvariantCulture)
== IISWebServer)
{
string id = webSite.Name;
string name = webSite.Properties[ServerComment].Value.ToString();
string path = webSite.PhysicalPath();
StoreSiteDataInComboBoxTable(id, name, path, order++, comboView);
StoreSiteDataInAvailableSitesTable(id, name, path, availableView);
}
}
}
}
private static void StoreSiteDataInComboBoxTable(string id, string name,
string physicalPath, int order, View comboView)
{
Record newComboRecord = new Record(5);
newComboRecord[1] = SessionEntry;
newComboRecord[2] = order;
newComboRecord[3] = id;
newComboRecord[4] = name;
newComboRecord[5] = physicalPath;
comboView.Modify(ViewModifyMode.InsertTemporary, newComboRecord);
}
private static void StoreSiteDataInAvailableSitesTable(string id, string name,
string physicalPath, View availableView)
{
Record newWebSiteRecord = new Record(3);
newWebSiteRecord[1] = id;
newWebSiteRecord[2] = name;
newWebSiteRecord[3] = physicalPath;
availableView.Modify(ViewModifyMode.InsertTemporary, newWebSiteRecord);
}
// determines if IIS7 upwards is installed so we know whether to use metabase
private static bool IsIIS7Upwards
{
get
{
bool isV7Plus;
using (RegistryKey iisKey = Registry.LocalMachine.OpenSubKey(IISRegKey))
{
isV7Plus = (int)iisKey.GetValue(MajorVersion) >= 7;
}
return isV7Plus;
}
}
#endregion
}
public static class ExtensionMethods
{
private const string IISEntry = "IIS://localhost/W3SVC/";
private const string Root = "/root";
private const string Path = "Path";
public static string PhysicalPath(this Site site)
{
if (site == null) { throw new ArgumentNullException("site"); }
var root = site.Applications.Where(a => a.Path == "/").Single();
var vRoot = root.VirtualDirectories.Where(v => v.Path == "/")
.Single();
// Can get environment variables, so need to expand them
return Environment.ExpandEnvironmentVariables(vRoot.PhysicalPath);
}
public static string PhysicalPath(this DirectoryEntry site)
{
if (site == null) { throw new ArgumentNullException("site"); }
string path;
using (DirectoryEntry de = new DirectoryEntry(IISEntry
+ site.Name + Root))
{
path = de.Properties[Path].Value.ToString();
}
return path;
}
}
}
UI
<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
<CustomAction Id="UpdatePropsWithSelectedWebSite" BinaryKey="IISWebSiteCA"
DllEntry="UpdatePropsWithSelectedWebSite" Execute="immediate"
Return="check" />
<Binary Id="IISWebSiteCA" SourceFile="$(var.MyCA.TargetDir)MyCA.CA.dll" />
</Fragment>
<Fragment>
<UI>
<Dialog Id="InstallationAddress" Width="370" Height="270" Title="Experian">
<Control Id="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17"
Default="yes" Text="!(loc.WixUINext)">
<Condition Action="disable">WEBSITE = "" OR VD = ""</Condition>
<Condition Action="enable"><![CDATA[WEBSITE <> "" AND VD <> ""]]></Condition>
</Control>
<Control Id="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17"
Text="!(loc.WixUIBack)" />
<Control Id="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17"
Cancel="yes" Text="!(loc.WixUICancel)">
<Publish Event="SpawnDialog" Value="CancelDlg">1</Publish>
</Control>
<Control Id="Title" Type="Text" X="15" Y="6" Width="219" Height="15" Transparent="yes"
NoPrefix="yes" Text="!(loc.SelectInstallAddress)" />
<Control Id="Description" Type="Text" X="25" Y="23" Width="340" Height="15" Transparent="yes" NoPrefix="yes" Text="!(loc.SelectInstallationDescription)" />
<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="105" Width="290" Height="10"
NoPrefix="yes" Text="!(loc.Site)" />
<Control Id="SelectWebSiteCombo" Type="ComboBox" X="20" Y="117" Width="250" Height="16"
Property="WEBSITE" Sorted="yes" ComboList="yes" />
<Control Type="Text" Id="VirtualDirectoryLabel" Width="290" Height="10" X="20" Y="140"
Text="!(loc.VirtualDirectory)" />
<Control Type="Edit" Id="VirtualDirectoryTextbox" Width="250" Height="18" X="20" Y="152"
Property="VD" />
<Control Type="Text" Id="InfoText1" Width="350" Height="17" X="10" Y="55"
Text="!(loc.Info1)" />
<Control Type="Text" Id="InfoText2" Width="350" Height="17" X="10" Y="75"
Text="!(loc.Info2)" />
</Dialog>
</UI>
</Fragment>
</Wix>
Two quick things:
- I don't see where you're scheduling your action here (just its definition and Binary entry), so I'm not convinced it's being run; the entries in a verbose log could confirm or refute that. Even if it is running...
- Session.Log does not work on an action launched from a dialog (Windows Installer limitations), so even if this is running it won't say more than that the action was launched. One common workaround is to ensure you're logging property changes, and change a property:
Session["LOGHACK"] = message;
精彩评论