Dec 28, 2008

Design Time Feature Implementation in Silverlight Toolkit

Introduction

This is the second post of my Silverlight Design Time Features series. The first post Design Time Features in Silverlight Toolkit showed design time features of Silverlight Toolkit controls. This post explains how it is implemented. Hopefully this post can help explaining our source code, demonstrating how to implement design time features for Silverlight, and providing a framework and source code readers can use directly in their own projects.

Overview

Download "Silverlight Toolkit - Binaries, Samples, Documentation, Unit Tests and Sources" of December 2008 release, open Source\Silverlight.Controls.sln in Visual Studio, and we will see that there are 12 design projects under Design solution folder, three for each control assembly.

Silverlight.Controls.sln in Visual Studio

Take Controls project for example, there are three design projects:

  • Controls.Design: builds Windows.Controls.Design.dll, which contains design time features shared by both Visual Studio and Expression Blend.
  • Controls.Expression.Design: builds Windows.Controls.Expression.Design.dll, which contains design time features for Expression Blend only.
  • Controls.VisualStudio.Design: builds Windows.Controls.VisualStudio.Design.dll, which contains design time features for Visual Studio only.

There is also a Design.Common folder that contains two files: Extensions.cs and MetadataBase.cs, which are shared by all design projects.

It is highly recommended that you read Justin Angel's blog post Silverlight Design Time Extensibility: it provides good background/overview information on Silverlight design time extensibility, on which below implementation is based.

Project

All 12 design projects follow the same implementation pattern, so I will just use Control.Design project to explain them all.

Load Silverlight.Controls.sln under Source directory into Visual Studio for the first time, we will see screen like below: 

Silverlight.Controls.Design.csproj in Visual Studio

  • Controls.Design has a project dependency on Controls project. This dependency ensures that Controls project is built first, and Controls.Design has a reference to Microsoft.Windows.Controls assembly built by Controls project. A design time assembly always references the run time assembly it provides design time features for.
  • Controls.Design references Microsoft.Windows.Design & Microsoft.Windows.Design.Extensibility assemblies, both of which are under directory %DevEnvDir%\PublicAssemblies\ (c:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\PublicAssemblies\ on my laptop). A design time assembly often references those two assemblies, which provide the designer extensibility framework. A design time assembly always references System assembly too, which contains System.ComponentModel namespace, where many of the metadata attributes (like CategoryAttribute, DescriptionAttribute) are defined.
  • Controls.Design references System.Windows assembly. We can see the reference in Controls.Design.csproj too:

        <Reference Include="System.Windows, Version=2.0.5.0, Culture=neutral, PublicKeyToken=7cec85d7bea7798e, processorArchitecture=MSIL">
            <SpecificVersion>False</SpecificVersion>
            <HintPath>..\Binaries\System.Windows.dll</HintPath>
            <Private>False</Private>
        </Reference>


    Please note:
    • Control.Design is a Windows Class Library project, not a Silverlight project; and the generated Microsoft.Windows.Controls.Design.dll is a .NET assembly run on desktop inside designers like Visual Studio or Expression Blend, not a Silverlight assembly run inside a browser, even though Microsoft.Windows.Controls.Design.dll is the design time assembly for the Silverlight run time assembly Microsoft.Windows.Controls.dll.
    • The referenced System.Windows assembly is actually a Silverlight assembly (version 2.0.5.0). System.Windows assembly may not be needed by all design projects for Silverlight controls. We need it here mostly because of the GetMemberName<T>(Expression<Func<T, object>> expr) method in Extension.cs that we will discuss more later in the post. The mix of .NET and Silverlight references creates all kinds of interesting issues, as we will see soon.

      Below ildasm screenshot shows the mixed references to .NET and Silverlight assemblies by Microsoft.Windows.Controls.Design.dll:
      ildasm Microsoft.Windows.Controls.Design.dll Microsoft.Windows.Controls.Design assembly manifest
      • .ver 2:0:21024:1838 is the version number of Silverlight Toolkit 2008 December release.
      • .ver 3:5:0:0 is the version number for .NET framework 3.5.
      • .ver 2:0:0:0 is the version number for Silverlight 2.0.
    • When you first load Controls.Design project into Visual Studio, there is a "The referenced component 'System.Windows' could not be found" warning next to the System.Windows assembly reference in project window, and in Error List window too. This is because Controls.Design is a .NET project instead of Silverlight, and we don't know where Silverlight is installed (it is under C:\Program Files\Microsoft Silverlight\2.0.31005.0 on my laptop, and under C:\Program Files (X86)\Microsoft Silverlight\2.0.31005.0 on my desktop). The solution is to have a pre-build event command ..\CopySystemWindows.bat that copies System.Windows.dll from Silverlight install direction to ..\Binaries. So once you've built the project, the warning will go away, as we will see shortly.
  • In above Visual Studio screenshot, there is another warning sign next to the file Microsoft.Windows.Controls.XML in project widow, when you load Controls.Design project into Visual Studio for the first time. This file is generated by building the dependent Controls project, because in Controls project's Properties -> Build tab, the option "XML documentation file" is checked, and has a path of "..\Binaries\Microsoft.Windows.Controls.XML". If you build Controls.Design project, the warning will go away, as we will see shortly.
  • Controls.Design project has a Metadata.cs file, and links to Extension.cs and MetadataBase.cs in Design.Common folder. All 12 design projects have those three files. We will discuss them in more detail later.

Build Controls.Design project or the whole solution, we can see:

Build output of Silverlight.Control.sln

  • All projects built fine with 0 errors and 0 warnings.
  • The warning signs next to System.Windows reference and Microsoft.Windows.Controls.XML file disappeared.
  • The code analysis of the build process takes a long time, and it generates two warnings. The first one, CA0060, is another issue of the mixed .NET and Silverlight references and code in Extension.cs. You can get rid of the warning by copying System.dll and System.Core.dll from Silverlight installation directory to Source\Binaries directory and building again.

MetadataBase.cs

MetadataBase.cs, together with Metadata.cs, implement a framework for design time metadata registration:

  • DescriptionAttributes are automatically generated for public control classes (ie, subclasses of FrameworkElement) and their public properties from their "/// <summary>" XML documentation comments in source code. (Good commenting pays! :-)
  • To hide a control class from a designer, add a line like below to AddAttributes method in the project's Metadata.cs file:
        builder.AddCallback(typeof(TreeViewItem), b => b.AddCustomAttributes(new ToolboxBrowsableAttribute(false)));
  • To register other custom attributes for a control class, add a XxxMetadata.cs file to the project, like the ViewboxMetadata.cs discussed later .

If you like the framework, you can use MetadataBase.cs and Metadata.cs directly in your own project, subject to Microsoft Public License included at the beginning of all our source files.

MetadataBase.cs:

// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Windows;
using System.Xml.Linq;
using Microsoft.Windows.Design.Metadata;

namespace Microsoft.Windows.Controls.Design.Common
{
    /// <summary>
    /// MetadataRegistration class.
    /// </summary>
    public class MetadataRegistrationBase
    {
        /// <summary>
        /// Build design time metadata attribute table.
        /// </summary>
        /// <returns>Custom attribute table.</returns>
        protected virtual AttributeTable BuildAttributeTable()
        {
            AttributeTableBuilder builder = new AttributeTableBuilder();

            AddDescriptions(builder);
            AddAttributes(builder);
            AddTables(builder);

            return builder.CreateTable();
        }

        /// <summary>
        /// Find all AttributeTableBuilder subclasses in the assembly 
        /// and add their attributes to the assembly attribute table.
        /// </summary>
        /// <param name="builder">The assembly attribute table builder.</param>
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Design time dll should not fail!")]
        private static void AddTables(AttributeTableBuilder builder)
        {
            Debug.Assert(builder != null, "AddTables is called with null parameter!");

            Assembly asm = Assembly.GetExecutingAssembly();
            foreach (Type t in asm.GetTypes())
            {
                if (t.IsSubclassOf(typeof(AttributeTableBuilder)))
                {
                    try
                    {
                        AttributeTableBuilder atb = (AttributeTableBuilder)Activator.CreateInstance(t);
                        builder.AddTable(atb.CreateTable());
                    }
                    catch (Exception e)
                    {
                        Debug.Assert(false, string.Format(CultureInfo.InvariantCulture, "Exception in AddTables method: {0}", e));
                    }
                }
            }
        }

        /// <summary>
        /// Gets or sets the case sensitive resource name of the embedded XML file.
        /// </summary>
        protected string XmlResourceName { get; set; }

        /// <summary>
        /// Gets or sets the assembly FullName for types' assembly-qualified names.
        /// </summary>
        protected string AssemblyFullName { get; set; }

