I <3 Steve McConnell*
Coding Horror
programming and human factors
by Jeff Atwood

February 09, 2005

ASP.NET About Box (Page)

I had a request for an ASP.NET version of my windows forms About Box. This is a good idea that I've considered in the past, so I took the time to convert it today:

ASP.NET about form screenshot

Clicking details will provide a dump of all loaded assemblies in summary form, with links to the full list of the attributes for each assembly-- exactly the same information the windows forms version provides.

The main page is a HTML template; you can make it look however you want by modifying the markup-- no recompilation required. To display entry assembly attributes, use the named properties defined in the About class, eg Version <%=Version%>, or call EntryAssemblyAttrib(key) with any arbitrary attribute key you want to display.

Oh, and populate your damn assembly attributes, because that's where all this information is automatically derived from:

<Assembly: AssemblyTitle("About Page Demo")>
<Assembly: AssemblyDescription("A website demonstrating the About page")>
<Assembly: AssemblyCompany("Atwood Heavy Industries")>
<Assembly: AssemblyProduct("Demos")>
<Assembly: AssemblyCopyright("© 2005, Atwood Heavy Industries")>
<Assembly: AssemblyTrademark("All Rights Reserved")>

I don't think this is enough to justify another CodeProject article, so I'll host it here and link it from the comments in the original article.

Download the ASP.NET About Page VS.NET 2003 project (12kb)

Posted by Jeff Atwood    View blog reactions

 

« Virtual PC 2004 tips Keyboarding »

 

Comments

c# version of about.aspx.vb if anyone finds this:
using System;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

using System.Diagnostics;
using System.Collections;
using System.Collections.Specialized;
using System.IO;
using System.Reflection;
using System.Text.RegularExpressions;
using Microsoft.Win32;

