## Virtualizing a Windows 2000 Installation

August 29, 2011

If you want to save your applications that do not run under Windows 7, you sooner or later need to migration them into a VM.

First, you need to create a disk image of the original installation: I removed the boot disks from the PCs, and connected them to W7 using an IDE-to-USB converter.

To take a snapshot of the disk, I used vConverter and disk2vhd on different occasions. vConverter creates .vmdk files, where disk2vhd uses the .vhd format. Both file formats can be used by VirtualBox.

Before you start your favorite snapshot tool, you should

• uninstall any software you do not need in the VM
• clean up unused files on the disk: temp files, downloads, browser history, recycle bin, etc.
• defrag the harddisk

After preparing the harddisk, create the image.

Start VirtualBox and create a new Virtual Machine. Add the image as boot disk.

In the System tab, I had to enable the IO-APIC setting to get the VM to boot. Add a CD drive in the VM configuration to allow for mounting and installing the Extension Pack once the VM is running.

After installing the extensions (previously called Guest Additions), you can adjust the display settings to a higher resolution. (you will be prompted to set color depth to 32-bit)

If you could not perform the uninstall or cleanup operations on the original harddisks, you can now clean the harddisk inside the VM.

Note that if the VM is offline, you can mount the disk image using Windows 7′s Disk Management and resize the volumes inside the image file.

However I could not find any information on how to shrink VHD files reliably using VirtualBox: VBoxManage modifyhd does not support the –compact option for vhd’s.

The following links did not sound very reassuring, so I did not try them:

VHDResizer can shrink a vhd file, but may leave you with an unbootable image. Virtual PC seems to have an option to compact a vhd file, but the “empty” space needs to be filled with zeros.

So it’s best to clean up before creating the image to avoid complications later on.

## Minor Update of VS Solution Dependency Visualizer

August 24, 2011

The latest version 0.60 of VS Solution Dependency Visualizer contains a couple of UI improvements.

The VS solution file (.sln) and C# project files (.csproj) are now parsed in a background thread which displays the current activity. So no more long operations blocking the application.

Another new feature is the “Reload Solution” button. This allows you to make changes to the solution in Visual Studio, and update the changes into VS Solution Dependency Visualizer. The selections made in the solution’s tree view survive the reload.

The (inappropriate) distinction between “GAC assemblies” and “System assemblies” has been dropped, as the difference between the two was originally the “Copy Local” attribute. (A future version will parse and display more detailed information about projects and assemblies and their relations)

The installer adds VS Solution Dependency Visualizer as an External Tool, but it still needs to be manually activated by the user.

As usual, the latest release of VS Solution Dependency Viewer is available for download here.

## Even “More Static” Virtual Static Interface Methods in C#

August 24, 2011

The implementation of pseudo-static virtual methods I sketched in my previous post has one problem:

the implementation is currently not truly static since a StaticClass object is instantiated every time it is referenced

To fix this behavior, we need to cache the instances of the pseudo-static classes. Which immediately raises another question: Do we instantiate a pseudo-static class as a singleton for all classes that declare the pseudo-static class, or do we instantiate on a per-class level.

public enum StaticClassInstantiation
{
Singleton,
PerClass
}

[AttributeUsage(AttributeTargets.Class,
AllowMultiple = false, Inherited = true)]
public class StaticClassInstantiationAttribute : Attribute
{
StaticClassInstantiation inst;

public StaticClassInstantiationAttribute(
StaticClassInstantiation instantiation)
{
this.inst = instantiation;
}

public StaticClassInstantiation Instantiation { get { return inst; } }
}

The StaticClassInstantiationAttribute defines the instantiation policy of a pseudo-static class. The StaticHelper helper class (introduced in the previous post) will interpret this attribute, so we need to extend the class to handle the new semantics:

public static class StaticHelper
{
static Dictionary<Type, object> singletons =
new Dictionary<Type, object>();        // index is static class
static Dictionary<Type, object> classes =
new Dictionary<Type, object>();            // index is class

These dictionaries keep track of instances of pseudo-class, created as singletons or per-class.

CreateInstance() retrieves or creates and stores these instances:

    static Type GetStaticType(Type t)
{
var attrs = t.GetCustomAttributes(
typeof(StaticClassAttribute), true);
if (attrs.Length == 0)
return null;
return (attrs[0] as StaticClassAttribute).Type;
}

static object CreateInstance(Type objectclass, Type staticclass)
{
if (staticclass == null) return null;    // or throw NotImplemented

var attrs = staticclass.GetCustomAttributes(
typeof(StaticClassInstantiationAttribute), true);
if (attrs.Length == 0 ||
(attrs[0] as StaticClassInstantiationAttribute).Instantiation
== StaticClassInstantiation.PerClass)  // arbitrary default
{
if (classes.ContainsKey(objectclass))
return classes[objectclass];

var c = staticclass.GetConstructor(Type.EmptyTypes)
.Invoke(new object[] { });
return c;
}
else
{
if (singletons.ContainsKey(staticclass))
return singletons[staticclass];

var c = staticclass.GetConstructor(Type.EmptyTypes)
.Invoke(new object[] { });
return c;
}
}

Get() methods are adapted to call CreateInstance():

    public static object Get<T>()
{
var t = GetStaticType(typeof(T));
return CreateInstance(typeof(T), t);
}

public static S Get<T, S>() where S: class
{
return Get<T>() as S;
}

public static object Get(object o)
{
var t = GetStaticType(o.GetType());
return CreateInstance(o.GetType(), t);
}

public static S Get<S>(object o) where S: class
{
return Get(o) as S;
}
}

We extend the sample of the previous post by adding a property to the sample interface and its implementing classes, and defining the instantiation policies using the new StaticClassInstantiation attribute:

public interface IStaticSampleClass
{
void Hello();
string Data { get; set; }
}

[StaticClassInstantiation(StaticClassInstantiation.Singleton)]
public class StaticSampleClass : IStaticSampleClass
{
public void Hello()
{
Console.WriteLine("hello from " + this.GetType().Name);
}
public string Data { get; set; }
}

[StaticClassInstantiation(StaticClassInstantiation.PerClass)]
public class StaticSampleClass2 : IStaticSampleClass
{
public void Hello()
{
Console.WriteLine("bye from " + this.GetType().Name);
}
public string Data { get; set; }
}

[StaticClass(typeof(StaticSampleClass))]
public class SampleClass
{
}

public class SampleClassEx : SampleClass
{
}

[StaticClass(typeof(StaticSampleClass2))]
public class SampleClass2 : SampleClass
{
}

public class SampleClass3 : SampleClass2
{
}

To test the implementation, we set and get the pseudo-static property Data. StaticSampleClass is declared as singleton, and StaticSampleClass2 as per-class:

StaticHelper.Get<SampleClass, IStaticSampleClass>().Data =
"set SampleClass";
StaticHelper.Get<SampleClassEx, IStaticSampleClass>().Data =
"set SampleClassEx";
StaticHelper.Get<SampleClass2, IStaticSampleClass>().Data =
"set SampleClass2";
StaticHelper.Get<SampleClass3, IStaticSampleClass>().Data =
"set SampleClass3";

Console.WriteLine("get SampleClass: " +
StaticHelper.Get<SampleClass, IStaticSampleClass>().Data);
Console.WriteLine("get SampleClassEx: " +
StaticHelper.Get<SampleClassEx, IStaticSampleClass>().Data);
Console.WriteLine("get SampleClass2: " +
StaticHelper.Get<SampleClass2, IStaticSampleClass>().Data);
Console.WriteLine("get SampleClass3: " +
StaticHelper.Get<SampleClass3, IStaticSampleClass>().Data);

As expected, the resulting output is:

get SampleClass: set SampleClassEx
get SampleClassEx: set SampleClassEx
get SampleClass2: set SampleClass2
get SampleClass3: set SampleClass3

Final note: the instantiation of the pseudo-static classes in this example is not thread-safe. Thread-safety should be handled in the CreateInstance() method. A simple solution is to call the StaticHelper.Get() method in the static constructors of classes defining a [StaticClass] attribute.

## Virtual Static Interface Methods in C# – Implementation

August 23, 2011

I sketched the question of “virtual static interface methods” in my previous post.

The idea behind my implementation of this feature is to use a class-level attribute to identify the pseudo-static class for a given class:

class FooStatic
{
}

[StaticClass(typeof(FooStatic))]
class Foo
{
}

The pseudo-static class is a normal C# class, therefore we can use available mechanisms such as interfaces and inheritance for it.

Let’s define the attribute:

[AttributeUsage(AttributeTargets.Class, AllowMultiple=false, Inherited=true)]
public class StaticClassAttribute : Attribute
{
Type _type;
public StaticClassAttribute(Type _type)
{
this._type = _type;
}

public Type Type { get { return _type; } }
}

Ok, that was easy.

Next, we need a helper class to retrieve the StaticClass attribute from a given type:

public static class StaticHelper
{
static Type GetStaticType(Type t)
{
var attrs = t.GetCustomAttributes(typeof(StaticClassAttribute), true);
if (attrs.Length == 0) return null;
return (attrs[0] as StaticClassAttribute).Type;
}

And some user-friendly helper methods to instantiate an object of the attribute’s class value:

    public static object Get<T>()
{
var t = GetStaticType(typeof(T));
if (t == null) return null;		// or throw NotImplemented
return t.GetConstructor(Type.EmptyTypes).Invoke(new object[] { });
}

public static S Get<T, S>() where S: class
{
return Get<T>() as S;
}

public static object Get(object o)
{
var t = GetStaticType(o.GetType());
if (t == null) return null;		// or throw NotImplemented
return t.GetConstructor(Type.EmptyTypes).Invoke(new object[] { });
}

public static S Get<S>(object o) where S: class
{
return Get(o) as S;
}
}

Let’s write our sample static interface and static classes

public interface IStaticSampleClass
{
void Hello();
}
public class StaticSampleClass : IStaticSampleClass
{
public void Hello()
{
Console.WriteLine("hello from " + this.GetType().Name);
}
}
public class StaticSampleClass2 : IStaticSampleClass
{
public void Hello()
{
Console.WriteLine("bye from " + this.GetType().Name);
}
}

and a couple of sample classes

[StaticClass(typeof(StaticSampleClass))]
public class SampleClass
{
}
public class SampleClassEx : SampleClass
{
}

[StaticClass(typeof(StaticSampleClass2))]
public class SampleClass2 : SampleClass
{
}

As you can see, SampleClass and SampleClass2 each declare their respective pseudo-static class, whereas SampleClassEx inherits it from SampleClass.

What does it do?

static void Main(string[] args)
{
object staticclass;
staticclass = StaticHelper.Get<SampleClass>();
Console.WriteLine("static type of SampleClass " + staticclass.GetType().Name);
staticclass = StaticHelper.Get<SampleClassEx>();
Console.WriteLine("static type of SampleClassEx " + staticclass.GetType().Name);
staticclass = StaticHelper.Get<SampleClass2>();
Console.WriteLine("static type of SampleClass2 " + staticclass.GetType().Name);
StaticHelper.Get<SampleClass, IStaticSampleClass>().Hello();
StaticHelper.Get<SampleClass2, IStaticSampleClass>().Hello();

var o = new SampleClass();
staticclass = StaticHelper.Get(o);
Console.WriteLine("static type of SampleClass object " + staticclass.GetType().Name);
StaticHelper.Get<IStaticSampleClass>(o).Hello();

}

The code attempts to retrieve the pseudo-static classes from the classes SampleClass* by using StaticHelper.Get<T>().

The result of Get<T, S> can also be type-cast to an expected interface or class (see lines invoking Hello()).

Finally, the pseudo-static class can also be retrieved via the type of an object parameter to Get(object).

The output of this code is:

static type of SampleClass StaticSampleClass
static type of SampleClassEx StaticSampleClass
static type of SampleClass2 StaticSampleClass2
hello from StaticSampleClass
bye from StaticSampleClass2
static type of SampleClass object StaticSampleClass
hello from StaticSampleClass

One note at the end: While the code I presented works in principal, the implementation is currently not truly static since a StaticClass object is instantiated every time it is referenced. The next post will cover this issue as well.

## Virtual Static Interface Methods in C# – Introduction

August 23, 2011

For quite some time now I wanted to blog about a feature I missed in C# for a long time, what I used to call static interfaces or static class interfaces.

In a class definition you can always add static properties and methods. In my opinion, these static elements really are elements of  a separate class which acts as a “StaticClass” for the original class. For example, a framework might need to operate on static methods defined by such “StaticClasses” depending on the type of a parameter passed to the framework.

Pseudo Code:

static interface IStaticClass
{
void DoSomething();
}
class Foo : IStaticClass
{
public static virtual void DoSomething()
{
Console.WriteLine("hello from Foo");
}
}
class Bar : IStaticClass
{
public static virtual void DoSomething()
{
Console.WriteLine("hello from Bar");
}
}
class FooEx : Foo
{
public static override void DoSomething()
{
Console.WriteLine("hello from FooEx");
}
}
void Main()
{
Bar bar = new Bar();
Foo foo = new FooEx();

// magic happens here:
(bar.GetType() as IStaticClass).DoSomething();
foo.class.DoSomething();
}

Of course I know that this is not possible in C#, and my personal impression is now that such a solution (or the desire to have such a solution) would clearly be an indication of bad design.

One reason for this indication is that there is a fundamental difference between virtual methods and static methods:

Static methods are statically identified by Namespace + Class name + Method name + Parameter types.

Virtual methods are dynamically identified by Method name + Parameter types of the Virtual Method Table of the Class definition (Namespace + Class name), optionally implementing an Interface definition (Namespace + Interface name), of an instantiated object.

I am not alone, though. A quick search on StackOverflow to research for these blogs came up with a quite a lot of questions:

The next two blogs illustrate a solution using techniques already available in C#.

## Programmatically Add External Tool to Visual Studio

August 22, 2011

I tried to find some information on adding an External Tool to Visual Studio using a Setup Project, and came across this Social.MSDN entry.

While it deals with Visual Studio 2005, the information can easily be modified to work in later versions of Visual Studio.

For Visual Studio 2005

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\8.0\External Tools\

For Visual Studio 2008

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\External Tools\

For Visual Studio 2010

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\10.0\External Tools\

Under the key for your tool, you need to add ALL values (from MSDN above):

“ToolArg”=”$(ItemPath)” “ToolOpt”=dword:00000008 “ToolCmd”=”notepad.exe” “ToolDir”=”$(TargetDir)”

The flags that can be or’ed together in “ToolOpt” are the following:

#define TOOLOPT_ISAPPGUI      0×01
#define TOOLOPT_CLOSEONEXIT   0×02
#define TOOLOPT_PROMPTFORARGS 0×04
#define TOOLOPT_USEOUTPUTWIN  0×08
#define TOOLOPT_SAVEALLDOCS   0×10
#define TOOLOPT_UNICODE       0×40

Note that Setup Project expects decimal notation for DWORD values.

At first, my tool was not displayed because I did not provide the ToolDir value as it is not relevant for execution.

Providing all the values displays the tool’s menu as disabled (grayed).

Opening the External Tools dialog and clicking OK enabled to entry for my new external tool.

## Why do I need the [Flags] Attribute?

August 19, 2011

As a developer you often take things for granted because they are “accepted best practices” until somebody asks about the most basic and simplest things, “why do I need [X]“, and you think, well that’s obvious, until you realize the (correct) answer was not so obvious at all, especially if the documentation is not accurate.

It happened to me when I came across the SO question, “What does the [Flags] Attribute Really do?

Time to investigate.

[Flags] can be attributes to Enums, and Enums are type-safe int’s (basically).

In C#, you can apply bitwise operators such as | and & (or, and), etc., so the documentation of the FlagsAttribute class

Indicates that an enumeration can be treated as a bit field; that is, a set of flags.

raises some questions: “can be?”, “as opposed to what?”, “is it really necessary?”

First, the enum values have to be powers of 2 to make the Flags attribute behave as expected.

Let’s take colors as example: 3 different color enumerations (yes, we only have 1-bit color depth here )

enum ColorEnum
{
None,
Red,
Green,
Blue
}

enum SimpleColor
{
Red = 1,
Green = 2,
Blue = 4
}

[Flags]
enum FlagsColor
{
Red = 1,
Green = 2,
Blue = 4,
White = 7
}

ColorEnum can only take a single color, SimpleColor (powers of 2) *could* take a combination of colors, but lacks the [Flags] attribute, and FlagsColor has [Flags] and defines White as combination of Red|Green|Blue.