        /// <summary>
        /// Create description attribute from run time assembly xml file.
        /// </summary>
        /// <param name="builder">The assembly attribute table builder.</param>
        [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Design time dll should not fail.")]
        private void AddDescriptions(AttributeTableBuilder builder)
        {
            Debug.Assert(builder != null, "AddDescriptions is called with null parameter!");

            if (string.IsNullOrEmpty(XmlResourceName) || 
                string.IsNullOrEmpty(AssemblyFullName))
            {
                return;
            }

            XDocument xdoc = XDocument.Load(new StreamReader(
                Assembly.GetExecutingAssembly().GetManifestResourceStream(XmlResourceName)));
            if (xdoc == null)
            {
                return;
            }

            foreach (XElement member in xdoc.Descendants("member"))
            {
                try
                {
                    string name = (string)member.Attribute("name");
                    bool isType = name.StartsWith("T:", StringComparison.OrdinalIgnoreCase);
                    if (isType ||
                        name.StartsWith("P:", StringComparison.OrdinalIgnoreCase))
                    {
                        int lastDot = name.Length;
                        string typeName;
                        if (isType)
                        {
                            typeName = name.Substring(2);
                        }
                        else
                        {
                            lastDot = name.LastIndexOf('.');
                            typeName = name.Substring(2, lastDot - 2);
                        }
                        typeName += AssemblyFullName;

                        Type t = Type.GetType(typeName);
                        if (t != null && t.IsPublic && t.IsClass && 
                            t.IsSubclassOf(typeof(FrameworkElement)))
                        {
                            string desc = member.Descendants("summary").FirstOrDefault().Value;
                            desc = desc.Trim();
                            desc = string.Join(" ", desc.Split(new char[] { ' ', '\t', '\n' }, StringSplitOptions.RemoveEmptyEntries));

                            if (isType)
                            {
                                builder.AddCallback(t, b => b.AddCustomAttributes(new DescriptionAttribute(desc)));
                            }
                            else
                            {
                                string propName = name.Substring(lastDot + 1);
                                PropertyInfo pi = t.GetProperty(propName);
                                MethodInfo mi;
                                if (pi != null && (mi = pi.GetSetMethod()) != null && mi.IsPublic)
                                {
                                    builder.AddCallback(t, b => b.AddCustomAttributes(propName, new DescriptionAttribute(desc)));
                                }
                            }
                        }
                    }
                }
                catch (Exception e)
                {
                    Debug.Assert(false, string.Format(CultureInfo.InvariantCulture, "Exception in AddDescriptions method: {0}", e));
                }
            }
        }

        /// <summary>
        /// Provide a place to add custom attributes without creating a AttributeTableBuilder subclass.
        /// </summary>
        /// <param name="builder">The assembly attribute table builder.</param>
        protected virtual void AddAttributes(AttributeTableBuilder builder)
        {
        }
    }
}

MetadataBase.cs implements the MetadataRegistrationBase class. Let's discuss some its key methods:

AddDescriptions

All run time assembly projects have "XML documentation file" option checked, and with a path like "..\Binaries\Microsoft.Windows.Controls.XML", in Project -> Properties -> Build tab, Output section. You can also find the setting in .csproj file like below:

<DocumentationFile>..\Binaries\Microsoft.Windows.Controls.Design.XML</DocumentationFile>

Below is an excerpt of Microsoft.Windows.Controls.XML to show what the generated documentation XML file looks like:

<?xml version="1.0"?>
<doc>
    <assembly>
        <name>Microsoft.Windows.Controls</name>
    </assembly>
    <members>
        <member name="T:Microsoft.Windows.Controls.Viewbox">
            <summary>
            Defines a content decorator that can stretch and scale a single child to
            fill the available space.
            </summary>
            <QualityBand>Preview</QualityBand>
        </member>
        <member name="F:Microsoft.Windows.Controls.Viewbox.ChildElementName">
            <summary>
            Name of  child element in Viewbox's default template.
            </summary>
        </member>
        <member name="M:Microsoft.Windows.Controls.Viewbox.IsValidStretchValue(System.Object)">
            <summary>
            Check whether the passed in object value is a valid Stretch enum value.
            </summary>
            <param name="o">The object typed value to be checked.</param>
            <returns>True if o is a valid Stretch enum value, false o/w.</returns>
        </member>
        <member name="P:Microsoft.Windows.Controls.Viewbox.Child">
            <summary>
            Gets or sets the single child element of a Viewbox.
            </summary>
        </member>
    </members>
</doc>

The name attribute of each <member> element follows the pattern:

  • "T:Microsoft.Windows.Controls.Viewbox": "T:" indicates this is a type, followed by the type's full name;
  • "F:Microsoft.Windows.Controls.Viewbox.ChildElementName": "F:" indicates this is a field, followed by the field's fully qualified name;
  • "M:Microsoft.Windows.Controls.Viewbox.IsValidStretchValue(System.Object)": "M:" indicates this is a method, followed by the method's fully qualified name and parameters;
  • "P:Microsoft.Windows.Controls.Viewbox.Child": "P:" indicates this is a property, followed by the property's full name;

Control.Design project links to the Microsoft.Windows.Controls.XML file as a embedded resource:

Microsoft.Windows.Controls.XML as embedded resource 
Controls.Design.csproj: <EmbeddedResource Include="..\Binaries\Microsoft.Windows.Controls.XML" />

AddDescriptions method parses the embedded XML file and generates DescriptionAttribute for public control classes and their public properties:

MetadataBase.cs:134: builder.AddCallback(t, b => b.AddCustomAttributes(new DescriptionAttribute(desc)));
MetadataBase.cs:143: builder.AddCallback(t, b => b.AddCustomAttributes(propName, new DescriptionAttribute(desc)));

AddAttributes

AddAttributes is usually overridden in Metadata.cs file to add a ToolboxBrowsableAttribute(false) custom attribute for control classes that should not show up in a designer's toolbox:

  • if the control class should be hidden from all designers, add the ToolboxBrowsableAttribute(false) custom attribute to the Xxx.Design project;
  • if it should be hidden from Visual Studio only, add the custom attribute to Xxx.VisualStudio.Design project;
  • if it should be hidden from Expression Blend only, add the custom attribute to Xxx.Expression.Design project;

Below is AddAttributes implementation in Metadata.cs of Controls.VisualStudio.Design project:

/// <summary>
/// Provide a place to add custom attributes without creating a AttributeTableBuilder subclass.
/// </summary>
/// <param name="builder">The assembly attribute table builder.</param>
protected override void AddAttributes(AttributeTableBuilder builder)
{
    builder.AddCallback(typeof(TreeViewItem), b => b.AddCustomAttributes(new ToolboxBrowsableAttribute(false)));
}
AddTables

To add attributes other than ToolboxBrowsableAttribute(false) for a type, add a XxxMetadata.cs file like ViewboxMetadata.cs below to the appropriate design project:

ViewboxMetadata.cs:

// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.

using System.ComponentModel;
using Microsoft.Windows.Controls.Design.Common;
using Microsoft.Windows.Design.Metadata;

namespace Microsoft.Windows.Controls.Design
{
    /// <summary>
    /// To register design time metadata for Viewbox.
    /// </summary>
    internal class ViewboxMetadata : AttributeTableBuilder
    {
        /// <summary>
        /// To register design time metadata for Viewbox.
        /// </summary>
        public ViewboxMetadata()
            : base()
        {
            AddCallback(
                typeof(Viewbox),
                b =>
                {
                    b.AddCustomAttributes(Extensions.GetMemberName<Viewbox>(x => x.BorderThickness), new BrowsableAttribute(false));
                    b.AddCustomAttributes(Extensions.GetMemberName<Viewbox>(x => x.BorderBrush), new BrowsableAttribute(false));
                    b.AddCustomAttributes(Extensions.GetMemberName<Viewbox>(x => x.Background), new BrowsableAttribute(false));
                    b.AddCustomAttributes(Extensions.GetMemberName<Viewbox>(x => x.Foreground), new BrowsableAttribute(false));

                    b.AddCustomAttributes(Extensions.GetMemberName<Viewbox>(x => x.Child), new CategoryAttribute(Properties.Resources.CommonProperties));
                    b.AddCustomAttributes(Extensions.GetMemberName<Viewbox>(x => x.Stretch), new CategoryAttribute(Properties.Resources.CommonProperties));
                    b.AddCustomAttributes(Extensions.GetMemberName<Viewbox>(x => x.StretchDirection), new CategoryAttribute(Properties.Resources.CommonProperties));
                });
        }
    }
}
  • It is recommended to follow the naming convention here. Take Viewbox for example, the file name is ViewboxMetadata.cs, and the class name is ViewboxMetadata.
  • The metadata class must inherit from AttributeTableBuilder.
  • You can add custom attributes in the constructor of the metadata class:
    • You can use either the call back model (as in ViewboxMetada.cs above) or the direct model. The callback model is supposedly more efficient.
      • direct model example:
            AddCustomAttributes(
                typeof(Viewbox),   // type
                "BorderThickness", // property name
                new Attribute[] { new BrowsableAttribute(false) }); // custom attribute array
    • To provide the property name parameter to AddCustomAttribute call, you can either use the method Extensions.GetMemberName() as in ViewboxMetadata.cs (will discuss more about it later in the post) to get the property name in a type safe way, or provide the property name directly as string like "BorderThickness" in above direct model implementation.

The AddTables(AttributeTableBuilder builder) method:

  • enumerates all subclasses of AttributeTableBuilder in the executing assembly
  • creates an instance of each found AttributeTableBuilder subclass:
        AttributeTableBuilder atb = (AttributeTableBuilder)Activator.CreateInstance(t);
  • add the attribute table of the found class to builder:
        builder.AddTable(atb.CreateTable());

Metadata.cs

Metadata.cs implements MetadataRegistration class, which inherits from MetadataRegistrationBase class implemented in MetadataBase.cs. It also implements IRegisterMetadata interface.

Metadata.cs:

// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.

using System.Reflection;
using Microsoft.Windows.Controls.Design.Common;
using Microsoft.Windows.Design.Metadata;

namespace Microsoft.Windows.Controls.Design
{
    /// <summary>
    /// MetadataRegistration class.
    /// </summary>
    public class MetadataRegistration : MetadataRegistrationBase,  IRegisterMetadata
    {
        /// <summary>
        /// Design time metadata registration class.
        /// </summary>
        public MetadataRegistration()
            : base()
        {
            AssemblyName asmName = typeof(Viewbox).Assembly.GetName();
            XmlResourceName = asmName.Name + ".Design." + asmName.Name + ".XML"; // "Microsoft.Windows.Controls.Design.Microsoft.Windows.Controls.XML"
            AssemblyFullName = ", " + asmName.FullName;
        }

        /// <summary>
        /// Borrowed from System.Windows.Controls.Toolbox.Design.MetadataRegistration:
        /// use a static flag to ensure metadata is registered only one.
        /// </summary>
        private static bool _initialized;

        /// <summary>
        /// Called by tools to register design time metadata.
        /// </summary>
        public void Register()
        {
            if (!_initialized)
            {
                MetadataStore.AddAttributeTable(BuildAttributeTable());
                _initialized = true;
            }
        }

        /// <summary>
        /// Provide a place to add custom attributes without creating a AttributeTableBuilder subclass.
        /// </summary>
        /// <param name="builder">The assembly attribute table builder.</param>
        protected override void AddAttributes(AttributeTableBuilder builder)
        {
        }
    }
}

Let's discuss some of its key methods:

MetadataRegistration

This constructor initializes two key fields:

  • XmlResourceName: resource name of the embedded documentation XML file, used by MetadataRegistrationBase.AddDescriptions method.
  • AssemblyFullName: full name of the run time assembly this design time assembly is for.

You need to replace the type Viewbox with your own class if you use Metadata.cs in your own design project.

Register

This is the only method of IRegisterMetadata interface. It adds the custom attribute table, built from AddDescripions, AddAttributes & AddTables methods described above, to designer's metadata store:
        MetadataStore.AddAttributeTable(BuildAttributeTable());

Extensions.cs

This file contains the implementation of extension method GetMemberName<T>(Expression<Func<T, object>> expr) that is used to get the name of a member of a type with compile time check and IntelliSense:

GetMemberName extension method

The idea was originally proposed by Jafar Husain (see his blog post Symbols in C# 3.0), and then improved by Justin Angel. It is a great trick to avoid typos, but the drawback is that it pulls in Silverlight references and assemblies into an otherwise pure .NET assembly. Here is the complete source:

Extensions.cs:

// (c) Copyright Microsoft Corporation.
// This source is subject to the Microsoft Public License (Ms-PL).
// Please see http://go.microsoft.com/fwlink/?LinkID=131993 for details.
// All other rights reserved.

using System;
using System.Linq.Expressions;

namespace Microsoft.Windows.Controls.Design.Common
{
    /// <summary>
    /// This set of internal extension methods provide general solutions and 
    /// utilities in a small enough number to not warrant a dedicated extension
    /// methods class.
    /// </summary>
    internal static class Extensions
    {
        /// <summary>
        /// Helper method to get member name with compile time verification to avoid typo.
        /// </summary>
        /// <typeparam name="T">The containing class of the member whose name is retrieved.</typeparam>
        /// <param name="expr">The lambda expression usually in the form of o => o.member.</param>
        /// <returns>The name of the property.</returns>
        public static string GetMemberName<T>(Expression<Func<T, object>> expr)
        {
            Expression body = ((LambdaExpression)expr).Body;
            MemberExpression memberExpression = body as MemberExpression;
            if (memberExpression == null)
            {
                memberExpression = (MemberExpression)((UnaryExpression)body).Operand;
            }
            return memberExpression.Member.Name;
        }
    }
}

Conclusion

This post described the implementation of design time features in the December 2008 release of Silverlight Toolkit, and introduced a simple framework for implementing design time feature for Silverlight controls. You can model the implementation and reuse the framework in your own projects. The framework is still very primitive, supporting metadata registration only, since that's all Blend supports for now. I will look into improving the framework and design time features for Silverlight Toolkit, like adding custom inline/extended/dialog editors, design time data, design time only behaviors etc.

As I stated in previous post Design Time Features in Silverlight Toolkit, design time experiences for controls are very important. It not only improves the experience and productivity of developers who use those controls with deign time features, but also improves the experience of end users, since more and more applications give users more flexibility in customizing user interface, like changing layout by dragging and dropping controls, or changing a control's settings, just like in a designer, except it is at run time.

Dec 24, 2008

Quick Blend Tutorial for Developers

Introduction

XAML is an major invention of WPF, and XMAL "programming" is an important part of WPF/Silverlight development. Even though XAML is XML and can be authored manually, it is mostly designed for tools, so good authoring tools are more important to XAML than to other languages like C# or HTML. Blend to XAML is what Visual Studio is to C#. There are many good tutorials and documents for Blend, like Jesse Liberty's Expression Blend for Developers. This post is more like "Learn Blend in 15 Minutes", aiming to give an quick overview and tutorial of Blend to developers who are authoring XAML mostly in Visual Studio or notepad, and so they will be interested and know how to get started in using Blend.

Project Management

Blend duplicates a lot of Visual Studio functionality and has great integration with it. It is very common (or even recommended, should I say) to have a WPF/Silverlight project open in both Blend and Visual Studio, using Blend for XAML authoring and Visual Studio for C# coding (or whatever language you prefer), and using either or both for project management.

Project manage in Blend is very similar to that in Visual Studio, via menu, Project Panel and Results Panel:
Blend: Project Management

  • Menu: the File, Edit, Project and Help menus are very similar to those in Visual Studio. Through those menus, you can create/open WPF/Silverlight projects, add items/references etc, and even build and run the solution, just like in Visual Studio.
    File Menu Project Menu
  • Project Panel: it is very similar to Visual Studio's Solution Explorer. You can right click on any .xaml/.cs file in the project panel and select "Edit in Visual Studio", and the project and the selected file will be open in Visual Studio for editing.
    Edit in Visual Studio Context Menu
  • Result Panel: the Output tab and Error tab are similar to Output window and Error List window in Visual Studio.

UI Authoring

UI authoring is done mostly via three panels: 
Blend: UI Authoring

  • Objects and Timeline Panel: the objects panel makes the logical tree structure very clear. It is easy to navigate the logical tree by expanding/collapsing/selecting an element in the tree, or restructure the tree by dragging and dropping elements along the tree. The selected element is highlighted in the objects panel, and in both the design view and xaml view of the documents panel; the properties panel displays the properties and events of the selected element. The internal node with yellow border (LayoutRoot in above screenshot) is the current default container.
  • Documents Panel: the documents panel is a tabbed window, with one tab for each open xaml file. It has a design view and a xaml view for each file.
    • You can use the tabs at the top of the documents window to switch among open documents or close documents(only one tab in above screenshot, since only one file page.xaml is open.).
    • You can switch among Design view, XAML view, or Split view of the selected document. You can also set the default document view in Tools -> Options -> Documents:
      Documents View Options
    • There is a breadcrumb control at the top of the design view (also called artboard) to show the logic tree path of the selected element. This is another way to navigate the logical tree besides the objects panel, and to switch among authoring modes as we will see shortly.
    • You can zoom the design view by
      • select the zoom icon from the toolbox, then click or alt click on the artboard;
      • use the zoom combobox right below the artboard: zoom combobox
      • use the View menu items or their shortcut keys: View menu
    • The design view is a WYSIWYG designer, where you can drag and drop an element from the toolbox or Asset Library along the left border of Blend window:
      Toolbox  Asset Library
    • The XAML view allows direct XAML authoring. It doesn't have IntelliSense yet, but you can use the design view and the error tab in the Results panel below to correct errors while you type.
  • Properties Panel: properties panel has two views: properties and events, indicated by two buttons at the top right corner of the panel.
    • In properties view: 
      image
      • properties of the selected element is grouped into categories, like Layout, Common Properties;
      • properties usually have helpful tooltip, like Content property tooltip in above screenshot;
      • properties usually have inline/extended/dialog editor(s) to help user pick a value, like the value of HorizontalAlignment property in above screenshot can be set by clicking one of the four buttons, instead of typing in values like "Center" in XAML view directly;
      • if you give a property a value (so it no longer takes the default value), the little square to its right will become white, like the Alignment properties in above screenshot;
      • you can click the down arrow (which has "Show advanced properties" tooltip) in most categories to see more properties of that category;
      • you can find a property quickly by typing part of its name in the Search box at the top of the properties panel:
        Search box in Properties Panel
    • In events view:
      Properties Panel: Events view
      • you can type the name of the event handler for an event, and Blend will open the project in Visual Studio, with a prototype implementation of the event handler in the code behind file:
        Blend opens Visual Studio for event handler coding
         

Animation Authoring

To create an animation, click the plus sign in Objects and Timeline panel, the "Create Storyboard Resource" dialog will pop up, asking for the name of the new storyboard:
Create Storyboard Resource

Blend then enters timeline recording mode. Let's create an animation that will double the size of the button. The key steps in creating an animation:

  1. create a keyframe, by first dragging the yellow timeline to a keytime, and then clicking the "Record Keyframe" green plus button;
  2. edit properties of UI elements at keytime, as described in the above UI Authoring section;
  3. repeat steps 1 and 2 for each keyframe;

As we can see in below screenshot:

  • in Objects and Timeline Panel:
    • the name of the timeline being edited: Storyboard1;
    • the keyframe being edited: keytime is 1 second, indicated by the yellow timeline;
    • the double arrow icons to the left of the nodes in the objects panel (button, RenderTransform, Scale, ScaleX, ScaleY in below screenshot) and the white dots along the timeline in the timeline panel show property changes for objects at the keytime ;
    • we can use the play controls at the top of the timeline panel, or drag the yellow timeline, to run the animation;
  • in Artboard of Documents Panel:
    • the red border, red recording button, and the red label "Timeline recording is on" all indicate Blend is in timeline recording mode;
    • you can click the red recording button to stop the recording mode and switch back to UI authoring mode;
  • In Properties Panel:
    • value of target properties: like ScaleTransform.ScaleX is set to 2;

Blend: Animation Authoring

Blend uses XXXAnimationUsingKeyFrames and generates one keyframe for each object/property/keyframe trio. We can fine tune the properties of a keyframe itself by first selecting it in the Objects Panel, and then setting its properties in the properties panel, like in below screenshot:

  • KeyTime is "00:00:01"
  • target object is button, target path is roughly RenderTransform.ScaleTransform.ScaleX
  • Value is "2"
  • KeySpline is "0.5,0.1,0.5,0.9"

Blend: Animation Authoring

we can also fine tune the properties of the storyboard in the same way, like in below screenshot:

  • Storyboard1 is selected
  • AutoReverse and RepeatBehavior properties are changed

Blend: Animation Authoring

This is the XAML code Blend generated for above simple animation:

  1: <Storyboard AutoReverse="True" RepeatBehavior="Forever" x:Name="Storyboard1">
  2: 	<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="button" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleX)">
  3: 		<SplineDoubleKeyFrame KeySpline="0.495999991893768,0.104000002145767,0.5,0.898999989032745" KeyTime="00:00:01" Value="2"/>
  4: 	</DoubleAnimationUsingKeyFrames>
  5: 	<DoubleAnimationUsingKeyFrames BeginTime="00:00:00" Storyboard.TargetName="button" Storyboard.TargetProperty="(UIElement.RenderTransform).(TransformGroup.Children)[0].(ScaleTransform.ScaleY)">
  6: 		<SplineDoubleKeyFrame KeyTime="00:00:01" Value="2"/>
  7: 	</DoubleAnimationUsingKeyFrames>
  8: </Storyboard>
  9: 

Clearly it is much easier to author animations in Blend instead of writing XAML manually.

Template Authoring

The beauty and power of WPF/Silverlight is the capability to re-template a control. It is rather easy to do that in Blend: select an menu item from the "Edit Control Parts (Template) -> Edit Template" context menu, either from Objects and Timeline Panel, or from the breadcrumb in Artboard:

Blend: Template Authoring

"Edit a Copy..." menu item is also an easy way to get the default template of a control. In below screenshot, we can see:

  • default template components in Objects and Timeline Panel
  • visual state groups, visual states, and transitions in Interaction Panel
  • properties of template components in Properties Panel. The orange square to the right of Background property indicates it is template bound.

Blend: Template Authoring

Drill in further: select the Disabled visual state, and we can see that the disabled visual in artboard is generated by the visual state transition storyboard that animates the Opacity property of the DisabledVisualElement from 0% to 55% in 0 second:

Blend: Template Authoring

Please note that near the top of the Object and Timeline panel, it shows Disabled State instead of a storyboard, indicating Blend is in state recording mode.

Now let's make some change to the default template by changing the focus visual state transition to skew the button when it is focused:

Template Authoring

