In WIX am in-need of a dot net managed custom code to dynamically populate a combo box with the values of sql server instances in that network.
I tried to google but go
OP didn't ask, but in case someone is wondering, yes, You can do this in script, as well. This example populates a ListBox, not a ComboBox, but the idea is the same.
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 by 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.
var query = "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)";
var createCmd = Session.Database.OpenView(query);
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");
// do a WMI query to get the list of Websites on the server
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.
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;
}
There are a few supporting methods:
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.
};
// spool an informational message into the MSI log, if it is enabled.
function LogMessage(msg)
{
var record = Session.Installer.CreateRecord(0);
record.StringData(0) = "CustomActions: " + msg;
Session.Message(MsgKind.Log, record);
}
// 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) = "CustomActions: Exception: 0x" + decimalToHexString(exc.number) + " : " + exc.message;
Session.Message(MsgKind.Error + Icons.Critical + Buttons.btnOkOnly, record);
}
function decimalToHexString(number)
{
if (number < 0)
number = 0xFFFFFFFF + number + 1;
return number.toString(16).toUpperCase();
}
And yes, I disagree philosophically with Rob Mensching's viewpoint that Script Custom Actions suck
[CustomAction]
public static ActionResult FillServerInstances(Session xiSession)
{
xiSession.Log("Begin CustomAction");
xiSession.Log("Opening view");
View lView = xiSession.Database.OpenView("DELETE FROM ComboBox WHERE ComboBox.Property='DBSRVR'");
lView.Execute();
lView = xiSession.Database.OpenView("SELECT * FROM ComboBox");
lView.Execute();
int Index = 1;
bool flag = false;
try
{
foreach (DataRow dr in Microsoft.SqlServer.Management.Smo.SmoApplication.EnumAvailableSqlServers(false).Rows)
{
String InstanceName = dr["Name"].ToString();
if (InstanceName.Equals(xiSession["ComputerName"] + @"\" + xiSession["SQLINSTANCENAME"], StringComparison.InvariantCultureIgnoreCase))
{ flag = true; }
Record lRecord = xiSession.Database.CreateRecord(4);
xiSession.Log("Setting record details");
lRecord.SetString(1, "DBSRVR");
lRecord.SetInteger(2, Index);
lRecord.SetString(3, InstanceName);
lRecord.SetString(4, InstanceName);
xiSession.Log("Adding record");
lView.Modify(ViewModifyMode.InsertTemporary, lRecord);
++Index;
}
}
catch (Exception ex)
{
logException(xiSession, ex);
}
if (flag)
{
xiSession["DBSRVR"] = xiSession["ComputerName"].ToString() + @"\" + xiSession["SQLINSTANCENAME"].ToString();
}
lView.Close();
xiSession.Log("Closing view");
lView.Close();
return ActionResult.Success;
}
There's a C++ CA to do exactly this in http://msiext.codeplex.com.