To test the different enum definitions, we call the enum’s .ToString() method, cast the enum values to int, and Parse() the result of the ToString() (using C#4 in VS2010):

ColorEnum ce = ColorEnum.Red;
Console.WriteLine("ColorEnum Red: " + ce.ToString());
Console.WriteLine("ColorEnum Red: " + ((int)ce).ToString());
Console.WriteLine("  parse: " +
Enum.Parse(typeof(ColorEnum), ce.ToString(), true).ToString());

which has the unsurprising result:

ColorEnum Red: Red
ColorEnum Red: 1
parse: Red

A bit unexpected, but you can use bitwise operators on enums without a [Flag] attribute

ce = ColorEnum.Red | ColorEnum.Green;
Console.WriteLine("ColorEnum Red | Green: " + ce.ToString());
Console.WriteLine("  parse: " +
Enum.Parse(typeof(ColorEnum), ce.ToString(), true).ToString());

Since Red==1, Green==2, and Blue==3, the result is

ColorEnum Red | Green: Blue
parse: Blue

Let’s experiment with SimpleColor, enum holding values of power of 2, but without the [Flags] attribute:

SimpleColor sc = SimpleColor.Red;
Console.WriteLine("SimpleColor Red: " + sc.ToString());
Console.WriteLine("  parse: " +
Enum.Parse(typeof(SimpleColor), sc.ToString(), true).ToString());

sc = SimpleColor.Red | SimpleColor.Green;
Console.WriteLine("SimpleColor Red | Green: " + sc.ToString());
Console.WriteLine("  parse: " +
Enum.Parse(typeof(SimpleColor), sc.ToString(), true).ToString());

Note that Red|Green gives 3, and the enum does not define a symbol for value 3:

SimpleColor Red: Red
parse: Red
SimpleColor Red | Green: 3
parse: 3

Finally, the FlagsColor enum with [Flags] attribute:

FlagsColor fc = FlagsColor.Red;
Console.WriteLine("FlagsColor Red: " + fc.ToString());
Console.WriteLine("  parse: " +
Enum.Parse(typeof(FlagsColor), fc.ToString(), true).ToString());
fc = FlagsColor.Red | FlagsColor.Green;
Console.WriteLine("FlagsColor Red | Green: " + fc.ToString());
Console.WriteLine("  parse: " +
Enum.Parse(typeof(FlagsColor), fc.ToString(), true).ToString());

Console.WriteLine("  parse FlagsColor as SimpleColor: " +
Enum.Parse(typeof(SimpleColor), fc.ToString(), true).ToString());
Console.WriteLine("  parse FlagsColor as ColorEnum: " +
Enum.Parse(typeof(ColorEnum), fc.ToString(), true).ToString());

The first part produces what is expected:

FlagsColor Red: Red
parse: Red
FlagsColor Red | Green: Red, Green
parse: Red, Green

But what about the stringified bit combination “Red, Green” if it is parsed by the other enums?

  parse FlagsColor as SimpleColor: 3
parse FlagsColor as ColorEnum: Blue

Seems that the Enum.Parse() method ignores the (lack of the) [Flags] attribute!

The .HasFlag() method also works regardless of the [Flags] attribute:

Console.WriteLine("ColorEnum has Red? " + ce.HasFlag(ColorEnum.Red));
Console.WriteLine("SimpleColor has Red? " + sc.HasFlag(SimpleColor.Red));
Console.WriteLine("FlagsColor has Red? " + fc.HasFlag(FlagsColor.Red));

Console.WriteLine("ColorEnum has Blue? " + ce.HasFlag(ColorEnum.Blue));
Console.WriteLine("SimpleColor has Blue? " + sc.HasFlag(SimpleColor.Blue));
Console.WriteLine("FlagsColor has Blue? " + fc.HasFlag(FlagsColor.Blue));

results in:

ColorEnum has Red? True
SimpleColor has Red? True
FlagsColor has Red? True
ColorEnum has Blue? True
SimpleColor has Blue? False
FlagsColor has Blue? False

Finally, testing a composite bit value:

fc = FlagsColor.Red | FlagsColor.Green | FlagsColor.Blue;
Console.WriteLine("FlagsColor RGB: " + fc.ToString());
Console.WriteLine("  parse: " +
Enum.Parse(typeof(FlagsColor), "Red, Green, Blue", true).ToString());
Console.WriteLine("FlagsColor has White? " + fc.HasFlag(FlagsColor.White));

results in

FlagsColor RGB: White
parse: White
FlagsColor has White? True

If you work with VB.Net rather than C#, this entry on social.msdn may be interesting for you:

Although C# happily allows users to perform bit operations on enums without the FlagsAttribute, Visual Basic does not. So if you are exposing types to other languages, then marking enums with the FlagsAttribute is a good idea.

It also states

(The [Flags] attribute) makes it clear that the members of the enum are designed to be used together.

Some things should be described more explicitly in the documentation.

## Registering UserControls in web.config, and Handling Nested UserControls

August 14, 2011

As I recently migrated an ASP.Net Web Site Project to a Web Application Project, I also tried to “clean up” the markup files by

• moving the registration of the project’s UserControls from the aspx/ascx markup to the web.config configuration file
• and naming the UserControls’ tagName and tagPrefix values consistently

Visual Studio (2008) compilation was successful, and even aspnet_compiler did not complain. The errors only started to occur upon hitting Run.

The page ‘/Default.aspx’ cannot use the user control ‘/WebUserControl.ascx’, because it is registered in web.config and lives in the same directory as the page.

Die Seite “/Default.aspx” kann das Benutzersteuerelement “/WebUserControl.ascx” nicht verwenden, weil es in “web.config” registriert ist und sich im gleichen Verzeichnis befindet wie die Seite.

I found an acknowledgement of this behavior in an MS Connect entry:

Posted by Microsoft on 5/26/2005 at 11:03 PM
Ideally, this would work, but there are some issues with the internal implementation that makes this scenario non-trivial to support. We will look into removing this restriction in a future version. The best thing to do is to put the user controls in their own folder.

My steps to resolve this error were:

• Move the UserControls registered in the web.config file to a separate directory
• If you have nested UserControls, the you need to split them into different directories

In the current project, I ended up with this directory structure:

One consequence of moving UserControls to a directory is that any HyperLinks in those UserControls now include the control’s directory name in the HyperLink URL if you use relative paths.

This can be fixed by setting the UserControls’ AppRelativeTemplateSourceDirectory property correctly:

        protected override void OnInit(EventArgs e)
{
base.OnInit(e);
AppRelativeTemplateSourceDirectory = "~/";
}

If you use relative URL’s and your application files are laid out in a directory tree, have a look at this answer on StackOverflow.

There are of course occurrences of LoadControl(“foo.ascx”) in a couple of places which are affected by this UserControl migration. So what about using LoadControl(Type, Object[]) instead, and have the framework look up the control by its type name?

Computer says No.

If the UserControl contains other controls (which they usually do), then the child controls are not initialized by the LoadControl(Type) method (or at least this is what I understood from this explanation):

This is by-design since the type “TestUC” is actually the base type used by the partial class, it does not contain the proper code to instantiate TextBox1 reference, which is actually defined in the derived type. There are two workarounds: 1. Use LoadControl(“TestControl.ascx”), for all practical, this behaves identically to LoadControl(type) but it instantiates the derived type, which knows how to instantiate TextBox1. 2. Use a single file page and adds <%@ Reference %> directive to the page to reference the user control, and assign a classname to the ascx page. Then it’s safe to use LoadControl(type).

## aspnet_compiler CS0436

August 9, 2011

After manual migration of a VS2008 Web Site Project to a Web Application Project, the aspnet_compiler would display hundreds of warnings of the type

c:\Users\[user]\AppData\Local\Temp\Temporary ASP.NET Files\[webproject]\be406d28\f38d
820c\App_Web_otb8hok3.0.cs(184):

The type ‘type’ in ‘assembly’ conflicts with the imported type ‘type2′ in ‘assembly’. Using the type defined in ‘assembly’.

Der Typ “Typ” in “Assembly” verursacht einen Konflikt mit dem importierten Typ “Typ2″ in “Assembly”. Der in “Assembly” definierte Typ wird verwendet.

What is called ‘assembly’ in the MSDN article is really the C# source file.

Apparently the problem was caused by a minor oversight:

I forgot to replace the pages’ CodeFile attribute with the CodeBehind attribute.

The last paragraph of this blog on a similar compiler warning pointed me to the right direction.

## ASP.Net 4 Page Life Cycle

August 8, 2011

The sequence of methods and events that each Page, MasterPage, and Control processes during the ASP.Net life cycle has been a recurring topic in this blog:

Each time, I linked to some external charts displaying the sequence of events.

The existing charts (that I know of) also do not cover Nested MasterPages which have been introduced in VS 2008.

Time to create my own diagram, and here it is (right-click to save):