  • select the Focused visual state in Interaction Panel, click the arrow plus icon on its right, select one of the three options (those options are for visual state transitions to and from this state, and between particular two states in the visual state group);
  • Blend is in visual state recording mode: focused is the selected visual state;
  • add a new skew transform to the top grid as discussed in Authoring Animations section. Notice that the button in artboard is now skewed. The WYSIWYG artboard makes animation authoring easy and intuitive.
  • delete the FocusVisualElement template component in Objects panel since we don't need it any more. Notice the tooltip at the top indicating Blend automatically deleted the visual state transition that shows the FocusVisualElement as well.

The Blend generated XAML for above custom template is 102 lines, 6179 characters! Imagine the difficulty in authoring that by hand!

Once we have a template, we can apply it to UI elements of the style's target type (or its sub type):

Apply Template

Style Authoring

Using style is usually better than setting properties on objects directly, since styles can be reused, are easy to maintain, and help separate presentation from content (the UI elements). Style authoring is similar to template authoring discussed above, except it is much simpler. You have to use the Object -> Edit Style menu to enter the style authoring mode. There is no context menu for it, which makes style authoring less discoverable.

Blend: Style Authoring

Select Create Empty... menu item, and the Create Style Resource dialog pops up, allow you to specify the name and location of the new style resource:

Create Style Resource Dialog

Then Blend enters style authoring mode:

  • the Objects and Timeline panel displays highlighted Style element instead of the UI logical tree;
  • the breadcrumb on top of artboard also indicates we are authoring LayoutRoot's style with a palette icon;
  • let's add something to the style by setting Background via the brush editor, notice the artboard reflects the change immediate;
  • the XAML view of documents panel shows the simple XAML generated for style authoring;

Blend: Style Authoring

Once we have a style resource, applying it is easy: just select the object and the use Object -> Edit Style -> Apply Resource menu item.

Apply Style

Resource Management

Storyboard, styles and templates authored above are all resources. We can use the Resources Panel to manage resources:

Resources Panel

  • we can see all the resources in both app.xaml (global resources) and current xaml file page.xaml that is being edited (local resources).
    • Not sure why Storyboard1 created in Animation Authoring section does not show up. Blend bug or user error?
  • we can edit a resource via its context menu or edit resource button:
    Resource Context Menu
  • we can also drag and drop a resource along the tree to change its location (and visibility), like moving between app.xaml and page.xaml, or [UserControl] and button.

Conclusion

To recap, Blend makes UI authoring easy and intuitive:

  • toolbox (and asset library) and artboard allows WYSIWYG UI authoring;
  • objects panel helps to organize all the UI elements in the logical tree;
  • properties panel simplifies property setting with editors;

Building on top of the intuitive UI authoring, Blend simplifies animation authoring by introducing the timeline recording mode:

  • Blend always uses XXXAnimationUsingKeyFrames, so a storyboard is always a collection of keyframes;
  • Blend generates keyframes indirectly by letting user set UI element properties at keytimes via its intuitive UI authoring interface;
  • Blend always generates all four transforms like below, to make its keyframe generation uniform for transforms:

<Button.RenderTransform>
    <TransformGroup>
        <ScaleTransform/>
        <SkewTransform/>
        <RotateTransform/>
        <TranslateTransform/>
    </TransformGroup>
</Button.RenderTransform>

On top of its UI and animation authoring, Blend simplifies template authoring by introducing the template authoring mode and state recording mode. A template consists of template parts and visual states. Template parts are just a logical tree of UI elements, and visual state transitions are storyboards.

Style authoring is even simpler: a style is just a collection of property setters. Blend introduces style authoring mode to distinguish between regular UI authoring and style authoring.

So Blend is a great XAML authoring tool. It is not just for designers, but can also give developers big productivity boost. Hopefully this post gives you a good overview of Blend and get you started in exploring Blend, enthusiastically :-). Thanks!

Dec 19, 2008

Silverlight Debugging with WinDbg and SOS

Introduction

In previous post Silverlight Debugging With Visual Studio, I demonstrated that Visual Studio is a powerful and the most convenient tool for debugging managed applications. But for some advanced debugging scenarios, like memory leak, deadlock, runaway process/thread, kernel debugging, and advanced remote/crash/UI debugging etc, WinDbg (or cdb, ntsd, kd) is usually preferred, if not the only option.

Debugging Tools for Windows

Overview

WinDbg is part of the Debugging Tools for Windows package:

Debugging Tools for Windows

Even though WinDbg is a GUI program, it is mostly used as a command line debugger like (and often with) kd, cdb and ntsd, and share the same debug engine and commands. It is super powerful and extensible, particularly well suited for system/low-level debugging like drivers and other operating system components. It does require good knowledge of hardware, operating system, and compiler to get the most out of the toolset. It's a great(or perfect, should I say :-) for native debugging, with its extensive and extensible commands and graphic user interface. Managed debugging support is via SOS debug extension, isn't nearly as good as native debugging, but there is tremendous value in being able to do integrated debugging across managed and native boundary, and for Silverlight, it is the only tool I am aware of that can load SOS and do integrated debugging.

Commands

WinDbg/cdb/ntsd/kd have been around for a long time, so they are very well documented. It's highly recommended that you read about Debugging Tools for Windows on microsoft.com and msdn. Here I only give a brief overview of its commands, to give you a feel and quick reference. All commands come with run time help (via ?|.help|!help [<cmd>] as discussed below), so an overview is usually enough to get your started.

WinDbg has three sets of commands:

Standard Commands

Mainly:

  • B* commands to set/clear/list/enable/disable breakpoints on address or access
  • D* commands to examine memory(stack/heap, local/global variables and parameters), E* command to modify memory, and S* to search memory
  • K* commands to examine call stack
  • P, T, G* commands for execution control

below ? command output is an great overview and frequent reference for people using windbg/cdb/ntsd/kd:

0:006> ?

Open debugger.chm for complete debugger documentation

