| John's profileJohn BarshingerPhotosBlogLists | Help |
|
October 27 How to modify the VS2005 Installer to set the ASP.NET version and create Application PoolsIn my previous blog entry, I complained about no satisfactory installer out there I could buy that meets my requirements for installing an ASP.NET application. In this entry, I will discuss the solution I came up with to use the built-in VS2005 installer to automatically set the ASP.NET version for the application being installed and to create an Application Pool to use for that application if being installed on Windows 2003/IIS6.
Basically, what you need to do is create a custom installer class and call this class from the "install" action of your Custom Actions view on your Web Application installer project. You also need to pass some parameters from the installer to your custom installer class. All this is well documented in MSDN or other web sites you can google so I won't waste time with that here. Instead, I'll just show the code to do all the work. Pieces of this are available by searching the internet as well but this shows the whole thing together from a code perspective.
You can pass data to your customer installer class via the "CustomActionData" attribute in the properties for the custom action. In our case, we set this attribute as follows:
/iisv="[IISVERSION]" /tv="[TARGETVDIR]" /ts="[TARGETSITE]"
Here's the code (updated 11/13/2006):
using System;
using System.Collections; using System.ComponentModel; using System.Configuration.Install; using System.Diagnostics; using System.DirectoryServices; using System.IO; namespace InstallerCustomAction
{ [RunInstaller(true)] public partial class SetASPNetVersionAndCreateAppPool : Installer { public const bool debug = false; public const string MetaBasePathToAppPools = "IIS://localhost/W3SVC/AppPools"; public const string IIS6 = "#6"; public const string TargetASPNETVersion = "v2.0.50727"; public const string TargetSiteTag = "ts"; public const string TargetVDirTag = "tv"; public const string IISVersionTag = "iisv"; public const string SSSolutionsAppPoolName = "SS Solutions " + TargetASPNETVersion; protected StreamWriter logFile = null;
public SetASPNetVersionAndCreateAppPool()
{ InitializeComponent(); } public override void Install(IDictionary stateSaver) { base.Install(stateSaver); if (debug)
{ logFile = File.CreateText(@"C:\SANVACAP.txt"); WriteToLogFile("IISVersion: " + Context.Parameters[IISVersionTag]); WriteToLogFile("TargetVDir: " + Context.Parameters[TargetVDirTag]); WriteToLogFile("TargetSite: " + Context.Parameters[TargetSiteTag]); } // the following parameters must be passed in via the CustomActionData property of the CustomAction for the Install Method
// /iisv=[IISVERSION] /tv=[TARGETVDIR] /ts=[TARGETSITE] if ((!string.IsNullOrEmpty(Context.Parameters[TargetSiteTag])) && (!string.IsNullOrEmpty(Context.Parameters[TargetVDirTag]))) { string targetVDir = Context.Parameters[TargetVDirTag]; string targetSite = Context.Parameters[TargetSiteTag]; if (targetSite.StartsWith("/LM/")) { targetSite = targetSite.Substring(4); } // if we're installing on IIS6, create an Application Pool and put the assign the vDir to that pool
string IISVersion = Context.Parameters[IISVersionTag]; if ((!string.IsNullOrEmpty(IISVersion)) && (IISVersion.Equals(IIS6))) { CreateWebApplicationAndAppPool(targetSite, targetVDir, SSSolutionsAppPoolName); } // assign the asp net version to the vDir SetWebApplicationASPNetVersion(targetSite, targetVDir, TargetASPNETVersion); } if (debug) logFile.Close();
} #region Utility Methods
/// <summary> /// CreateWebApplicationAndAppPool("W3SVC/2", "UniversitySite", "MyAppPool"); /// </summary> /// <param name="targetSite">metabase path to the website to use</param> /// <param name="targetVDir">virtual directory version</param> /// <param name="appPoolName">appPoolName is of the form "<name>", for example, "MyAppPool"</param> protected void CreateWebApplicationAndAppPool(string targetSite, string targetVDir, string appPoolName) { string metabasePath = String.Format("IIS://localhost/{0}/Root/{1}", targetSite, targetVDir); WriteToLogFile(String.Format("Assigning application {0} to the application pool named {1}:", metabasePath, appPoolName));
try
{ DirectoryEntry vDir = new DirectoryEntry(metabasePath); string className = vDir.SchemaClassName.ToString(); if ((className.EndsWith("VirtualDir")) || (className.EndsWith("WebDirectory"))) { // put the vdir in the desired app pool, creating the app pool if necessary object[] param = { 2, appPoolName, true }; vDir.Invoke("AppCreate3", param); // assign the application friendly name vDir.Properties["AppFriendlyName"][0] = targetVDir; // save the changes
vDir.CommitChanges(); WriteToLogFile(" Done: classname=" + className); } else { WriteToLogFile(" Failed in AssignVDirToAppPool; only virtual directories can be assigned to application pools: classname=" + className); } } catch (Exception ex) { WriteToLogFile(String.Format("Failed in AssignVDirToAppPool with the following exception: {0}", ex.Message)); } } /// <summary> /// SetWebApplicationASPNetVersion("W3SVC/2", "UniversitySite", "v2.0.50727"); /// </summary> /// <param name="targetSite">metabase path to the website to use</param> /// <param name="targetVDir">virtual directory version</param> /// <param name="ASPNetVersion">asp net version number as specified in the path to the directory in the file system</param> protected void SetWebApplicationASPNetVersion(string targetSite, string targetVDir, string ASPNetVersion) { WriteToLogFile(String.Format("Assigning ASPNetVersion {0} to the vDir named {1} on WebSite {2}:", ASPNetVersion, targetVDir, targetSite)); try
{ ProcessStartInfo processStartInfo = new ProcessStartInfo(); processStartInfo.FileName = Path.Combine(Environment.GetEnvironmentVariable("SystemRoot"),
@"Microsoft.NET\Framework\" + ASPNetVersion + @"\aspnet_regiis.exe"); processStartInfo.Arguments = string.Format("-s {0}/Root/{1}", targetSite, targetVDir);
processStartInfo.CreateNoWindow = true;
processStartInfo.UseShellExecute = false; Process.Start(processStartInfo);
WriteToLogFile(" Done executing: " + processStartInfo.FileName + " " + processStartInfo.Arguments);
} catch (Exception ex) { WriteToLogFile(String.Format("Failed in AssignASPNetVersionToVDir with the following exception: {0}", ex.Message)); } } protected void WriteToLogFile(string Message) { if (debug) logFile.WriteLine(Message); } #endregion
} } Installers for ASP.NET applicationsI spent a few days evaluating installers for my ASP.NET applications and was surprised to find that there are no acceptable installers out there (yet).
I looked at InstallShield (bought by Macrovision), Wise, and InstallAware. I will keep my opinions to myself about the good and bad of each since they are just opinions but I will point out the significant features that are missing from each for an ASP.NET installation.
These are the basic requirements for any ASP.NET install in today's world (in addition to the standard requirements)
Here are the results:
As you can see, nobody does it all yet out of the box.
EBA ComboBox and GridView DataKeyNames conflictIf i have a page that has a gridview on it and that gridview has DataKeyNames set to something then eba ComboBox stops getting the GetPage event. If i take DataKeyNames off then it starts working again.
this problem will go away if you turn off ViewStateEncryption for the page, for example:
<%@ Page Language="C#" AutoEventWireup="true" Codebehind=" UserCommentGVTest.aspx.cs" Inherits="SupportSite.UserCommentGVTest" ViewStateEncryptionMode="Never" %>
I'm not recommending you do this, just that this will fix the problem. I don't know exactly what is going on. But my guess is that since the GridView is creating an EncryptedViewState, it is somehow conflicting with the data the combo box needs to get from the regular ViewState.
eba should fix this problem because it is not secure to turn encryption off. I have posted this solution to the eba web site as well: http://forums.ebusiness-apps.com/viewtopic.php?p=4203 ImageButton uses inline style (SOMETIMES)An interesting discrepancy I've found in ASP.NET 2.0.50727 -- Some things get rendered differently depending on the Server OS. For example, we have ImageButtons with CssStyles assigned to them. On mouseover, we change the className and on mouseout, we change it back. Nothing too fancy going on there.
If the application is running on Windows 2003 or Windows XP (client machine OS is irrelevant), the ImageButton will be rendered with an inline style specified such as:
<input type="image" name="btnDelete" id="btnDelete" class="Vertical-Toolbar-Button" onmouseover="this.className='Vertical-Toolbar-Button-Hover'" onmouseout="this.className='Vertical-Toolbar-Button'" src="../../Images/iconExperience/garbage_empty48blue.gif" alt="Delete this Training Event" style="border-width:0px;" />
Now isn't that nice, hmm - inline styles override any regular style settings. In my css styles, I am turning the the border on/off to show a square around the images on mouseover. The inline style totally hoses this up since it has priority.
Interestingly on Vista, this has been fixed - see that same rendering below:
<input type="image" name="btnDelete" id="btnDelete" class="Vertical-Toolbar-Button" onmouseover="this.className='Vertical-Toolbar-Button-Hover'" onmouseout="this.className='Vertical-Toolbar-Button'" src="../../Images/iconExperience/garbage_empty48blue.gif" alt="Delete this Training Event" border="0" />
First, why does ASP.NET render something differently when rendering on different OS??? THAT SUX! This is why Java failed, you could never depend on what was going on if the gui was running on a different OS.
Second, obviously someone at Microsoft knows about this bug and has fixed it in some code branch at Microsoft. So maybe it will be fixed in the next ASP.NET patch. And yes, the ASP.NET version running on my Vista machine is also 2.0.50727.
Now, how to fix this -- I tried a bunch of things and stumbled upon something that works. I don't know why it works and I'm sure this is not the ideal solution...
{
...
document.all.btnDelete.style.borderWidth = 1;
...
}
< body class="Template1-Body" scroll="no" onload="window_onload()">If you want to search for all imagebuttons, you can do something like this in the window_onload function: // fix stupid .net bug with ImageButtons
var regExp = new RegExp('\\bImageButton\\b');
var elements = document.getElementsByTagName("input");
elements[i].style.borderWidth = 1;
}
}
The above javascript assumes you have the cssClass set to "ImageButton", so you may need to modify the regexp a bit. Here is an ImageButton definition where this applies:
<asp:ImageButton ToolTip="Save" ID="ImageButtonSaveNew" runat="server" ImageUrl="../../../images/IconExperience/disk_blue_ok24.gif" Height="24" Width="24" CausesValidation="true" OnClick="btnSaveNew_Click" CssClass="ImageButton" onmouseover="this.className='ImageButton-Hover'" onmouseout="this.className='ImageButton'" />
This problem did not occur in ASP.NET 1.1 and presumably will be fixed in the next version of ASP.NET if Vista is any indication...
|
|
|