namespace Example.AdminSite
{
public class Default : System.Web.UI.Page
{
private String _CallingAssemblyName;
private String _ExecutingAssemblyName;
private System.Reflection.Assembly _EntryAssembly;
private NameValueCollection _EntryAssemblyAttribCollection;

protected void Page_Load(object sender, EventArgs e)
{
_EntryAssembly = Assembly.GetExecutingAssembly();
_ExecutingAssemblyName = Assembly.GetExecutingAssembly().GetName().Name;
_CallingAssemblyName = Assembly.GetCallingAssembly().GetName().Name;
// for an ASP.NET app, entry assembly is something of a misnomer;
// what we really have is ExecutingAssembly
_EntryAssemblyAttribCollection = AssemblyAttribs(_EntryAssembly);
}

private void Page_PreRender(Object sender, System.EventArgs e)
{

}

public string AssemblyTitle { get { return EntryAssemblyAttrib("Title"); } }
public string Copyright { get { return EntryAssemblyAttrib("copyright"); } }
public string Description { get { return EntryAssemblyAttrib("description"); } }
public string Company { get { return EntryAssemblyAttrib("company"); } }
public string Product { get { return EntryAssemblyAttrib("product"); } }
public string Trademark { get { return EntryAssemblyAttrib("trademark"); } }
public string Version { get { return EntryAssemblyAttrib("version"); } }
public string BuildDate { get { return EntryAssemblyAttrib("builddate"); } }
public string RuntimeVersion { get { return Regex.Match(Environment.Version.ToString(), @"\d+.\d+.\d+").ToString(); } }
public String EntryAttrib(String key) { return EntryAssemblyAttrib(key); }

public void RenderDetails()
{
RenderAppDetails();
RenderAssemblyDetails();
}

///
/// exception-safe retrieval of LastWriteTime for this assembly.
///
/// File.get {LastWriteTime, or DateTime.MaxValue if exception wencountered.
protected internal DateTime AssemblyLastWriteTime(System.Reflection.Assembly a)
{
try
{
return File.GetLastWriteTime(a.Location);
}
catch (Exception exception)
{
return DateTime.MaxValue;
}
}

///
/// returns DateTime this Assembly wlast built. Will attempt to calculate from build number, if possible.
/// If not, the actual LastWriteTime on the assembly file will be returned.
///
/// Assembly to get { build date for
/// Don't attempt to use the build number to calculate the date
/// DateTime this assembly wlast built
protected DateTime AssemblyBuildDate(System.Reflection.Assembly a, bool ForceFileDate)
{
System.Version AssemblyVersion = a.GetName().Version;
DateTime dt;

if (ForceFileDate)
dt = AssemblyLastWriteTime(a);
else
{
dt = new DateTime(2000, 1, 1).AddDays(AssemblyVersion.Build).AddSeconds(AssemblyVersion.Revision * 2);
if (TimeZone.IsDaylightSavingTime(dt, TimeZone.CurrentTimeZone.GetDaylightChanges(dt.Year)))
dt = dt.AddHours(1);
if (dt > DateTime.Now || AssemblyVersion.Build
/// returns string name / string value pair of all attribs
/// for specified assembly
///
///
/// note that Assembly* values are pulled from AssemblyInfo file in project folder
///
/// Trademark = AssemblyTrademark string
/// Debuggable = True
/// GUID = 7FDF68D5-8C6F-44C9-B391-117B5AFB5467
/// CLSCompliant = True
/// Product = AssemblyProduct string
/// Copyright = AssemblyCopyright string
/// Company = AssemblyCompany string
/// Description = AssemblyDescription string
/// Title = AssemblyTitle string
///
private NameValueCollection AssemblyAttribs(System.Reflection.Assembly a)
{
String TypeName;
String Name;
String Value;
NameValueCollection nvc = new NameValueCollection();
Regex r = new Regex("(\\.Assembly|\\.)(?[^.]*)Attribute$", RegexOptions.IgnoreCase);

foreach (Object attrib in a.GetCustomAttributes(false))
{
TypeName = attrib.GetType().ToString();
Name = r.Match(TypeName).Groups["Name"].ToString();
Value = "";
switch (TypeName)
{
case "System.CLSCompliantAttribute":
Value = ((CLSCompliantAttribute)attrib).IsCompliant.ToString();
break;
case "System.Diagnostics.DebuggableAttribute":
Value = ((DebuggableAttribute)attrib).IsJITTrackingEnabled.ToString();
break;
case "System.Reflection.AssemblyCompanyAttribute":
Value = ((AssemblyCompanyAttribute)attrib).Company.ToString();
break;
case "System.Reflection.AssemblyConfigurationAttribute":
Value = ((AssemblyConfigurationAttribute)attrib).Configuration.ToString();
break;
case "System.Reflection.AssemblyCopyrightAttribute":
Value = ((AssemblyCopyrightAttribute)attrib).Copyright.ToString();
break;
case "System.Reflection.AssemblyDefaultAliasAttribute":
Value = ((AssemblyDefaultAliasAttribute)attrib).DefaultAlias.ToString();
break;
case "System.Reflection.AssemblyDelaySignAttribute":
Value = ((AssemblyDelaySignAttribute)attrib).DelaySign.ToString();
break;
case "System.Reflection.AssemblyDescriptionAttribute":
Value = ((AssemblyDescriptionAttribute)attrib).Description.ToString();
break;
case "System.Reflection.AssemblyInformationalVersionAttribute":
Value = ((AssemblyInformationalVersionAttribute)attrib).InformationalVersion.ToString();
break;
case "System.Reflection.AssemblyKeyFileAttribute":
Value = ((AssemblyKeyFileAttribute)attrib).KeyFile.ToString();
break;
case "System.Reflection.AssemblyProductAttribute":
Value = ((AssemblyProductAttribute)attrib).Product.ToString();
break;
case "System.Reflection.AssemblyTrademarkAttribute":
Value = ((AssemblyTrademarkAttribute)attrib).Trademark.ToString();
break;
case "System.Reflection.AssemblyTitleAttribute":
Value = ((AssemblyTitleAttribute)attrib).Title.ToString();
break;
case "System.Resources.NeutralResourcesLanguageAttribute":
Value = ((System.Resources.NeutralResourcesLanguageAttribute)attrib).CultureName.ToString();
break;
case "System.Resources.SatelliteContractVersionAttribute":
Value = ((System.Resources.SatelliteContractVersionAttribute)attrib).Version.ToString();
break;
case "System.Runtime.InteropServices.ComCompatibleVersionAttribute":
System.Runtime.InteropServices.ComCompatibleVersionAttribute x;
x = attrib as System.Runtime.InteropServices.ComCompatibleVersionAttribute;
Value = string.Format("{0}.{1}.{2}.{3}", x.MajorVersion, x.MinorVersion, x.RevisionNumber, x.BuildNumber);
break;
case "System.Runtime.InteropServices.ComVisibleAttribute":
Value = ((System.Runtime.InteropServices.ComVisibleAttribute)attrib).Value.ToString();
break;
case "System.Runtime.InteropServices.GuidAttribute":
Value = ((System.Runtime.InteropServices.GuidAttribute)attrib).Value.ToString();
break;
case "System.Runtime.InteropServices.TypeLibVersionAttribute":
System.Runtime.InteropServices.TypeLibVersionAttribute tlva = attrib as System.Runtime.InteropServices.TypeLibVersionAttribute;
Value = tlva.MajorVersion + "." + tlva.MinorVersion;
break;
case "System.Security.AllowPartiallyTrustedCallersAttribute":
Value = "(Present)";
break;
default:
// debug.writeline("** unknown assembly attribute '" & TypeName & "'")
Value = TypeName; break;
}

if (string.IsNullOrEmpty(nvc[Name]))
nvc.Add(Name, Value);
}

// add some extra values that are not in the AssemblyInfo, but nice to have
// codebase
try { nvc.Add("CodeBase", a.CodeBase.Replace("file:///", "")); }
catch (System.NotSupportedException ex) { nvc.Add("CodeBase", "(not supported)"); }

// build date
DateTime dt = AssemblyBuildDate(a);
if (dt == DateTime.MaxValue)
nvc.Add("BuildDate", "(unknown)");
else
nvc.Add("BuildDate", dt.ToString("yyyy-MM-dd hh:mm tt"));

// location
try { nvc.Add("Location", a.Location); }
catch (System.NotSupportedException ex) { nvc.Add("Location", "(not supported)"); }

// version
try
{
if (a.GetName().Version.Major == 0 && a.GetName().Version.Minor == 0)
nvc.Add("Version", "(unknown)");
else
nvc.Add("Version", a.GetName().Version.ToString());
}
catch (Exception ex) { nvc.Add("Version", "(unknown)"); }

nvc.Add("FullName", a.FullName);
return nvc;
}

///
/// Render a row with the specified key and value strings
///
public void RenderRow(String Key, String Value)
{
if (string.IsNullOrEmpty(Value)) return;
Response.Write("");
Response.Write("");
Response.Write("");
Response.Write(Key);
Response.Write("");
Response.Write("");
Response.Write(Value);
Response.Write("");
Response.Write(Environment.NewLine);
}

///
/// populates the Application Information listview
///
private void RenderAppDetails()
{
Response.Write("Application Details");
Response.Write("");
Response.Write("KeyValue");

System.AppDomain d = System.AppDomain.CurrentDomain;
RenderRow("Application Name", d.SetupInformation.ApplicationName);
RenderRow("Application Base", d.SetupInformation.ApplicationBase);
RenderRow("Cache Path", d.SetupInformation.CachePath);
RenderRow("Configuration File", d.SetupInformation.ConfigurationFile);
RenderRow("Dynamic Base", d.SetupInformation.DynamicBase);
RenderRow("Friendly Name", d.FriendlyName);
RenderRow("License File", d.SetupInformation.LicenseFile);
RenderRow("private Bin Path", d.SetupInformation.PrivateBinPath);
RenderRow("Shadow Copy Directories", d.SetupInformation.ShadowCopyDirectories);
RenderRow("Executing Assembly", _ExecutingAssemblyName);
RenderRow("Calling Assembly", _CallingAssemblyName);

Response.Write("");
Response.Write("");
}

///
/// RenderRow Assembly Information listview with ALL assemblies
///
private void RenderAssemblyDetails()
{
Response.Write("");
Response.Write("Loaded Assemblies");
Response.Write("");
Response.Write("");
Response.Write("AssemblyVersion");
Response.Write("BuiltFilename");

// sort the assemblies first
SortedList sl = new SortedList();
foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies())
{
sl.Add(a.GetName().Name, null);
}

foreach (DictionaryEntry de in sl)
RenderAssemblySummary(MatchAssemblyByName(Convert.ToString(de.Key)));

Response.Write("");
Response.Write("");
foreach (DictionaryEntry de in sl)
{
Assembly a = MatchAssemblyByName(Convert.ToString(de.Key));
Response.Write("");
Response.Write("" + a.GetName().Name + "");
Response.Write("");
Response.Write("");
Response.Write("");
Response.Write("KeyValue");
RenderAssemblyDetails(a);
Response.Write("");
Response.Write("");
}
}

///
/// Render Assembly Information listview with summary view for a specific assembly
///
private void RenderAssemblySummary(Assembly a)
{
NameValueCollection nvc = AssemblyAttribs(a);
String strAssemblyName = a.GetName().Name;

Response.Write("");
Response.Write("");
Response.Write("");
Response.Write(strAssemblyName);
Response.Write("");
if (strAssemblyName == _CallingAssemblyName)
Response.Write(" (calling)");

if (strAssemblyName == _ExecutingAssemblyName)
Response.Write(" (executing)");

Response.Write("");
Response.Write(nvc["version"]);
Response.Write("");
Response.Write(nvc["builddate"]);
Response.Write("");
Response.Write(Path.GetFileName(nvc["codebase"]));
Response.Write("");
}

///
/// retrieves a cached value from the entry assembly attribute lookup collection
///
private String EntryAssemblyAttrib(String strName)
{
if (string.IsNullOrEmpty(_EntryAssemblyAttribCollection[strName]))
return "[Assembly: Assembly" + strName + "(\"\")]";
else
return _EntryAssemblyAttribCollection[strName].ToString();
}

///
/// Render details for a single assembly
///
private void RenderAssemblyDetails(System.Reflection.Assembly a)
{
// this assembly string is only available in framework versions 1.1+
RenderRow("Image Runtime Version", a.ImageRuntimeVersion);
RenderRow("Loaded from GAC", a.GlobalAssemblyCache.ToString());

NameValueCollection nvc = AssemblyAttribs(a);
foreach (String strKey in nvc)
RenderRow(strKey, nvc[strKey]);
}

///
/// matches assembly by Assembly.get {Name.Name; returns nothing if no match
///
private Assembly MatchAssemblyByName(String AssemblyName)
{
foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies())
{
if (a.GetName().Name == AssemblyName)
return a;
}
return null;
}


#region Event wireup

protected override void OnInit(EventArgs e)
{
this.Load += new EventHandler(Page_Load);
this.PreRender += new EventHandler(Page_PreRender);
base.OnInit(e);
}

#endregion
}
}

Scott on January 7, 2008 06:42 PM







(hear it spoken)


(no HTML)




Content (c) 2008 Jeff Atwood. Logo image used with permission of the author. (c) 1993 Steven C. McConnell. All Rights Reserved.