B[C|D|E][<bps>] - clear/disable/enable breakpoint(s)
BL - list breakpoints
BA <access> <size> <addr> - set processor breakpoint
BP <address> - set soft breakpoint
D[type][<range>] - dump memory
DT [-n|y] [[mod!]name] [[-n|y]fields]
   [address] [-l list] [-a[]|c|i|o|r[#]|v] - dump using type information
DV [<name>] - dump local variables
E[type] <address> [<values>] - enter memory values
G[H|N] [=<address> [<address>...]] - go
K <count> - stacktrace
KP <count> - stacktrace with source arguments
LM[k|l|u|v] - list modules
LN <expr> - list nearest symbols
P [=<addr>] [<value>] - step over
Q - quit
R [[<reg> [= <expr>]]] - view or set registers
S[<opts>] <range> <values> - search memory
SX [{e|d|i|n} [-c "Cmd1"] [-c2 "Cmd2"] [-h] {Exception|Event|*}] - event filter
T [=<address>] [<expr>] - trace into
U [<range>] - unassemble
version - show debuggee and debugger version
X [<*|module>!]<*|symbol> - view symbols
? <expr> - display expression
?? <expr> - display C++ expression
$< <filename> - take input from a command file

Hit Enter...

<expr> unary ops: + - not by wo dwo qwo poi hi low
       binary ops: + - * / mod(%) and(&) xor(^) or(|)
       comparisons: == (=) < > !=
       operands: number in current radix, public symbol, <reg>
<type> : b (byte), w (word), d[s] (doubleword [with symbols]),
         a (ascii), c (dword and Char), u (unicode), l (list)
         f (float), D (double), s|S (ascii/unicode string)
         q (quadword)
<pattern> : [(nt | <dll-name>)!]<var-name> (<var-name> can include ? and *)
<range> : <address> <address>
        : <address> L <count>

User-mode options:
~ - list threads status
~#s - set default thread
| - list processes status
|#s - set default process

x86 options:
DG <selector> - dump selector
<reg> : [e]ax, [e]bx, [e]cx, [e]dx, [e]si, [e]di, [e]bp, [e]sp, [e]ip, [e]fl,
        al, ah, bl, bh, cl, ch, dl, dh, cs, ds, es, fs, gs, ss
        dr0, dr1, dr2, dr3, dr6, dr7
        fpcw, fpsw, fptw, st0-st7, mm0-mm7
         xmm0-xmm7
<flag> : iopl, of, df, if, tf, sf, zf, af, pf, cf
<addr> : #<16-bit protect-mode [seg:]address>,
         &<V86-mode [seg:]address>

Open debugger.chm for complete debugger documentation

Meta Commands

Those start with a dot, mostly for advanced debugging. Some important commands you should know:

  • .symfix and .sympath for using symbol server
  • .srcfix and .srcpath for using source server
  • .load, .loadby, .unload, .chain, .setdll for managing debug extensions
  • .server, .remote for remote debugging

It is well worth your time to get familiar with symbol server, source server and remote debugging: they will make your life a lot more easier. below is the output of .help command:

0:006> .help
. commands:
   .abandon - abandon the current process
   .allow_exec_cmds [0|1] - control execution commands
   .allow_image_mapping [0|1] - control on-demand image file mapping
   .apply_dbp [<options>] - add current data breakpoint state to a
                            register context
   .asm [<options>] - set disassembly options
   .asm- [<options>] - clear disassembly options
   .attach <proc> - attach to <proc> at next execution
   .block { <commands> } - brackets a set of commands for nested execution
   .bpsync [0|1] - special breakpoint behavior for multithreaded debuggees
   .break - break out of the enclosing loop
   .breakin - break into KD
   .cache [<options>] - virtual memory cache control
   .call <fn>(<arg1>, <arg2>, ...) - run a function in the debuggee
   .catch { <commands> } - catch failures in commands
   .chain - list current extensions
   .childdbg <0|1> - turn child process debugging on or off
   .clients - list currently active clients
   .closehandle [<options>] [<handle>] - close the given handle
   .continue - continue the enclosing loop
   .copysym [<options>] <path> - copy current symbol files to a directory
   .create <command line> - create a new process
   .createdir [<options>] [<path>] - control process creation options
   .cxr <address> - dump context record at specified address
                    k* after this gives cxr stack
   .dbgdbg - attach a debugger to the current debugger
   .debug_sw_wow [0|1] - allow interaction with software WOW emulation
   .detach - detach from the current process/dump
   .dml_file <file> - output DML content from file
   .dml_flow <start> <addr> - show basic block code flow
   .dml_start [<options>] - navigable overview of debugger activities
   .do { <commands> } (<cond>) - execute <commands> until <cond> is zero
   .drivers - This command was removed -- use 'lm' or .reload -l)
   .dump [<options>] <filename> - create a dump file on the host system
   .dvalloc [<options>] <bytes> - VirtualAlloc memory in the debuggee
   .dvfree [<options>] <offset> <bytes> - VirtualFree memory in the debuggee
   .echo ["<string>"|<string>] - echo string
   .echotime - output debugger time
   .echotimestamps [0|1] - toggle timestamp output on events
   .ecxr - dump context record for current exception
   .effmach [<machine>] - change current machine type
   .else { <commands> } - if/then/else conditional execution
   .elsif (<cond>) { <commands> } [<else clauses>] - if/then/else conditional
                                                     execution
   .enable_long_status [0|1] - dump LONG types in default base
   .enable_unicode [0|1] - dump USHORT array/pointers and unicode strings
   .endsrv <id> - disable the given engine server
   .endpsrv - cause the current session's remote server to exit
   .enumtag - enumerate available tagged data
   .event_code - display cached event instructions
   .eventlog - display log of recent events
   .events - display and select available events
   .eventstr - display any event strings registered by debuggee
   .exepath [<dir>[;...]] - set executable search path
   .exepath+ [<dir>[;...]] - append executable search path
   .expr - control expression evaluator
   .exptr <address> - do .exr and .cxr for EXCEPTION_POINTERS
   .exr <address> - dump exception record at specified address
   .extmatch [<opts>] <pattern> - display all extensions matching pattern
   .extpath <opts> [<dir>[;...]] - set extension search path
   .extpath+ <opts> [<dir>[;...]] - append extension search path
   .f+ - set current stack frame to caller of current frame
   .f- - set current stack frame to callee of current frame
   .fiber <address> - sets context of fiber at address
                      resets context if no address specified
   .fiximports <pattern> - attempts to link imports for images
   .fnent <address> - dump function entry for the given code address
   .fnret <fnaddr> [<retval>] - display formatted return value
   .for ( <init> ; <cond> ; <step> ) { <commands> } - execute <commands> and
                                                      <step> until <cond> is
                                                      zero
   .force_radix_output [0|1] - dump integer types in default base
   .force_system_init [<options>] - force pending systems to initialize if possible
   .force_tb - forcibly allow branch tracing
   .foreach [opts] ( <alias> { <tcmds> } ) { <ecmds> } - execute <ecmds> for
                                                         each token in the
                                                         output of <tcmds>
   .fpo <options> - control override FPO information
   .frame [<frame>] - set current stack frame for locals
   .formats <expr> - displays expression result in many formats
   .help [<options>] - display this help
   .holdmem <options> [range] - hold and compare memory data
   .if (<cond>) { <commands> } [<else clauses>] - if/then/else conditional
                                                  execution
   .ignore_missing_pages [0|1] - control kernel summary dump missing
                                 page error message
   .imgscan <options> - scan memory for PE images
   .jdinfo <jdi_addr> - interpret AeDebug information
   .kframes <count> - set default stack trace depth
   .kill - kill the current process
   .lastevent - display the last event that occurred
   .leave - exit the enclosing .catch
   .lines - toggle line symbol loading
   .load <name> - add this extension DLL to the extension chain
   .loadby <name> <mod> - add the extension DLL in the module
                          directory to the extension chain
   .locale [<locale>] - set the current locale
   .logfile - display log status
   .logopen [<file>] - open new log file
   .logappend [<file>] - append to log file
   .logclose - close log file
   .netsyms [0|1] - allow/disallow net symbol paths
   .netuse [<options>] - manage net connections
   .noshell - disable shell commands
   .noversion - disable extension version checking
   .ofilter <pattern> - filter debuggee output against the given pattern
   .ocommand <prefix> - treat output with the given prefix as a command
   .opendump <file> - open a dump file
   .outmask <mask> - set bits in the current output mask
   .outmask- <mask> - clear bits in the current output mask
   .pcmd [<options>] - control per-prompt command
   .pop [<options>] - pop state
   .prefer_dml [0|1] - control DML mode default
   .printf "<format>", <args...> - formatted output
   .process [<address>] - sets implicit process
                          resets default if no address specified
   .prompt_allow [<options>] - control what information can be displayed
                               at the prompt
   .push [<options>] - push state
   .quit_lock [<options>] - locks session against unexpected quit
   .readmem <file> <range> - read raw memory from a file
   .record_branches [0|1] - controls recording of processor branching
   .reload [<image.ext>[=<address>,<size>]] - reload symbols
   .restart - request a session restart
   .remote <pipename> - start remote.exe server
   .secure [0|1] - disallow operations dangerous for the host
   .send_file <options> - send files to remote server
   .server <options> - start engine server
   .servers - list active remoting servers
   .setdll <name> - debugger will search for extensions in this DLL first
   .shell [<command>] - execute shell command
   .show_read_failures [<opts>] - control extra read failure output
   .show_sym_failures [<opts>] - control extra symbol failure output
   .sleep <milliseconds> - debugger sleeps for given duration
                           useful for allowing access to a machine that's
                           broken in on an ntsd -d
   .srcfix [<path extra>] - fix source search path
   .srcfix+ [<path extra>] - append fixed source search path
   .srcnoisy [0|1] - control verbose source loading output
   .srcpath [<dir>[;...]] - set source search path
   .srcpath+ [<dir>[;...]] - append source search path
   .step_filter [<opts>] ["<pattern>[;<pattern>...]"] - Set symbol patterns
                                                        to skip when stepping
   .symfix [<localsym>] - fix symbol search path
   .symfix+ [<localsym>] - append fixed symbol search path
   .symopt <flags> - set symbol options
   .symopt+ <flags> - set symbol options
   .symopt- <flags> - clear symbol options
   .sympath [<dir>[;...]] - set symbol search path
   .sympath+ [<dir>[;...]] - append symbol search path
   .thread [<address>] - sets context of thread at address
                         resets default context if no address specified
   .time - displays session time information
   .ttime - displays thread time information
   .tlist - list running processes
   .typeopt <flags> - set/clear type options
   .unload <name> - remove this extension DLL from the list of extension DLLs
   .unloadall - remove all extension DLLs from the list of extensions DLLs
   .wake - wake up a .sleep'ing debugger
   .while (<cond>) { <commands> } - execute <commands> while <cond> is non-zero
   .writemem <file> <range> - write raw memory to a file

Use ".hh <command>" or open debugger.chm in the debuggers directory to get
detailed documentation on a command.

Extension Commands

Those start with !, usually for a particular area, like SOS extension commands for managed debugging. You can use !<ext>.help to list commands of an extension, like !exts.help. For advanced system debugging, like detecting memory leak, deadlock etc, you have to be very familiar with those extension commands.

SOS

Overview

SOS is the acronym for Son of Strike (don't ask me why), a debug extension for managed debugging. I believe it was invented by CLR team for CLR debugging, so it has been around for a long time, and there are lots of good information on the web. Some recommended readings:

Each version of CLR has its own SOS (.NET Framework 3.5 still uses CLR 2.0, and Silverlight has its own CLR):

Directory of C:\Program Files\Debugging Tools for Windows\clr10
09/19/2007  07:01 PM           948,784 sos.dll
               1 File(s)        948,784 bytes

Directory of C:\Program Files\Microsoft Silverlight\2.0.31005.0
10/05/2008  02:16 AM           495,424 sos.dll
               1 File(s)        495,424 bytes

Directory of C:\Windows\Microsoft.NET\Framework\v1.1.4322
07/15/2004  12:35 AM           319,488 SOS.dll
               1 File(s)        319,488 bytes

Directory of C:\Windows\Microsoft.NET\Framework\v2.0.50727
07/27/2008  10:03 AM           392,184 SOS.dll
               1 File(s)        392,184 bytes

Commands

SOS run time help is so great that you almost don't need any other doc other than a few try and error and some frustration :-)

0:016> !help
-------------------------------------------------------------------------------
SOS is a debugger extension DLL designed to aid in the debugging of managed
programs. Functions are listed by category, then roughly in order of
importance. Shortcut names for popular functions are listed in parenthesis.
Type "!help <functionname>" for detailed info on that function.

Object Inspection                  Examining code and stacks
-----------------------------      -----------------------------
DumpObj (do)                       Threads
DumpArray (da)                     CLRStack
DumpStackObjects (dso)             IP2MD
DumpHeap                           U
DumpVC                             DumpStack
GCRoot                             EEStack
ObjSize                            GCInfo
FinalizeQueue                      EHInfo
PrintException (pe)                COMState
TraverseHeap                       BPMD

Examining CLR data structures      Diagnostic Utilities
-----------------------------      -----------------------------
DumpDomain                         VerifyHeap
EEHeap                             VerifyObj
Name2EE                            FindRoots
SyncBlk                            HeapStat
DumpMT                             GCWhere
DumpClass                          ListNearObj (lno)
DumpMD                             GCHandles
Token2EE                           GCHandleLeaks
EEVersion                          FinalizeQueue (fq)
DumpModule                         FindAppDomain
ThreadPool                         SaveModule
DumpAssembly                       ProcInfo
DumpSigElem                        StopOnException (soe)
DumpRuntimeTypes                   DumpLog
DumpSig                            VMMap
RCWCleanupList                     VMStat
DumpIL                             MinidumpMode
                                   AnalyzeOOM (ao)

Examining the GC history           Other
-----------------------------      -----------------------------
HistInit                           FAQ
HistStats
HistRoot
HistObj
HistObjFind
HistClear

Sample Debug Demonstration

I will use the same simplest Silverlight application in previous post (Silverlight Debugging With Visual Studio) to demonstrate WinDbg debugging with SOS:

WinDbg

Here is the debug log with my annotations (start with //):

// set up symbol path to use symbol server
0:013> .sympath
Symbol search path is: <empty>
0:013> .symfix C:\Users\ningz\AppData\Local\Temp\Symbols
0:013> .sympath
Symbol search path is: SRV*C:\Users\ningz\AppData\Local\Temp\Symbols*\\symbols\symbols

// add pdb path for the application being debugged
0:013> .sympath+"C:\Users\ningz\Documents\Visual Studio 2008\Projects\SilverlightApplication1\SilverlightApplication1\Bin\Debug"
Symbol search path is: SRV*C:\Users\ningz\AppData\Local\Temp\Symbols*
http://msdl.microsoft.com/download/symbols;C:\Users\ningz\Documents\Visual Studio 2008\Projects\SilverlightApplication1\SilverlightApplication1\Bin\Debug

// reload all symbols
0:013> .reload
Reloading current modules
..................................................................................................................


// set up source path to use source server
0:013> .srcpath
Source search path is: <empty>
0:013> .srcfix
Source search path is: SRV*
0:013> .srcpath+"C:\Users\ningz\Documents\Visual Studio 2008\Projects\SilverlightApplication1\SilverlightApplication1"
Source search path is: SRV*;C:\Users\ningz\Documents\Visual Studio 2008\Projects\SilverlightApplication1\SilverlightApplication1


// load SOS. Please note:
// 1. it seems mscorwks is replaced with coreclr in Silverlight
// 2. instead of .loadby, you can also use .load "C:\Program Files\Microsoft Silverlight\2.0.31005.0\sos.dll"
0:013> .loadby sos coreclr
Loaded c:\Program Files\Microsoft Silverlight\2.0.31005.0\sos extension DLL


//set a breakpoint for button click event handler
0:013> !bpmd SilverlightApplication1 SilverlightApplication1.Page.Button_Click
Found 1 methods...
MethodDesc = 03983a30
Adding pending breakpoints...
0:013> bl
0 e 0428215b     0001 (0001)  0:****
0:013> g
ModLoad: 70800000 7080c000   C:\Windows\system32\dwmapi.dll


// click the button and break into windbg
Breakpoint 0 hit
*** WARNING: Unable to verify checksum for SilverlightApplication1.dll
SilverlightApplication1!Button_Click:
04850398 55              push    ebp
Integrated managed debugging does not support enumeration of local variables.
See
http://dbg/managed.htm for more details.
Managed frame processing failed, HRESULT 0x80004005

// compare !threads and ~* output
0:005> !threads // list managed threads only
ThreadCount:      3
UnstartedThread:  0
BackgroundThread: 3
PendingThread:    0
DeadThread:       0
Hosted Runtime:   yes
                                      PreEmptive   GC Alloc           Lock
       ID OSID ThreadOBJ    State     GC       Context       Domain   Count APT Exception
   5    1 1b08 043576e0       220 Disabled 04f9f214:04f9ffe8 06f529d8     0 STA
   9    2  928 043689c8      b220 Enabled  00000000:00000000 0433c738     0 MTA (Finalizer)
  10    3 1e8c 06f509d0      1220 Enabled  00000000:00000000 0433c738     0 Ukn
0:005> ~* // list all threads
   0  Id: 162c.1a28 Suspend: 1 Teb: 7ffde000 Unfrozen
      Start: iexplore!wWinMainCRTStartup (00a52d79)
      Priority: 0  Priority class: 32  Affinity: 3
   2  Id: 162c.1020 Suspend: 1 Teb: 7ffdc000 Unfrozen
      Start: ole32!CRpcThreadCache::RpcWorkerThreadEntry (76dfe0cc)
      Priority: 0  Priority class: 32  Affinity: 3
   3  Id: 162c.1e00 Suspend: 1 Teb: 7ffda000 Unfrozen
      Start: ntdll!TppWaiterpThread (778ab61b)
      Priority: 0  Priority class: 32  Affinity: 3
   4  Id: 162c.1f00 Suspend: 1 Teb: 7ffd9000 Unfrozen
      Start: msvcrt!_endthreadex+0x6f (76162da8)
      Priority: 0  Priority class: 32  Affinity: 3
.  5  Id: 162c.1b08 Suspend: 1 Teb: 7ffd8000 Unfrozen
      Start: IEFRAME!CTabWindow::_TabWindowThreadProc (6c163fdb)
      Priority: 0  Priority class: 32  Affinity: 3
   7  Id: 162c.170 Suspend: 1 Teb: 7ffd6000 Unfrozen
      Start: npctrl!CXcpDispatcher::DispatcherTimerCallback (6a6d16f4)
      Priority: 0  Priority class: 32  Affinity: 3
   8  Id: 162c.1cd0 Suspend: 1 Teb: 7ffd5000 Unfrozen
      Start: coreclr!DebuggerRCThread::ThreadProcStatic (640eb5cd)
      Priority: 0  Priority class: 32  Affinity: 3
   9  Id: 162c.928 Suspend: 1 Teb: 7ffd4000 Unfrozen
      Start: coreclr!Thread::intermediateThreadProc (63fb8e4f)
      Priority: 0  Priority class: 32  Affinity: 3
  10  Id: 162c.1e8c Suspend: 1 Teb: 7ffd3000 Unfrozen
      Start: coreclr!Thread::intermediateThreadProc (63fb8e4f)
      Priority: 0  Priority class: 32  Affinity: 3
  11  Id: 162c.1fac Suspend: 1 Teb: 7ffaf000 Unfrozen
      Start: agcore!CMulticoreTaskScheduler::WorkerThread (660547de)
      Priority: 0  Priority class: 32  Affinity: 3
  12  Id: 162c.1b78 Suspend: 1 Teb: 7ffae000 Unfrozen
      Start: agcore!CMulticoreTaskScheduler::WorkerThread (660547de)
      Priority: 0  Priority class: 32  Affinity: 3
  13  Id: 162c.1d18 Suspend: 1 Teb: 7ffdb000 Unfrozen
      Start: ntdll!RtlUserThreadStart (778c9a78)
      Priority: 0  Priority class: 32  Affinity: 3
Managed frame processing failed, HRESULT 0x80004005
Integrated managed debugging does not support enumeration of local variables.
See
http://dbg/managed.htm for more details.


// compare !clrstack & k output
0:005> !clrstack -p // list managed stack frames only
OS Thread Id: 0x1b08 (5)
ESP       EIP    
0450f850 04800398 SilverlightApplication1.Page.Button_Click(System.Object, System.Windows.RoutedEventArgs)
    PARAMETERS:
        this (<CLR reg>) = 0x04f59c78
        sender (<CLR reg>) = 0x04f5b948
        e (0x0450f854) = 0x04f9f1f0

0450f858 048cde0d System.Windows.Controls.Primitives.ButtonBase.OnClick()
    PARAMETERS:
        this (0x0450f864) = 0x04f5b948

0450f870 048cdd10 System.Windows.Controls.Button.OnClick()
    PARAMETERS:
        this = <no data>

0450f880 048cdc3d System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp(System.Windows.Input.MouseButtonEventArgs)
    PARAMETERS:
        this (0x0450f884) = 0x04f5b948
        e (0x0450f880) = 0x04f9f188

0450f890 048cdba1 System.Windows.Controls.Control.OnMouseLeftButtonUp(System.Windows.Controls.Control, System.EventArgs)
    PARAMETERS:
        ctrl = <no data>
        e = <no data>

0450f8a0 0483a0e7 MS.Internal.JoltHelper.FireEvent(IntPtr, IntPtr, Int32, System.String)
    PARAMETERS:
        unmanagedObj (0x0450f8f8) = 0x04355c48
        unmanagedObjArgs (0x0450f8f4) = 0x0707be00
        argsTypeIndex (0x0450f928) = 0x000000a9
        eventName (0x0450f924) = 0x04f9f16c

0450fa74 63ef17b0 [GCFrame: 0450fa74]
0450fb30 63ef17b0 [ContextTransitionFrame: 0450fb30]
0450fc28 63ef17b0 [UMThkCallFrame: 0450fc28]
0:005> kP // list all stack frames
ChildEBP RetAddr 
0450f868 048cde0d SilverlightApplication1!Button_Click(<HRESULT 0x80004001>)
0450f868 048cdd10 System_Windows!System.Windows.Controls.Primitives.ButtonBase.OnClick(<HRESULT 0x80004001>)+0x5d
0450f878 048cdc3d System_Windows!System.Windows.Controls.Button.OnClick(<HRESULT 0x80004001>)+0x48
0450f888 048cdba1 System_Windows!System.Windows.Controls.Primitives.ButtonBase.OnMouseLeftButtonUp(<HRESULT 0x80004001>)+0x85
0450f898 0483a0e7 System_Windows!System.Windows.Controls.Control.OnMouseLeftButtonUp(<HRESULT 0x80004001>)+0x41
0450f91c 63ef17b0 System_Windows!MS.Internal.JoltHelper.FireEvent(<HRESULT 0x80004001>)+0x1b7
0450f934 63ef5f98 coreclr!CallDescrWorker+0x33
0450f9b0 63f032bc coreclr!CallDescrWorkerWithHandler+0x8e
0450f9d0 63f0345c coreclr!ForwardCallToManagedMethod+0x55
0450faf4 63fc4abf coreclr!DoUMThunkCallWorker+0x199
0450fbe0 04c513d2 coreclr!DoUMThunkCall+0x1b4
WARNING: Frame IP not in any known module. Following frames may be wrong.
0450fc10 6607e0db 0x4c513d2
0450fc70 6a6dc1fc agcore!CCoreServices::CLR_FireEvent+0x183
Managed frame processing failed, HRESULT 0x80004005
0450fc84 6a6dc187 npctrl!CommonBrowserHost::CLR_FireEvent+0x2a
0450fcc8 6a6dc048 npctrl!CControlBase::ScriptCallback+0x366
0450fd08 6a6dc013 npctrl!CXcpDispatcher::OnScriptCallback+0xee
0450fd14 6a6dbfef npctrl!CXcpDispatcher::OnWindowMessage+0x3a
0450fd2c 7731f8d2 npctrl!CXcpDispatcher::WindowProc+0x7f
0450fd58 7731f794 USER32!InternalCallWinProc+0x23
0450fdd0 77320008 USER32!UserCallWinProcCheckWow+0x14b


// examine heap, note the convenience of strong typed object heap,
// instead of un-typed bytes as in native heap
0:005> !dumpheap -type SilverlightApplication1
Address       MT     Size
04f4f31c 046336b0       36    
04f59c78 04633a6c       68    
total 2 objects
Statistics:
      MT    Count    TotalSize Class Name
046336b0        1           36 SilverlightApplication1.App
04633a6c        1           68 SilverlightApplication1.Page
Total 2 objects
0:005> !do 04f59c78
Name:        SilverlightApplication1.Page
MethodTable: 04633a6c
EEClass:     0463196c
Size:        68(0x44) bytes
File:        SilverlightApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
047d97a0  40000dc        4 ...al.NativeResource  0 instance 04f5a5c0 m_nativePtr
04853ab8  40000df        8 ... System.Windows]]  0 instance 00000000 _valueTable
047d9874  40000e0        c ...reTypeEventHelper  0 instance 04f5a600 _coreTypeEventHelper
04856388  40000e3       10 ...bject, mscorlib]]  0 instance 04f79a58 _treeChildren
047ae03c  40000e4       14 ...rnal.IManagedPeer  0 instance 00000000 _treeParent
03d50a90  40000dd      2b0        System.IntPtr  1   shared   static StaticNativePointer
    >> Domain:Value  0433c738:NotInit  06f529d8:00000000 <<
03d50f28  40000de      2b4       System.Boolean  1   shared   static _isCoreCreate
    >> Domain:Value  0433c738:NotInit  06f529d8:0 <<
047ae384  40000e1        8 ....DependencyObject  0   shared   static PropertyReferencesHolder
    >> Domain:Value  0433c738:NotInit  06f529d8:04f489b8 <<
04661618  40000e2        c ...flection.Assembly  0   shared   static _executingAssembly
    >> Domain:Value  0433c738:NotInit  06f529d8:00000000 <<
047d0b90  40000f1       18 ...rs.AutomationPeer  0 instance 00000000 m_pAP
047d7404  40000e8       10 ...ependencyProperty  0   shared   static OpacityProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a4a8 <<
047d7404  40000e9       14 ...ependencyProperty  0   shared   static ClipProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a4d0 <<
047d7404  40000ea       18 ...ependencyProperty  0   shared   static RenderTransformProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a4f8 <<
047d7404  40000eb       1c ...ependencyProperty  0   shared   static OpacityMaskProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a50c <<
047d7404  40000ec       20 ...ependencyProperty  0   shared   static RenderTransformOriginProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a534 <<
047d7404  40000ed       24 ...ependencyProperty  0   shared   static IsHitTestVisibleProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a548 <<
047d7404  40000ee       28 ...ependencyProperty  0   shared   static VisibilityProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a570 <<
047d7404  40000ef       2c ...ependencyProperty  0   shared   static RenderSizeProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a598 <<
047d7404  40000f0       30 ...ependencyProperty  0   shared   static UseLayoutRoundingProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a5ac <<
00000000  4000108       1c                       0 instance 00000000 BindingValidationError
04856ae4  400010a       20 ...angedEventHandler  0 instance 00000000 DPChanged
048bc8a4  400010b       24 ...angedEventHandler  0 instance 00000000 DataContextChanged
03d50f28  400010c       2c       System.Boolean  1 instance        0 _isDataContextBound
0481f43c  400010d       28 ...angedEventHandler  0 instance 00000000 _sizeChanged
047d7404  40000f2       34 ...ependencyProperty  0   shared   static TriggersProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a144 <<
047d7404  40000f3       38 ...ependencyProperty  0   shared   static ResourcesProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a158 <<
047d7404  40000f4       3c ...ependencyProperty  0   shared   static LanguageProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a180 <<
047d7404  40000f5       40 ...ependencyProperty  0   shared   static ActualWidthProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a194 <<
047d7404  40000f6       44 ...ependencyProperty  0   shared   static ActualHeightProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a1a8 <<
047d7404  40000f7       48 ...ependencyProperty  0   shared   static WidthProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a1bc <<
047d7404  40000f8       4c ...ependencyProperty  0   shared   static HeightProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a1d0 <<
047d7404  40000f9       50 ...ependencyProperty  0   shared   static MinWidthProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a1e4 <<
047d7404  40000fa       54 ...ependencyProperty  0   shared   static MaxWidthProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a1f8 <<
047d7404  40000fb       58 ...ependencyProperty  0   shared   static MinHeightProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a20c <<
047d7404  40000fc       5c ...ependencyProperty  0   shared   static MaxHeightProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a220 <<
047d7404  40000fd       60 ...ependencyProperty  0   shared   static HorizontalAlignmentProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a234 <<
047d7404  40000fe       64 ...ependencyProperty  0   shared   static VerticalAlignmentProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a248 <<
047d7404  40000ff       68 ...ependencyProperty  0   shared   static MarginProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a25c <<
047d7404  4000100       6c ...ependencyProperty  0   shared   static StyleProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a284 <<
047d7404  4000101       70 ...ependencyProperty  0   shared   static ParentProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a2ac <<
047d7404  4000102       74 ...ependencyProperty  0   shared   static NameProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a2c0 <<
047d7404  4000103       78 ...ependencyProperty  0   shared   static TagProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a2d4 <<
047d7404  4000104       7c ...ependencyProperty  0   shared   static TagInternalProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a2fc <<
04851464  4000105       80 ...ndows.RoutedEvent  0   shared   static LoadedEvent
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a420 <<
047d7404  4000106       84 ...ependencyProperty  0   shared   static CursorProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a440 <<
047d7404  4000107       88 ...ependencyProperty  0   shared   static InstanceDelegateStoreProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a468 <<
047d7404  4000109       8c ...ependencyProperty  0   shared   static DataContextProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a494 <<
047a1e2c  400010e       90 ...rence, mscorlib]]  0   shared   static _staticDelegateList
    >> Domain:Value  0433c738:NotInit  06f529d8:00000000 <<
00000000  4000264       30 ...angedEventHandler  0 instance 00000000 IsEnabledChanged
047d7404  4000252      430 ...ependencyProperty  0   shared   static IsTabStopProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59ce4 <<
047d7404  4000253      434 ...ependencyProperty  0   shared   static TabIndexProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59cf8 <<
047d7404  4000254      438 ...ependencyProperty  0   shared   static TemplateProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59d20 <<
047d7404  4000255      43c ...ependencyProperty  0   shared   static TabNavigationProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59d48 <<
047d7404  4000256      440 ...ependencyProperty  0   shared   static PaddingProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59d70 <<
047d7404  4000257      444 ...ependencyProperty  0   shared   static HorizontalContentAlignmentProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59d98 <<
047d7404  4000258      448 ...ependencyProperty  0   shared   static VerticalContentAlignmentProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59dc0 <<
047d7404  4000259      44c ...ependencyProperty  0   shared   static BackgroundProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59de8 <<
047d7404  400025a      450 ...ependencyProperty  0   shared   static BorderBrushProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59dfc <<
047d7404  400025b      454 ...ependencyProperty  0   shared   static BorderThicknessProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59e10 <<
047d7404  400025c      458 ...ependencyProperty  0   shared   static FontSizeProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59e24 <<
047d7404  400025d      45c ...ependencyProperty  0   shared   static FontFamilyProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59e4c <<
047d7404  400025e      460 ...ependencyProperty  0   shared   static ForegroundProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59e60 <<
047d7404  400025f      464 ...ependencyProperty  0   shared   static FontWeightProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59e88 <<
047d7404  4000260      468 ...ependencyProperty  0   shared   static FontStyleProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59eb0 <<
047d7404  4000261      46c ...ependencyProperty  0   shared   static FontStretchProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59ed8 <<
047d7404  4000262      470 ...ependencyProperty  0   shared   static DefaultStyleKeyProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59eec <<
047d7404  4000263      474 ...ependencyProperty  0   shared   static IsEnabledProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59f00 <<
04857f14  4000265      478 ... System.Windows]]  0   shared   static _resourceDictionaryCache
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5cae4 <<
03cf5668  4000266      47c      System.Object[]  0   shared   static _controlDelgates
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59f14 <<
047d7404  4000501      77c ...ependencyProperty  0   shared   static ContentProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59cd0 <<
04852580  4000002       34 ...ows.Controls.Grid  0 instance 04f79770 LayoutRoot
04852a10  4000003       38 ...s.Controls.Button  0 instance 04f5b948 button
03d50f28  4000004       3c       System.Boolean  1 instance        1 _contentLoaded

// two lines above is the button object of the page object
// remember its address 04f5b948, since we will refer to it later
// we can get the same address for the button object via dumpheap command:
0:005> !dumpheap -mt 04852a10 
Address       MT     Size
04f5b948 04852a10       92    
total 1 objects
Statistics:
      MT    Count    TotalSize Class Name
04852a10        1           92 System.Windows.Controls.Button
Total 1 objects
0:005> !gcwhere 04f5b948 // button is in gen0, since there is no GC yet
Address            Gen   Heap   segment            begin              allocated           size
04f5b948   0      0     04f40000   04f41000   04f9fff4    0x5c(92)
0:005> !gcroot 04f5b948  // see who's holding reference to the button object
Note: Roots found on stacks may be false positives. Run "!help gcroot" for
more info.
// registry references
ecx:Root:  04f59c78(SilverlightApplication1.Page)->
  04f5b948(System.Windows.Controls.Button)->
edx:Root:  04f5b948(System.Windows.Controls.Button)->
// stack references
Scan Thread 5 OSTHread 1b08
ESP:450f860:Root:  04f5ca18(System.Windows.RoutedEventHandler)->
  04f59c78(SilverlightApplication1.Page)->
  04f5b948(System.Windows.Controls.Button)->
ESP:450f864:Root:  04f5b948(System.Windows.Controls.Button)->
ESP:450f874:Root:  04f5b948(System.Windows.Controls.Button)->
ESP:450f884:Root:  04f5b948(System.Windows.Controls.Button)->
ESP:450f894:Root:  04f5b948(System.Windows.Controls.Button)->
ESP:450f8d4:Root:  04f5b948(System.Windows.Controls.Button)->
ESP:450f8e4:Root:  04f5b948(System.Windows.Controls.Button)->
Scan Thread 9 OSTHread 928
Scan Thread 10 OSTHread 1e8c
// domain references
DOMAIN(06F529D8):HANDLE(Pinned):46412f8:Root:  05f44260(System.Object[])->
  04f45234(System.Collections.Generic.List`1[[System.Object, mscorlib]])->
  04f59980(System.Object[])->
  04f59c78(SilverlightApplication1.Page)->
  04f5b948(System.Windows.Controls.Button)->
Managed frame processing failed, HRESULT 0x80004005
Integrated managed debugging does not support enumeration of local variables.
See
http://dbg/managed.htm for more details.


// examine the button object itself
0:005> !do 04f5b948
Name:        System.Windows.Controls.Button
MethodTable: 04852a10
EEClass:     0482e5f0
Size:        92(0x5c) bytes
File:        c:\Program Files\Microsoft Silverlight\2.0.31005.0\System.Windows.dll
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
047d97a0  40000dc        4 ...al.NativeResource  0 instance 04f5bd58 m_nativePtr
04853ab8  40000df        8 ... System.Windows]]  0 instance 04f5c0bc _valueTable
047d9874  40000e0        c ...reTypeEventHelper  0 instance 04f5bd88 _coreTypeEventHelper
04856388  40000e3       10 ...bject, mscorlib]]  0 instance 04f5bee8 _treeChildren
047ae03c  40000e4       14 ...rnal.IManagedPeer  0 instance 04f79770 _treeParent
03d50a90  40000dd      2b0        System.IntPtr  1   shared   static StaticNativePointer
    >> Domain:Value  0433c738:NotInit  06f529d8:00000000 <<
03d50f28  40000de      2b4       System.Boolean  1   shared   static _isCoreCreate
    >> Domain:Value  0433c738:NotInit  06f529d8:0 <<
047ae384  40000e1        8 ....DependencyObject  0   shared   static PropertyReferencesHolder
    >> Domain:Value  0433c738:NotInit  06f529d8:04f489b8 <<
04661618  40000e2        c ...flection.Assembly  0   shared   static _executingAssembly
    >> Domain:Value  0433c738:NotInit  06f529d8:00000000 <<
047d0b90  40000f1       18 ...rs.AutomationPeer  0 instance 00000000 m_pAP
047d7404  40000e8       10 ...ependencyProperty  0   shared   static OpacityProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a4a8 <<
047d7404  40000e9       14 ...ependencyProperty  0   shared   static ClipProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a4d0 <<
047d7404  40000ea       18 ...ependencyProperty  0   shared   static RenderTransformProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a4f8 <<
047d7404  40000eb       1c ...ependencyProperty  0   shared   static OpacityMaskProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a50c <<
047d7404  40000ec       20 ...ependencyProperty  0   shared   static RenderTransformOriginProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a534 <<
047d7404  40000ed       24 ...ependencyProperty  0   shared   static IsHitTestVisibleProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a548 <<
047d7404  40000ee       28 ...ependencyProperty  0   shared   static VisibilityProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a570 <<
047d7404  40000ef       2c ...ependencyProperty  0   shared   static RenderSizeProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a598 <<
047d7404  40000f0       30 ...ependencyProperty  0   shared   static UseLayoutRoundingProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a5ac <<
00000000  4000108       1c                       0 instance 00000000 BindingValidationError
04856ae4  400010a       20 ...angedEventHandler  0 instance 00000000 DPChanged
048bc8a4  400010b       24 ...angedEventHandler  0 instance 00000000 DataContextChanged
03d50f28  400010c       2c       System.Boolean  1 instance        0 _isDataContextBound
0481f43c  400010d       28 ...angedEventHandler  0 instance 00000000 _sizeChanged
047d7404  40000f2       34 ...ependencyProperty  0   shared   static TriggersProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a144 <<
047d7404  40000f3       38 ...ependencyProperty  0   shared   static ResourcesProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a158 <<
047d7404  40000f4       3c ...ependencyProperty  0   shared   static LanguageProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a180 <<
047d7404  40000f5       40 ...ependencyProperty  0   shared   static ActualWidthProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a194 <<
047d7404  40000f6       44 ...ependencyProperty  0   shared   static ActualHeightProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a1a8 <<
047d7404  40000f7       48 ...ependencyProperty  0   shared   static WidthProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a1bc <<
047d7404  40000f8       4c ...ependencyProperty  0   shared   static HeightProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a1d0 <<
047d7404  40000f9       50 ...ependencyProperty  0   shared   static MinWidthProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a1e4 <<
047d7404  40000fa       54 ...ependencyProperty  0   shared   static MaxWidthProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a1f8 <<
047d7404  40000fb       58 ...ependencyProperty  0   shared   static MinHeightProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a20c <<
047d7404  40000fc       5c ...ependencyProperty  0   shared   static MaxHeightProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a220 <<
047d7404  40000fd       60 ...ependencyProperty  0   shared   static HorizontalAlignmentProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a234 <<
047d7404  40000fe       64 ...ependencyProperty  0   shared   static VerticalAlignmentProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a248 <<
047d7404  40000ff       68 ...ependencyProperty  0   shared   static MarginProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a25c <<
047d7404  4000100       6c ...ependencyProperty  0   shared   static StyleProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a284 <<
047d7404  4000101       70 ...ependencyProperty  0   shared   static ParentProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a2ac <<
047d7404  4000102       74 ...ependencyProperty  0   shared   static NameProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a2c0 <<
047d7404  4000103       78 ...ependencyProperty  0   shared   static TagProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a2d4 <<
047d7404  4000104       7c ...ependencyProperty  0   shared   static TagInternalProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a2fc <<
04851464  4000105       80 ...ndows.RoutedEvent  0   shared   static LoadedEvent
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a420 <<
047d7404  4000106       84 ...ependencyProperty  0   shared   static CursorProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a440 <<
047d7404  4000107       88 ...ependencyProperty  0   shared   static InstanceDelegateStoreProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a468 <<
047d7404  4000109       8c ...ependencyProperty  0   shared   static DataContextProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5a494 <<
047a1e2c  400010e       90 ...rence, mscorlib]]  0   shared   static _staticDelegateList
    >> Domain:Value  0433c738:NotInit  06f529d8:00000000 <<
00000000  4000264       30 ...angedEventHandler  0 instance 00000000 IsEnabledChanged
047d7404  4000252      430 ...ependencyProperty  0   shared   static IsTabStopProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59ce4 <<
047d7404  4000253      434 ...ependencyProperty  0   shared   static TabIndexProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59cf8 <<
047d7404  4000254      438 ...ependencyProperty  0   shared   static TemplateProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59d20 <<
047d7404  4000255      43c ...ependencyProperty  0   shared   static TabNavigationProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59d48 <<
047d7404  4000256      440 ...ependencyProperty  0   shared   static PaddingProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59d70 <<
047d7404  4000257      444 ...ependencyProperty  0   shared   static HorizontalContentAlignmentProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59d98 <<
047d7404  4000258      448 ...ependencyProperty  0   shared   static VerticalContentAlignmentProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59dc0 <<
047d7404  4000259      44c ...ependencyProperty  0   shared   static BackgroundProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59de8 <<
047d7404  400025a      450 ...ependencyProperty  0   shared   static BorderBrushProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59dfc <<
047d7404  400025b      454 ...ependencyProperty  0   shared   static BorderThicknessProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59e10 <<
047d7404  400025c      458 ...ependencyProperty  0   shared   static FontSizeProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59e24 <<
047d7404  400025d      45c ...ependencyProperty  0   shared   static FontFamilyProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59e4c <<
047d7404  400025e      460 ...ependencyProperty  0   shared   static ForegroundProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59e60 <<
047d7404  400025f      464 ...ependencyProperty  0   shared   static FontWeightProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59e88 <<
047d7404  4000260      468 ...ependencyProperty  0   shared   static FontStyleProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59eb0 <<
047d7404  4000261      46c ...ependencyProperty  0   shared   static FontStretchProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59ed8 <<
047d7404  4000262      470 ...ependencyProperty  0   shared   static DefaultStyleKeyProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59eec <<
047d7404  4000263      474 ...ependencyProperty  0   shared   static IsEnabledProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59f00 <<
04857f14  4000265      478 ... System.Windows]]  0   shared   static _resourceDictionaryCache
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5cae4 <<
03cf5668  4000266      47c      System.Object[]  0   shared   static _controlDelgates
    >> Domain:Value  0433c738:NotInit  06f529d8:04f59f14 <<
03cf44e8  40002b4       34        System.Object  0 instance 00000000 _treeContent
047d7404  40002b3      530 ...ependencyProperty  0   shared   static ContentTemplateProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5bd30 <<
047d7404  40002b5      534 ...ependencyProperty  0   shared   static ContentProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5bd44 <<
03d50f28  40005aa       40       System.Boolean  1 instance        1 _isLoaded
03d50f28  40005ab       41       System.Boolean  1 instance        1 _isMouseCaptured
03d50f28  40005ac       42       System.Boolean  1 instance        0 _isSpaceKeyDown
03d50f28  40005ad       43       System.Boolean  1 instance        0 _isMouseLeftButtonDown
04852208  40005ae       48 System.Windows.Point  1 instance 04f5b990 _mousePosition
048b5f98  40005af       38 ...mation.Storyboard  0 instance 00000000 _currentState
03d50f28  40005b0       44       System.Boolean  1 instance        0 _suspendStateChanges
0481fae0  40005b1       3c ...outedEventHandler  0 instance 04f5ca18 Click
047d7404  40005a6      7e0 ...ependencyProperty  0   shared   static ClickModeProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5ba7c <<
047d7404  40005a7      7e4 ...ependencyProperty  0   shared   static IsFocusedProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5bb38 <<
047d7404  40005a8      7e8 ...ependencyProperty  0   shared   static IsMouseOverProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5bbd8 <<
047d7404  40005a9      7ec ...ependencyProperty  0   shared   static IsPressedProperty
    >> Domain:Value  0433c738:NotInit  06f529d8:04f5bc40 <<

// dump button.Click field (9 lines above)
0:005> !do 04f5ca18
Name:        System.Windows.RoutedEventHandler
MethodTable: 0481fae0
EEClass:     0482c99c
Size:        32(0x20) bytes
File:        c:\Program Files\Microsoft Silverlight\2.0.31005.0\System.Windows.dll
Fields:
      MT    Field   Offset                 Type VT     Attr    Value Name
03cf44e8  40001e0        4        System.Object  0 instance 04f59c78 _target
04663c58  40001e1        8 ...ection.MethodBase  0 instance 00000000 _methodBase
03d50a90  40001e2        c        System.IntPtr  1 instance  463c08c _methodPtr
03d50a90  40001e3       10        System.IntPtr  1 instance        0 _methodPtrAux
03cf44e8  40001e4       14        System.Object  0 instance 00000000 _invocationList
03d50a90  40001e5       18        System.IntPtr  1 instance        0 _invocationCount

0:005> !do -nofields 04f59c78 // verify _target is page
Name:        SilverlightApplication1.Page
MethodTable: 04633a6c
EEClass:     0463196c
Size:        68(0x44) bytes
File:        SilverlightApplication1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null

// verify _methodPtr points to Button_Click
// see how they are all linked together
0:005> ln 463c08c
(0463c08c)   SilverlightApplication1.Page.Button_Click(System.Object, System.Windows.RoutedEventArgs)
0:005> u 463c08c // jit-ed method stub
SilverlightApplication1.Page.Button_Click(System.Object, System.Windows.RoutedEventArgs):
0463c08c b8303a6304      mov     eax,4633A30h
0463c091 89ed            mov     ebp,ebp
0463c093 e900431c00      jmp     SilverlightApplication1!Button_Click (04800398)
0463c098 0000            add     byte ptr [eax],al
0463c09a 0000            add     byte ptr [eax],al
0463c09c 0000            add     byte ptr [eax],al
0463c09e 0000            add     byte ptr [eax],al
0463c0a0 0000            add     byte ptr [eax],al
0:005> u eip // jit-ed native code
SilverlightApplication1!Button_Click:
04800398 55              push    ebp
04800399 8bec            mov     ebp,esp
0480039b 83ec08          sub     esp,8
0480039e 894dfc          mov     dword ptr [ebp-4],ecx
048003a1 8955f8          mov     dword ptr [ebp-8],edx
048003a4 833d7435630400  cmp     dword ptr ds:[4633574h],0
048003ab 7405            je      SilverlightApplication1!Button_Click+0x1a (048003b2)
048003ad e8862a895f      call    coreclr!JIT_DbgIsJustMyCode (64092e38)
0:005> !ip2md 04800398
MethodDesc:  04633a30
Method Name: SilverlightApplication1.Page.Button_Click(System.Object, System.Windows.RoutedEventArgs)
Class:       0463196c
MethodTable: 04633a6c
mdToken:     06000008
Module:      046333b4
IsJitted:    yes
CodeAddr:    04800398
0:005> !dumpil 04633a30
ilAddr = 047e03fe
IL_0000: nop
IL_0001: ret

When I have time, I may write a blog post on CLR internals, to demonstrate how objects are laid out in memory, how method calls are dispatched etc.

Conclusion

This is a long post, but still it barely constitute an overview of WinDbg and SOS. Good understanding of CLR is important for coding and debugging, WinDbg with SOS is the best tool to explore CLR and do advanced debugging. Hope this short yet long post will help you get started in exploring CLR and Silverlight. Thanks!