 MSDN Magazine December 2007

MSDN Magazine December 2007

Map LINQ: Create Dynamic Maps with Visual Basic 9.0 and WPF

Scott Wisniewski uses Windows Presentation Foundation data binding along with LINQ and new XML features in Visual Basic to build an interactive app that maps demographic data.

Scott Wisniewski VSTO: Build Office-Based Solutions Using WPF, WCF, And LINQ

With Visual Studio Tools for Office you can easily build Office-based services that harness the power of Windows Communication Foundation, Windows Presentation Foundation, and LINQ.

Andrew Whitechapel Cmdlets: Extend Windows PowerShell With Custom Commands

Jim Truher presents an introduction to writing and registering your own custom cmdlets to be used in Windows PowerShell.

Jim Truher Interop: Connect To Mainframe Apps With BizTalk Adapters and .NET

If you have a significant investment in legacy COBOL applications running on mainframes, you need the BizTalk Adapters for Host Integration Server .

Bashar “Bash” Badawi Security: Authenticate Users Across Organizations Using ADFS

Jack Couch looks at how to set up ADFS and when to use it; he then shows how to connect to an outside organization to offer single sign-on.

Jack Couch Editor's Note: Take a Deep Breath

This month Howard Dierking reflects on 2007 and looks forward to exciting possibilities in 2008.

Howard Dierking Toolbox: Live Chat, Code Conversion, Multiple Monitors, and More

This month, more of the tools you need to get your job done.

Scott Mitchell CLR Inside Out: Writing Reliable .NET Code

Here's a look at how code fails and techniques for writing more reliable and resilient managed code.

Alessandro Catorcini and Brian Grunkemeyer Data Points: Data Binding in WPF

WPF is one of the most important new technologies in the .NET Framework 3.0. This month John Papa introduces its data binding capabilities.

John Papa Cutting Edge: Inside the Microsoft AJAX Library

Dino Esposito introduces the Microsoft AJAX Library and the JavaScript library for ASP.NET AJAX 1.0.

Dino Esposito Test Run: UI Automation with Windows PowerShell

This installment of Test Run is a guide to using Windows PowerShell to perform ultra lightweight UI automation.

Dr. James McCaffrey Service Station: Extending WCF with Custom Behaviors

WCF extensibility points allow you to customize the process by which messages are translated, formatted, and sent over the wire for more customized services.

Aaron Skonnard Netting C++: EEK!—Time to Design the Mouse

This month begins the design of the actual mouse class for EEK!

Stanley B. Lippman .NET Matters: IFileOperation in Windows Vista

The author creates a managed wrapper to use the new IFileOperations interface in Windows Vista from managed code.

Stephen Toub Windows with C++: Windows Template Library 8.0

Kenny Kerr takes a look at Windows Template Library (WTL) support for new Windows Vista control enhancements, including task dialogs, Aero wizards, and open file dialogs.

Kenny Kerr { End Bracket }: You Should Learn SharePoint

Robert Bogue explains five reasons why you should make learning Microsoft SharePoint Technologies a priority, including its framework for user interface components and the embedded search engine.

Robert Bogue


Map LINQ: Create Dynamic Maps with Visual Basic 9.0 and WPF

Map LINQ

Create Dynamic Maps with Visual Basic 9.0 and WPF

Scott Wisniewski
This article is based on a prerelease version of Visual Studio. All information herein is subject to change.
Code download available at: MapsWithVB9WPF2007_12.exe (30813KB)
This article discusses:
  • WPF dependency properties
  • Querying XML using LINQ
  • Drawing the map
  • Visualizing map data
This article uses the following technologies:
WPF, Visual Basic 9.0, LINQ
Contents
WPF Data Binding
Application Object Model
Map Classes
Importing Map Data
Map Data Format
Importing the Data
Drawing the Map
Visualizing Population Data
Final Thoughts

I
've always had a bit of a fascination with drawing maps. I also think that Visual Studio® 2008 and Visual Basic® 9.0 are amazing. As a result, when I got the opportunity to play around with Visual Studio and then write an article about it, I figured the perfect topic would be a tutorial on how to draw maps using Visual Basic. This would give me the chance to not only demonstrate some cool Visual Basic features, but also to give you a working sample you could use as a basis for adding similar functionality to your own programs.

The result of my work is shown in Figure 1, which displays a screenshot of an application that uses a heat map to visualize the population of the United States. The application was built by heavily leveraging the Windows® Presentation Foundation (WPF) data-binding infrastructure, which has granted me the ability to effectively separate my application's domain-specific logic from the user interface visualization.

Figure 1 Population Mapping Application
Figure 1 Population Mapping Application(Click the image for a smaller view)
Figure 1 Population Mapping Application
Figure 1 Population Mapping Application (Click the image for a larger view)

I'll start with a brief overview of some preliminary WPF data-binding topics in the next section, then I'll spend the rest of the article walking through how I wrote the application. I'll first describe the application's Visual Basic object model, and then I'll discuss how I was able to use the innovative new XML features in Visual Basic 9.0 and LINQ to implement my data-processing logic. I'll conclude by showing you how to use WPF to effectively visualize the application's data.

WPF Data Binding

The WPF data-binding infrastructure is based around the concepts of dependency objects and dependency properties. A dependency object provides support for change notification and the ability to dynamically fetch and retrieve property values. A dependency property is a property associated with a dependency object. Together, they form the core infrastructure behind many areas of WPF, including data binding, animation, and styles.

Dependency objects are represented by instances of types derived from the DependencyObject class. Similarly, dependency properties are represented by instances of DependencyProperty that have been registered with the WPF property system.

When used in an application's data model, dependency properties vastly simplify code. They allow many tasks to be implemented directly rather than requiring controls to be explicitly manipulated by the programmer. Through the WPF declarative data binding and styling mechanisms, changes can then be automatically propagated to the UI without the need for custom logic.

This makes the code underlying WPF user interfaces significantly easier to read, write, and maintain. It also promotes cleaner application architecture by encouraging better separation between individual layers. Finally, and most importantly, it lets you take advantage of tools such as Expression BlendTM to design user interfaces in a visual manner, concentrating on elements of style rather than on application logic.

For a simple example of using dependency properties, see Figure 2. There I show a portion of the definition of the MapRegion class. MapRegion inherits from DependencyObject. Its primary purpose is to represent a geographical region within a map. It declares two dependency properties, RegionName and IsSelected. The dependency properties are declared by calling the shared Register method on the DependencyProperty class, providing the name of the property, its type, and the type of the class containing it (in this case MapRegion). The returned dependency properties are then assigned to public, shared, read-only fields declared on the class. This is done mainly to conform to WPF conventions, which expose the metadata for dependency properties as shared fields on the classes that define them.

Figure 2 Snippet of the MapRegion Class (Close Figure 2)
Imports System.Collections.ObjectModel

Public Class MapRegion
    Inherits DependencyObject

    Public Shared ReadOnly RegionNameProperty As DependencyProperty _
        = DependencyProperty.Register("RegionName", GetType(String), _
        GetType(MapRegion))
    Public Shared ReadOnly IsSelectedProperty As DependencyProperty _
        = DependencyProperty.Register("IsSelected", GetType(Boolean), _
        GetType(MapRegion))

    '...

    Public Property RegionName() As String
        Get
            Return CStr(GetValue(RegionNameProperty))
        End Get
        Set(ByVal value As String)
            SetValue(RegionNameProperty, value)
        End Set
    End Property

    Public Property IsSelected() As Boolean
        Get
            Return CBool(GetValue(IsSelectedProperty))
        End Get
        Set(ByVal value As Boolean)
            SetValue(IsSelectedProperty, value)
        End Set
    End Property

The dependency properties are then exposed to code via wrapper properties that invoke the GetValue and SetValue methods defined in the DependencyObject class, providing the appropriate DependencyProperty object as a parameter. This enables consumers of the MapRegion class to access dependency properties like any other normal instance property, while simultaneously taking advantage of the rich dependency property system in WPF.

It's worth mentioning that the actual property data is not stored directly as fields in the MapRegion class. Instead, WPF manages the storage for each dependency property attached to a dependency object, making them readily available through calls to SetValue and GetValue.

You should also note that a dependency property can be registered with a dependency object only once. Attempting to register two dependency properties with the same name on the same type will result in an exception.

In my example, I ensure that each dependency property is registered only once by declaring shared read-only fields with explicit initialization expressions that call DependencyProperty.Register. This causes the registration to be executed as part of the MapRegion class's static constructor, which the common language runtime (CLR) ensures will be run exactly once (before any code referencing the MapRegion class executes).

The XAML code shown in Figure 3 shows an example of how dependency properties can be used to automatically propagate changes from a domain model to an application's user interface. It defines a style that, when applied to a Polygon control, will cause the polygon to be rendered with a thick orange border when its associated MapRegion has its IsSelected property set to true, and with a thin black border otherwise. This enables the code driving the application to simply mark a region as selected and then have the UI respond accordingly.

Figure 3 Polygon Style Utilizing Data Triggers (Close Figure 3)
<Style x:Key="RegionPolygonStyle" TargetType="Polygon">
    <Style.Triggers>
        <DataTrigger Binding="{Binding Path=Region.IsSelected}"
            Value="True">
            <Setter Property="Stroke" Value="Orange" />
            <Setter Property="StrokeThickness" Value="20"/>
        </DataTrigger>
        <DataTrigger Binding="{Binding Path=Region.IsSelected}"
            Value="False">
            <Setter Property="Stroke" Value="Black"/>
            <Setter Property="StrokeThickness" Value="5" />
        </DataTrigger>
    </Style.Triggers>
</Style>

Application Object Model

WPF data binding is awesome, so I set out to enable it in my application wherever possible. My goals were to be able to write clear, concise, and domain-specific code to drive the app, and to enable the use of Expression Blend to design its interface.

To accomplish this, I created an object model for representing map information that heavily utilizes the WPF dependency property system. It consists of three main classes—Maps, MapRegion, and MapPolygon—all derived from DependencyObject. The Map class is the root of the object model and is used to represent a map containing a list of regions. The MapRegion class represents a region or geographic entity on a map. It contains a list of polygons, with each polygon describing the boundaries of one of the contiguous geographic areas defining the region. The MapPolygon class represents a polygon within a region and contains a collection of Point objects describing its vertices.

The Map class has five dependency properties: BoundingBox, ScaleX, ScaleY, TranslateX, and TranslateY. The BoundingBox property is an instance of the Rect class and stores the smallest rectangular area that contains all the regions on the map. The ScaleX, ScaleY, TranslateX, and TranslateY properties are defined to support zooming and panning on the map. These properties are important because the coordinates of the points on the map represent offsets, in kilometers, from the intersection of the equator and the prime meridian. WPF, on the other hand, uses a different coordinate system, one that is defined in terms of offsets, in logical pixels (one logical pixel is equal to 1/96th of an inch), from the upper-left corner of a window.

Because of this, the ScaleX, ScaleY, TranslateX, and TranslateY properties are used to do the following:

  1. Scale the map so that it can fit within a window.
  2. Flip the map vertically so that it is not drawn upside down. (In the world view, Y coordinates increase as you travel north; in the WPF view, Y coordinates increase as you move toward the bottom of a window.)
  3. Move the map so that its upper-left corner corresponds to the upper-left corner of the control that displays the map.

You can also use those properties to implement zoom and pan functionality within the UI. For example, multiplying the ScaleX and ScaleY values by 1.5 causes the map to be zoomed in by 150 percent, and adding 50 to its TranslateX property causes the view of the map to be moved 50 kilometers to the west. It's worth pointing out that changing these values does not mutate the coordinates in the map. Instead, the values are used to inform the user interface of what it needs to do in order to display the map.

The Map class exposes its region collection via the Regions property. Unlike the other properties on the class, however, Regions is not a dependency property. Instead, it is a read-only property typed as an ObservableCollection(of MapRegion). It's not declared as a dependency property because I did not need the ability to assign an entire collection at a time. The collection itself is, however, mutable, and regions can be added and removed from the map using the Add and Remove methods of the ObservableCollection class. Also, because the collection is exposed as an ObservableCollection, it can still participate in the WPF data-binding system. For example, if a ListBox is data-bound to an ObservableCollection, then simply adding a new item to that collection will cause a new item to appear in the ListBox. Similarly, removing an item from the collection will cause it to be removed from the ListBox.

Map Classes

The MapRegion class defines two dependency properties: BoundingBox and RegionName. The BoundingBox property defines the rectangular area containing the region and RegionName stores the name of the region. The class also defines two collection properties: Polygons and FipsCodes. The Polygons collection stores the polygons that define the region and FipsCodes stores the set of numeric FIPS (Federal Information Processing Standard) codes associated with the region. The class uses FIPS codes because the polygon data I use in the application was downloaded from the United States Census Bureau, which uses FIPS codes to uniquely identify geographical entities within the United States. More information on the Federal Information Processing Standard can be downloaded from itl.nist.gov/fipspubs/by-num.htm. Also, technical information about the U.S. Census Bureau's public domain TIGER Geographic Database can be accessed at census.gov/geo/www/cob.

The MapPolygon class defines two properties. The first is a read-only dependency property named Region that stores a reference to the region containing the polygon. The second is a collection property named Points that stores the set of Point structures that define the vertices of the polygon. You can see the full source for the class in Figure 4.

Figure 4 MapPolygon.vb (Close Figure 4)
Public Class MapPolygon
    Inherits DependencyObject

    Private Shared ReadOnly RegionPropertyKey As DependencyPropertyKey _
        = DependencyProperty.RegisterReadOnly("Region",  _
        GetType(MapRegion), GetType(MapPolygon), New PropertyMetadata())
    Public Shared ReadOnly RegionProperty As DependencyProperty

    Private m_Points As PointCollection

    Shared Sub New()
        RegionProperty = RegionPropertyKey.DependencyProperty
    End Sub

    Sub New()
        SetRegion(Nothing)
        m_Points = New PointCollection
    End Sub

    Friend Sub SetRegion(ByVal r As MapRegion)
        SetValue(RegionPropertyKey, r)
    End Sub

    Public ReadOnly Property Region() As MapRegion
        Get
            Return CType(GetValue(RegionProperty), MapRegion)
        End Get
    End Property

    Public ReadOnly Property Points() As PointCollection
        Get
            Return m_Points
        End Get
    End Property
End Class

The Region property is exposed as a read-only dependency property. This allows the WPF property system to detect changes to the property value but does not allow it to be modified by WPF. A read-only dependency property is declared by calling the RegisterReadOnly method of the DependencyProperty class. Unlike the Register method, RegisterReadOnly returns a DependencyPropertyKey instance instead of returning a DependencyProperty.

A dependency property key is an object that allows read-only dependency properties to be modified. They are useful for implementing classes that need to update property values internally and have those changes detected by other types but do not allow external modification of the value. As a result, DependencyPropertyKeys are generally not exposed from the class that defines them. That is why the RegionPropertyKey field is marked as private. A second shared field, RegionProperty, is then used to publicly expose a read-only alias of it.

The Region property is read-only because it was designed to not be externally editable. Instead, the MapRegion class will automatically set and clear the value as a polygon is added or removed from its region collection. It does this by handling the CollectionChangedEvent for the polygon collection and propagating the appropriate Region instance through to the affected polygons.

The implementation of the event handler is shown in Figure 5. It works by calling the SetRegion extension method on the NewItems and OldItems collections stored in the handler's NotifyCollectionChangedEventArgs parameter. The extension method then calls the MapPolygon class's SetRegion method on each item in the target collection. The instance method is marked as friend to discourage it from being called. The extension method implementation looks like this:

Figure 5 MapRegion.PolygonCollectionChanged Method (Close Figure 5)
Private Sub PolygonCollectionChanged(ByVal sender As Object, ByVal e As _
    System.Collections.Specialized.NotifyCollectionChangedEventArgs) _
    Handles m_polygons.CollectionChanged
    Select Case e.Action
        Case Specialized.NotifyCollectionChangedAction.Add
            e.NewItems.Cast(Of MapPolygon).SetRegion(Me)
        Case Specialized.NotifyCollectionChangedAction.Remove
            e.OldItems.Cast(Of MapPolygon).SetRegion(Nothing)
        Case Specialized.NotifyCollectionChangedAction.Replace
            e.NewItems.Cast(Of MapPolygon).SetRegion(Me)
            e.OldItems.Cast(Of MapPolygon).SetRegion(Nothing)
    End Select
End Sub

<Extension()> _
Sub SetRegion(ByVal x As IEnumerable(Of MapPolygon), _
    ByVal r As MapRegion)
    For Each item In x
        item.SetRegion(r)
    Next
End Sub

Unlike the collection properties on the Map and MapRegion classes, the Points collection on the MapPolygon class is not an ObservableCollection(of T). Instead it uses the built-in WPF PointsCollection class. This is the same type that the WPF Polygon control uses to store the set of points it contains. By using this class in the data model, it is then possible to data bind a Polygon control directly to a MapPolygon instance.

Importing Map Data

The application loads its map data from XML files stored on disk. I created these files by downloading raw ASCII cartographic boundary files from the U.S. Census Bureau, which describe the boundaries of every state and county (and equivalent areas) within the United States. The state boundary data is used to draw a map of the United States and the county boundary data is used to visualize population data on the map. I then took the downloaded data and converted it into XML files for easier processing. The code for converting the files into XML is available in this article's download. The raw data files can be downloaded at census.gov/geo/www/cob/bdy_files.html.

Using the Visual Studio Create Schema feature, I then automatically generated an XML schema that described the contents of the XML files. This enabled me to then use the new Visual Basic XML IntelliSense® feature, which shows XML member access expressions based on any schema files included in a project (see Figure 6).

Figure 6 Visual Basic XML IntelliSense
Figure 6 Visual Basic XML IntelliSense(Click the image for a smaller view)
Figure 6 Visual Basic XML IntelliSense
Figure 6 Visual Basic XML IntelliSense (Click the image for a larger view)

Map Data Format

The schema describing the XML map files defines a simple format consisting of a root File tag that in turn contains a sequence of Region tags. Each Region tag contains a series of FipsCode and Polygon tags, with each Polygon tag containing a sequence of Vertex and Island tags.

A Region tag is used to describe a single region. It defines two attributes, both of which are required: Type and Name. The Type attribute provides a description of the type of the region. For the purposes of my application, I always use the values State or County. In general, however, any value can be used for the Type attribute. The Name attribute describes the region's name.

Each FipsCode tag defined in a region describes one of its FIPS code entries. The last FipsCode tag within a Region defines the region's numeric ID. The other FipsCode tags recursively describe the numeric IDs of the region's parents.

For the purposes of my application, all regions will have at most two associated FipsCode values. In particular, regions describing states (or state equivalent areas) will always have one entry that describes the FIPS code associated with that state. Regions describing counties (or county equivalent areas) will always have two entries, with the first entry denoting the FIPS code of the state that contains the county and the second denoting the FIPS code of the county itself. The numeric ID of any particular region is always guaranteed to be unique within the scope of its immediate parent.

Each of a region's Polygon tags describes a single polygon contained within it. The polygon is composed of a series of Vertex tags, with each tag describing a single vertex. The Vertex tag defines five attributes, all of which are required: Ordinal, Longitude, Latitude, X, and Y. The Ordinal attribute defines an ordering for a polygon's vertices. Its primary purpose is to make it possible to correctly order vertices should they be processed by queries that do not preserve ordering. It is currently not used by my application. The Longitude and Latitude attributes describe the longitudinal and latitudinal coordinates of the vertex. Similarly, the X and Y attributes define the coordinates of the vertex when projected into rectangular space and are what the application uses for drawing maps.

The X and Y attributes were computed using the simple projection shown in Figure 7. This projection is not a particularly accurate one and may not be suitable for some scenarios. In those cases, it is possible to calculate new values directly using Longitude and Latitude rather than using the X and Y values stored in the file. For my application, which does not require a high degree of geographical precision, this was sufficient.

Figure 7 An Incredibly Simple Map Projection
Figure 7 An Incredibly Simple Map Projection

An Island tag within a polygon defines a hole enclosed within it that is not part of its containing region. It contains a collection of Vertex tags that describe the boundaries of the hole. I've included them in the data files primarily for completeness; they are not used by my application. This does mean that there may be a few very small areas of the map that show slightly incorrect values when data is visualized on it. For the purposes of my application, this was not an issue, and so I simply ignored it.

If you have an application where accuracy in this regard is important, or are mapping regions where polygon islands are prevalent, you easily can solve the problem by controlling the order in which the map is rendered. By first drawing the polygon containing an island and then drawing the island on top of it, it is possible to accurately render polygon islands. In many cases this may also require extra processing to determine areas of overlap and construct the appropriate ordering.

Importing the Data

Using LINQ, importing map data into the application is relatively easy. The code that does this is shown in Figure 8. It defines a procedure, LoadFile, that takes two parameters: file and list. The file parameter is a string that contains the full path of the XML file to load from disk. The list parameter is a reference to a collection of MapRegion instances. The procedure then opens the XML file, processes it, and inserts all of the regions it defines into the provided collection.

Figure 8 Loading MapRegion Instances from an XML File (Close Figure 8)
Private Sub LoadFile(ByVal file As String, ByVal list As ICollection(Of _
    MapRegion))
    Dim doc = XDocument.Load(file)
    Dim q = _
        From v In doc.<File>.<Region>.<Polygon>.<Vertex> _
        Let _
            Polygon = v.Parent, _
            Region = v.Parent.Parent _
        Group By _
            Polygon, _
            Region _
        Into _
            Group, _
            MinX = MinDBL(v.@X), _
            MinY = MinDBL(v.@Y), _
            MaxX = MaxDBL(v.@X), _
            MaxY = MaxDBL(v.@Y) _
        Select _
            TheMapPolygon = (From tmp In Group Select _
            tmp.v).ToMapPolygon(), _
            Region, _
            BoundingBox = New Rect(New Point(MinX, _
            MinY), New Point(MaxX,  MaxY)) _
        Group By _
            Region _
        Into _
            Group, _
            BoundingBox = Enclose(BoundingBox) _
        Select _
            Region = New MapRegion(Region.@Name, (From item In Group _
            Select item.TheMapPolygon), (From f In _
            Region...<FipsCode> Select CInt(f.Value)), BoundingBox)

    list.AddRange(q)
End Sub

The body of LoadFile is extremely simple. The procedure works by first loading the provided file into an XDocument instance, defining a query to traverse the document and convert it into a collection of MapRegion instances, and then finally executing the query and inserting its results into the provided output collection.

The query is composed of several different pieces. The first piece is its From clause, which uses the XML Member access expression doc.<File>.<Region>.<Polygon>.<Vertex> to retrieve all polygon Vertex tags in the document as the basis for the query. It's worth noting that this does not use doc...<Vertex>, which would grab all Vertex tags in the document, including those defined inside of Island tags. Instead, it only includes Vertex tags that are directly defined inside Polygon tags. The second part of the query is the Let clause. This clause introduces two new variables—Polygon and Region—which are assigned to the Polygon and Region tags that contain each vertex.

The third part of the query is its first Group By clause. This simply groups the list of vertices by their containing polygon and then calculates the polygon's bounding box. The region containing the polygon is also included in the grouping key, thus enabling it to be used later in the query. The polygon's bounding box is computed by calculating the minimum and maximum values of its vertex's X and Y attributes. This is done by calling the custom aggregate functions MinDBL and MaxDBL, which are defined by two extension methods:

<Extension()> _
Function MinDBL(Of T)(ByVal x As IEnumerable(Of T), _
    ByVal y As Func(Of T, String)) As Double
    Return x.Min(Function(z) CDbl(y(z)))
End Function

<Extension()> _
Function MaxDBL(Of T)(ByVal x As IEnumerable(Of T), _
    ByVal y As Func(Of T, String)) As Double
    Return x.Max(Function(z) CDbl(y(z)))
End Function

These simply define aggregate functions that calculate minimum or maximum values on their provided arguments after first converting it to type Double.

The Group By clause is followed by a Select clause that converts the raw group by results into a more usable form. In particular, it defines a subquery that takes the group resulting from the Group By, projects out just the vertex from it, and then converts the resulting collection into an instance of the MapPolygon class using the ToMapPolygon extension method:

<Extension()> _
Function ToMapPolygon(ByVal items As IEnumerable(Of XElement)) As _
    MapPolygon
    Dim ret As New MapPolygon
    ret.Points.AddRange(From item In items Select item.ToPoint())
    Return ret
End Function

<Extension()> _
Function ToPoint(ByVal x As XElement) As Point
    Return New Point(x.@X, x.@Y)
End Function

It also converts the raw MinX, MinY, MaxX, and MaxY variables into an instance of the Rect class.

The next part of the query is its second Group By clause, which aggregates the results of the preceding select by Region and calculates the bounding box containing all the region's polygons. The bounding box is calculated using the Enclose custom aggregate function, whose definition is shown in Figure 9. The last part of the query is the final Select clause, which constructs a MapRegion instance for each result returned from the second Group By. It does this using two subqueries, one of which projects out the polygon instance stored in every member of Group, and the other of which extracts the set of FIPS codes stored in Region.

Figure 9 Enclose Aggregate Function (Close Figure 9)
<Extension()> _
Function Enclose(Of T)(ByVal collection As IEnumerable(Of T), ByVal _
    selector As Func(Of T, Rect)) As Rect
    Dim ret As Rect
    Dim first As Boolean = True

    For Each item In collection

        If first Then
            ret = selector(item)
            first = False
        Else
            ret.Enclose(selector(item))
        End If
    Next

    Return ret

End Function
<Extension()> _
Sub Enclose(ByRef theBox As Rect, ByVal otherBox As Rect)

    Dim X = Math.Min(theBox.X, otherBox.X)
    Dim Y = Math.Min(theBox.Y, otherBox.Y)
    Dim Width = Math.Max(theBox.BottomRight.X, otherBox.BottomRight.X) _
        - X + 1
    Dim Height = Math.Max(theBox.BottomRight.Y, otherBox.BottomRight.Y) _
        - Y + 1
    theBox.X = X
    theBox.Y = Y
    theBox.Width = Width
    theBox.Height = Height

End Sub

Drawing the Map

Once a map has been loaded into memory, displaying it in a window is trivial. The XAML code in Figure 10 shows an easy way to do this. Here I define a simple Canvas control containing two transformations for its Render Transform, and a single ItemsControl named MapViewer as its content. The transformations are used to translate the coordinates of the regions in the map so that they will correctly display within the canvas. The ItemsControl is used to visually render the map's contents. All data binding is done using implicit data contexts. In the case of the MasterCanvas control, this will always be set to an instance of the Map class.

Figure 10 XAML Code for Displaying a Map (Close Figure 10)
<Canvas Grid.Row="2" Grid.Column="1" Name="MasterCanvas">
    <Canvas.RenderTransform>
        <TransformGroup>
            <TranslateTransform X="{Binding Path=TranslateX}"
                Y="{Binding Path=TranslateY}"/>
            <ScaleTransform ScaleX="{Binding Path=ScaleX}"
                ScaleY="{Binding Path=ScaleY}" />
        </TransformGroup>
    </Canvas.RenderTransform>

    <ItemsControl Name="MapViewer" ItemsPanel="{DynamicResource SimpleCanvasTemplate}" ItemsSource="{Binding Path=Regions}" ItemTemplate="{DynamicResource RegionTemplate}" />
</Canvas>

TranslateTransform binds its TranslateX and TranslateY properties to the equivalent properties on the Map class. Typically, these values are set on the map class to be the negated value of the upper left-hand corner of the Map's bounding box. This has the effect of moving the map's contents so that its upper left-hand corner corresponds with the upper left-hand corner of the canvas. Similarly, the ScaleTransform binds its ScaleX and ScaleY properties to the associated properties on the Map class. In most cases, they are set to the ratio of the canvas's width to the map's width and the negated ratio of the canvas's height to the map's height.

The following code shows the implementation of the ScaleTo method in the Map class:

Public Sub ScaleTo(ByVal size As Size)
    If BoundingBox.Width <> 0 AndAlso BoundingBox.Height <> 0 Then
        ScaleX = size.Width / BoundingBox.Width
        ScaleY = -size.Height / BoundingBox.Height
        TranslateX = -BoundingBox.Left
        TranslateY = -BoundingBox.Bottom
    End If
End Sub

When given a Size structure that contains the height and width of MasterCanvas, the method will transform the map so that it is displayed entirely within the canvas. This method is called in response to the canvas's SizeChanged event, thereby causing the map to scale to fill available space when the canvas's containing window is resized:

Private Sub WindowSizeChanged() Handles Me.SizeChanged
    If m_Map IsNot Nothing Then
        m_Map.ScaleTo(New Size(MasterCanvas.ActualWidth,
            MasterCanvas.ActualHeight))
    End If
End Sub

It's worth noting that the ordering of the transformations within the containing TransformGroup is important. In particular, the TranslateTransformation must happen before the ScaleTransform. This allows arguments to both transformations to be specified in terms of the domain model, rather than in terms of the UI. If the ScaleTransform was listed first, then the values of the TranslateTransform would need to be specified in pixels rather than in kilometers, which would require an explicit conversion. Specifying the TranslateTransform first greatly simplifies the application's object model.

The ItemsControl is used to render the contents of the map onto the canvas. It behaves similarly to the ListBox control. In fact, ItemsControl is the base class for ListBox and defines most of its data binding behavior. Unlike the ListBox, however, the ItemsControl does not render items as list. This makes it much better suited for rendering maps, which clearly should not be displayed as a list.

ItemsControl binds its ItemsSource property to the map's Regions collection and utilizes the SimpleCanvasTemplate and RegionTemplate resources to define how everything is rendered. The SimpleCanvasTemplate, used for the ItemsControl's ItemsPanel property, simply creates an empty Canvas. Its purpose is to define the container used to host each of the items created by the control's ItemTemplate. A canvas is used primarily because canvases allow their contents to be explicitly positioned. Other "container controls," such as a StackPanel, would attempt to apply automatic layout logic that ultimately would draw the map incorrectly.

The RegionTemplate resource defines the visual layout used for each Region in the map. It does this by simply declaring a nested ItemsControl for each region and binding its ItemSource to the region's Polygon collection. Like MapViewer, the nested ItemsControl defined in RegionTemplate uses a canvas for its ItemsPanel (in fact it reuses SimpleCanvasTemplate). Unlike MapViewer, however, it uses the PolygonTemplate resource to render its items.

The PolygonTemplate resource defines a data template that renders a WPF polygon control given an instance of the MapPolygon class. It binds each Polygon's Points collection to the Points property on the MapPolygon instance. It's worth mentioning that the Polygon control has its Fill property explicitly set to Transparent. This is necessary to enable the Polygon to respond to many mouse events. WPF differentiates between polygons that have a Fill property set and those that do not. A polygon with a missing Fill brush is considered to be a shell polygon, while a polygon with an explicitly set Fill brush is considered to be a filled polygon. Mouse events such as MouseUp, MouseEnter, and MouseLeave for a filled polygon will fire when the mouse is inside the polygon's interior. With shell polygons, the inside of the shape is not considered part of its definition. In that case, placing the mouse inside the polygon will not cause any of its mouse events to fire. By setting a polygon's fill to transparent, however, it becomes possible to enable interaction with the mouse without obscuring other data drawn on the map.

Another interesting aspect of the polygon template is that it uses declarative, trigger-based styles to drive the appearance of the polygon. In particular, it references the RegionPolygonStyle resource that defines two distinct styles for rendering polygons. Those polygons whose regions have IsSelected values of true are rendered with a thick orange border, and those whose IsSelected values are false are rendered with a regular, thin black border. This event-handling code shows how such styles can be easily used to implement cool UI features like input tracking:

Private Sub Polygon_MouseEnter(ByVal sender As Polygon, ByVal e As _
    System.Windows.Input.MouseEventArgs)
    CType(sender.DataContext, MapPolygon).Region.IsSelected = True
End Sub

Private Sub Polygon_MouseLeave(ByVal sender As Polygon, ByVal e As _
    System.Windows.Input.MouseEventArgs)
    CType(sender.DataContext, MapPolygon).Region.IsSelected = False
End Sub

Figure 11 shows an example of this in action.

Figure 11 Using a Mouse Event Trigger for Highlighting a Region
Figure 11 Using a Mouse Event Trigger for Highlighting a Region(Click the image for a smaller view)
Figure 11 Using a Mouse Event Trigger for Highlighting a Region
Figure 11 Using a Mouse Event Trigger for Highlighting a Region (Click the image for a larger view)

Visualizing Population Data

Visualizing population data on top of the map is also simple. XAML code that does this is shown in Figure 12. It modifies the XAML used to draw the map by adding a second ItemsControl named DataLayer to MasterCanvas. Because DataLayer is located inside MasterCanvas, its contents will be transformed in the same way as MapViewer's. This allows the data bound to DataLayer to also be specified in terms of the underlying domain model rather than in terms of the user interface.

Figure 12 Modified XAML for Displaying a Heat-Map (Close Figure 12)
<Canvas Grid.Row="2" Grid.Column="1" Name="MasterCanvas">
    <Canvas.RenderTransform>
        <TransformGroup>
            <TranslateTransform X="{Binding Path=TranslateX}"
                Y="{Binding Path=TranslateY}"/>
            <ScaleTransform ScaleX="{Binding Path=ScaleX}" ScaleY="{Binding Path=ScaleY}" />
        </TransformGroup>
    </Canvas.RenderTransform>

    <ItemsControl Name="MapViewer" ItemsPanel="{DynamicResource SimpleCanvasTemplate}" ItemsSource="{Binding Path=Regions}" ItemTemplate=
    "{DynamicResource RegionTemplate}" />
    <ItemsControl Name="DataLayer" ItemsPanel=
        "{DynamicResource SimpleCanvasTemplate}" ItemTemplate=
        "{DynamicResource DataLayerTemplate}"/>

</Canvas>

Unlike MapViewer, DataLayer is not intended to inherit its data context directly from MasterCanvas. Instead, it is designed to have the data to be visualized set explicitly as its ItemsSource. The data provided should be a collection of objects defining two properties: Color and Points. The Color property should be of type Brush and defines how the polygon described by Points should be colored on the map. The Points property should be an instance of PointsCollection that defines the vertices of the polygon to be colored. This is the DataTemplate used to render DataLayer's elements:

<DataTemplate x:Key="DataLayerTemplate">
        <Polygon Stroke="Transparent" Fill="{Binding Path=Color}" Points="{Binding Path=Points}" />
</DataTemplate>

The way it works is by simply creating a polygon and binding its Fill and Points properties to the appropriate properties on the underlying element.

The LoadData method shown in Figure 13 is responsible for loading the primary map data into an instance of the Map class, setting it up as the DataContext for MasterCanvas, constructing the heat map, and then binding DataLayer to it.

Figure 13 LoadData (Close Figure 13)
Public Sub LoadData()
    Dim list As New List(Of MapRegion)

    LoadFile("PolygonData\2000\states.xml", list)

    Dim q = From r In list Where Not excludedStates.Contains(r.RegionName)

    m_Map = New Map()

    Dim first As Boolean = True
    Dim bb As Rect

    For Each item In q
        m_Map.Regions.Add(item)

        If (first) Then
            bb = item.BoundingBox
            first = False
        Else
            bb.Enclose(item.BoundingBox)
        End If
    Next

    m_Map.BoundingBox = bb
    MasterCanvas.DataContext = m_Map
    m_Map.ScaleTo(New Size(MasterCanvas.ActualWidth, _
        MasterCanvas.ActualHeight))

    Dim counties = New List(Of MapRegion)

    LoadFile("PolygonData\2000\counties.xml", counties)

    Dim doc = XDocument.Load("PopulationData\CountyPopulation.xml")

    Dim q2 = _
        From _
            state In m_Map.Regions _
        Join _
            county In counties _
        On _
            state.FipsCodes(0) Equals county.FipsCodes(0) _
        Join _
            pd In doc.<CountyPopulationFile>.<CountyPopulation> _
        On _
            CInt(pd.@CountyFipsCode) Equals county.FipsCodes(1) And _
            CInt(pd.@StateFipsCode) Equals state.FipsCodes(0) _
        Where _
            CDate(pd.@Date).Year = 2006 _
        From _
            Polygon In county.Polygons _
        Select _
            Color = New SolidColorBrush(colorMapEntries.Interprolate( _
                CDbl(pd.@Population))), Points = Polygon.Points

    q2 = q2.ToList()

    DataLayer.ItemsSource = q2
    'Force the state boundaries to be on top of the county regions....
    MasterCanvas.Children.Remove(MapViewer)
    MasterCanvas.Children.Add(MapViewer)

    Dim legendInfo(colorMapEntries.Length - 2) As Object

    For i = 0 To colorMapEntries.Length - 2
        legendInfo(colorMapEntries.Length - (i + 2)) = _
        New With{.StartColor = colorMapEntries(i).FillColor, _
        .StopColor = colorMapEntries(i + 1).FillColor, .Value = _
        If(colorMapEntries(i + 1).ShowInIndex, colorMapEntries( _
        i + 1).Value.ToString("n"), "")}
    Next

    Legend.ItemsSource = legendInfo

End Sub

The portion of the method that is particularly interesting is the query, which is used to build the heat map. It works by joining the set of states displayed on the map, the set of counties loaded from disk, and XML population data. It joins the set of states with the set of counties in order to filter out counties that are defined in states that are not shown on the map. The key for the join, on both sides, is each region's first FIPS code. In the case of a State, this will correspond to its numeric ID. In the case of a County, it corresponds to the counties containing a state's numeric ID. The XML population data is then joined with results in order to associate each county's population value with the MapRegion instance defining the county.

The results of the join are then filtered to include population data only for the year 2006. This is necessary because the XML population data contains data for multiple years, and in this case I am only interested in showing one particular year on the map. Failing to filter based on year will also result in multiple overlapping polygons with different values being drawn on the map for the same region. The filtered results are cross-joined with the Polygons collection for each county. This effectively expands a collection of county regions into a collection of polygons.

The final portion of the query is a Select statement that extracts the point collection from each polygon, calculates the color it should be displayed in based on its associated population value, and then constructs a SolidColorBrush that will render the region in that color. The color values are calculated using the colorMapEntries array, which defines a mapping between population values and colors. Essentially, each entry in the array defines a color and an associated upper-bound population value. The particular color used for a county is then interpolated from the two color map entries between which its population sits.

The interpolation is implemented by the Interpolate extension method defined in Figure 14. It works by doing a binary search through the colorMapEntries array, looking for the smallest entry that has a population value greater than the provided value. It then takes that entry and the preceding entry and uses them to interpolate the appropriate color value. The way this is done is by calling the Interpolate instance method that is defined on the ColorMapEntry class.

Figure 14 Interpolate Extension Method (Close Figure 14)
<Extension()> _
Function Interpolate(ByVal entries() As ColorMapEntry, ByVal value As _
    Double) As Color
    Dim upperBound = entries.LeastUpperBound(value)
    Dim c As Color = Nothing

    If upperBound >= entries.Length Then
        c = entries(upperBound - 1).FillColor()
    ElseIf upperBound = 0 Then
        c = entries(0).FillColor
    Else
        c = entries(upperBound - 1).Interpolate(entries( _
            upperBound), value)

    End If

    Return c
End Function
<Extension()> _
Function LeastUpperBound(ByVal entries() As ColorMapEntry, ByVal v As _
    Double) As Integer

    Dim ret = Array.BinarySearch(entries, v, _
        New ColorMapEntryToValueComparer)
    If ret < 0 Then
        ret = ret Xor -1
    End If

    Return ret
End Function

Final Thoughts

The great thing about my app is that it was easy to write. In particular, I made extensive use of Expression Blend to define its interface, thus making it a breeze to add stunning visual effects. I also used XML to store all the data the application uses to run. This enabled me to use the new integrated XML features in Visual Basic, along with LINQ, to painlessly implement all of the data manipulation logic. I also designed the interface to be built on top of the WPF data-binding infrastructure, thereby creating a cleanly separated, well-architected application that was built on top of a rich object model.

Essentially, by using Visual Basic, WPF, Expression Blend, and LINQ, I was able to quickly and effectively throw together an application that makes relatively sophisticated visualizations from an existing body of data. This application could be easily extended to view data from different years, or manipulate the data in various ways. All of the code is available in the download for this article, so feel free to experiment and see what is possible.

Scott Wisniewski is a Software Design Engineer at Microsoft, where he works on the Visual Basic compiler. For the upcoming Visual Studio "Orcas" release, he worked on several features, including nullable types, error correction, and extension methods. You can e-mail Scott at scottwis@microsoft.com or contact him through the Visual Basic Team blog (blogs.msdn.com/vbteam).

© 2007 Microsoft Corporation and CMP Media, LLC. All rights reserved; reproduction in part or in whole without permission is prohibited.


VSTO: Build Office-Based Solutions Using WPF, WCF, And LINQ

VSTO

Build Office-Based Solutions Using WPF, WCF, And LINQ

Andrew Whitechapel
This article is based on a prerelease version of Visual Studio 2008. All information herein is subject to change.
Code download available at: VSTONet2007_12.exe (419KB)
This article discusses:
  • How VSTO makes Office development more powerful
  • Using WCF, WPF, and LINQ in Office solutions
  • Easily adding advanced features to your Office apps
  • Building services the easy way
This article uses the following technologies:
VSTO, WPF, WCF, LINQ
Contents
The Sample Solution
Building the WCF Service
The WPF UserControl
Connecting the WPF UserControl to VSTO
Consuming the WCF Service in the VSTO Solution
Security
Using LINQ, XLinq, and Lambda Expressions in VSTO
The Future of Office-Based Apps

V
isual Studio® 2008 introduces an array of new features aimed at a wide range of customer solution types. Now you can build a Visual Studio Tools for Office (VSTO) solution that uses Windows® Presentation Foundation (WPF), Windows Communication Foundation (WCF) and language integrated query expressions (LINQ), which I'll show you shortly.

The new technologies provide opportunities for building exciting solutions with behavior that was previously difficult or impossible to achieve. For example, though Microsoft® Office Excel® 2007 has powerful charting features, you can build an even richer experience when you combine the Excel calculation engine with enhanced UI and data visualization using WPF 3D animated graphics.

As Office evolves into a true development platform, office-based solutions are becoming increasingly sophisticated, less document-focused, and more loosely coupled. The need for a support framework and a design-time toolset for building service-oriented solutions that connect a rich Office client with powerful server-side functionality and remote data is neatly filled by WCF. Visual Studio 2008 provides a simple GUI wizard that lets you consume WCF services without having to worry about service metadata, protocols, or XML configuration.

LINQ enables developers to build more intuitive, greatly simplified code for querying data. One of the LINQ features Office developers will especially appreciate is the use of extension methods to support the traditional Office object model pattern of methods that have optional or explicit reference parameters.

With Visual Studio 2008, you can build a solution that incorporates the native capabilities of an Office client application combined with the sophisticated UI capabilities of WPF that's connected to remote data and services via WCF and uses the RAD features of LINQ to manipulate that data.

The Sample Solution

The solution I'll be building is deliberately simple so that I can focus on the individual technologies and integration issues rather than the business functionality. Figure 1 gives you an idea of the user experience. There are two processes involved: a standalone WCF service hosted in a console application and a VSTO add-in running in Word 2007. The add-in offers a custom task pane, which includes a WPF custom control. As the user clicks the buttons in the control, the add-in responds by calling into the WCF service to fetch some XML data. The add-in then processes this data in a variety of ways, using LINQ and XLinq, including lambda expressions and expression trees, and formats text that it then inserts into the active document. If you want to work through the sample solution yourself, download and install the Visual Studio 2008 Beta 2 release, available at msdn2.microsoft.com/vstudio/aa700831.

Figure 1 Runtime Behavior of the Sample Solution
Figure 1 Runtime Behavior of the Sample Solution(Click the image for a smaller view)
Figure 1 Runtime Behavior of the Sample Solution
Figure 1 Runtime Behavior of the Sample Solution (Click the image for a larger view)

The point of this sample is not to show the specific runtime functionality; instead, the main idea is to demonstrate that all the new Visual Studio 2008 technologies (WPF, WCF, and LINQ) work seamlessly with Office solutions. Another goal of this sample solution is to provide food for thought for building exciting new user experiences.

Figure 2 illustrates the architecture of the solution. Using WCF and LINQ within a VSTO application offers a lightweight approach and is no different than using these features in a Windows Forms or console application. Using WPF is more interesting, though, as you'll see.

Figure 2 Architecture of the Sample Solution
Figure 2 Architecture of the Sample Solution(Click the image for a smaller view)
Figure 2 Architecture of the Sample Solution
Figure 2 Architecture of the Sample Solution (Click the image for a larger view)

One aspect of VSTO is the ability to build solutions that use managed controls within native Office UI windows. VSTO supports placing managed (Windows Forms) controls in arbitrary Windows Forms dialogs, in app-level custom task panes, in the document actions pane, in Outlook® custom form regions, and on the surface of a Word or Excel document. The Microsoft .NET Framework 3.5 (shipping with Visual Studio 2008) includes the WindowsFormsIntegration.dll, which contains the Windows Forms Integration ElementHost class. This provides a bridge between Windows Forms and WPF. Thus, VSTO also supports placing WPF controls in all the places you can put Windows Forms controls.

One of the interesting design decisions you get to make here is where to handle events. For example, when the user clicks one of the WPF Button controls, you can handle this at multiple levels: in the WPF UserControl, in the Windows Forms UserControl, or in the add-in. Where you handle the event, and whether you bubble it up further, depends a lot on how reusable you want the various controls to be.

Building the WCF Service

The way I went about starting to build the project is I began with a simple console application called ImageServiceHost. My next step was to add a WCF Service item to the project, which I decided to call ImageService. It is important for you to understand that adding a Windows Communication Foundation Service to the project in Visual Studio causes starter code to automatically be generated for the service, including an interface that represents the service contract and class that implements that interface. Configuration entries that specify behaviors and endpoints for the service are provided in a standard app.config file.

I had to change the service contract and its implementation in order to remove the wizard-generated DoWork method and replace it with a new method, called GetDefinition, that takes a keyword string and returns a string of XML data:

[ServiceContract()]
public interface IImageService
{
    [OperationContract]
    string GetDefinition(string myValue);
}

I could get the XML data from anywhere; realistically, the WCF service would be running on a server and could perhaps source its data from a server-side database or some line-of-business (LOB) system such as SAP or Siebel. In my sample, however, I will run the service on the local machine and source the data from a static XML string resource.

The XML data source I'll create will contain a number of item elements, each of which represents a simple mapping of a keyword to a definition:

<Dictionary>
    <Item>
        <Key>Frangipani</Key>
        <Definition>Some description goes here.</Definition>
    </Item>
    <Item>
        <Key>Toucan</Key>
        <Definition>...etc</Definition>
    </Item>
</Dictionary>

If I add this XML data file to the project resources, I can implement the service constructor to load the XML data from resources. Then, I can implement the GetDefinition contract method to return the item that matches the keyword the user requested. At the same time, I echo the return string to the console window. Note that the sample code is simplified and does not include the exception handling you would normally incorporate:

public string GetDefinition(string keyword)
{
    XmlDocument xmlDoc = new XmlDocument();
    xmlDoc.LoadXml(Properties.Resources.ImageData);
    String xPath = String.Format(
        "descendant::Item[Key='{0}']", keyword);
    XmlNode node = xmlDoc.DocumentElement.SelectSingleNode(xPath);
    String nodeXml = node.OuterXml;
    Console.WriteLine(nodeXml);
    return nodeXml;
}

Next, the console application simply needs to start and stop the service.

static void Main(string[] args)
{
    ServiceHost s = new ServiceHost(typeof(ImageService));
    s.Open();
    Console.WriteLine("press ENTER to stop the service");
    Console.ReadLine();
    s.Close();
}

With the service now defined, implemented, and configured, I can build this piece of the solution and start it running. I've set it up so that the service runs in the console application, waiting for incoming calls, until the user presses Enter in the console window.

The WPF UserControl

There are two obvious ways you can use WPF in an application: to enhance the user interface and to provide sophisticated data visualization. My example focuses on the rich UI capabilities. The important point here is how easily you can incorporate WPF controls in VSTO solutions. I could demonstrate that with an extremely simple WPF control—perhaps a grid with a button on it—but it would be a shame not to take advantage of the sophisticated graphics capabilities of WPF, so I'll use a custom WPF UserControl that provides a fish-eye animation behavior. I've based it on the HyperBar sample for Microsoft Expression BlendTM (available at blogs.msdn.com/expression/articles/516599.aspx). It's also based on Paul Tallett's FishEyePanel described at codeproject.com/WPF/Panels.asp. The major difference I'll introduce is to put the WPF control in a non-WPF window so you can see it hosted in a native Office window.

To begin, I'll create a Windows WPF UserControl Library project—this produces some starter XAML and the corresponding C# codebehind. The first thing to do is change the UserControl class name from UserControl1 to something more meaningful—FishEyeControl. This FishEyeControl will contain a collection of buttons managed by a custom panel, named FishEyePanel, which derives from System.Windows.Controls.Panel. In FishEyePanel, I set up event handlers for three mouse events (MouseMove, MouseEnter, MouseLeave) so I can invalidate the panel and cause it to be re-rendered whenever the user moves the mouse over it. Here is one of the mouse event handlers:

public class FishEyePanel : Panel
{
    public FishEyePanel()
    {
        this.MouseMove += new
            MouseEventHandler(FishEyePanel_MouseMove);
    }

    private void FishEyePanel_MouseMove(object sender,
        MouseEventArgs e)
    {
        this.InvalidateArrange();
    }
}

To connect the FishEyePanel to the FishEyeControl, I had to set up the ItemsControl property on the FishEyeControl, like so:

<UserControl x:Class="WPFFishEye.FishEyeControl"
    xmlns:uc="clr-namespace:WPFFishEye">
    <Grid>
        <ItemsControl>
            <ItemsControl.ItemsPanel>
                <ItemsPanelTemplate>
                    <uc:FishEyePanel/>
                </ItemsPanelTemplate>
            </ItemsControl.ItemsPanel>
        </ItemsControl>
    </Grid>
</UserControl>

Next, I set up some data resources for the panel. Instead of trying to set up a more realistic Web service or LOB data source, I'll just use local files as resources. In this sample, I'll use a simple list of .jpg files as the images on the buttons. These JPGs are in external files, so I need a little code to take the simple file name (such as Frangipani.jpg) and convert it to a fully qualified path that will be valid at run time. For this, I can define an implementation of IValueConverter. This is a common technique for performing custom conversion during data binding. The IValueConverter interface defines two methods, but I only care about one—Convert. The data-binding engine calls this method when it propagates a value from the binding source to the binding target. In my implementation, I simply use the CodeBase of the current assembly and prepend this as the path to the image file name.

public class ImagePathConverter : IValueConverter
{
    public object Convert(object value, Type targetType,
        object parameter,
        System.Globalization.CultureInfo culture)
    {
        string path =
            Assembly.GetExecutingAssembly().CodeBase.Substring
            ("file:///".Length);
        path = Path.GetDirectoryName(path) + "\\Images\\";
        return string.Format("{0}{1}", path, (string)value);
    }
}

Then, back in the FishEyeControl.xaml, I connect these pieces in a ResourceDictionary for the control and use the ResourceDictionary in the ItemsControl (see Figure 3). Note that the DataTemplate for the items within the panel is set to be a Button with an Image, and the Image uses the ImagePathConverter during data binding.

Figure 3 Template for the Databound Button Controls (Close Figure 3)
<UserControl.Resources>
    <ResourceDictionary>
        <XmlDataProvider x:Key="ItemImages" XPath="ItemImages/ItemImage">
            <x:XData>
                <ItemImages >
                    <ItemImage Image="Frangipani.jpg" Width="50"/>
                    <ItemImage Image="Toucan.jpg" Width="50"/>
                    <!-- etc -->
                </ItemImages>
            </x:XData>
        </XmlDataProvider>
        <uc:ImagePathConverter x:Key="ImagePathConverter" />
    </ResourceDictionary>
</UserControl.Resources>

<Grid>
    <ItemsControl
    DataContext="{Binding Source={StaticResource ItemImages}}"
    ItemsSource="{Binding}">
        <ItemsControl.ItemsPanel>
            <ItemsPanelTemplate>
                <uc:FishEyePanel/>
            </ItemsPanelTemplate>
        </ItemsControl.ItemsPanel>

        <ItemsControl.ItemContainerStyle>
            <Style TargetType="{x:Type ContentPresenter}">
                <Setter Property="ContentTemplate">
                    <DataTemplate>
                        <Button Name="b1" Click="buttonClick">
                            <Image
Source="{Binding Converter={StaticResource ImagePathConverter},
XPath=@Image}"/>
                        </Button>
                    </DataTemplate>
                </Setter>
            </Style>
        </ItemsControl.ItemContainerStyle>
    </ItemsControl>
</Grid>

This definition also specifies the buttonClick event handler for each Button. Although I could handle the event within the WPF control itself, as well as within the hosting Windows Forms control or the add-in, I'll handle it only in the WPF control and the add-in. I implement the event handlers in FishEyeControl.xaml.cs by extracting the button Image name and firing the event again. The VSTO add-in will react to the event by invoking the WCF service, based on the Image name (see Figure 4).

Figure 4 Handling the FishEye Click Event (Close Figure 4)
public partial class FishEyeControl : System.Windows.Controls.UserControl
{
    public event FishEyeEvent FishEyeClickEvent;

    private void buttonClick(object sender, RoutedEventArgs e)
    {
        Button buttonSender = (Button)sender;
        Image buttonImage = (Image)buttonSender.Content;
        ImageSource imageSource = buttonImage.Source;
        String imageName = imageSource.ToString();
        int lastSlash = imageName.LastIndexOf('/') + 1;
        String buttonName = imageName.Substring(
            lastSlash, imageName.Length - lastSlash - ".jpg".Length);

        FishEyeEventArgs fe = new FishEyeEventArgs(buttonName);
        if (FishEyeClickEvent != null)
        {
            FishEyeClickEvent(sender, fe);
        }
    }
}

Next I define a custom EventArgs type so that when I refire the event I can send the simple name of the button Image out to the listener as shown in the following,

public delegate void FishEyeEvent(object source, FishEyeEventArgs e);

public class FishEyeEventArgs : EventArgs
{
    public String ButtonName;

    public FishEyeEventArgs(String name)
    {
        ButtonName = name;
    }
}

Recall that I'm invalidating the FishEyePanel when the user moves the mouse over it. I also want to control how the rendering behaves so that I can provide the fish-eye animation behavior. To take control of rendering, I must implement two overrides, MeasureOverride and LayoutOverride, which implement a two-pass layout system. During the measurement pass, the parent (FishEyeControl) calls the child (FishEyePanel) to find out how much space the child needs. The standard behavior here is for the child control to query its own children to find out how much space they need and then pass the result back up to the parent. WPF uses a model where controls can be nested almost indefinitely, so in this context, the entire tree is queried for its space requirements and the information is passed back up to the ultimate parent. In the layout pass, a similar recursion takes place, where the parent decides on the sizing of its children and passes this down, and each child in turn passes the information down to its children. This calls the ArrangeOverride method, where I can tell the children their size and lay them out.

In my implementation of MeasureOverride, I use the UIElement.DesiredSize property to try to give the children all the space they need. In ArrangeOverride, I add scale transforms and translate transforms to every child, and calculate how wide they need to be. In this sample, I scale the child button that is directly under the mouse to be larger than the others. The two buttons on either side of this are smaller, but still larger than all the other buttons. The remaining buttons are equal in size across the space left over. For further details, you can examine the code supplied in the download that accompanies this article on the MSDN® Magazine Web site (msdn.microsoft.com/msdnmag/code07.aspx). The net result is that with each mouse movement, I adjust the size of the buttons to provide the familiar fish-eye effect.

Connecting the WPF UserControl to VSTO

The basic VSTO solution is a Word 2007 add-in called WordImageSelecter. Once the initial add-in project is created, I can add the WPF UserControl project to the solution. This allows me to take advantage of the enhanced design-time support in Visual Studio 2008 for integrating WPF controls within Windows Forms projects.

The next step is to create a simple Windows Forms UserControl within the add-in project. This will eventually host the custom WPF UserControl. In Visual Studio 2008, any WPF UserControl in any project in your solution will show up in the ToolBox, so you can drag and drop it directly onto the Windows Forms UserControl design surface, as shown in Figure 5.

Figure 5 WPF Control Windows Forms Design Surface
Figure 5 WPF Control Windows Forms Design Surface

This action generates all the code needed to host the WPF control in the Windows Forms control, specifically via an ElementHost object. The ElementHost is designed for integrating Windows Forms and WPF. In this example, the Windows Forms UserControl hosts the ElementHost, which in turn hosts the WPF UserControl. This also adds the required references to the custom WPFFishEye.dll, as well as to the standard PresentationCore, PresentationFramework, UIAutomationProvider, WindowsBase, and WindowsFormsIntegration DLLs. The Windows Forms control is very simple—its only purpose is to host the WPF control, so I don't need to build in any significant functionality. Note that I expose the hosted WPF FishEyeControl as a property so that I can sink the FishEyeEvent exposed by the FishEyeControl (as shown in Figure 6).

Figure 6 Initialize the Control (Close Figure 6)
private System.Windows.Forms.Integration.ElementHost elementHost;
private WPFFishEye.FishEyeControl fishEye;
public WPFFishEye.FishEyeControl FishEye
{
    get { return fishEye; }
}

private void InitializeComponent()
{
    this.elementHost =
        new System.Windows.Forms.Integration.ElementHost();
    this.fishEye = new WPFFishEye.FishEyeControl();
    this.elementHost.Dock = System.Windows.Forms.DockStyle.Fill;
    this.elementHost.Child = this.fishEyeControl;
    this.Controls.Add(this.elementHost);
}

Now, I must connect the Windows Forms control to a custom task pane in Word. To do this, I need to implement the ThisAddIn_Startup method to create the custom task pane, specifying the Windows Forms control as the control to host, and sink the custom FishEyeClickEvent:

private WFControl wfControl;

private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
    Microsoft.Office.Tools.CustomTaskPane taskPane=
        this.CustomTaskPanes.Add(new WFControl(), "ImageSelecter");
    wfControl = (WFControl)taskPane.Control;
    wfControl.FishEye.FishEyeClickEvent +=
        new FishEyeEvent(FishEye_FishEyeClickEvent);
    taskPane.Visible = true;
}

In the implementation of the FishEyeClickEvent handler, for now I can simply display a message box. If you're following along, you could test the add-in at this point because the custom task pane and WPF control pieces are complete:

private void FishEye_FishEyeClickEvent(object source,
    FishEyeEventArgs e)
{
    System.Windows.Forms.MessageBox.Show(e.ButtonName);
}

You can use this same general technique for placing WPF controls anywhere in VSTO solutions you are allowed to put Windows Forms controls—in arbitrary Windows Forms dialogs, in app-level custom task panes, in the document actions pane, in Outlook custom form regions, and on the surface of a Word or Excel document. Note that for a Word or Excel document, you can use the regular design-time support for building a Windows Forms control that incorporates your WPF control, but then you have to stop short of actually putting the control on the document surface at design time, because the special VSTO Excel and Word designers don't support it.

That said, programmatically adding a hosted WPF control to a VSTO document solution is really no effort at all. The VSTO runtime infrastructure already supports any arbitrary Windows Forms control, including controls that host WPF controls. The runtime is already in place and has been since Visual Studio 2005. So, the only programmatic task you have left is to instantiate the outer Windows Forms control (that's hosting your WPF control, via ElementHost) and add it to the solution.

private void Sheet1_Startup(object sender, System.EventArgs e)
{
    string controlName = "MyWfControl";
    WFControl wfControl = new WFControl();
    wfControl.Tag = Controls.AddControl(wfControl, 50,50, 208,250,
        controlName);
    wfControl.Name = controlName;
}

Given the nature of WPF hosting within VSTO solutions, it should be clear that one of the limitations is that you cannot use Windows Vista AeroTM Glass in the normal way. That is, if your WPF control uses Aero Glass, it will only be transparent to the underlying Windows Forms hosting control, not to the document surface or native Office window. Of course, you can mitigate this by specifying a background image as part of your control; then any Aero Glass controls you lay on top can be transparent to that image.

Consuming the WCF Service in the VSTO Solution

There are several ways to develop the necessary client-side code to consume a WCF service. You can generate metadata exchange information and client proxy code from the compiled service assembly or from the running service itself. You can execute svcutil.exe manually to generate the code, or you can use the Add Service Reference wizard in Visual Studio 2008.

With Visual Studio 2005, you have to use svcutil manually. To do this, first start the service, then open a command window and navigate to the client project folder. Then run svcutil to generate the client-side proxy code. An example command line might look like this:

"C:\Program Files\Microsoft SDKs\Windows\v6.0\Bin\svcutil" /language:C#
/config:app.config http://localhost:8080/ImageServiceHost/ImageService/mex
/n:*,WordImageSelecter

This specifies the URL for the running service along with the metadata exchange endpoint address. It also indicates that C# should be used as the target language, that the configuration information should be output to a file named app.config, and that WordImageSelecter should be the namespace (in this sample, this is the namespace for the add-in project).

Once svcutil has generated the C# proxy code and the app.config, they must be added to the add-in project. Also, a reference to System.ServiceModel.dll must be added.

Visual Studio 2008 provides a graphical wizard you can use instead of running svcutil manually. To use the wizard, start the service, then right-click the add-in project in Solution Explorer and select the Add Service Reference option from the context menu. In the Add Service Reference dialog, enter the service URL and the target namespace, then click Go. This fetches the service contract and configuration information from the running service and populates the Services and Operations lists, as shown in Figure 7. Click OK to generate the proxy code.

Figure 7 Add Service Reference Dialog
Figure 7 Add Service Reference Dialog(Click the image for a smaller view)
Figure 7 Add Service Reference Dialog
Figure 7 Add Service Reference Dialog (Click the image for a larger view)

Now you can add a proxy class field to the add-in class, initialize it in ThisAddIn_Startup, and invoke the WCF Service GetDefinition method through the proxy. In the FishEyeClickEvent handler, replace the message box with a call to GetDefinition, parse the returned XML to extract the Key and Definition, and insert these into the Word document. Note that after inserting the Key text, I move to the end of the insertion before adding the Definition text—this is so that I don't overwrite the previous selection (see Figure 8).

Figure 8 Using the WCF Service Client-Side Proxy (Close Figure 8)
internal ImageServiceClient serviceClient;

private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
    // Connect to the WCF service.
    serviceClient = new ImageServiceClient();

    // (previously discussed code omitted for brevity).
}

private void FishEye_FishEyeClickEvent(object source, FishEyeEventArgs e)
{
    //System.Windows.Forms.MessageBox.Show(e.ButtonName);
    // Invoke the WCF Service and parse the returned XML.
    XmlDocument xmlDoc = new XmlDocument();
    xmlDoc.LoadXml(
        Globals.ThisAddIn.serviceClient.GetDefinition(e.ButtonName));
    String keyText = xmlDoc.SelectSingleNode(
        "descendant::Item/Key").InnerText;
    String definitionText = xmlDoc.SelectSingleNode(
        "descendant::Item/Definition").InnerText;

    // Insert the Key text into the Word document.
    this.Application.Selection.InsertAfter(
        String.Format("{0}{1}{2}",
        Environment.NewLine, keyText, Environment.NewLine));
    object titleStyle = Word.WdBuiltinStyle.wdStyleTitle;
    this.Application.Selection.set_Style(ref titleStyle);

    // Move to the end of the insertion.
    object gotoItem = Word.WdGoToItem.wdGoToLine;
    object gotoDirection = Word.WdGoToDirection.wdGoToLast;
    this.Application.Selection.GoTo(
        ref gotoItem, ref gotoDirection, ref missing, ref missing);

    // Insert the Definition text into the Word document.
    Word.Range insertionStart = this.Application.Selection.Range;
    this.Application.Selection.InsertAfter(
        String.Format("{0}{1}", definitionText, Environment.NewLine));
    object headingStyle2 = Word.WdBuiltinStyle.wdStyleHeading2;
    this.Application.Selection.set_Style(ref headingStyle2);
}

Security

On your development machine, security is set up for the add-in solution when you build it. When you deploy the solution, security will be set up as part of the publish operation. Office 2007 introduced new security features and streamlined the way security works for macros, ActiveX controls, and add-ins. All installed add-ins are now accessible through the Office Trust Center. VSTO also made very significant changes to its security model for Visual Studio 2008. The model no longer relies on the version-specific code access security (CAS) repository and is instead integrated into the ClickOnce model. VSTO also layers on additional security for both add-ins and document solutions that intelligently uses digital certificates and/or a registry-based store of the user's trust decisions. VSTO solutions always require FullTrust because they interop with unmanaged code (the Office host), and this requirement extends to the WPF and WCF pieces that form part of the solution on the client. With Visual Studio 2005 and with projects for Office 2003 in Visual Studio 2008, you need to set up security specifically for the client-side WCF .config file (the app.config for the WordImageSelecter.dll). This is because the .config file is an entry point into the code and must be explicitly trusted in CAS policy. When building projects for Office 2007 in Visual Studio 2008, this is no longer necessary because trust is granted to a solution, including any dependent assemblies and other ancillary files that form part of the solution. At this point, you can test the add-in again now that both the WPF and WCF pieces are in place.

Using LINQ, XLinq, and Lambda Expressions in VSTO

The final piece to explore is the use of LINQ in the context of a VSTO solution. With Visual Studio 2008, most projects automatically include references to System.Core and System.Xml.Linq, where most of the LINQ classes are defined. LINQ is a set of language extensions that allows you to perform SQL-like queries on an open-ended range of data sources in a type-safe manner. In the sample add-in, all the LINQ work is done in the FishEyeClickEvent handler, where I perform some simple analyses on the XML data returned from the WCF service. Although the example is simple, it illustrates how powerful the feature is.

To begin with, I can use XLinq to extract the Key and Definition from the XML string, instead of the more cumbersome XPath I used initially:

XElement item = XElement.Parse(
    Globals.ThisAddIn.serviceClient.GetDefinition(e.ButtonName));
String keyText = (string)item.Element("Key");
String definitionText = (string)item.Element("Definition");

Another feature of LINQ is implicitly typed local variables. Type inference is invoked by using the var keyword to instruct the compiler to infer the type of a variable from the expression in which it is used. Here, we'll use this technique to get the count of characters in the Definition text:

var chars = from c in definitionText
            select c;
int charCount = chars.Count();

Next, we can use a lambda expression to get the number of four-letter words in the Definition text. A lambda expression is similar to an anonymous delegate:

List<string> definitionWords =
    new List<string>(definitionText.Split(new char[] { ' ' }));
var fourLetterWords = definitionWords.FindAll(
    x => (x.Length == 4));

Besides using lambda expressions in place of anonymous delegates, you can also build an expression tree from lambda expressions. This allows you to treat code (the lambda expression) as if it were data. In the following example, I take the lambda expression y => (y.Length == 4) and map it to an Expression object so that I can output a LISP-style translation of the expression. In this example, this expression will produce the output "Equal Length 4":

Expression<Func<string, bool>> f = y => (y.Length == 4);
BinaryExpression be = (BinaryExpression)f.Body;
MemberExpression me = (MemberExpression)be.Left;
ConstantExpression ce = (ConstantExpression)be.Right;

this.Application.Selection.InsertAfter(
    String.Format("{0} chars, {1} four-letter words [{2} {3} {4}] {5}",
    charCount, fourLetterWords.Count,
    be.NodeType, me.Member.Name, ce.Value,
    Environment.NewLine));

The last feature to use is extension methods. Many people complain that developing code in C# for Office is painful because many methods in the Office object models take a large number of optional parameters and (especially with Word), in many cases, parameters must be passed explicitly by reference. This is not a problem with Visual Basic because Visual Basic takes care of the translation for you and hides the underlying complexity. For example, the code listed earlier to move the selection to the end of the previous insertion required calling the GoTo method. This method takes four parameters, all explicitly passed by reference, and includes two that are optional.

To mitigate this problem, I can write an extension method, which is a static method in a custom class. I can make it look like it extends one of the Office interfaces by specifying that interface as the type of the first parameter, which must also use the this keyword. This extension method can internally use the standard Office GoTo method:

public static class RangeExtender
{
    public static void GoTo(this Word.Application wordApplication,
        Word.WdGoToItem gotoItem, Word.WdGoToDirection gotoDirection)
    {
        object what = gotoItem;
        object which = gotoDirection;
        object missing = Type.Missing;
        wordApplication.Selection.GoTo(
            ref what, ref which, ref missing, ref missing);
    }
}

I can then use this extension method in place of the one defined by the Office object model:

this.Application.GoTo(
    Word.WdGoToItem.wdGoToLine, Word.WdGoToDirection.wdGoToLast);

The Future of Office-Based Apps

As more and more developers build Office-based solutions with managed code, it becomes increasingly important that features introduced in the .NET Framework and in new releases of Visual Studio work seamlessly within an Office context. In this article, you've seen how you can build a VSTO solution that uses WPF, WCF, and LINQ to utilize services from within an Office application. The example solution uses a simple WCF service to retrieve static data, parses the data with LINQ, and uses WPF to provide an enhanced UI. A more typical real-world solution would likely use a range of WCF services to work with multiple server-side LOB systems, and could use WPF to provide sophisticated data visualization in addition to UI elements. LINQ could be used either on the server, in the middle tier, or on the client to build intuitive, maintainable functionality for data querying and manipulation.

It is clear that the designer enhancements for WPF integration with Windows Forms and compiler enhancements for language-integrated querying make Visual Studio 2008 a compelling addition to your enterprise development toolbox.

Andrew Whitechapel spent many years as an architect consultant building enterprise solutions for a wide range of customers, and he is now Program Manager Technical Lead for the VSTO team at Microsoft.


Cmdlets: Extend Windows PowerShell With Custom Commands

Cmdlets

Extend Windows PowerShell With Custom Commands

Jim Truher
Code download available at: PowerShell2007_12.exe (154KB)
This article discusses:
  • Windows PowerShell SDK
  • Using the System.IO.IsolatedStorage namespace
  • Overriding default cmdlet behavior
  • Installing and using custom cmdlets
This article uses the following technologies:
Windows PowerShell, .NET Framework, Isolated Storage
Contents
An Overview of Cmdlets
Cmdlet Attribute
Cmdlet Class Definition
Parameter Definition
Common Parameters
ShouldProcess Parameters
Parameter Sets
Method Overrides
Emitting Results
Reporting Error Conditions
Troubleshooting Messages
Cmdlet Groups
Creating a Snap-In
Formatting
Installing and Loading a PSSnapIn
Putting It All Together

A
s you've probably already discovered, Windows PowerShellTM is a powerful and flexible tool. You may not know, however, that you can extend Windows PowerShell by writing your own cmdlets. In this article, I will show you how to create your own cmdlets by writing three custom cmdlets that allow you to interact with IsolatedStorage.

I chose IsolatedStorage for these samples because I have not seen any other cmdlets related to IsolatedStorage and I thought its functionality would be useful, offering a place to keep data that won't conflict with other applications.

In a nutshell, the System.IO.IsolatedStorage namespace lets you create and use isolated stores. You can read and write data that less-trusted code cannot access, preventing sensitive information from being exposed. Basically, data is only available to the current user or the assembly in which the code exists (it can also be isolated by domain).

When using IsolatedStorage in these examples, I will be saving a key/value pair as strings. IsolatedStorage can store any type of data you need, but I'm sticking to strings for the purposes of this article. Remember that this article is really about cmdlets—IsolatedStorage merely provides a sample for me to work with. Also keep in mind I'm merely providing a starting point here. When you're ready to go much deeper into creating your own custom cmdlets, be sure to check out the Windows PowerShell SDK.

An Overview of Cmdlets

When Microsoft created Windows PowerShell, it was designed to make it easy to create other command-line tools that offer the same consistency and reliability as the tools that shipped as a part of Windows PowerShell. This is in large part because the shell has a single parser for all cmdlets—this model allows the dev team to be sure that all argument parsing, error handling, and so on is done similarly for everything that a user might do.

As a result, there are some pretty significant differences between a Windows PowerShell cmdlet and commands in other standalone shell environments. For instance, a cmdlet is an instance of a Microsoft® .NET Framework class; it is not a standalone executable. Cmdlets generally output objects rather than text and should not format their output. A cmdlet processes its input objects from an object pipeline rather than from a stream of text. A cmdlet should not parse its own arguments and it should not specify a presentation for errors. Finally, cmdlets are record-oriented and generally process a single object at a time.

Cmdlets have a specific structure; they must be attributed in a particular way and they must be derived from a specific base class. If a particular cmdlet supports parameters, those parameters must also be attributed in a specific way, and the cmdlet must provide implementations of some specific methods. If you're wondering what all this means, don't worry. I'll explain each of these requirements in more detail.

Cmdlet Attribute

To declare a .NET class as a cmdlet, you attribute the class with the CmdletAttribute attribute (which is the only required attribute for any cmdlet). When you specify the CmdletAttribute attribute, you must specify a verb and noun name pair, which will be used as the name of the cmdlet. This should describe what the cmdlet does and what sort of resource the cmdlet works with.

Note that the CmdletAttribute attribute itself is a .NET class. The properties of the class correspond to the parameters available when using the cmdlet.

The noun portion of the cmdlet name allows you to differentiate your custom cmdlet from other cmdlets. The noun part specifies the resources upon which the cmdlet acts. Ideally, the noun used in cmdlet naming should be very specific; if you've got a generic term, you should use that term as a suffix to your cmdlet. For example, if you create a get-server cmdlet for your SQL database, you should use "New-SQLDatabase" rather than "New-Database". The combination of specific noun and verb names makes it easy for the user to discover cmdlets quickly and anticipate functionality. Remember that these should be words that people will recognize, so you shouldn't use reserved punctuation (slashes, brackets, and so on) or wildcard characters in your cmdlet names.

Since I am creating cmdlets that work with Windows® IsolatedStorage, I'll use that as the basis for my noun. It may be a bit long, but with cmdlet names, the more specific, the better.

There are some pretty strong guidelines regarding what verbs should be used for verb names. Remember that consistency is important in Windows PowerShell, so be sure you use the appropriate verbs. By using one of the predefined verb names, you will improve consistency between your custom cmdlets, the included cmdlets, and cmdlets created by other users. For example, to retrieve data you use Get, rather than Retrieve or Acquire. Common verbs used in Windows PowerShell include: Add, Clear, Copy, Get, Join, Lock, Move, New, Remove, Rename, Select, Set, Split, and Unlock. You can tell what each is used for just from its name. In this article I'll create three cmdlets: one to set the data contents of the IsolatedStorage, one to retrieve the contents, and one to remove the IsolatedStorage file. Thus, I will create three cmdlets: Set-IsolatedStorageData, Get-IsolatedStorageData, and Remove-IsolatedStorageFile.

Cmdlet Class Definition

Now I need to create the code that will implement my cmdlets, starting with my Set-IsolatedStorageData cmdlet. First I declare the cmdlet class:

[Cmdlet(VerbsCommon.Set , "IsolatedStorage",
SupportsShouldProcess=true)]

public class SetIsolatedStorageCommand : PSCmdlet

Notice that I'm using Pascal casing and that the name of the class includes the verb and noun name for the cmdlet. Strictly speaking, this isn't necessary, but it makes my code much more readable.

The cmdlet attribution indicates that I'm using a common verb, in this case Set. If possible, you should always use common verbs when creating cmdlets. Setting SupportsShouldProcess to True indicates that the cmdlet supports calls to the ShouldProcess method, which provides the cmdlet the opportunity to prompt the user for verification before an action that changes the system is performed. If this attribute isn't present or is set to False (which is the default value), it would indicate that the cmdlet does not support calls to the ShouldProcess method.

All cmdlets that change resources outside of Windows PowerShell should set the SupportsShouldProcess property to true when declaring the CmdletAttribute attribute. This allows the cmdlet to call the ShouldProcess method before performing its action. If the ShouldProcess call returns false, the action will not be taken. (For more information about the confirmation requests generated by ShouldProcess call, see the MSDN® documentation at msdn2.microsoft.com/ bb204629.) The Confirm and WhatIf cmdlet parameters are available only for cmdlets that support ShouldProcess calls.

The last bit declares the class and uses PSCmdlet as the base class, which means that I'll get all the extended behaviors associated with a Windows PowerShell cmdlet.

Windows PowerShell supports cmdlets that are derived from two different base classes: PSCmdlet and Cmdlet. A cmdlet derived from PSCmdlet gives you access to the Windows PowerShell runtime. This enables calls to other scripts, and allows access to the Windows PowerShell providers for working with session state. PSCmdlet also provides access to the Windows PowerShell logging features, though this comes at the price of being a bit bigger and leaves you dependent upon the Windows PowerShell runtime.

Cmdlets derived from the Cmdlet class offer only the fewest dependencies on the Windows PowerShell runtime. This has some benefits—these cmdlets are a bit smaller since they have less functionality, and you're less likely to encounter problems due to changes in Windows PowerShell over time. In addition, these cmdlets can be easily included in other applications without the Windows PowerShell runtime.

If your plan is to create a cmdlet that will always be part of the Windows PowerShell environment, you should use PSCmdlet as your base class. However, if you think that your code will be used in more than just Windows PowerShell, you should use Cmdlet as a base class.

For my examples, I am deriving from PSCmdlet. When you are creating your cmdlet, you will need to reference System.Management.Automation.dll, which can be tricky to find since it's only in the Global Assembly Cache (GAC), but I've got a trick to help with that, which I'll share in just a moment.

Parameter Definition

Next I need to consider the parameters for my cmdlet. Parameters allow the user to provide input to the cmdlet.

Cmdlet parameter names should be consistent across the cmdlet design. The Windows PowerShell SDK has detailed suggestions on parameter names and how you should use them in your cmdlet to help ensure consistency with other cmdlets you may come across.

To declare parameters for a cmdlet, you must first define the properties that represent the parameters. To inform the Windows PowerShell runtime that a property is a cmdlet parameter, you add a ParameterAttribute attribute to the property definition.

Parameters must be explicitly marked as public; ones that are not marked as public default to internal and are not found by the Windows PowerShell runtime. This can lead to some confusion when you're trying to figure out why your cmdlet doesn't have the parameters you think it should have.

In my sample, I know that all my cmdlets will need a name that designates the actual name of the IsolatedStorage file. So here is the parameter declaration—a Name parameter:

private string _name = "PowerShellIsolatedStore";

/// <summary>name of store</summary>
[Parameter]
public string Name
{
    get { return _name; }
    set { _name = value; }
}

When you create a parameter, you should choose whether it is positional or named. With a positional parameter you don't need to provide the parameter name—just the value:

PS> cd c:\windows

By setting Position=num as part of the attribute, you designate what position is used for that parameter. If a parameter is not positional, you leave off the Position attribute and use the parameter name from the command line to provide a value.

The documentation recommends that you make frequently used parameters positional whenever possible. The only problem with this guidance is that if you have many parameters, it can be a bit much to remember. Of course, even when a parameter is positional, the parameter name can still be used from the command line.

Cmdlet parameters can be defined as mandatory, meaning that they must have a value assigned before the Windows PowerShell runtime will invoke the cmdlet. Alternatively, they can be defined as optional. By default, all parameters are defined as optional. To define an optional parameter, then, simply omit the Mandatory property in the attribute declaration. Since I'll be storing a key and a value in the isolated storage, I need to create parameters so I can gather those values (see Figure 1).

Figure 1 Parameters Needed to Gather Key and Value (Close Figure 1)
private string _key = null;
[Parameter(
            Mandatory=true,
            Position=1,
            ValueFromPipelineByPropertyName=true
            )]
public string Key
{
    get { return _key; }
    set { _key = value; }
}

private string _value = null;

/// <summary>the value to store</summary>
 [Parameter(
            Mandatory=true,
            Position=2,
            ValueFromPipelineByPropertyName=true
            )]
public string Value
{
    get { return _value; }
    set { _value = value; }
}

Cmdlet parameters can also have aliases. To tell Windows PowerShell that a parameter has an alias, you add an AliasAttribute attribute to the property definition. The basic syntax for declaring the attribute is [Alias("alias")]. In my example, I create an alias called Filename that applies to the Name parameter, like so:

private string _name = "PowerShellIsolatedStore";
/// <summary>name of store</summary>
[Alias("Filename")]
[Parameter]
public string Name
{
    get { return _name; }
    set { _name = value; }
}

Common Parameters

Windows PowerShell reserves a few parameter names, referred to as Common parameters, which you can't use: WhatIf, Confirm, Verbose, Debug, ErrorAction, ErrorVariable, OutVariable, and OutBuffer. In addition, the following aliases for these parameter names are reserved: vb, db, ea, ev, ov, and ob.

ShouldProcess Parameters

Another group of parameters, the ShouldProcess parameters, are present only when the cmdlet specifies the SupportsShouldProcess keyword in its CmdletAttribute attribute.

When your cmdlet supports ShouldProcess, you have access to the following parameters at runtime: Confirm and WhatIf. Confirm specifies whether user confirmation is required before a cmdlet performs an action that modifies the system. True indicates that confirmation is required; false indicates that it's not required.

WhatIf specifies whether a cmdlet should inform the user what changes would have been made if the action were performed but without the action occurring. True indicates that the user is informed without the action occurring, False indicates that the action should occur.

When SupportsShouldProcess is specified, cmdlets that attempt to declare these parameters will fail when trying to register the cmdlet. You shouldn't define these parameters in your cmdlet directly. Instead, include SupportsShouldProcess when you use the [Cmdlet(...)] attribute.

Parameter Sets

Windows PowerShell uses the concept of parameter sets. This enables you to write a single cmdlet that exposes different sets of parameters to the user and returns different information based on the parameters specified by the user. For example, the Get-EventLog cmdlet (built into Windows PowerShell) returns different information when the user specifies the List or LogName parameter. When LogName is specified, the cmdlet returns information about the events in a given event log. However, when List is specified, the cmdlet returns information about the log files themselves (not the event information they contain). In this case, List and LogName identify two different parameter sets.

When multiple parameter sets are defined, the cmdlet can indicate which parameter set to use if Windows PowerShell doesn't have enough information to make that determination. The parameter set that is used in this case is referred to as the default parameter set, and is specified using the DefaultParameterSet keyword of the CmdletAttribute declaration. Note that I'm not using parameter sets in my sample cmdlets.

Method Overrides

The Cmdlet class provides virtual methods, shown in Figure 2, that can be used to process records. One or more of these methods must be overridden by all derived cmdlet classes.

Figure 2 Virtual Methods (Close Figure 2)

Method | Purpose
BeginProcessing | Provides optional one-time, preprocessing functionality for the cmdlet.
ProcessRecord | Provides record-by-record processing functionality for the cmdlet. It may be called any number of times or not at all, depending on the input of the cmdlet.
EndProcessing | Provides optional one-time, post-processing functionality for the cmdlet.
StopProcessing | Stops processing when the user stops the cmdlet asynchronously, such as by entering the key combination Ctrl+C.

Since my cmdlets deal with files, I'll use the BeginProcessing method to implement the code used to open the IsolatedStorage files, as shown in Figure 3. There are a couple of things worth noting. First, I'm opening the files in create mode, which is a system change, so I should wrap that code in a ShouldProcess block. That means I will have an opportunity to tell the user what I'm going to do before I actually do it. (This is done when the user uses either Confirm or WhatIf arguments.) Also notice that I am going to catch exceptions and wrap them with ThrowTerminatingError. In this sample, if anything goes wrong, it shouldn't proceed because the file open will have failed. It's usually bad form to catch all exceptions, but since I'm wrapping them in ThrowTerminatingError, I'll be able to provide a bit more information in case of failure.

Figure 3 Using the BeginProcessing Method (Close Figure 3)
protected override void BeginProcessing()
{
    try
    {
        if ( ShouldProcess( Name ))
        {
            WriteVerbose("Opening Isolated Storage: " + Name);
            isoStore =  this.GetMyStore();
            fs = new IsolatedStorageFileStream(
                Name,
                FileMode.OpenOrCreate|FileMode.Append,
                FileAccess.Write,
                isoStore
                );
            sw = new StreamWriter(fs);
            WriteDebug("Stream encoding: " + sw.Encoding);
        }
    }

    catch ( Exception e )

    {
        this.closeStreams();
        ThrowTerminatingError(
            new ErrorRecord(
                e,
                "OpenIsolatedStorage",
                ErrorCategory.NotSpecified,
                Name
                )
            );
    }
}

In my implementation, I'll use ProcessRecord to do the work of creating new entries for data in isolated storage (see Figure 4). Note the use of the try/catch statements, in order to add more information in case an error occurs. In this case I'm using WriteError instead of ThrowTerminatingError; this is because I don't need to stop the pipeline in case of a bad write.

Figure 4 Using ProcessRecord in Set-IsolatedStorageData (Close Figure 4)
protected override void ProcessRecord()
{
    try
    {
        // Remember ShouldProcess may not have opened the file
        if(sw != null )
        {
            WriteVerbose("Setting " + Key + " = " + Value);
            sw.WriteLine(Key + "=" + Value);
        }
    }
    catch ( Exception e )
    {
        WriteError(
            new ErrorRecord(
                e,
                "SetIsolatedStorageValue",
                ErrorCategory.NotSpecified,
                Name
                )
            );
    }
}

Since I opened the isolated storage from within BeginProcessing, I'll use the EndProcessing method to close the files. I'm not reading or writing to the isolated storage file in Remove-IsolatedStorageFile; therefore, I'll use EndProcessing to remove the file in that cmdlet. Here's what the EndProcessing code looks like for the Get-IsolatedStorageData and Set-IsolatedStorageData cmdlets:

protected override void EndProcessing()
{
     if (sw != null ) { sw.Close(); }
     if (fs != null ) { fs.Close(); }
     if (isoStore != null ) { isoStore.Close(); }
}

The code in Remove-IsolatedStorageFile is a bit trickier. In this cmdlet, I delete the file itself using the appropriate methods from the IsolatedStorage object:

    if(ShouldProcess("Remove Isolated Storage"))
    {
         WriteVerbose("Deleting Isolated Storage: " + Name);
         isoStore = this.GetMyStore();
         isoStore.DeleteFile(Name);
    }

Notice that I'm using ShouldProcess again. Since I'm making a change to the system, I need to notify the user of what I'm about to do, should they want this information.

Emitting Results

Windows PowerShell is all about results, but you have to find a balance in the way you provide those results. The balance involves making sure you return as much information as you can without causing too much impact on the performance (such as by using too much memory, taking too long to execute, and so on). Since I'll be saving text to a file, I could just return the text all by itself. While this would be OK, I want to demonstrate something better, so I will provide the key and the value.

When I was first learning about IsolatedStorage, I noticed that it's pretty tough to find the actual file being used for the storage, so I want to include that information in my results. This will make the results more useful. By returning the key, value, and the path to the data, my object looks like this:

public class IsolatedStorageData
{
    public string Key;      // The Key
    public string Value;    // The Value
    public string FullName; // The path to the storage
}

I also have to consider what sort of string the object's ToString method should return. By default, most .NET objects return just the name of the type—this isn't very useful. So I'll have the ToString method return the Value rather than the name of the type.

In order to get the value of the actual filename for the FullName member, I need to do a little reflection because this information isn't surfaced as part of the IsolatedStorage information. So the implementation of the object that I'll use for the results will look like what's shown in Figure 5.

Figure 5 Object Used for the Results (Close Figure 5)
public class IsolatedStorageData
{
    public string Key;      // The Key
    public string Value;    // The Value
    public string FullName; // The path to the storage
    public override string ToString() { return Value; }
    public IsolatedStorageData(
        string _key,
        string _value,
        IsolatedStorageFileStream _fs
        )
    {
        Key = _key;
        Value = _value;
        FullName = _fs.GetType() .
            GetField("m_FullPath",
                BindingFlags.Instance|BindingFlags.NonPublic ) .
                    GetValue(_fs).ToString();
    }
}

Again, remember that in order to provide consistency across all cmdlets, Windows PowerShell cmdlets return objects into a pipeline rather than text to a stream. WriteObject is what the cmdlet uses to emit the results. After retrieving the data from the IsolatedStorage file, I convert that to an instance of an IsolatedStorageData type and use WriteObject to emit those results into the pipeline.

Reporting Error Conditions

When running code, there will be times when things just don't do what you want and some sort of error occurs. Windows PowerShell has some fairly sophisticated behaviors for these circumstances and its error-reporting capabilities allow for very fine-grained control over what happens.

Sometimes when an error occurs, it's not catastrophic. For example, if you want to remove thousands of files in a directory, failing to remove one or two of those files won't invalidate all the other file deletions. These are non-terminating errors—that is, it's still an error, but it's not an error that will result in you having to stop whatever you're doing. You can continue to remove the files that are removable.

However, there are operations that you can't recover from. Suppose you need to create a temporary file to hold on to some data that you'll use later. If you can't create and use the temporary file, there's no point in proceeding with the rest of the operation because the data you'll need won't be available. This qualifies as a TerminatingError. In Windows PowerShell, two different cmdlet methods—WriteError and ThrowTerminatingError—allow you to make this distinction.

WriteError is used whenever there's some sort of exceptional circumstance in the execution of your cmdlet that isn't fatal to the overall operation of the cmdlet. The method takes as an argument an instance of an ErrorRecord, which allows you to include more than just the exception (the cause of the error).

You really shouldn't throw an exception in a cmdlet. Instead, ThrowTerminatingError allows you to stop the execution of the pipeline and provide much more information than you could with an exception.

You'll notice in the code for my cmdlets that I use ThrowTerminatingError in BeginProcessing but then use WriteError in the ProcessRecord methods. This is because if I can't get access to the IsolatedStorage, there's not much I'll be able to do with regard to the operation of the cmdlet in general. However, if I encounter an error while reading the file, I may be able to recover and continue.

Troubleshooting Messages

Windows PowerShell provides a number of ways to communicate with the user. There are three methods you can use to notify the user when you want to communicate something that isn't a result or an error. You should know that when you use these methods, you can't redirect the messages that will be sent, but you can suppress them by setting some preferences in the shell. These methods communicate directly with the hosting application, in this case PowerShell.exe, and in turn write to the console window. Preference variables offer a variety of behaviors, from not writing anything to asking whether the message should be written before continuing.

I use these in my sample cmdlets a little more than you might because I want to show how they can help. If you have complicated code that needs more than just error or results, be sure to use these available methods. The last thing you want to do is use something like System.Console.WriteLine from a cmdlet. First, it's pretty bad practice and second, you shouldn't depend on the hosting application since the console may not even be present.

You should use WriteVerbose if you have extra out-of-band information you want to pass to the user. This isn't for developer messages; it's for letting your user know what's going on under the covers. The interface is pretty simple. Here's an example from my Remove-IsolatedStorageFile, which uses WriteVerbose to output that it's about to remove the IsolatedStorage file:

if(ShouldProcess("Remove Isolated Storage"))
{
WriteVerbose("Deleting Isolated Storage: " + Name);
isoStore = this.GetMyStore();
isoStore.DeleteFile(Name);
}

You use WriteDebug when you want to communicate with developers, giving them a chance to troubleshoot errant behavior in your cmdlet. Typically, you would attach a debugger to the system, but you can't always do that in the field. WriteDebug will help in these situations.

WriteWarning is yet another way to communicate with the user, providing information about the execution of your cmdlet. (I haven't used this method in my sample cmdlets.) A good example of using WriteWarning is if you want to change the behavior of a cmdlet over time. You can use WriteWarning, for instance, if you were to deprecate a parameter and wanted to tell the user to stop using it in favor of a new parameter.

Cmdlet Groups

All three of my cmdlets pertain to IsolatedStorage, so the nouns all have the same root: IsolatedStorage. Two of the cmdlets are for working with the actual data (Set-IsolatedStorageData and Get-IsolatedStorageData) while the other removes the file (Remove-IsolatedStorageFile).

Since all of these cmdlets have a parameter in common (the name of the actual file that will be used), I don't have to implement the same parameter in all of the cmdlets. Instead, I can create a base class, IsolatedStorageBase, that derives from PSCmdlet, and the cmdlets will derive from IsolatedStorageBase (see Figure 6). This is a good way to ensure that you are consistent from cmdlet to cmdlet.

Figure 6 IsolatedStorageBase Base Class (Close Figure 6)
public class IsolatedStorageBase : PSCmdlet
{
    [Parameter]
    public string Name
    ...
}
[Cmdlet(VerbsCommon.Set, "IsolatedStorageData",SupportsShouldProcess = true)]
public class SetIsolatedStorageDataCommand: IsolatedStorageBase
{
...
}
[Cmdlet(VerbsCommon.Get, "IsolatedStorageData")]
public class GetIsolatedStorageDataCommand: IsolatedStorageBase
{
...
}
[Cmdlet(VerbsCommon.Remove,"IsolatedStorageFile",SupportsShouldProcess = true)]
public class RemoveIsolatedStorageFileCommand: IsolatedStorageBase
{
...
}

Creating a Snap-In

In order to use these new cmdlets, you need to add them to the Windows PowerShell environment. Windows PowerShell has the ability to dynamically add cmdlets to the session via a snap-in. To avoid potential confusion with MMC snap-ins, a Windows PowerShell snap-in is called a PSSnapIn.

To create a PSSnapIn, you need to write a bit of code that will perform two tasks. First, it provides identification for your snap-in so it can be distinguished from other snap-ins installed on your system. Second, it provides information for properly installing the snap-in and for creating the appropriate registry entries to allow Windows PowerShell to find the assembly.

There are two types of Windows PowerShell snap-ins in the System.Management.Automation namespace: PSSnapIn and CustomPSSnapIn. You should use PSSnapIn when you want to register all the cmdlets and providers automatically in one assembly. CustomPSSnapIn should be used when you want to register a subset of the cmdlets and providers in one assembly or when you want to register cmdlets and providers that are in different assemblies. The code for my snap-in is shown in Figure 7. It's quite straightforward. I simply override the appropriate members and then I'm pretty much finished.

Figure 7 Snap-In for My Custom Cmdlets (Close Figure 7)
// This class defines the properties of a snapin
[RunInstaller(true)]
public class IsolatedStorageCmdlets : PSSnapIn
{
    /// <summary>Creates an instance of DemoSnapin class.</summary>
    public IsolatedStorageCmdlets() : base()
    {
    }
    ///<summary>The snap-in name that is used for registration</summary>
    public override string Name
    {
        get { return "IsolatedStorageCmdlets"; }
    }
    /// <summary>Gets vendor of the snap-in.</summary>
    public override string Vendor
    {
        get { return "James W. Truher";  }
    }
    /// <summary>Gets description of the snap-in. </summary>
    public override string Description
    {
        get { return "Isolated Storage Cmdlets"; }
    }
    /// <summary>The format file for the snap-in. </summary>
    private string[] _formats = { "IsolatedStorage.Format.ps1xml" };
    public override string[] Formats
    {
        get { return _formats ; }
    }
}

Formatting

Notice in Figure 7 that there is a value for the Formats member of this snap-in. This allows you to create formatting directives for the objects that the cmdlets emit. In this case, I have a simple object, but I don't want to present all the members by default—I want to be sure that I print only the information that is useful, which in this case is the key and the value.

The creation of these format files could fill an article by itself, and the topic is beyond the scope of this article. Figure 8 provides sample code that creates a table format with the two columns I'm interested in: the key and value of the IsolatedStorageData object. For the purposes of this example, you can just create a file named IsolatedStorage.Format.ps1xml in the same directory as the assembly and when the snap-in is loaded, you shouldn't see any errors.

Figure 8 Sample Format File (Close Figure 8)
<Configuration>
  <ViewDefinitions>
    <View>
    <Name>IsolatedStorage</Name>
    <ViewSelectedBy>
      <TypeName>IsolatedStorageSnapin.IsolatedStorageData</TypeName>
    </ViewSelectedBy>
    <TableControl>
      <TableHeaders>
        <TableColumnHeader>
          <Label>Key</Label>
          <Width>12</Width>
      </TableColumnHeader>
      <TableColumnHeader />
        </TableHeaders>
          <TableRowEntries>
            <TableRowEntry>
              <TableColumnItems>
                <TableColumnItem>
                  <PropertyName>Key</PropertyName>
                </TableColumnItem>
                <TableColumnItem>
                  <PropertyName>Value</PropertyName>
                </TableColumnItem>
            </TableColumnItems>
          </TableRowEntry>
        </TableRowEntries>
      </TableControl>
    </View>
  </ViewDefinitions>
</Configuration>

Installing and Loading a PSSnapIn

Installing a snap-in is extremely easy. Just run Installutil.exe with the path to your assembly. When this utility runs, it creates some registry entries under HKLM\SOFTWARE\Microsoft\PowerShell\1\PowerShellSnapins\<snapinname>. When Windows PowerShell loads a snap-in, these entries are used to load the assembly and find the various configuration files. It is also worth mentioning that Installutil.exe is the recommended installation method during development only, as the utility's actions don't properly register their uninstallation dependencies. For installation in production environments, you should configure the registry entries directly.

Loading the snap-in is also easy. The main cmdlets you will use are Add-PSSnapIn, Remove-PSSnapIn, and Get-PSSnapIn. Not surprisingly, add-PSSnapIn adds one or more Windows PowerShell snap-ins to the current session. Remove-PSSnapIn removes snap-ins from the current session. And Get-PSSnapIn retrieves the Windows PowerShell snap-ins on the computer.

Note that Remove-PSSnapIn doesn't actually unload the assembly. It just removes the cmdlets and providers from the lists that Windows PowerShell uses to find cmdlets and access providers.

Putting It All Together

Once I do everything I've just described, I am ready to start using my new custom cmdlets. Of course, I am doing everything from the command line. First, I must compile the cmdlet and snap-in code. Remember when I said I'll need to compile this code with a reference to System.Management.Automation.dll? This DLL can be found both in the SDK and in the GAC, but if you don't have the SDK installed, don't worry about it. This little script can easily create the Snap-In assembly. The first thing that I need to do is create an alias for the C# compiler, once that is established I find the location of the System.Management.Automation.dll and compile the assembly:

New-Alias csc "${V2Framework}\csc.exe"
$SMADLL = [PSObject].Assembly.Location
csc /target:library IsolatedStorageSnapin.cs /r:$SMADLL

Now that I have compiled my assembly, I can use Installutil.exe to register my new snap-in, as shown in Figure 9. After the installation has been completed successfully, I can find the new PSSnapIn:

Figure 9 Registering the Snap-In
Figure 9 Registering the Snap-In(Click the image for a smaller view)
Figure 9 Registering the Snap-In
Figure 9 Registering the Snap-In (Click the image for a larger view)

PS> get-PSSnapIn -reg iso*

Name        : IsolatedStorageCmdlets
PSVersion   : 1.0
Description : Isolated Storage Cmdlets

Then I add the snap-in to my session:

PS> add-PSSnapIn IsolatedStorageCmdlets

And now I can use the cmdlets, as shown in Figure 10!

Figure 10 Using the Cmdlets
Figure 10 Using the Cmdlets(Click the image for a smaller view)
Figure 10 Using the Cmdlets
Figure 10 Using the Cmdlets (Click the image for a larger view)

Jim Truheris currently a Program Manager in Microsoft Research Incubation working on the Response Point product. Before moving to Microsoft Research, James was a Program Manager on the Windows PowerShell team and created the Windows PowerShell scripting language with Bruce Payette.


Interop: Connect To Mainframe Apps With BizTalk Adapters and .NET

Interop

Connect To Mainframe Apps With BizTalk Adapters and .NET

Bashar "Bash" Badawi
This article discusses:
  • Connecting to a host system
  • Exposing COBOL apps as endpoints
  • .NET Framework integration
  • Terminal screen-scraping
This article uses the following technologies:
BizTalk Server, Host Integration Server, .NET Framework 2.0
Contents
BizTalk Adapters for Mainframes
Making the Connection
Exposing COBOL Apps as Endpoints
Integration Beyond Adapters
Wrapping Up

I
magine you were a bank CEO back in the 1970s. Your customer base was growing rapidly, and keeping paper records was proving to be an expensive and a risky business. Where could you go to automate record-keeping? What were your options for more efficient storage?

As it turned out, most such organizations invested in mainframes or minicomputers, such as the AS/400 and VAX systems—and it was on these systems that many programmers learned their trade. Common Business Oriented Language (COBOL), which was originally developed in 1959, became the dominant development language on these systems.

You might be surprised to know that COBOL is still a common language for business apps. According to a 2001 Gartner report on enterprise programming language adoption, more than 60 percent of the businesses surveyed still used COBOL. Almost every major industry relies on COBOL. Most companies that use mainframe systems for business-critical applications and data storage have continued investing in COBOL development—and probably will do so even after migrating away from mainframes. As recently as 2006, the New York Stock Exchange had moved its stock options clearing and billing systems to Windows®-based servers, but continued running its COBOL applications in recompiled form.

In the early days, mainframe connectivity was limited to terminals connected via coaxial cable to a controller that was, in turn, connected to a front-end processor (FEP), much like current networks. The network protocol was typically SNA (System Network Architecture), though mainframes later came to support TCP/IP as well. Another popular choice for wire-level connectivity was IP-DLC and High Performance Routing. The original design of SNA did not support routing, so special routing hardware or software was used to deliver the packets to the FEP. Since many COBOL applications were SNA protocol-specific, IP-DLC was developed as an alternative in which IP is the main carrier and SNA is somewhat encapsulated within it.

Within SNA there are sub-protocols based on the concept of a logical unit (LU)—an end user or application that interacts with the network. LU 0 was used for custom protocols, LU 2 for a 3270 data stream targeting terminal-based applications, LU 3 for printing from the mainframe, and LU 6.2 for application-to-application communication. Integrating with LU 2/3270 (the green screen often associated with mainframe interfaces) is commonly known as screen-scraping, where an application traverses the screen looking for meaningful data in fixed positions.

When dealing with these LU protocols, your choice is often between utilizing an existing toolset and developing your own custom solution. This becomes a rather obvious decision given the complexities of the latter option. In either case, you would have to develop or use a low-level network handler and then move up the OSI stack to supply the additional logic needed to handle the specific requirements of the target system, including byte conversion using different code pages, packet assembly and disassembly, session connection, conversation management, and security. This is no small task.

BizTalk Adapters for Mainframes

BizTalk ® Adapters for Host Integration Server help you connect to mainframe and midrange systems and integrate your Windows systems with their host applications, data sources, messaging, and security. By connecting these technologies, the resources residing on a mainframe system become another BizTalk-managed endpoint that can be used as a send or receive location, for example.

There are four main adapters in Host Integration Server 2006:

  • BizTalk Adapter for Host Applications
  • BizTalk Adapter for WebSphere MQ
  • BizTalk Adapter for Host Files
  • BizTalk Adapter for DB2

In addition to the BizTalk adapters that Host Integration Server offers, network connectivity components for TCP/IP, SNA, and TN3270 are also provided to supply a transport layer for the data to reach the host.

Once installed, the adapters are registered so that BizTalk can locate them, and you can add them in the BizTalk administration console as shown in Figure 1.

Figure 1 BizTalk Adapters in the Administration Console
Figure 1 BizTalk Adapters in the Administration Console(Click the image for a smaller view)
Figure 1 BizTalk Adapters in the Administration Console
Figure 1 BizTalk Adapters in the Administration Console (Click the image for a larger view)

In addition to the adapters themselves, all the adapter features are also installed in order to provide direct integration with the mainframe from within Visual Studio®. For data access, the DB2 and host file Microsoft® .NET Framework 2.0 managed providers are installed and available to be used either programmatically or through integration with Server Explorer in Visual Studio. A Host File Designer and DB2 Data Access Wizard are also provided so that users have the ability to directly access and design host files.

Network connectivity tools, a transaction integrator, and Host Integration Server projects are also provided as part of the design-time experience, and there's sample code to address the vast majority of supported scenarios for connectivity to mainframes and AS/400 systems. There's even a Host Simulator for those who don't happen to have a mainframe at home!

The SimHost can be found under the System folder in the Installation Folder.

Making the Connection

In order to connect and choose the right protocol, you need to get the appropriate parameters and security credentials from the mainframe administrator. When connecting to a DB2 database, for example, you are presented with options for TCP/IP and SNA (LU 6.2). These settings apply across all layers of integration with the mainframe.

When connecting using TCP/IP, a port number is required as the mainframe will listen on that port for all incoming requests. SNA configuration requires more input from the mainframe administrator, and the connection has to be configured using the SNA Manager MMC snap-in, shown in Figure 2. For both configurations, the SDK provides worksheets to help you gather the data you need from the mainframe administrator.

Figure 2 Configuring an SNA Connection
Figure 2 Configuring an SNA Connection(Click the image for a smaller view)
Figure 2 Configuring an SNA Connection
Figure 2 Configuring an SNA Connection (Click the image for a larger view)

Accessing DB2 databases is a breeze with the BizTalk Adapters for Host Data. By using the adapter for DB2, it is possible to connect, read or write data, and execute common SQL commands against DB2. Host Integration Server further simplifies this functionality by providing a Data Access Wizard for testing purposes.

Aside from the BizTalk Adapter functionality, the DB2 adapter also provides a .NET Framework data provider for DB2 that can be used to directly access information in DB2 database servers. The data provider for DB2 is based on the Microsoft network client for DB2, which provides pooling, schema generation, transactions, and static SQL.

After you connect to the database, the BizTalk Port Transport Properties contains the string needed to make the connection available to the BizTalk runtime.

When you have the host adapter for DB2 installed along with the .NET Framework managed provider, you can also access DB2 data on the host programmatically as the code in Figure 3 demonstrates. Debugging and tracing tools are provided for both TCP/IP and SNA protocols.

Figure 3 Accessing a DB2 Database (Close Figure 3)
using Microsoft.HostIntegration.MsDb2Client;
[WebMethod(TransactionOption=TransactionOption.RequiresNew)]

public DataSet GetAccountsFromDB2(string connString)
{
    MsDb2Connection conn = new MsDb2Connection(connString);
    MsDb2DataAdapter da = new MsDb2DataAdapter();
    DataSet ds = new DataSet();

    try
    {
        conn.ConnectionPooling = false;
        conn.Open();
        da.SelectCommand = new MsDb2Command();
        da.SelectCommand.Connection = conn;
        da.SelectCommand.CommandText = "SELECT * FROM MSDB2BANK";
        da.Fill(ds);
        ContextUtil.SetComplete();
    }

    catch(Exception e)
    {
        ContextUtil.SetAbort();
        throw e;
    }

    finally
    {
        conn.Close();
    }

    return ds;
}

Exposing COBOL Apps as Endpoints

The BizTalk Adapter for Host Applications is based on the Transaction Integrator (TI), formerly known as COM Transaction Initiator (COMTI). The adapter extends TI technology to provide COBOL applications and transactions as endpoints to be managed and consumed by BizTalk and .NET clients.

Before digging deeper into the BizTalk Adapter for Host Applications, you need to understand how the programming model affects how you call any COBOL transaction on the host in the design and runtime environments. The importance of achieving connectivity as noted earlier is that it plays a major role in deciding which programming model you will use to call COBOL applications from BizTalk and any .NET application.

The purpose of choosing a programming model is to determine how to interface with the mainframe. The BizTalk Adapter for Host Applications does not install any components on the host, so you're on the hook for figuring out the host-side aspect of integrating with your mainframe applications.

If TCP/IP is the protocol chosen for connectivity, you have two options. The first is the MSLink model, where a listener application resides on the host. The listener intercepts inbound calls and dispatches them to the appropriate COBOL application, as depicted in Figure 4.

Although the diagram looks intimidating, the concepts it shows are not complicated. The best way to simplify it is to borrow an analogous process. If you consider IIS as an example, the listener block in the diagram represents the TCP/IP stack after it is loaded at boot time and bound to a network adapter. Once the IP stack is loaded, IIS is configured to communicate with it via a port list. In Figure 4, this would be the concurrent server. In IIS, the most common port obviously is port 80; in the mainframe world the most common ports are 3000 and higher.

Figure 4 Host App Integration via an MSLink Listener
Figure 4 Host App Integration via an MSLink Listener

The server transaction program (TP) is slightly more complex, but to keep it simple, think of the ASP.NET HTTP ISAPI extension. When IIS receives a page request for a file of type ASPX or ASMX, it hands the request over to the IIS extension. The extension then uses the ASP.NET runtime to process the inbound request, handle the response to the client, and so on.

In the MSLink model, there are three essential components. First is the listener, which is supplied by IBM and used for TCP/IP port and socket management. Once the listener receives an incoming request from the .NET client, it accepts the connection, instructs the .NET client to send the data Transaction Request Message (TRM), and starts the concurrent server.

Second is the concurrent server, which handles incoming requests from the .NET clients, parses them, extracts the transaction the .NET client wants to call, and makes the actual call. It also keeps the connection open waiting on the server TP to respond. Once returned, the socket is closed.

Finally, the server TP executes the end transaction the client actually intended to call. The TP receives the data, invokes the COBOL logic, and returns the result to the concurrent server, which in turn sends it back down to the .NET caller.

The concurrent server contains all the TCP socket-specific code. In fact, Microsoft supplies a sample listener in the SDK called MSCMTICS.CBL for customers as guidance. Using an MSLink listener is the preferred method since it requires no additional changes to the COBOL applications you can call, assuming that they are callable (for example, they have no screen output).

The second approach to TCP/IP connectivity is the User Data model, where each callable COBOL application contains the TCP socket code and can be called directly from within your .NET code as depicted in Figure 5.

Figure 5 Host Integration via User Data and .NET
Figure 5 Host Integration via User Data and .NET

Building on the MSLink example, and to maintain consistency in my analogies, I will stay with the IIS metaphor. Compared to the three-step MSLink process in Figure 4, the User Data model combines steps two and three. This would be similar to developing your own IIS Server, say, or Windows Communication Foundation (WCF) channel, to listen in on a port, pick up the inbound request, process it, and return the call back to the client, all in the same running process. In the User Data model, the concurrent server and the end transaction the user intended on calling all reside within one TP on the mainframe.

It is important to note the difference between the two here: the MSLink model is less intrusive since nothing has to be done to the called transaction and all the TCP socket code is handled in a generic listener. Contrast this to the User Data model, which requires the socket code to be present in every transaction the client wants to call. Another important feature to note is that, in the MSLink model, the end transaction is called in a non-blocking manner, whereas in the User Data model the entire process is executed in a single iteration.

If SNA or IP-DLC is the connectivity method in use, these programming models still apply, but there is no need for a listener application since the mainframe supplies a native transaction that acts as a listener called CSMI by default (but could have a different name). This transaction uses a Distributed Program Link (DPL) that acts as a gateway among calling clients and the end transaction the user wishes to invoke as shown in Figure 6. By using a DPL, there is no need to install any software on the mainframe, hence the advantage of this approach to a non-DPL, SNA-based transaction communication. With a DPL the .NET client is disguised as just another mainframe calling a CICS TP.

Figure 6 Host Integration without a Listener
Figure 6 Host Integration without a Listener

When the CICS link has been chosen, the .NET client sends the request; the mirror transaction or CSMI then takes control of the inbound request and passes the COMMAREA to the TP after calling it. Once the business logic is invoked and processed, CSMI returns the data back to the client.

Once the programming model has been identified, you can start exposing COBOL applications as endpoints and .NET assemblies. To do so you will need a COBOL copybook from the mainframe developer. The copybook is similar to a C++ structure definition in that it identifies the input and output data the COBOL application expects. The COBOL copybook is imported into Visual Studio using the Import COBOL wizard (see Figure 7).

Figure 7 Importing the COBOL Copybook
Figure 7 Importing the COBOL Copybook(Click the image for a smaller view)
Figure 7 Importing the COBOL Copybook
Figure 7 Importing the COBOL Copybook (Click the image for a larger view)

After a successful import, a .NET assembly is produced and you have the option of choosing the type restrictions as Web service, None, or BizTalk Adapter for Host Applications. Choosing the latter will generate all the artifacts needed to add this to a BizTalk project and enlist the COBOL transaction in any business process or orchestration managed by BizTalk.

When this step is complete, you'll need to deploy the .NET assembly and provide the adapter with the metadata needed to call the COBOL application. The metadata describes the Remote Environment (RE), also known as the mainframe. The RE is configured by using the BizTalk Adapters for Host Applications TI Manager MMC snap-in.

After the RE is configured, the .NET assembly can be associated with the target RE. You can also now define the RE in the port parameters.

So far you've seen how to expose a COBOL application or transaction from within .NET as a BizTalk endpoint. The imported COBOL copybook exposed the data, and the RE provided the interface to the mainframe where execution of the business logic takes place. To move that business logic to the Windows platform, vendors such as Micro Focus and Fujitsu provide CLR-compliant COBOL .NET compilers and Visual Studio plug-ins to import the COBOL code and run it natively on Windows. These tools can even go a step further and translate the COBOL code to C#, for example.

Integration Beyond Adapters

Two other important integration features are not provided as adapters, but they can be exposed to BizTalk as endpoints: Host Initiated Processing (HIP) and Session Integrator.

HIP allows the mainframe COBOL applications to call any .NET assembly residing on the Windows Server® platform to participate in any BizTalk business process. HIP has design time tools integrated with Visual Studio, and it uses the TI MMC snap-in for managing the metadata needed to host the HIP listener, in addition to the security policies needed to secure the endpoint. The communication transport follows the same guidelines highlighted earlier and Microsoft provides sample COBOL code to test HIP connectivity and functionality.

Session Integrator provides a set of .NET objects that enable you to do screen scraping of COBOL terminal applications. It's a server-based technology and can come in handy if the COBOL applications you are trying to integrate with are not callable and write directly to the screen with the UI not separated from the business logic. Figure 8 shows what a sample screen-scraping application looks like. The data is exposed via a .NET object, which turns the screen canvas into rows and columns that you can read data from or write data to.

Figure 8 Screen-Scraping Application Based on Session Integrator
Figure 8 Screen-Scraping Application Based on Session Integrator(Click the image for a smaller view)
Figure 8 Screen-Scraping Application Based on Session Integrator
Figure 8 Screen-Scraping Application Based on Session Integrator (Click the image for a larger view)

Wrapping Up

The BizTalk Adapters for Host Integration Server are provided by Microsoft to achieve interoperability and integration with mainframes and AS/400 legacy systems. The adapters enable enlisting and exposing COBOL, DB2, and other mainframe resources as BizTalk endpoints. As you've seen here, using the adapters is certainly a lot less cumbersome than rolling your own integration components due to the complexity of the legacy mainframe environment. In addition, the adapters do not require any footprint on the host, which is appealing to IT departments faced with a continuing shortage of individuals with the skills to develop and manage host applications.

For more information about legacy host integration issues, see the TechNet Host Integration Server TechCenter at technet.microsoft.com/his. You'll find a variety of resources there, including the informative whitepaper "Introduction to BizTalk Adapters for Host Systems" (technet.microsoft.com/library/bb726925.aspx).

Bashar "Bash" Badawi has 20 years of IT experience in developing Microsoft-based solutions and consulting. Bash is currently a Senior Program Manager with the BizTalk/Host Integration Server group based in the field helping customers and partners understand and implement enterprise solutions.


Security: Authenticate Users Across Organizations Using ADFS

Security

Authenticate Users Across Organizations Using ADFS

Jack Couch
This article is based in part on a prerelease version of Windows Live ID. Details herein are subject to change.
This article discusses:
  • Using ADFS for SSO in a single organization
  • Configuring ADFS on the server and client
  • Connecting to an external organization
  • Expanding trust further with ADFS
This article uses the following technologies:
ADFS, ADAM
Contents
Using ADFS for SSO in a Single Organization
Registration, Authentication, and Authorization
Create the ADAM Account Store
Configure the ADFS Server
Configure the IIS Server and Applications
Importing Certificates
Including UnderMyControl.com
Add a Resource Partner
Add an Account Partner
Set Up a New User
Set Up a New Resource
Query for Granted Permissions
Expanding Trust to Tailspin Toys
Looking Forward—Windows Live ID Integration
Summary

A
ctive Directory® Federation Services (ADFS) was introduced in Windows Server® 2003 for organizations that need to participate in standards-based identity federation. With ADFS, you can more easily validate identity data from other organizations, leading to greater interoperability with your partners. In this article, I'll take you on a guided tour of ADFS in action, using the experiences of a fictitious online service provider (A. Datum Corporation) that uses ADFS to interact with a real online service provider (UnderMyControl.com) and a fictitious customer (Tailspin Toys).

Using ADFS for SSO in a Single Organization

The A. Datum Corporation is an online service provider that offers two Web applications to consumers. The first Web application enables online document sharing and storage; the second provides online music publishing for independent artists. Rather than using two separate account stores, which would require users to keep track of user names and passwords for each application, A. Datum used ADFS and ADAM (Active Directory Application Mode) to create a single account store, which enables users to access both applications using a single user name and password.

In many situations, an ADFS server will act as either an account partner or a resource partner. An ADFS server acting as an account partner is configured to interact with an account store (either ADAM or Active Directory) to authenticate users. An ADFS server acting as a resource partner is configured to support ADFS-aware applications. In this scenario, the ADFS server provides access both to the applications and to the account store.

By using ADAM in conjunction with ADFS, you can enable single sign-on (SSO) so that customers don't have to remember a multitude of passwords. Figure 1 shows how A. Datum deployed this solution in its organization.

Figure 1 A Single Sign-On Solution
Figure 1 A Single Sign-On Solution

Note: the Active Directory server is only present because the ADFS server must be domain-joined. Active Directory is not used in this scenario for any other purpose.

Registration, Authentication, and Authorization

When new users sign up with either the file storage or music publishing service, they go through the typical registration process: selecting a user name, setting a password, and providing profile information. This information is then stored in the ADAM account store, allowing that user to authenticate with all applications hosted by A. Datum. Figure 2 shows typical code for connecting to ADAM.

Figure 2 Typical Code for Connecting to ADAM (Close Figure 2)
const long ADS_OPTION_PASSWORD_PORTNUMBER = 6;
const long ADS_OPTION_PASSWORD_METHOD = 7;

const int ADS_PASSWORD_ENCODE_REQUIRE_SSL = 0;
const int ADS_PASSWORD_ENCODE_CLEAR = 1;

System.DirectoryServices.AuthenticationTypes AuthTypes =
    AuthenticationTypes.Signing |
    AuthenticationTypes.Sealing |
    AuthenticationTypes.Secure;

// connect to ADAM store
System.DirectoryServices.DirectoryEntry objUsers = new DirectoryEntry(
    "LDAP://10.0.0.212:389/cn=Users,dc=adamstore,dc=com",
    "ADATUM\\administrator","Pa$$word!",AuthTypes);

// create user object
DirectoryEntry user = objUsers.Children.Add(
    "cn="+UsernameTB.Text, "user");

// set user properties
user.Properties["msDS-UserDontExpirePassword"].Value=true;
user.Properties["userPrincipalName"].Value = UsernameTB.Text;
user.CommitChanges();

// set port number, method, and password.
user.Invoke("SetOption", new object[] { ADS_OPTION_PASSWORD_PORTNUMBER,
    389 });
user.Invoke("SetOption", new object[] { ADS_OPTION_PASSWORD_METHOD,
    ADS_PASSWORD_ENCODE_CLEAR});
user.Invoke("SetPassword", new object[] { PasswordTB.Text });

Once a user has created an account, when he attempts to access either the file storage or music publishing services the Web server will redirect him to the ADFS server. The ADFS server then prompts the user for his user name and password, communicates with the ADAM server to validate the user's credentials and determine group membership, issues the client a security token, and then redirects the client back to the Web application.

The security token contains information about the user's identity and group membership, which are called claims. The application uses the identity information to verify that the user successfully authenticated to the ADFS server, and it uses the group membership information to make authorization decisions. For example, the music application from A. Datum only allows a user to upload music if the user has the group claim Musicians in the security token:

// check if the user is a Musician and therefore has write (upload)
// permission
if (!User.IsInRole("Musicians"))
{
    // if the user doesn't have permission, show an error message
    Response.Clear();
    Response.Write("You do not have permission to upload files");
    return;
}

If the user now wants to access the other application hosted by A. Datum, he does not need to reenter his credentials. This is because the user already has an ADFS-issued security token that both applications trust. Although A. Datum could have accomplished SSO for its customers by using other techniques, ADFS allows the company to easily expand its trust relationships simply by adding additional account and resource partners.

Create the ADAM Account Store

You must have Windows Server 2003 R2 running on your server and be logged on with local administrator privileges to add the ADAM account store. You create the store by first installing ADAM and then by creating an ADAM instance. Installing ADAM is performed through the Control Panel: select Add/Remove Programs, then Add/Remove Windows Components. Once there, select the Active Directory Application Mode (ADAM) checkbox within Active Directory Services. After ADAM is installed, the All Programs menu within the Start menu will contain a "Create an ADAM instance" item; select this item. When prompted, select a unique instance and a new application directory partition. Once you have created an ADAM instance, you can connect to it using ADAM ADSI Edit. You will need to do this so that you give the account you plan to use the necessary permissions.

Configure the ADFS Server

In order to configure and run ADFS, you must first have IIS installed. You must also use the SelfSSL tool from the IIS 6 Resource Kit (go.microsoft.com/fwlink/?LinkId=36285) to create and install a self-signed SSL server authentication certificate.

The next step is to install ADFS by checking the Federation Service checkbox within Add or Remove Programs. You will be prompted to enable ASP.NET, as this is also needed for ADFS to function. Once installation is complete, the federation service endpoint (the URL for the federation service) and the URI (unique name for the federation service) can be modified with the Active Directory Federation Services snap-in.

Now you can add the ADAM account store. You can do this by right-clicking Account Stores and selecting New. The wizard will ask you for the ADAM server name, the URI (unique name that represents the ADAM store), the search base distinguished name (the location of the user accounts), and the Lightweight Directory Access Protocol (LDAP) attribute used for the user name (generally the userPrincipalName attribute is used).

Claims are user attributes that ADFS will store in the user's security token once the user is authenticated. Group claims indicate membership within a particular group. If a user account in the ADAM account store indicates the user is a member of the Musicians group, the A. Datum ADFS server places the group claim Musicians in the user's security token. Within the Active Directory Federation Services snap-in, right-click Organization Claims and select New Organization Claim. This will launch a wizard and allow you to select the type of claim (Group Claim) and provide the name (Musicians).

You associate the ADAM Musicians group with the Musicians group claim just created by creating a group claim extraction. This is done by right-clicking your account store and selecting New Group Claim Extraction. Here you will specify the LDAP attribute that represents the group membership. For example, you might specify the attribute memberof and the group CN=Musicians, CN=Users,DC=adamstore,DC=com. You will also select the Musicians group claim you just created from a dropdown list.Finally, in order to make the ADFS service aware of the applications for which it will be providing authentication, simply select New Application in the ADFS snap-in and provide the URL of the application.

Configure the IIS Server and Applications

For claims-aware applications, very little configuration is required on the IIS server that's hosting the application. First, you will want to install a server authentication certificate on the IIS server. Again, this can be done using SelfSSL in the IIS 6 Resource Kit. Next, you should install the claims-aware agent within Add/Remove Windows Components. Finally, configure the application to use ADFS for authentication. For a claims-aware application (an application built to use ADFS for authentication), all ADFS-related configuration is done in the app's web.config file. Figure 3 shows the web.config file from the music sharing application by A. Datum.

Figure 3 Web.config File from A. Datum Music Sharing App (Close Figure 3)
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <configSections>
    <sectionGroup name="system.web">
      <section name="websso"
        type="System.Web.Security.SingleSignOn.WebSsoConfigurationHandler,
         System.Web.Security.SingleSignOn, Version=1.0.0.0,
         Culture=neutral,
         PublicKeyToken=31bf3856ad364e35, Custom=null" />
    </sectionGroup>
  </configSections>

<system.web>
  <sessionState mode="Off" />
  <compilation defaultLanguage="c#" debug="true">
  <assemblies>
    <add assembly="System.Windows.Forms, Version=2.0.0.0, Culture=neutral,
        PublicKeyToken=B77A5C561934E089"/>
    <add assembly="System.Design, Version=2.0.0.0, Culture=neutral,
        PublicKeyToken=B03F5F7F11D50A3A"/>
    <add assembly="System.DirectoryServices, Version=2.0.0.0,
        Culture=neutral,
        PublicKeyToken=B03F5F7F11D50A3A"/>
    <add assembly="System.Web.Security.SingleSignOn, Version=1.0.0.0,
        Culture=neutral,
        PublicKeyToken=31bf3856ad364e35, Custom=null"/>
    <add assembly="System.Web.Security.SingleSignOn.ClaimTransforms,
        Version=1.0.0.0,
        Culture=neutral, PublicKeyToken=31bf3856ad364e35, Custom=null"/>
  </assemblies>
  </compilation>

  <customErrors mode="Off"/>
  <authentication mode="None" />

  <httpModules>
    <add name="Identity Federation Services Application
        Authentication Module"
        type="System.Web.Security.SingleSignOn.WebSsoAuthenticationModule,
        System.Web.Security.SingleSignOn, Version=1.0.0.0,
        Culture=neutral, PublicKeyToken=31bf3856ad364e35, Custom=null" />
  </httpModules>

  <websso>
    <authenticationrequired />

    <urls>
      <returnurl>https://www.adatum.com/Music/</returnurl>
    </urls>

    <cookies writecookies="true">
      <path>/Music</path>
    </cookies>

    <fs>https://adatum.ad.adatum.com/adfs/fs/
        federationserverservice.asmx</fs>
  </websso>
</system.web>

<system.diagnostics>
  <switches>
    <add name="WebSsoDebugLevel" value="255" />
  </switches>

  <trace autoflush="true" indentsize="3">
    <listeners>
      <add name="LSLogListener"
        type="System.Web.Security.SingleSignOn.BoundedSizeLogFileTrace
        Listener, System.Web.Security.SingleSignOn, Version=1.0.0.0,
        Culture=neutral,
        PublicKeyToken=31bf3856ad364e35, Custom=null"
        initializeData="c:\logdir\claimapp.log" />
    </listeners>
  </trace>
</system.diagnostics>

</configuration>

There are several areas of interest within Figure 3. Note the <configSections> block. The section name websso specifies the ADFS SSO.dll so it can be loaded by ASP.NET. The <assemblies> section lists several items, including the SingleSignOn and ClaimTransforms assemblies that ADFS uses.

The module declared in the httpModules section intercepts all HTTPS requests in order to validate the authentication by ensuring it is signed by the resource federation service. If a valid authentication cookie is not present, this httpModule will redirect the client to the federation service for authentication.

In the <websso> section, notice the <authenticationrequired/> tag. If this is not present, anonymous access to the site will be allowed and it is up to the individual page events to validate the security token before granting access. The HTTPmodule will still validate the authentication cookie, but it will not redirect the client to the federation service if a valid authentication cookie is not presented.

The <urls> section lets you specify the URL used by the ADFS server to redirect the client back to the application after the client has been authenticated. It's also used to identify the application when redirecting clients to the ADFS server. This must end with a forward slash (/):

<urls>
<returnurl>https://www.adatum.com/Music/</returnurl>
</urls>

The <cookies> section is used by ADFS to store the security token issued by the ADFS server on the client:

<cookies writecookies="true">
<path>/Music</path>
</cookies>

Finally, the <fs> tag specifies the location of the federation service URL so that the application can successfully authenticate user requests.

Importing Certificates

Because we are using self-signed certificates for A. Datum, we need to install certificates into the trusted root store for all the clients and most of the servers in use. This can be avoided by only using certificates issued by certificate authorities already installed in the trusted root store. You add certificates to the trusted root store using the Certificates Microsoft Management Console (MMC) snap-in. Figure 4 lists the clients, servers, and certificates that need to be installed using this procedure.

Figure 4 Required Certificates and Devices (Close Figure 4)

Device Type | Certificates Required
Web application servers (IIS) | Token signing certificate of the organization's ADFS service. Server authentication certificate of the organization's ADFS servers.
Client computers | Token signing certificates for all ADFS services. Server authentication certificates for all ADFS servers and Web application servers.
Resource ADFS servers | Token signing certificates for all account partner ADFS services.

Including UnderMyControl.com

Release 1.0 of the applications from A. Datum had very simple access control requirements. For the music publishing application, users were either musicians or fans. Musicians could upload music and everyone else could download all the music available. Simple access requirements were also needed for the file storage application. Either you were allowed to upload and download documents or you were only allowed to download documents.

With release 2.0 of these services, A. Datum wanted to greatly expand the access control capabilities of the file-sharing app and allow the owner of a document to assign specific permissions to other users. For example, if Bob was the owner of a specific document, he would be able to assign read permissions to Sue and read/write permissions to Tom. This would typically require that A. Datum rewrite much of the application logic and create a lot of new UI elements, but, because the company was already using ADFS, it decided to outsource its access control management needs to Web services provided by UnderMyControl.com to allow users to manage discretionary access control lists on resources.

As you can see in Figure 5, UnderMyControl.com also uses ADFS, but in this case to grant access to its application. By adding A. Datum as a trusted account partner on the UnderMyControl.com ADFS server and adding UnderMyControl.com as a trusted resource partner on the A. Datum ADFS server, A. Datum users can access UnderMyControl.com with the security token issued by A. Datum. In other words, they do not have to log in to UnderMyControl.com with a separate user name and password.

Figure 5 Outsourcing Access Control Management
Figure 5 Outsourcing Access Control Management

Add a Resource Partner

In order to establish the necessary trust relationship, A. Datum added UnderMyControl.com as a resource partner. This is done with the ADFS MMC snap-in by right-clicking Resource Partners and selecting New Resource Partner. From within the wizard you will need to provide the URL of the UnderMyControl.com ADFS service and select Federated Web SSO (indicating that no Active Directory trusts are involved).

Add an Account Partner

After configuring the A. Datum ADFS server with UnderMyControl.com as a resource partner, the trust relationship is completed by adding A. Datum as an account partner on the UnderMy Control.com ADFS server. This is done by right-clicking Account Partners and selecting New | Account Partner. Here you will need to provide the URL of the A. Datum ADFS service and select Federated Web SSO. You will also need to provide the token signing certificate used by A. Datum when creating security tokens your ADFS service will trust.

Set Up a New User

Although the user accounts are located in the A. Datum ADAM account store, UnderMyControl.com still needs to be aware of all users to allow other users to grant each other permissions. This is accomplished by creating a user profile in UnderMyControl.com using the Web service. When creating a user that will be authenticated via ADFS, don't provide a password:

// connect to Under My Control web service
umc.Service svc = new umc.Service();
svc.CookieContainer = new System.Net.CookieContainer();
Session["CookieContainer"] = svc.CookieContainer;

// create a new UMC user
System.Guid userGuid;
svc.CreateAccount(UsernameTB.Text, "", "" , out userGuid);

Because UnderMyControl.com allows users to manage permissions on resources, it needs a profile for each user that can be granted access and that can grant access to other users; however, the A. Datum account (user name and password) is stored exclusively in the ADAM server of A. Datum.

Set Up a New Resource

When a new document or song is uploaded to one of the A. Datum applications, the app creates an associated resource in UnderMyControl.com and sets the default permissions using the UnderMyControl.com Web service, as shown in Figure 6.

Figure 6 Create an Associated Resource (Close Figure 6)
// connect to the Under My Control web service
umc.Service svc = new umc.Service();
svc.CookieContainer =
    (System.Net.CookieContainer)Session["CookieContainer"];

// guid for the Under My Control music project
Guid projectGuid = new Guid("5f2739ff-a441-44fd-8c68-545a6d654e69");

// guid for the newly created resource
Guid resourceGuid;

// create new resource with name set to uploaded file name
svc.CreateResource(FileUpload.FileName, projectGuid, out resourceGuid);

// Set the permissions for the resource
// add the user as an owner of the new resource
svc.AddOwnerToObject( (Guid)Session["User_GUID"], resourceGuid);

// UMC permission ids for this project
const int READ_PERMISSION = 13;
const int WRITE_PERMISSION = 14;

// give user read (aka download) and write permissions
svc.CreateACL( (Guid) Session["User_GUID"], resourceGuid,
    READ_PERMISSION);
svc.CreateACL( (Guid) Session["User_GUID"], resourceGuid,
    WRITE_PERMISSION);

Query for Granted Permissions

When any user attempts to access a document or song, the A. Datum application first queries for that user's granted permissions in the UnderMyControl service and then only allows those actions. Figure 7 shows how you would perform authorization on a resource.

Figure 7 Connect Programmatically to UnderMyControl (Close Figure 7)
// connect to the Under My Control web service
umc.Service svc = new umc.Service();
svc.CookieContainer =
    (System.Net.CookieContainer)Session["CookieContainer"];

// ID for WRITE_PERMISSION in UMC for this project
const int WRITE_PERMISSION = 14;

// check if the user has write permissions for the resource in UMC
bool hasPermission = false;
if(!svc.HasACL((Guid)Session["User_GUID"], resourceGuid,
    WRITE_PERMISSION, out hasPermission)||!hasPermission)
{
    // if the user doesn't have permission, show an error message
    Response.Clear();
    Response.Write(
        "You do not have permission to overwrite this file");
    return;
}

Expanding Trust to Tailspin Toys

After version 2.0 of the file-sharing app was released with enhanced access control, consumer interest soared, but so did corporate interest. To the surprise of A. Datum, the company found itself in an agreement to provide document management to a large toy maker, Tailspin Toys. Tailspin wanted to use the A. Datum document storage application to allow employees anytime access to documents and to share them with colleagues, partners, and customers. However, Tailspin did not want to ask each of its employees to create a user account with A. Datum and maintain another user name and password. Again, the solution was ADFS.

A. Datum added Tailspin Toys as an account partner, allowing its employees to access A. Datum applications using their Tailspin Toys security tokens, as shown in Figure 8. The procedures for establishing the account and resource partner relationships on the A. Datum ADFS server and on the Tailspin Toys ADFS server are the same as those used to establish the trust relationship with UnderMyControl.com.

Figure 8 Tailspin Toys and A. Datum Implementation
Figure 8 Tailspin Toys and A. Datum Implementation

Of course, for the purposes of this discussion, Tailspin Toys has an oversimplified deployment. In a real deployment, an Account Federation Server Proxy would be used as an intermediary between the internal corporate network containing the Active Directory server and the external-facing ADFS server. For more information on using Account Federation Server Proxies, see the ADFS Design and Deployment Guide on the TechNet Web site.

Looking Forward—Windows Live ID Integration

Tailspin Toys proved to be the first of many organizations that outsourced document management to A. Datum with the same ADFS configuration. But as A. Datum continued to grow, it learned that many of its customers were small businesses using Office Live to host their employee accounts. The company also learned that many of its non-business users already had Windows LiveTM ID accounts. Because Windows Live ID is slated to support ADFS in the future, A. Datum realizes that both of these groups of users could eventually have the ability to use their existing user accounts by simply adding Windows Live ID as another account partner. Figure 9 shows a completed setup.

Figure 9 A. Datum and Windows Live ID Implementation
Figure 9 A. Datum and Windows Live ID Implementation

Using Windows Live ID as an account partner is not currently supported. The Microsoft Active Directory Federation Services product team and the Microsoft Windows Live ID team are currently developing a strong integration capability to enable various scenarios such as the one listed in this section. This integration work will be enabled as part of future releases of both ADFS and the Windows Live ID service.

Summary

Thanks to ADFS, A. Datum enjoyed greater flexibility in extending trust relationships to other organizations. This allows A. Datum to provide a more user-friendly experience by reducing the hassle of creating and maintaining multiple user accounts.

Jack Couch started his career in military intelligence and spent the past decade providing information security expertise to high-security government agencies and Fortune 500 companies. Recently, Jack was the Security Release Manager for Microsoft Windows Vista. He is currently the Managing Partner at DeepIntel, a security consulting firm dedicated to improving the security of large organizations. Jack can be contacted at jack@deepintel.com.


Editor's Note: Take a Deep Breath

Editor's Note
Take a Deep Breath
Howard Dierking

As I write this, I'm on a flight from Seattle to the East Coast. I'll spend a few days in our New York office, then I'm off to a conference in Texas. From there, it's back to Redmond for another couple of meetings and, finally, I head to Tech•Ed EMEA in Barcelona. Conference season is obviously in full swing—another way to tell that 2007 is nearly over! How did we get here so fast? It's a good time to reflect on where we've come as developers this year and what 2008 might bring.

While 2007 saw the launch of several different technologies, two primary themes emerged: AJAX and the Microsoft .NET Framework 3.0. AJAX was inaugurated as a first-class Web application development platform with the ASP.NET AJAX Extensions. The extensions library introduced both client and server libraries that can abstract much of the plumbing detail involved in connecting JavaScript code running in the browser with business logic running on an application server. Additionally, the library established a new mechanism for dynamically associating script behaviors with existing controls, provided localization functionality on both client and server, and much more.

The .NET Framework 3.0 introduced three technologies that are already causing a dramatic shift in how applications are architected. Windows Presentation Foundation (WPF) took the traditional message-pump, clipping-rectangle architecture of Win32 and flipped it on its head, leveraging advancements in graphic cards and offering Windows developers the declarative, container-based mode of authoring user interface elements long enjoyed by Web application developers. Windows Communication Foundation (WCF) pulled together all the various methods of creating a distributed application under one abstraction layer. And Windows Workflow Foundation (Windows WF) added a declarative workflow modeling language and execution engine, functionality that was previously available only as an additional app, not as a platform service.

When you consider the enormity of the .NET Framework 3.0 and AJAX, feeling overwhelmed as a developer is completely understandable. The implications that technologies like Windows WF and WCF bring to traditional application architectural patterns such as the rich domain pattern are potentially significant. For example, is it now a best practice to abandon all of those OO practices and split class hierarchies into pairs of logic service classes and data token classes that can easily be passed between remote services? What are the best practices around unit-testing long-running workflows? When is it better to use AJAX script service methods as opposed to partial rendering?

And if those questions aren't significant enough, 2008 looks likely to accelerate the trend. Ever since MIX 2007, Silverlight has been poised to take the world by storm, enabling a richer browser-based user experience than has yet been possible in Web application development. Moreover, with the release of LINQ in Visual Studio 2008 and the ADO.NET Entity Framework (following soon after the Visual Studio release), how we think about data access is about to undergo a major paradigm shift. And along with all of the great platform technologies being released, the 2007 Office system, including SharePoint technologies running on the application tier, continues to grow in its capabilities to provide highly integrated, functional apps for businesses.

So if you're like me and are wondering when we're going to get two seconds to take a breath, keep holding on. Like my travel schedule, it doesn't look like it's going to let up any time soon. As always, though, we at MSDN Magazine will continue to do what we do best—help you sort through the noise and grow into the best developer out there, hopefully entertaining you a bit along the way! —Howard

Thanks to the following Microsoft technical experts for their help with this issue: Derek Del Conte, Beatriz de Oliveira Costa, Chris Davis, Steve Fox, Matt Gibbs, Lee Holmes, Chris Houser, John Justice, Matt Steele, Nenad Stefanovic and Stephen Toub.


Toolbox: Live Chat, Code Conversion, Multiple Monitors, and More

Toolbox
Live Chat, Code Conversion, Multiple Monitors, and More
Scott Mitchell

Contents
Add Live Help to Your Microsoft ASP.NET Web Site
Quickly Convert between Visual Basic and C# Code
Manage Multiple Desktop Monitors
Have Fun Learning Design Patterns

Add Live Help to Your Microsoft ASP.NET Web Site

I was recently visiting a hiking supply Web site searching for a particular piece of equipment. After searching in vain for several minutes, I noticed a link to chat with a sales associate. Clicking it opened a new window, allowing me to chat with a gentleman named Chad. I explained what I was looking for and, just a few moments later, got a link to the item. Such live chat functionality is becoming increasingly common on the Internet. Chat is a more responsive customer service mechanism than e-mail and more cost-effective than telephone support.

Most live chat systems are sold as a service. You add a bit of HTML to your site to display the "Live Chat" link, and your customer service operators go to the service provider's site and wait to assist visitors. The advantage of this approach is that there's nothing to install or maintain on your site. However, such models are usually priced on the volume of customers or the number of concurrent operators, which means the price of your live chat will grow right along with your business!

If you want more control over the chat system's implementation and pricing, check out .netLIVEHELP Version 2.0 by Corporate Web Solutions. Unlike service-based systems, .netLIVEHELP ships as a series of Microsoft® ASP.NET 2.0 Web pages and Web services, along with a desktop application for operators. There's a flat, one-time price per Web site, regardless of the number of customers or operators. Plugging .netLIVEHELP into your site is relatively straightforward: you simply create a virtual directory pointing to the folder containing the ASP.NET files and Web services. Adding a "Live Chat" button to a page is as easy as adding one line of HTML.

When a customer initiates a chat session, the software routes the conversation to the operator with the lightest call load. The contents of the conversation are logged, as are metrics such as the customer's browser, IP address, and the Web pages visited on the site.

I was impressed with the .netLIVEHELP's operator and administrator interfaces. The operator signs on through a desktop application containing a system tray icon that flashes when a customer has been routed there. Clicking the system tray icon opens the chat window, which includes features like spell checking and text formatting. Operators can perform chats with multiple customers simultaneously and can also track visiting customers' current and past chats as well as the pages they've visited. And it is possible to integrate data from external applications, so that, for example, an operator can view the customer's shopping cart.

Administrators can perform the same functions as an operator, but can also view statistics. The statistics can be summarized across the whole company for a given date range or inspected on an operator-by-operator basis. The statistics include metrics like the total number of past and ongoing chats, average chat time, and average waiting time.

Price: $395 per Web site license.

dotnetlivehelp.com

Live Chat Enables More Responsive Customer Service
Live Chat Enables More Responsive Customer Service (Click the image for a smaller view)
Live Chat Enables More Responsive Customer Service
Live Chat Enables More Responsive Customer Service (Click the image for a larger view)

Quickly Convert between Visual Basic and C# Code

Since all Microsoft .NET Framework applications share a common type system and runtime regardless of the programming language used, the differences between .NET programming languages are only skin deep. This language parity lessens the learning curve for developers and enables seamless integration of .NET components whether or not they were created in the same language. While popular .NET programming languages like Visual Basic® and C# are equivalent in the eyes of the .NET runtime, they are not necessarily so in the eyes of developers, who may be adept in one language but not in the other. Such a language barrier can prove limiting when inheriting code or a legacy application at work or when studying code snippets in books or on the Web that are not in your language of choice.

The optimal solution is to become fluent in both Visual Basic and C#, but that's not always possible. The next-best option is to use an automated code converter like Tangible Software Solution's Instant C# version 2.8 or Instant VB version 2.0. Instant C# quickly converts Visual Basic code to C#, while Instant VB converts C# to Visual Basic. Both products can convert a snippet of code, an ASP.NET page, all the code within a specified folder, or an entire Visual Studio project.

While Visual Basic and C# compile into equivalent intermediate language, there is "syntactic sugar" unique to each language. Instant C# and Instant VB include options that let you specify how to handle these differences when converting. For example, when converting Visual Basic code to C# code, Instant C# allows you to indicate whether to convert Visual Basic functions—InStr, Mid, StrComp, and so on—to .NET Framework equivalents or whether to leave them as is and import the Microsoft.VisualBasic namespace. Likewise, you can also indicate whether to convert the Visual Basic vbCrLf keyword to System.Environment.NewLine or "\r\n" in the resulting C# code. And the code formatting options allow you to dictate whether to include braces for single-line block statements and, if so, whether the opening brace should appear on its own line.

The most powerful configuration options are the post-conversion replacement settings. You can specify an unlimited number of regular expression patterns and corresponding replacement text and Instant C# and Instant VB will apply these substitutions to the converted code.

Despite the underlying similarities between Visual Basic and C#, there are slight but profound differences that make a 100 percent accurate conversion impossible. The good news is that Instant C# and Instant VB have a successful conversion rate—over 99.9 percent—on code samples provided by Microsoft.

Price: $179 for Instant C# or Instant VB.

tangiblesoftwaresolutions.com

Manage Multiple Desktop Monitors

One of the most cost-effective ways to improve a developer's productivity is to give him another monitor. Developers often face tasks that involve simultaneously processing information from two windows: a running application and the debugger window is a classic example. Having two (or more) monitors at your workstation is priceless when working on such tasks since it allows you to have both windows on-screen at once without the need for incessant Alt+Tabbing.

While Windows® has long supported multiple monitors, the Windows Shell has never really been adapted to handle more than one screen. The main drawback is that only one monitor can contain the taskbar and thus it houses the icons for all of the apps on all of the monitors. As a result, the taskbar becomes cluttered and searching for a particular application can be frustrating as there's no visual relationship between an application's taskbar icon and the monitor it resides on.

Fortunately, there are third-party window management tools designed to work specifically in multiple-monitor environments. One such tool is UltraMon version 2.7.1 by Realtime Soft. The UltraMon Smart Taskbar extends the taskbar across all screens and locates application icons in the appropriate taskbar. UltraMon also adds two buttons next to each window's Minimize, Maximize, and Close buttons: Maximize to desktop and Move window. The Maximize to desktop maximizes the window to stretch across all of the monitors; with a single click, Move window sends the window to a different monitor.

Other features include the ability to stretch wallpaper across all monitors and to launch a screen saver that spans all monitors. UltraMon also provides an API that can be accessed through the Windows Script Host.

UltraMon is an affordable, lightweight program that greatly enhances the multiple-monitor experience. If you regularly use two or more monitors, you owe it to yourself to give UltraMon a try—you won't be disappointed.

Price: $39.95.

realtimesoft.com/ultramon

The UltraMan Smart Taskbar Extends the Taskbar across All Screens
The UltraMan Smart Taskbar Extends the Taskbar across All Screens (Click the image for a smaller view)
The UltraMan Smart Taskbar Extends the Taskbar across All Screens
The UltraMan Smart Taskbar Extends the Taskbar across All Screens (Click the image for a larger view)

Have Fun Learning Design Patterns

At any bookstore, you'll find rows of books on design patterns. Design patterns are general, high-level solutions used by software architects to solve common software design problems. The problem is that most books on design patterns stodgily enumerate key patterns, dryly explaining when and how each is to be used.

A notable exception is Head First Design Patterns by Eric and Elisabeth Freeman (O'Reilly, 2004), which covers all of the essential design patterns in an entertaining manner. It dispenses with pages of boring text and replaces it with humor-filled lists, questions and answer sections, a variety of diagrams, and heavily annotated code snippets. Gone too are the canonical examples. Instead of using employee or product-related examples, Head First Design Patterns illustrates how and when to use a particular design patterns through silly applications involving pizza, ducks, chocolate, and gumball machines.

Despite the book's somewhat cartoonish layout, it is full of useful information. Head First Design Patterns introduces and describes design patterns from the perspective of a common problem all system architects have faced at one time or another: how to add new functionality to an existing object hierarchy. It presents a duck simulator application as an example and steps through the concerns and challenges. It then delves into the strategy pattern and how to implement it to address this challenge. Later chapters cover other essential patterns: the decorator, the observer, the factory, the singleton, the iterator, the adapter, and more.

When designing software, the same sorts of challenges arise again and again. Design patterns provide architects with a repeatable, proven way to tackle these problems. Head First Design Patterns makes it fun and easy to learn some of the most useful patterns and how to apply them.

Price: $44.95.

oreilly.com

Send your questions and comments for Scott to toolsmm@microsoft.com.

Scott Mitchell, author of numerous books and founder of 4GuysFromRolla.com, is an MVP who has been working with Microsoft Web technologies since 1998. Scott is an independent consultant, trainer, and writer. Reach him at Mitchell@4guysfromrolla.com or via his blog at ScottOnWriting.NET.


CLR Inside Out: Writing Reliable .NET Code

CLR Inside Out
Writing Reliable .NET Code
Alessandro Catorcini and Brian Grunkemeyer and Brian Grunkemeyer

Code download available at: CLRInsideOut2007_12.exe (156KB)
Contents
A Look at Runtime Failure
Dealing with Failure
Recycling the Host Process
Mirrored Processes
Recycling a Portion of the Process
What Is an AppDomain?
State Corruption
Detecting Shared State
Escalation Policy
Resiliency to Escalation
Writing Reliable Code
Choosing a Reliability Bar
Clean Up Code
Constrained Execution Regions
Reliability Contracts
Mitigating Failures the Hard Way
When to Use a Strong Reliability Contract
SafeHandle
Using Locks
Hard and Soft OOM Conditions

W
hen we talk about something being reliable, we're referring to it being dependable and predictable. When it comes to software, however, there are other key attributes that must also be present for the code to be considered reliable.

Software needs to be resilient, meaning that it will continue to function in the face of internal and external disruptions. It must be recoverable, such that it knows how to restore itself to a previously known, consistent state. The software needs to be predictable, so it will provide timely and expected service. It must be undisruptable, meaning that changes and upgrades won't affect its service. And, finally, the software must be production-ready, meaning that it contains a minimal number of bugs and will require only a limited number of updates. When these criteria are met, then the software can be considered truly reliable.

These key attributes of reliable code depend on various factors—some depend on the overall architecture of the software, some depend upon the OS on which the software will run, and others depend on the tools used to develop the application and the framework on which it is built. Resilience is an attribute that relies on every layer, and an application will only be as resilient as its weakest link.

Now consider Microsoft® .NET Framework-based applications. These apps delegate to the runtime certain operations that in a native environment either did not exist (such as just-in-time compilation of IL code) or were under the direct control of the developer (such as memory management). In terms of reliability, the platform itself can introduce its own points of failure that impact the reliability of the applications that run on top of it. It's important to understand where these breakdowns can occur and what techniques you can use to create more reliable .NET-based apps.

A Look at Runtime Failure

There are certain exceptional events that can occur at any time and in any code section. These events, which we will call asynchronous exceptions, are resource exhaustions (out of memory and stack overflows), thread aborts, and access violations. (In the execution of managed code, access violations occur in the runtime.)

This last case is not very interesting—if this event does actually occur, it means that a serious bug in the implementation of the common language runtime (CLR) is being exposed and should be fixed. The first two cases, however, deserve deeper analysis.

In theory, we would imagine that resource exhaustions would be gracefully managed by the runtime and that they would never affect the ability of application code to continue running. That's just theory, though—reality is more complex.

To explain, we'll start by taking a look at how some popular server applications deal with out-of-memory (OOM) events. Server applications, such as ASP.NET and Exchange Server 2007, that require very high availability have achieved this through AppDomain and process recycling. The operating system provides a very powerful mechanism to clean up memory and most other resources used by a process—all this is done for you when the process terminates.

In a client scenario, when memory pressure gets to the point that even small allocations fail, the overall system reaches such a level of unresponsiveness due to extensive thrashing and paging that the user is much more likely to reach for the reset button or the task manager than to allow any recovery code to run. In a sense, the user's initial reaction is to perform the same action manually that ASP.NET or Exchange 2007 will do automatically.

Some OOMs may not even be caused by any particular issue with the running code. Another process running on the machine or another AppDomain running in the process may be hogging the available resource pool and causing allocations to fail. In this sense, you should consider resource exhaustions to be asynchronous in that they can occur at any time in the execution of code and they may depend on environmental factors external to and independent from the running code.

This problem is exacerbated by the fact that the runtime may allocate memory to perform operations related to its own workings. Here are a few examples of allocations that happen in the runtime that could fail in a constrained resource environment:

  • boxing and unboxing
  • delayed class loading until the first use of the class
  • remoted operations on MarshalByRef objects
  • certain operations on strings
  • security checks
  • JITing methods

This is just a partial list of the many internal operations in the runtime that may need to allocate resources, but it should give you an idea of why it is not really practical to predict and mitigate the consequences of any specific allocation failure.

In the family of the asynchronous exceptions, thread aborts have a special role. Thread aborts are not errors due to resource exhaustions (like OOMs and SOs), but they too can happen at any time. If you abort a thread from a different thread, the point on the aborted thread where the exception will be raised is completely random.

Stack overflows also have their own idiosyncrasies. Stack space is reserved per thread and committed eagerly, so it should always be possible to avoid any competition for the resource. But there are problems with this. It is a guessing game to predict how much stack is enough for every application. The OS limits the amount of stack space per thread. There are issues with presenting an exception via Structured Exception Handling (SEH) when the thread is low on stack space due to unwinding issues. And reentrancy and recursion rule out computing a finite upper bound on the stack space required by a method.

In a nutshell: it is practically impossible to forecast when OutOfMemoryException, StackOverflowException, and ThreadAbortException might occur. Thus, writing backout code to attempt to recover from asynchronous exceptions is not practical on a large scale.

You may be wondering whose responsibility it is to make the application reliable if the runtime cannot guarantee resilience to asynchronous exceptions. The ASP.NET example we just discussed hinted at the answer. While the application code is responsible for handling the common synchronous exceptions, it cannot handle asynchronous ones; this must be handled by the host process. In the case of ASP.NET, this is where the logic is contained that triggers process recycling when memory consumption goes beyond a known threshold.

In other more sophisticated cases, hosts like SQL ServerTM 2005 utilize the CLR's hosting APIs to decide whether to abort the managed thread running a transaction and roll it back, unload an AppDomain, or even suspend the execution of all managed code on the server. The default policy for an application is that the host process will be killed, but there are several techniques you can use to embrace and extend this approach or to override this behavior. For further reading on the CLR's hosting APIs, see the August 2006 CLR Inside Out column (msdn.microsoft.com/msdnmag/issues/06/08/CLRInsideOut).

Dealing with Failure

By now, you've probably identified the key to solving this problem. A resilient application is one that isolates the work in units that can independently fail without affecting the other units. So far, three models have been proven successful at creating resilient, managed applications.

The first model is to make the process itself the unit of failure and isolate the managed code execution in one or more worker processes that can die and spawn liberally.

The second is to keep two redundant processes working in parallel doing the processing, with one active and the other dormant. On failure, the dormant process takes over and spawns another dormant process to act as a backup in case of another failure.

The third model is to make the AppDomain the unit of failure and ensure that the process is never affected by any failure occurring in managed code or in the runtime.

We'll look at these three approaches in more depth, analyzing the cost of implementation and the different ways that there are to approach the design.

Recycling the Host Process

Say we accept the fact that resource exhaustion could tear down the process hosting the CLR. And say the system is built in such a way that the work is isolated in one or more child processes supervised by a master process whose job it is to manage the lifetime of the worker processes. Under these circumstances, we have a cheap and effective solution with which to provide a reliable system. The system will not be resilient, but it will be fully recoverable and predictable in its behavior.

This approach is ideal for whenever you are handling a large number of independent stateless requests, such as Web requests in ASP.NET, and you want to isolate the execution of their processing. If an asynchronous exception is raised, a worker process dies and the requests being worked on will simply not be serviced—they need to be resubmitted. This, however, makes the approach ill-suited for long and expensive operations in which the cost of resubmitting the job may be too high.

Still, this is the cheapest way to provide a reliable service running managed code. You are effectively making use of the runtime's default behavior.

Mirrored Processes

IT shops make extensive use of redundancy for everything from local disk drives to entire servers. If a disk or server fails, a second one that is in sync with the first quickly takes over.

A similar approach can be taken with a process. You can design software that runs two copies of a process on the same machine, each receiving the same input and producing the same output. Under circumstances where the main process fails due to a temporary fault in that specific process (as opposed to a reproducible bug that will affect all instances of the process), this model will provide some resiliency. You should also use a transacted store in this scenario to ensure that any failed requests can be safely rolled back.

The National Aeronautics and Space Administration (NASA) uses a model like this for the computers on the space shuttle. When life-and-death operations depend on a computer, some degree of redundancy is a must.

NASA actually ran into a problem when using just two computers in this scenario: a situation occurred where the two computers disagreed on the result of a computation. Which one was right? With only two computers, you can't know which is correct in the event of a single unit failure. So, NASA added a third machine, and if two computers agree but the third returns a different result, the third is considered to be broken.

This level of redundancy is nice—unless one of your three boxes fails, because then you're back in the scenario where only two are available and you don't know which is correct in the event of a second failure. So NASA added a fourth, then talked itself into adding a fifth. Clearly, resolving non-crashing failures in a pair of mirrored processes is not simple.

Another problem with this approach is that if you run multiple processes on the same machine and one of them exhausts a system-level resource, there's a reasonable chance that the other process will need the same resource simultaneously. And this may cause the reserve process to fail in the same manner as the first. In fact, they may even both be competing with one another for the same resource.

Recycling a Portion of the Process

For a high level of resiliency, tearing down a process and restarting it or failing over to another process just won't do. What you really need to do is find the part of the application that failed and recycle that part. This requires isolating various parts of your application's process into recyclable chunks. Operations must either be stateless or they must use a transactional system to ensure that no writes occur or that they get backed out. Additionally, all resource usage must be freed when you recycle a part of the process.

When thinking about long-living servers, think about state corruption and lack of consistency. Consistency can apply to several different layers. While a simple linked list might be consistent, a complex data structure will require additional invariants. If a consumer has an invariant that all elements in a linked list also must be stored as values in a hash table, then consistency in the linked list doesn't mean the application is consistent. For this reason, you must treat the slightest possibility of corruption that breaks invariants to be a problem. If an asynchronous exception occurs, how much of the state may potentially be corrupted and how can a server be resilient to this corruption?

To carve an application into recyclable chunks, you must isolate operations from one another. When an asynchronous exception occurs, it may have caused state corruption. To avoid recycling the entire process, you need to have encapsulated a smaller portion of your process, including the set of failed operations and all relevant state information.

What Is an AppDomain?

An application domain, or AppDomain for short, is a sub-process unit of isolation for managed code. Most assemblies can be loaded into an AppDomain. When the AppDomain is unloaded, the assemblies can often be unloaded as well.

Each AppDomain gets its own copy of static variables. While threads can cross AppDomain boundaries, it is (almost) impossible to initiate communication across an AppDomain boundary without using .NET remoting or a similar technique. AppDomains give you a relatively firm boundary to contain code.

If an asynchronous exception occurs on a thread, you must decide how much corruption might be present and how to be resilient to that specific level of corruption.

State Corruption

There are three buckets that state corruption may fall into. The first is local state, which includes local variables and heap objects that are only used by a particular thread. The second is shared state, which includes anything shared across threads within the AppDomain, such as objects stored in static variables. Caches often fall into this category. The third is process-wide, machine-wide, and cross-machine shared state—files, sockets, shared memory, and distributed lock managers fall into this camp.

The amount of state that can be corrupted by an async exception is the maximum amount of state a thread is currently modifying. If a thread allocates a few temporary objects and doesn't expose them to other threads, only those temporary objects can be corrupted. But if a thread is writing to shared state, that shared resource may be corrupted, and other threads may potentially encounter this corrupted state. You must not let that happen. In this case, you abort all the other threads in the AppDomain and then unload the AppDomain. In this way, an asynchronous exception escalates to an AppDomain, causing it to unload and ensuring that any potentially corrupted state is thrown away. Given a transacted store like a database, this AppDomain recycling provides resiliency to corruption of local and shared state.

Detecting Shared State

Figuring out whether a thread is modifying shared state is not the easiest problem. It's not obvious in most code whether the value of a local variable on the stack was initialized locally, whether it was passed in as a parameter to the method, or whether it simply refers to an object that is stored in a static variable (or is reachable from a static variable).

A subtle demand about modifying shared state comes from the concurrency space: whenever you edit a static variable, your code almost inevitably holds a lock. So keeping a lock count on each thread informs our escalation policy as to whether a thread may potentially be editing shared state. It's true that locks are often taken while reading from shared state and no state corruption occurs while reading from shared state (assuming there is no lazy initialization), but this pessimistic heuristic is the tightest we have found, without requiring an unacceptable additional level of discipline from users.

You can safely use interlocked operations, as they are only safe for editing one piece of shared state atomically. And knowing whether they succeed is easy—they either do or they do not.

Escalation Policy

The CLR's escalation policy goes a bit further. We'd like to attempt to give user code a chance to clean up when aborting threads. So the CLR attempts to run finally blocks and finalizers when aborting threads and before the AppDomain is unloaded. But there is tension caused by the user's desire to run arbitrary cleanup code and the host's availability needs. You can impose some timeout on running finally blocks and finalizers and simply abort them if they do not finish within a reasonable time.

A harder question, though, is how to be resilient if an asynchronous exception occurs while accessing a process-wide, machine-wide, or cross-machine piece of state. We'll discuss this in more detail later.

Resiliency to Escalation

Escalation policy also imposes limits on code, both when written by users and library authors. For SQL Server, the CLR allows stored procedures to be written in managed code, with some very high restrictions on what it can express. For scalability, reliability, and security concerns, user code in SQL Server should not launch or terminate threads, it should minimize or completely avoid shared state, and it should not be allowed to access certain types of OS resources. However, trusted libraries such as the .NET Framework must access these resources, often on behalf of this relatively untrusted user code.

The CLR provides code access security as a first line of defense to tweak the set of permissions given to user code. However, the CLR does not include permissions for all interesting resource types. For this purpose, the CLR defines the HostProtectionAttribute attribute, which can be used to mark methods that raise programming model concerns, such as by having the ability to kill threads.

These limitations on user code are actually a very good thing because by restricting user code from accessing OS resources directly (along with other limitations), user code is freed from the responsibility of tracking its use of these resources.

In the process recycling world, whenever a process is killed, the OS frees all of the machine-wide resources used by the process. For AppDomain recycling to truly provide resiliency, AppDomain unloading must provide the same level of guarantees. Since an AppDomain is just a unit within the process, managed libraries that provide access to resources must fill the gap between the operating system's ability to clean up on process exit and the demands of AppDomain unloading.

Writing Reliable Code

AppDomain unloading must be clean. This is the guiding principle for writing libraries resilient to async exceptions. For a transacted host like SQL Server that guarantees its own consistency, all other concerns about user and library code (like correctness, performance, and maintainability) are secondary to the host's ability to guarantee its availability. If user code has a bug that occasionally causes a crash, the server will live on as long as it can make forward progress and doesn't degrade over time.

Clean AppDomain unloading and careful resource management allow your code to fight against the CLR's escalation policy, enabling you to identify code that must have an opportunity to run to ensure consistency of a resource or to ensure that no resources are leaked. In most cases, an asynchronous exception will have already happened or will be unpreventable—these are the tools your code will need to become resilient to failure. The most important advice for library writers is to use SafeHandle, and it's critical that you understand several other available features so you can fully grasp why to use SafeHandle.

Choosing a Reliability Bar

Not all code is equally critical. You should think about what level of reliability is required from a certain block of code. The techniques we describe can increase development costs, so a good engineer should determine how much resiliency is necessary for a block of code.

First, ask yourself what your code should do when a power failure occurs. As a starting point, all code obviously must be able to restart and work correctly after a power failure. Even if client applications will lose work and encounter data corruption, they still must, at a bare minimum, be able to start back up.

Designing mail servers to ensure they don't lose e-mail when a power failure occurs is a significantly harder problem. Similarly, software controlling a nuclear power plant must be able to tolerate such failures with greater resilience than, say, a basic productivity app. Figuring out where your application falls on this continuum should be trivial, and it will help inform your decisions on how much to invest in resiliency.

For most client applications, the surprising answer is to do very little. Surviving an async exception is overkill for most client apps. Killing the process and restarting is often sufficient. And when using the Windows Vista® Restart Manager APIs, this approach can help limit how much state is lost when the client app crashes. Outlook® offers a great example. If Outlook 2007 crashes on Windows Vista, it can recover and reopen all windows to the right spot. If you were composing a message when Outlook crashed, you might lose only the last minute or two of typing, rather than the entire message.

For libraries, the reliability bar is determined by the most aggressive host in which your code will run. If your library is used by hosts that recycle processes, your reliability needs are less than for a host that recycles AppDomains. However, if your library allows accesses to resources without using some of the techniques we describe, and your code is used in a host that recycles AppDomains, your library can eventually cause that host to fail.

Clean Up Code

You should use try/finally blocks and finalizers to clean up resources. As a first approximation, these tools give developers a simple way to restore state to a reasonable level of consistency. Try/finally blocks (and language-specific keywords like the C# "using" statement) will improve correctness and performance by ensuring that resources are cleaned up or disposed of at deterministic points in your code.

The problem with this approach, though, is that the CLR cannot easily guarantee that the code in your finally block will complete. While the CLR attempts to avoid aborting threads while running in finally blocks, resource exhaustion and thus async exceptions can occur at any time.

Likewise, we have no way of ensuring that any given finalizer will complete. Additionally, the CLR's escalation policy allows a host to abort threads in finally blocks and finalizers in case a finally block goes into an infinite loop or blocks indefinitely.

Constrained Execution Regions

A constrained execution region, or CER, is a reliability primitive that helps enable code to maintain consistency. This is best for when you are willing to work extremely hard to guarantee some limited amount of forward progress or ability to undo changes to an object. Constrained execution regions are a best-effort attempt from the runtime to run your code. With CERs, the runtime will hoist any CLR-induced failures to predictable locations. This doesn't guarantee that your code will run—you can't sprinkle CERs like fairy dust and expect your code to magically work—but if you're writing code with some rigid constraints, there is a good chance your code will run to completion.

CERs are exposed in three forms:

  • With ExecuteCodeWithGuaranteedCleanup, which is a stack-overflow safe form of a try/finally.
  • As a try/finally block preceded immediately by a call to RuntimeHelpers.PrepareConstrainedRegions. In this case, the try block isn't constrained, but all catch, finally, and fault blocks for that try are constrained.
  • As a critical finalizer. Here, any subclass of CriticalFinalizerObject has a finalizer that is eagerly prepared before an instance of the object is allocated.

(Note that there is a special case: SafeHandle's ReleaseHandle method, a virtual method that is eagerly prepared before the subclass is allocated and called from SafeHandle's critical finalizer.)

To give your code an opportunity to execute, the CLR will eagerly prepare your code, meaning it will JIT the statically discoverable call graph for your method. If you use delegates within a CER, the target method of the delegate must be eagerly prepared, preferably by calling RuntimeHelpers.PrepareMethod() on the method. Additionally, if you use ngen, you can mark the method with the PrePrepareMethodAttribute to eliminate some need to pull in the JIT. This eager preparation will eliminate CLR-induced out-of-memory exceptions.

With regard to thread aborts, the CLR will disable these over CERs. The CLR will also probe for some stack, if and only if the CLR's host (for example, any native app that uses the CLR's COM hosting interfaces to launch the CLR) wants to survive stack overflow. Barring bugs in the runtime itself, heap corruption, and hardware problems, this should eliminate all CLR-induced asynchronous exceptions caused by your code.

Reliability Contracts

You may have noticed that I keep drawing a line between CLR-induced failures and all other failures. Failures can occur at all levels of the system since each layer of the system has a different view of consistency. Consider an application that wants to maintain a list of items in sorted order. If you use an unsorted collection like List<T>, your code may look like this:

public static void InsertItem<T>(List<T> list, T item)
{
    // List<T> isn't sorted, but the app relies on it being sorted.
    list.Add(item);
    list.Sort();
}

Now imagine that all the methods on List<T> magically succeeded all the time. We'll use the hypothetical ReliableList<T> to imply this behavior. Imagine that Add never needs to grow the size of the list and the Sort routine always works (even with its indirect calls into comparers and CompareTo methods on the generic type T). If a ThreadAbortException occurs in InsertItem after the call to Add completed but before the call to Sort, we still run into a consistency problem.

From the perspective of ReliableList<T>, the list is fully consistent. The internal data structures in the list are in a perfectly fine state in that there is no extra item half-added to the collection. And we can continue to use the list without any problems. But, from the perspective of the application (remember that it required the list to be sorted), this invariant has been broken and the method hasn't been written to recover from this problem.

This scenario illustrates that consistency exists at various levels in the system. This is indicated to users via reliability contracts—a very coarse-grained mechanism for stating the extent of corruption if an asynchronous exception occurs. In this case, the method corrupted the list, and if we admit the possibility of an OutOfMemoryException during the Add method call, then we have to mark this method as potentially failing, like this:

[ReliabilityContract(Consistency.MayCorruptInstance, Cer.MayFail)]
public static void InsertItem<T>(ReliableList<T> list, T item)

Reliability contracts, which are required on all code called within CERs, serve primarily as documentation to the developer about whether to expect the possibility of a failure. Here, reliability contracts could do a better job of indicating corruption of parameters versus a "this" pointer, but these are really meant to start a reliability discussion between the author of the InsertItem method and the person calling it. In this case, someone calling InsertItem<T> will notice that it may cause corruption of an instance if it fails, so they will know that they need a mitigation strategy.

Mitigating Failures the Hard Way

Constrained execution regions offer some ways to mitigate failures in code like InsertItem. The options vary in complexity and some techniques may not work well if the code changes significantly from one version to another.

The most obvious technique is to try to understand the failure and avoid it. In this sample, it may be possible to hoist allocations to a place where you can recover from the failure, such as allocating the list with sufficient capacity to begin with. This would hoist any allocation failures. However, this requires that you understand InsertItem<T>, List<T>, and how they both fail.

Additionally, this technique doesn't solve the thread abort problem. The list may still not be sorted. Here, a CER could be used to ensure that the finally block is sorted all the time. Regardless of whether Add fails or not, we ensure consistency of the list by guaranteeing that it is sorted. This requires a strong reliability guarantee on our fictitious ReliableList<T>'s Sort method. But with that in place, we could write our code as shown in Figure 1, offering a stronger consistency guarantee in InsertItem's reliability contract.

Figure 1 A Stronger Consistency Guarantee (Close Figure 1)
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
 public static void InsertItem<T>(ReliableList<T> list, T item)
 {
     RuntimeHelpers.PrepareConstrainedRegions();  // CER marker
     try
     {
         // Add can fail with an OutOfMemoryException.
         // That's fine, since the list didn't change.
         list.Add(item);
         // Consider a failure here, perhaps from calling a second method.
     }
     finally
     {
         // Restore consistency by guaranteeing the list is sorted.
         list.Sort();
     }
 }

Keep in mind that these techniques don't work in a real-world implementation of this example since ReliableList<T> doesn't really exist. It would be particularly difficult to write due to the nature of sort algorithms—you need any comparer used by Sort and most likely T's CompareTo method to also be reliable.

But there's another technique similar to the recycling model. If you can live with the performance hit, you can copy the data, make your changes on your copy, and then expose your consistent data structure to the world. Here's one way to do this, complete with compromises to the method signature:

[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public static void InsertItem<T>(ref List<T> list, T item)
{
    // Forward progress
    List<T> copy = new List<T>(list);  // expensive copy operation
    copy.Add();
    copy.Sort();

    // Expose the results
    list = copy;
}

Here, we can use the real List<T>, but be sure to note that we take a significant performance hit by copying the data. Additionally, using a ref parameter here isn't really a preferable solution. However, a strong reliability contract saying "will not corrupt state" is often highly desirable.

Another technique is to try to make forward progress and provide backout code in case of a failure. This is similar to constructing a transaction around your changes and providing code to roll back the changes made in the transaction.

When to Use a Strong Reliability Contract

The previous examples show that a strong reliability contract is difficult to implement efficiently. Using CERs is like rocket science, since you need to be prepared for failures that can occur anywhere. Note that for CERs exposed as an annotated try/finally block, the try block is not a CER, so even multiple assignment operations may be interrupted by an async exception. For this reason, CERs are not for most people—better choices are to rely on a host's escalation policy, or for client applications, recycling the entire application.

That said, CERs are truly required in places that manipulate machine or process-wide state, such as a shared memory segment. And in these cases, business needs often require that these applications are written to recover from power failures anywhere in the code, so you not only have to write CERs, but you also have to be very careful about consistency throughout significant parts of the application. This complexity is why you don't want your summer intern writing control software for a nuclear power plant.

SafeHandle

You might be wondering whether you can safely return values from a method in the presence of asynchronous exceptions. You cannot safely do this! When you call an unhardened P/Invoke method like CreateFile that returns an IntPtr and then assign the return value to a local variable, two distinct machine instructions are generated, and threads can be aborted between any two machine instructions. In situations like this, using IntPtr to represent OS handles is fundamentally unreliable. But there is a solution.

When you access an OS resource, such as a file, socket, or event, you get a handle for that resource, and that resource must eventually be freed. SafeHandle ensures that if you can allocate a resource, you can free that resource. To provide this guarantee, SafeHandle aggregates multiple CLR features.

You define a subclass of SafeHandle and provide an implementation of the ReleaseHandle method. This method is a CER, called from SafeHandle's critical finalizer. Assuming that your ReleaseHandle method obeys all the CER rules, you are guaranteed that if you can successfully allocate an instance of a native resource, your ReleaseHandle method will get a chance to run.

SafeHandle also offers some key security features. It prevents handle recycling attacks by ensuring that no thread can free a handle while another thread is actively using it. It also prevents a subtle race condition between a class using a handle and its own finalizer. SafeHandle is also integrated with the P/Invoke marshaling layer. For any method that returns or consumes a handle, you can replace that handle with a subclass of SafeHandle. CreateFile for example, would return a SafeFileHandle, and WriteFile would take a SafeFileHandle as its first parameter.

Using Locks

Locks must be taken on the right types of objects. Consider the C# lock keyword, which expands into calling Monitor.Enter(Object), followed in a finally block by Monitor.Exit(Object). Locks should have a strong sense of identity—unboxed value types do not fit the bill, as they would get boxed each time you pass them as an Object. But the CLR also shares certain types across AppDomain boundaries in certain cases. Taking locks on Strings, Type objects, CultureInfo instances, and byte[]'s may end up taking a lock across AppDomain boundaries. Similarly, taking locks on any subclass of a MarshalByRefObject from outside of that class's implementation may lock on a transparent proxy instead of the real object in the correct appdomain, meaning you might not take the right lock! In naïvely written code, if an async exception occurs, the finally block to release the lock will not run and you may cause other threads to block indefinitely.

Do not define your own locks unless you are a true expert when it comes to memory models and concurrency and you have demonstrated a need for a better lock. Beyond the obvious pitfalls like alertable waits and how to spin on hyper-threaded CPUs, your lock must also cooperate with the CLR's escalation policy, which needs to keep track of whether each thread is holding a lock. Thread.BeginCriticalRegion and EndCriticalRegion can help the CLR identify when third-party locks are acquired and released.

Hard and Soft OOM Conditions

There is another mitigation technique besides AppDomain unloading for out-of-memory errors. The MemoryFailPoint class will attempt to predict whether a memory allocation will fail. You allocate a MemoryFailPoint for X megabytes of memory, where X is an upper bound on the expected additional working set for processing one request. You then process the request and call Dispose on the MemoryFailPoint.

If there is not enough memory available, the constructor throws an InsufficientMemoryException, which is a different exception type used to express the concept of a soft OOM. Apps can use this to throttle their own performance based on available memory. The exception is thrown before memory is actually allocated, when no corruption has occurred yet. Thus, this implies that there is no corruption of shared state and therefore there is no need for an escalation policy to kick in.

MemoryFailPoint does not reserve memory in the sense of reserving or committing physical pages of memory, so this technique is not iron-clad—you can get into races with other heap allocations in the process. But this technique does maintain an internal process-wide reservation count to keep track of all threads using a MemoryFailPoint in the process. And we believe this can reduce the frequency of hard OOMs requiring invocation of the framework's escalation policy.

Send your questions and comments to clrinout@microsoft.com.

Alessandro Catorcini and Brian Grunkemeyer is a Lead Program Manager in the CLR team. In Visual Studio 2005 he was responsible for the hosting API layer and CLR integration into SQL Server 2005.

Brian Grunkemeyer has been a Software Design Engineer on the .NET Framework since 1998. He has implemented a large portion of the Framework Class Libraries, as well as cross-cutting features, such as generics, managed code reliability, and versioning considerations.


Data Points: Data Binding in WPF

Data Points
Data Binding in WPF
John Papa

Code download available at: DataPoints2007_12.exe (161KB)
Contents
Data Binding Specifics
Creating a Simple Binding
Binding Modes
A Time to Bind
Binding to XML
Object Binding and DataTemplates
Sorting the Data
Forging Ahead

B
y now, many of you know that Windows® Presentation Foundation (WPF) makes it easy to design robust user interfaces. But what you probably didn't know is that it also provides powerful data-binding capabilities. With WPF, you can perform data manipulation using Microsoft® .NET Framework code, XAML, or a combination of both. You can bind to controls, public properties, XML, or objects, making data binding quick, flexible, and easier than ever. So let's take a look at how you can get started binding your controls to the data sources of your choice.

Data Binding Specifics

To use WPF data binding, you must always have a target and a source. The target of the binding can be any accessible property or element that is derived from DependencyProperty—an example is a TextBox control's Text property. The source of the binding can be any public property, including properties of other controls, common language runtime (CLR) objects, XAML elements, ADO.NET DataSets, XML Fragments, and so forth. To help you get the binding right, WPF includes two special providers—the XmlDataProvider and the ObjectDataProvider.

Now let's take a look at how the WPF data-binding techniques work and I'll present practical examples that illustrate their use.

Creating a Simple Binding

Let's start with a simple example that illustrates how to bind a TextBlock's Text property to a ListBox's selected item. The code displayed in Figure 1 shows a ListBox that has six ListBoxItems declared. The second TextBlock in the code example has a property called Text (specified in XAML property-element syntax with the XML child element <TextBlock.Text>), which will contain the text for the TextBlock. The Text property declares a binding to the ListBox's selected item with the <Binding> tag. The ElementName attribute of the Binding tag indicates the name of the control that the TextBlock's Text property is bound to. The Path attribute indicates the property of the element (in this case the ListBox) that we will be binding to. The result of this code is that when a color is selected from the ListBox, the name of that color is shown in the TextBlock.

Figure 1 Basic but Verbose Binding to a Control (Close Figure 1)
<StackPanel>
    <TextBlock Width="248" Height="24" Text="Colors:"
        TextWrapping="Wrap"/>
    <ListBox x:Name="lbColor" Width="248" Height="56">
        <ListBoxItem Content="Blue"/>
        <ListBoxItem Content="Green"/>
        <ListBoxItem Content="Yellow"/>
        <ListBoxItem Content="Red"/>
        <ListBoxItem Content="Purple"/>
        <ListBoxItem Content="Orange"/>
    </ListBox>
    <TextBlock Width="248" Height="24" Text="You selected color:" />
    <TextBlock Width="248" Height="24">
        <TextBlock.Text>
            <Binding ElementName="lbColor" Path="SelectedItem.Content"/>
        </TextBlock.Text>
    </TextBlock>
</StackPanel>

The code listed in Figure 1 can be modified slightly to use a shorthand syntax for data binding. For example, let's replace the TextBlock's <Binding> tag with the following code snippet:

<TextBlock Width="248" Height="24"
    Text="{Binding ElementName=lbColor,
    Path=SelectedItem.Content}" />

This syntax, called the attribute syntax, condenses the data binding code inside of the Text attribute of the TextBlock. Basically, the Binding tag gets pulled inside of the curly braces along with its attributes.

Binding Modes

I can take the previous example further, binding the background color of the TextBlock to the color that is selected in the ListBox. The following code adds a Background property to the TextBlock and uses the attribute binding syntax to bind to the value of the selected item in the ListBox:

<TextBlock Width="248" Height="24"
Text="{Binding ElementName=lbColor, Path=SelectedItem.Content,
    Mode=OneWay}"
x:Name="tbSelectedColor"
Background="{Binding ElementName=lbColor, Path=SelectedItem.Content,
    Mode=OneWay}"/>

When the user selects a color in the ListBox, the name of that color will appear in the TextBlock and the background color of the TextBlock will change to the selected color (see Figure 2).

Figure 2 Binding a Source to Two Targets
Figure 2 Binding a Source to Two Targets

Notice the statement in the previous example that sets the Mode attribute to OneWay. The Mode attribute defines the binding mode that will determine how data flows between the source and the target. In addition to OneWay, there are three other binding modes available: OneTime, OneWayToSource, and TwoWay,

When using OneWay binding, as shown in the preceding code snippet, the data flows from the source to the target each time a change is made on the source. And while I explicitly specified this binding mode in my example, OneWay binding is the default binding mode for the TextBlock's Text property and does not need to be specified. Like OneWay binding, OneTime binding sends data from the source to the target; however, it does this only when the application is started or when the DataContext changes and, as a result, does not listen for change notifications in the source. Unlike OneWay and OneTime binding, OneWayToSource binding sends data from the target to the source. Finally, TwoWay binding sends the source data to the target, and if there are changes in the target property's value, those will be sent back to the source.

In the previous example, I used OneWay binding because I wanted the source (the selected ListBoxItem) to be sent to the TextBlock whenever a change is made in the ListBox selection. I do not want changes from the TextBlock to go back to the ListBox. Of course, there is no way for a user to edit a TextBlock. If I want to explore TwoWay binding, I can add a TextBox to this code, bind its text and background color to the ListBox, and set the Mode to TwoWay. When a user selects a color in the ListBox, the color is shown in the TextBox and its background color changes. When that user types in a color (such as Cyan) in the TextBox, the name of the color is updated in the ListBox (target to source) and in turn, since the ListBox was updated, the new value is sent to all elements that are bound to the ListBox's SelectedItem property. This means that the TextBlock will also have its color updated and its text value set to the new color (see Figure 3).

Figure 3 TwoWay Binding in Action
Figure 3 TwoWay Binding in Action

Here's the code I used to bind the TextBlock (OneWay) and the TextBox (TwoWay) to the ListBox:

<TextBlock Width="248" Height="24"
    Text="{Binding ElementName=lbColor, Path=SelectedItem.Content,
    Mode=OneWay}" x:Name="tbSelectedColor"
    Background="{Binding ElementName=lbColor, Path=SelectedItem.Content,
    Mode=OneWay}"/>

<TextBox Width="248" Height="24"
    Text="{Binding ElementName=lbColor, Path=SelectedItem.Content,
    Mode=TwoWay}" x:Name="txtSelectedColor"
    Background="{Binding ElementName=lbColor, Path=SelectedItem.Content,
    Mode=OneWay}"/>

If I change the TwoWay mode back to OneWay, the user will be able to edit the color in the TextBox without causing the changed value to be sent back to the ListBox.

Selecting the appropriate binding mode is important. I often employ OneWay when I want to display read-only data to a user. I use TwoWay binding when I want the user to be able to change the data in the control and have that change reflected in the data source (a DataSet, object, XML, or another bound control). I find OneWayToSource to be a good choice when I want to allow a user to change the data source without having the data source bind its data back to the target. I have used OneTime binding when I was tasked with showing, in a read-only control, the state of the data as it was when the screen loaded. Using OneTime binding, a series of read-only controls was bound to the data and, as the user interacted with the form and the data source's values were changed, the bound controls remained unchanged. This provided a way for the users to compare the changes that were made. Additionally, OneTime binding is a good choice when your source doesn't implement INotifyPropertyChanged.

A Time to Bind

In the previous example, the TextBox allows TwoWay binding to the selected ListBoxItem in the ListBox. This flow of data from the TextBox back to the ListBox happens when the TextBox loses focus. To change the event that causes the data to be sent back to the source, you can specify a value for UpdateSourceTrigger, which is the binding property that defines when the source should be updated. There are three values that can be set for the UpdateSourceTrigger: Explicit, LostFocus, and PropertyChanged.

When you set the UpdateSourceTrigger to Explicit, the source will not be updated unless the BindingExpression.UpdateSource method is called from code. The LostFocus setting (the default value for the TextBox control) indicates that the source will be updated when the target control loses focus. The PropertyChanged value indicates that the target will update the source every time the target control's bound property changes. This setting is useful when you want to dictate when the binding will occur.

Binding to XML

Binding to data sources such as XML and objects is also handy. Figure 4 shows a sample of an XmlDataProvider that contains an embedded list of colors that will be used as a data source. The XmlDataProvider can be used to bind to an XML document or fragment that is either embedded in the XmlDataProvider tag or is in a file referred to in an external location.

Figure 4 XmlDataProvider (Close Figure 4)
<StackPanel>
<StackPanel.Resources>
<XmlDataProvider x:Key="MoreColors" XPath="/colors">
    <x:XData>
        <colors >
            <color name="pink"/>
            <color name="white"/>
            <color name="black"/>
            <color name="cyan"/>
            <color name="gray"/>
            <color name="magenta"/>
        </colors>
    </x:XData>
</XmlDataProvider>

Embedded XML content must be placed within a <x:XData> tag inside of an XmlDataProvider, as shown in Figure 4. The XmlDataProvider must be given an x:Key value so that it can be referred to by data-binding targets. Notice that the XPath attribute is set to "/colors". This attribute defines the level of the XML content that will be used as the data source. This becomes very useful when binding to a large XML structure that may be contained in a file or database and the data you want to bind to is not the root element.

An XmlDataProvider is a resource that can be placed inside of a context-specific resource. As Figure 4 shows, the XmlDataProvider is defined as a resource within the context of the StackPanel. This means that the XmlDataProvider will be available to all content inside of that StackPanel. Setting the context of a resource helps limit the exposure of a data source to the appropriate areas. This enables you to create well-defined, self-contained regions of both controls and supporting resources within your page, thus improving readability.

The syntax for binding to a resource is slightly different than it is for binding to an element. When binding to a control, you set the ElementName and the Path properties of the Binding. However, when you bind to a resource you set the Source and, since we are binding to an XmlDataProvider, you set the XPath property of the Binding as well. For example, the following code will bind the ListBox's items to the MoreColors resource. The Source property is set to a resource and it is specified as a StaticResource named MoreColors. The XPath property indicates that the items will be bound to the <color> element's name attribute within the XML data source:

<ListBox x:Name="lbColor" Width="248" Height="56"
    IsSynchronizedWithCurrentItem="True"
    ItemsSource="{Binding Source={StaticResource MoreColors},
    XPath=color/@name}">
</ListBox>

I specified StaticResource in this case because the XML will not change. If changes occur in the data source, they will not be sent to the target. The DynamicResource setting indicates the opposite—changes will be sent. This is useful when referencing system themes, languages in globalization, or fonts. A DynamicResource will allow these types of settings to be propagated throughout the UI elements that are bound to them dynamically.

The XmlDataProvider can also point to an external source for the XML content. For my example, I have a file named colors.xml that contains the list of colors I want my ListBox to be bound to. I can simply add a second XmlDataProvider resource to the StackPanel and direct it to the XML file. Notice I set the Source attribute to the name of the XML file and the x:Key to Colors:

<XmlDataProvider x:Key="Colors" Source="Colors.xml" XPath="/colors"/>

Both XmlDataProviders exist as resources within the same StackPanel. I can tell the ListBox to bind itself to this new resource by changing the name that the StaticResource is set to:

<ListBox x:Name="lbColor" Width="248" Height="56"
    IsSynchronizedWithCurrentItem="True"
    ItemsSource="{Binding Source={StaticResource Colors},
    XPath=color/@name}">
</ListBox>

Object Binding and DataTemplates

While the XmlDataProvider works great for XML, when you want to bind to an object or a list of objects, you can create an ObjectDataProvider as a resource. The ObjectDataProvider's ObjectType designates the object that will provide the data‑binding source while the MethodName indicates the method that will be invoked to get the data. For example, assuming I have a class called PersonService that has a method called GetPersonList that returns a List<Person>, the ObjectDataProvider would look like this:

<StackPanel.Resources>
<ObjectDataProvider x:Key="persons"
ObjectType="{x:Type svc:PersonService}"
MethodName="GetPersonList"></ObjectDataProvider>
</StackPanel.Resources>

If you want a more complete look, the PersonService and Person classes, as well as all other sample code, are contained in the code that accompanies this column.

There are a handful of other properties that are available on the ObjectDataProvider. The ConstructionParameters property allows you to pass parameters to the constructor of the class being invoked. You can also specify parameters using the MethodParameters property, and you can use the ObjectInstance property to specify an existing instance of an object as the source.

If you want the data to be retrieved asynchronously, you can set the IsAsynchronous property of the ObjectDataProvider to true. Then the user will be able to interact with the screen while waiting for the data to populate in the target control that is bound to the ObjectDataProvider's source.

When adding an ObjectDataProvider, you have to qualify the namespace of the data-source class. In this case, I have to add an xmlns attribute to the <Window> tag so that the svc shortcut is qualified and indicates the proper namespace:

xmlns:svc="clr-namespace:DataBindingWPF"

Now that the data source is defined via the ObjectDataProvider, I want to bind items in a ListBox control to this data. I want to display two lines of text in each ListBoxItem. The first line will display the FullName property of the Person instance in bold and the second line will show the Title and City of the instance. In XAML, this is quite simple using DataTemplates, which allow you to define a data visualization strategy that can be reused.

Figure 5 shows the completed XAML, which has a DataTemplate defined to display the Person information in the layout I designated. I set the DataType property of the DataTemplate to indicate that the DataTemplate will be referring to the Person class type. I do not specify the actual binding in the DataTemplate, as I will do this in the ListBox control. By omitting the Binding Source, the binding will be made to the current DataContext in scope.

In Figure 5, I set the ListBox's ItemsSource property to bind to the persons resource so that I could bind the data to the ListBox but not format it. The data is correctly displayed by setting the ItemTemplate property to the personLayout resource, which is the DataTemplate's key name. The ultimate result is a screen that looks like Figure 6.

Figure 5 Object Binding (Close Figure 5)
<Window x:Class="DataBindingWPF.ObjectBinding"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:svc="clr-namespace:DataBindingWPF"
Title="DataBindingWPF" Height="300" Width="300">
<StackPanel>
    <StackPanel.Resources>
        <ObjectDataProvider x:Key="persons"
          ObjectType="{x:Type svc:PersonService}"
          MethodName="GetPersonList" ></ObjectDataProvider>
            <DataTemplate x:Key="personLayout" DataType="Person">
                <StackPanel Orientation="Vertical">
                    <TextBlock Text="{Binding Path=FullName}"
                            FontWeight="Bold" Foreground="Blue">
                    </TextBlock>
                <StackPanel Orientation="Horizontal">
                    <TextBlock Text="{Binding Path=Title}"></TextBlock>
                    <TextBlock Text=", "></TextBlock>
                    <TextBlock Text="{Binding Path=City}"></TextBlock>
                </StackPanel>
            </StackPanel>
        </DataTemplate>
    </StackPanel.Resources>
    <TextBlock></TextBlock>
    <ListBox x:Name="lbPersons"
            ItemsSource="{Binding Source={StaticResource persons}}"
            ItemTemplate="{DynamicResource personLayout}"
            IsSynchronizedWithCurrentItem="True"/>
</StackPanel>
</Window>
Figure 6 Using a DataTemplate
Figure 6 Using a DataTemplate

Sorting the Data

If you want to sort your data in a specific way, you can bind to a CollectionViewSource instead of directly to the ObjectDataProvider. The CollectionViewSource then becomes the data source and serves as an intermediary that intercepts the data from the ObjectDataProvider; provides sorting, grouping, and filtering functionality; and then propagates it to the target.

The CollectionViewSource shown next has its Source attribute set to the resource name of the ObjectDataProvider (persons). I then defined a sort order for the data by indicating the properties to sort by and their direction:

<CollectionViewSource x:Key="personView"
    Source="{Binding Source={StaticResource persons}}">
    <CollectionViewSource.SortDescriptions>
    <ComponentModel:SortDescription

    PropertyName="City"
    Direction="Ascending" />
    <ComponentModel:SortDescription
    PropertyName="FullName"
    Direction="Descending" />
    </CollectionViewSource.SortDescriptions>
</CollectionViewSource>

The DataContext is used to bind all controls within a container control to a data source. This is very useful when you have several controls that all use the same binding source. The code could get repetitive if you indicated the binding source for every control. Instead, you can set the DataContext for the container of the controls to the binding source and simply omit the Source attribute from the contained controls. For example, here is a series of TextBlocks bound explicitly to the same binding source:

<StackPanel>
<TextBlock Text="{Binding Source={StaticResource personView},
    Path=FullName}"></TextBlock>
<TextBlock Text="{Binding Source={StaticResource personView},
    Path=Title}"></TextBlock>
<TextBlock Text="{Binding Source={StaticResource personView},
    Path=City}"></TextBlock>
</StackPanel>

Here are the same three TextBoxes bound to the DataContext, which refers back to the controls' StackPanel container:

<StackPanel DataContext="{Binding Source={StaticResource personView}}" >
<TextBlock Text="{Binding Path=FullName}"></TextBlock>
<TextBlock Text="{Binding Path=Title}"></TextBlock>
<TextBlock Text="{Binding Path=City}"></TextBlock>
</StackPanel>

If the container does not define a DataContext, then it will continue to look at the next outer nested container until it finds the current DataContext.

Forging Ahead

WPF data binding offers a great deal of flexibility and control over the type of data that can be bound to, and how it can be controlled and displayed. With so much power and so many choices, I'm sure you'll want to get your hands dirty right away.

Send your questions and comments for John to mmdata@microsoft.com.

John Papa is a Senior .NET Consultant with ASPSOFT (aspsoft.com) and a baseball fanatic who spends most of his summer nights rooting for the Yankees with his family and his faithful dog, Kadi. John, a C# MVP, has authored several books on ADO, XML, and SQL Server. He can often be found speaking at industry conferences such as VSLive or blogging at codebetter.com/blogs/john.papa.


Cutting Edge: Inside the Microsoft AJAX Library

Cutting Edge
Inside the Microsoft AJAX Library
Dino Esposito

Code download available at: CuttingEdge2007_12.exe (152KB)
Contents
Introducing the Microsoft AJAX Library
A Class According to the Microsoft AJAX Library
Built-In Classes in the Microsoft AJAX Library
Where's XMLHttpRequest?
Details of a Web Request
Proxy Objects for Script Services
The Partial Rendering Engine

T
here's an unavoidable connection between AJAX and JavaScript that is implied by the AJAX acronym, which stands for Asynchronous JavaScript and XML. JavaScript, though, is a simple programming language whose foundation was laid out quite a few years ago when object-oriented programming was considered overkill for Web pages. At the same time, principles of dynamic languages were considered too academic to be fully implemented in a broadly used programming language.

As a result, JavaScript contains a bit of everything: it supports some object-oriented semantics in a similar fashion as more fully object-oriented language like C#, and it has a number of dynamic binding features like today's Ruby or Python. JavaScript is, however, the language that zillions of Web pages use to make displayed pages more interactive rather than a static composition of text, tables, and graphics.

What is familiarly called JavaScript is actually the browser's implementation of the standardized ECMAScript language. You use the language primarily to embed functions in HTML pages that glue together a user's actions with elements in the page. The elements in the page—buttons, textboxes, and so on —are modeled after the schema defined in the Document Object Model (DOM). Using JavaScript, you can programmatically rename, edit, add, or delete elements in the displayed document and handle any events fired by such elements. In addition, you can perform any browser-specific action, such as opening or popping up a new window or—what makes AJAX so special—invoke the popular XMLHttpRequest object and place asynchronous calls to a remote URL.

The key fact to bear in mind is that JavaScript and the DOM, by themselves, no longer provide developers with the full programming power needed in the current age of the Web. They represent a kind of minimal toolbox to work with. To craft good AJAX pages, more is needed, such as a rich client-side JavaScript library.

A big benefit of AJAX is the ability to run more of a site's code on the client, thus enabling the browser to respond quickly to user actions. Typical AJAX actions, though, go far beyond simply switching images when the mouse rolls over an element or displaying a pop-up window with a menu and toolbar. AJAX actions include client-side data validation, advanced user interface features such as floating menus, drag and drop, pop-up panels, and sophisticated input. AJAX actions also include dispatching requests to the server in an out-of-band way that bypasses the classic model of replacing the entire page.

Whatever AJAX platform you want to consider must be backed by a rich JavaScript library. The library typically contains at least a ready-to-use set of client-side user interface gadgets and an application model with events that define a lifecycle for each loaded document. In ASP.NET AJAX 1.0 and in the AJAX platform integrated with ASP.NET 3.5, the JavaScript library of choice is the Microsoft® AJAX Library.

Introducing the Microsoft AJAX Library

The Microsoft AJAX Library consists of two related parts: JavaScript language extensions and a sort of base class library to provide predefined services and tools to developers. Although the notion of an object is pervasive, JavaScript by itself can't be presented as an object-oriented language, as it fails to fully and natively implement all of the three pillars of object-oriented programming: inheritance, polymorphism, and encapsulation. There's a bit of inheritance you can get through object prototypes and there's a bit of encapsulation you obtain via closures. So the Microsoft AJAX Library first endows the language with more powerful tools and then proceeds with the definition of new classes and programming tools.

The Microsoft AJAX Library itself is self-contained, written in JavaScript, and stored in a couple of .js files. This means that any programming environment that accepts JavaScript can successfully use the Microsoft AJAX Library provided that the constituent files are properly referenced. There are two main files involved in the Microsoft AJAX Library: MicrosoftAjax.js and MicrosoftAjaxWebForms.js. MicrosoftAjax.js defines language extensions supported by the Microsoft AJAX Library including namespaces, interfaces, enums, and inheritance. MicrosoftAjaxWebForms.js defines the partial rendering engine and the whole network stack.

In an ASP.NET AJAX application, you don't need to reference or embed any of these two files directly. This is one of the tasks that the ScriptManager control accomplishes for you:

<asp:ScriptManager runat="server" ID="ScriptManager1" />

Placed in an ASP.NET AJAX page, the preceding code guarantees that one or both of the Microsoft AJAX Library .js files will be downloaded to the client. If you're going to extend the capabilities of the browser's JavaScript in the context of a non-ASP.NET AJAX application, then you are going to have to reference any required .js files explicitly:

<head>
    <script src="microsoftajax.js"></script>
</head>

The source code of the Microsoft AJAX Library is located at ajax.asp.net/downloads. You can modify these scripts in your own pages.

A Class According to the Microsoft AJAX Library

In traditional JavaScript programming, you work by creating functions. Because a function is a first-class element, you can create new instances of a function and configure it for execution. Put another way, you use functions much as if they were objects in an object-oriented language. This is a pillar of the JavaScript architecture and can't be changed without the serious risk of breaking many of applications and a large number of existing browsers. The Microsoft AJAX Library performs some tricks and makes some assumptions with the final goal of making JavaScript functions look and behave like classic objects.

You create Microsoft AJAX Library classes using the prototype model. All JavaScript functions feature the prototype property. The prototype property is meant to represent the internal-yet-public schema of the object. All instances of that function will end up calling the same method instance if the method is defined as part of the function's prototype.

As an example, suppose you have a Timer object:

MsdnMag.Timer = new function() {
  // This is a sort of constructor
  // for the function
  :
  this._interval = 5000;
}

According to the Microsoft AJAX Library, this is the definition of a class. As you can see, though, it's a mere function definition. Through the preceding code, you define a function object that can be further instantiated in any page that links that code:

var timer = new MsdnMag.Timer();

Any code defined in the pseudo-constructor executes at this time. According to the preceding snippet, instantiating the function sets the internal variable _interval to 5000.

What should you do to add a couple of start and stop methods to the timer object? The prototype model specifies that you use several separate blocks of code to define a class. In an object-oriented programming language such as C#, all the code of a class is contained in a single class definition (with the obvious exception of partial classes). To add a method to the timer object, you need to add a seemingly unrelated piece of code, like this one:

MsdnMag.Timer = new function() {
  this._interval = 5000;
  this._timerID = null;
}
MsdnMag.T

        imer.prototype

.start = function(callback) {
   this._timerID = window.setTimeout(callback, this._interval);
}

Let's take a closer look at this code fragment, as it contains some important clues about Microsoft AJAX Library programming. To start out, compare this code to some equivalent C# code:

namespace MsdnMag {
    public class Timer {
        public Timer() {
            _interval = 5000;
            _timerID = null;
        }
        protected int _interval;
        protected object _timerID;
        public void start(Delegate callback) {
            // Any code that can create a timer
            _timerID = CreateTimer(callback, _interval);
        }
    }
}

The first thing you should notice is that you have no need to explicitly define _timerID and _interval as part of the Timer object in the Microsoft AJAX Library. You just assign both members a default value in the constructor. However, to mark them as members of the object, you should prefix them with the "this" keyword. If you don't, they would be considered private members of the function and not bound to the object's prototype. When, prefixed with the "this" keyword, you can invoke those members from any prototyped methods.

The MsdnMag.Timer function, as defined in this column, illustrates the recommended approach for building new classes on top of the Microsoft AJAX Library in ASP.NET AJAX and any other platform where the Microsoft AJAX Library is used. Take a look at Figure 1 and compare it to the preceding code snippets.

Figure 1 Closure for the MsdnMag.Timer Class (Close Figure 1)
//
//  MsdnMag.Timer :: closure
//

MsdnMag.Timer = function(interval)
{
    var _interval = interval;
    var _timerID = null;
    var _userCallback = null;

    this.CallbackInternal() {
        this._userCallback();
        this.start();
    }

    this.start(callback) {
        _userCallback = callback;
        _timerID = window.setTimeout(CallbackInternal, _interval)
    }

    this.stop() {
        _userCallback = null;
        _timerID = null;
    }
}

Here, the JavaScript object is written as a closure rather than as a prototyped object. Both models are supported by the JavaScript engine supplied by most browsers. So what's the difference? The closure model delivers an object that has all of its members entirely defined in the same overall context, much like a C# class. The prototype model serves up a class with a sort of distributed scheme. All members attached to the prototype of the object are shared by all instances of the object. The Microsoft AJAX Library is entirely written according to the prototype model and Microsoft strongly recommends that you use that model for your own objects. On the other hand, the Microsoft AJAX Library is just an abstraction layer on top of JavaScript, and JavaScript supports both closures and prototypes. Therefore, the choice between the programming styles is yours.

So what's the benefit of using the Microsoft AJAX Library? The library extends the JavaScript programming environment by adding a number of predefined objects that provide new language features such as namespaces, enumerated types, delegates, stronger typing, and inheritance. Figure 2 shows the MsdnMag.Timer object written as a fully qualified Microsoft AJAX Library object. The sample timer class accepts up to three arguments in the constructor to initialize the interval of the timer and the callback to run periodically. The timer is automatically restarted until explicitly stopped and a reset callback is also provided.

Figure 2 Prototype-Based Version of MsdnMag.Timer (Close Figure 2)
//
//  MsdnMag.Timer :: prototype model
//

// Define the namespace the following class(es) belong to
Type.registerNamespace("MsdnMag");

// Class constructor:: Initializes internal members
//
// interval      :: interval of the timer in milliseconds
// callback      :: JS function to execute periodically
// resetCallback :: JS function to execute when the timer is stopped
MsdnMag.Timer = function(interval, callback, resetCallback)
{
    this._interval = interval;
    this._timerID = null;
    this._userCallback = callback;
    this._resetCallback = resetCallback;

    // Define a delegate to invoke the user-defined callback
    this._callback = Function.createDelegate(this, this._callbackInternal);
}

// Internal wrapper for the user-defined callback
function MsdnMag$Timer$_callbackInternal()
{
    // Invoke the user-defined callback
    if (this._userCallback !== null)
        this._userCallback();

    // Restart the timer if a reset-callback is provided
    if (typeof(this._resetCallback) !== "undefined" &&
        this._resetCallback !== null)
        this.start();
}

// Official end-point to start the timer
function MsdnMag$Timer$start()
{
    this._timerID = window.setTimeout(this._callback, this._interval)
}

// Official end-point to stop the timer
function MsdnMag$Timer$stop()
{
    window.clearTimeout(this._timerID);
    this._timerID = null;

    // Execute the user-defined clear function
    if (typeof(this._resetCallback) !== "undefined" &&
        this._resetCallback !== null)
        this._resetCallback();
}

// Define the public interface of the class
MsdnMag.Timer.prototype =
{
    start               : MsdnMag$Timer$start,
    stop                : MsdnMag$Timer$stop,
    _callbackInternal   : MsdnMag$Timer$_callbackInternal
}

// Register the class with the Microsoft AJAX Library framework
MsdnMag.Timer.registerClass("MsdnMag.Timer");

Note that when you omit a declared parameter in a JavaScript function call, the actual value of the formal parameter evaluates to an undefined type. For this reason, you should check for both null values and undefined types. The following code shows a safe way to check for null-ness of JavaScript parameters:

if (typeof(this._resetCallback) !== "undefined" &&
   this._resetCallback !== null)
{
   ...
}

What if you want to add a public read/write property to a new object? In JavaScript you can't play tricks to make an attribute with a get accessor or a set modifier usable via a property-based programming interface. As long as you want it to be a property, and not just a public data container like a field, you need to read and write property values using ad hoc methods. The convention in use is get_XXX and set_XXX, where XXX is the name of the property. A sample implementation for an Interval property on the MsdnMag.Timer object is shown in Figure 3.

Figure 3 Adding Properties to MsdnMag.Timer (Close Figure 3)
function MsdnMag$Timer$get_Interval()
{
    if (arguments.length !== 0)
        throw Error.parameterCount();
    return this._interval;
}

function MsdnMag$Timer$set_Interval(value)
{
    var e = Function._validateParams(arguments, [{name: 'value', type:
        Number}]);
    if (e)
        throw e;

    this._interval = value;
}

// Define the public interface of the class
MsdnMag.Timer.prototype =
{
    get_Interval        : MsdnMag$Timer$get_Interval,
    set_Interval        : MsdnMag$Timer$set_Interval,
    start               : MsdnMag$Timer$start,
    stop                : MsdnMag$Timer$stop,
    _callbackInternal   : MsdnMag$Timer$_callbackInternal
}

In the get accessor, you check the number of passed arguments and raise a parameter count error if any are specified. Note that "arguments" is an internal array of JavaScript that returns the list of parameters for a function call.

The _validateParams method on the Microsoft AJAX Library Function object is an extension that makes it straightforward for developers to check types and number of method parameters:

var e = Function._validateParams(
      arguments,
      [{name: 'value', type: Number}]);

The function takes the argument list and a second array to describe the expected list of arguments. In the preceding code snippet, the method is expected to receive just one numeric argument with the formal name of value. The _validateParams method verifies that the arguments array contains just one numeric element and raises an exception otherwise.

Built-In Classes in the Microsoft AJAX Library

Now that you know how to create new classes in JavaScript using the Microsoft AJAX Library framework as a basis, let's explore the system classes built into the framework. Figure 4 describes all the JavaScript classes defined in the Microsoft AJAX Library. The intrinsic JavaScript objects such as Boolean, Date, and Number are extended to incorporate new methods and capabilities. For example, the Date object now includes a couple of new instance methods named localeFormat and format. You can use them to render a date using the locale culture information:

Figure 4 Microsoft AJAX Library Built-In Classes (Close Figure 4)

Intrinsics | Description
Array | Extends the native Array object with new search methods.
Boolean | Extends the native Boolean object with a parse method.
Date | Extends the native Date object with formatting methods.
Error | Extends the native Error object to make it similar to a managed exception object. Also exposes static properties to map predefined types of error.
Function | Extends the native Function object with delegates and utilities to check method signatures.
Object | Extends the native Object object with type information.
Number | Extends the native Number object with parsing and formatting methods.
RegExp | Simple wrapper around the native RegExp object.
String | Extends the native String object with formatting methods.
Type | Alias for Function where all OOP extensions are grouped.
Sys.UI Namespace | Description
Sys.UI.Behavior | Defines the foundation of behaviors used to extend the capabilities of new and existing ASP.NET server controls.
Sys.UI.Bounds | Defines a region in the page through top-left-bottom-right parameters.
Sys.UI.Control | Defines the foundation of Microsoft AJAX Library user interface controls.
Sys.UI.DomElement | Wrapper class for a DOM element rendered in the page.
Sys.UI.DomEvent | Wrapper class for a DOM-level event (such as keystrokes or mouse movements).
Sys.UI.Point | Defines a point in the page through (x,y) coordinates.
Foundation Classes | Description
Sys.Component | Defines the foundation of a Microsoft AJAX Library user interface component.
Sys.CultureInfo | Defines culture information.
Sys._Application | Internal class representing the lifecycle of the current page.
Sys._Debug | Internal class offering debug services.
Sys._ScriptLoader | Internal class taking care of loading external scripts.
Network Classes | Description
Sys.Net.NetworkRequestEventArgs | Defines the data associated with the Web request events.
Sys.Net._WebRequestManager | Internal class representing the centralized manager of Web requests used to set global parameters such as executor and timeout.
Sys.Net.WebRequest | Represents a Web request being made.
Sys.Net.WebRequestExecutor | Represents the object in charge of physically executing a Web request.
Sys.Net.WebServiceError | Wrapper class for any exceptions occurred during a service call.
Sys.Net.WebServiceProxy | Defines the foundation for JavaScript proxy classes of remote services.
Sys.Net.XMLHttpExecutor | Represents the Web request executor that uses XMLHttpRequest.
Service Helper Classes | Description
Sys.Services._AuthenticationService | Internal wrapper class for the server-side authentication Web service.
Sys.Services._ProfileService | Internal wrapper class for the server-side user profile Web service.
Sys.Services._RoleService | Internal wrapper class for the server-side role Web service. This class is only available in ASP.NET 3.5.
Sys.Services.ProfileGroup | Used to contain the information about profile groups.
Event Data Classes | Description
Sys.ApplicationLoadEventArgs | Defines the data associated with the pageLoad event.
Sys.CancelEventArgs | Defines the foundation for the data object of any interruptible event.
Sys.EventArgs | Defines the foundation for the data object of any event.
Sys.EventHandlerList | Helper class used to gather all events associated with the execution of Web requests.
Sys.PropertyChangedEventArgs | Defines the data associated with the property-changed event of Microsoft AJAX Library components.
Utilities | Description
Sys.Serialization.JavaScriptSerializer | Helper class used to deserialize ad hoc data inserted in the page and consumed by the Sys.CultureInfo object.
Sys.StringBuilder | Used to concatenate strings, the object works in much the same way as the managed StringBuilder class.
Partial Rendering Classes | Description
Sys.WebForms.PageRequestManager | Root object orchestrating any partial rendering requests and operation.
Sys.WebForms.PageLoadingEventArgs | Defines the data associated with the pageLoading event.
Sys.WebForms.PageLoadedEventArgs | Defines the data associated with the pageLoaded event.
Sys.WebForms.InitializeRequestEventArgs | Defines the data associated with the initializeRequest event.
Sys.WebForms.EndRequestEventArgs | Defines the data associated with the endRequest event.
Sys.WebForms.BeginRequestEventArgs | Defines the data associated with the beginRequest event.
Sys.UI._UpdateProgress | Client-side class to perform update progress operations.
Sys.UI._UpdateProgress | Client-side class to perform update progress operations.

var d = new Date();
d.localeFormat("dddd, dd MMMM yyyy, hh:mm:ss");

You can grab information about the current locale using the following code:

var localShortDate =
    Sys.CultureInfo.CurrentCulture.dateTimeFormat.ShortDatePattern;
var d = new Date();
d.localeFormat(localShortDate);

In particular, this code snippet retrieves the short date pattern according to the current culture and uses that to format a date.

Where does the Sys.CultureInfo object retrieve locale information? Information relating to the current culture is emitted in the client page whenever the EnableGlobalization property is set to true on the script manager:

<asp:ScriptManager ID="ScriptManager1" runat="server"
EnableScriptGlobalization="true" />

The script manager writes out a piece of JavaScript code that is then parsed and integrated with the Microsoft AJAX Library runtime using the JavaScriptSerializer class. The Date object also features culture-specific methods to try to build a new date object from arbitrary text.

The Number object has similar capabilities for culture-specific parsing and formatting.

The Boolean object counts an additional static parse method that recognizes words like "true" and "false".

Array and String are other intrinsic types that received significant extensions in the Microsoft AJAX Library. In particular, the Array type features a few new static methods to add, remove, search, and iterate through items. The String type has been made as close as possible to the managed String class by adding methods such as endsWith, startsWith, trimming, formatting, and integration with string builders.

All objects now return precise type information. Whatever object you query for, its type method is now capable of telling you the exact type. Here's how to proceed:

myTimer = new MsdnMag.Timer(2000, updateUI, timerReset);
alert(Object.getTypeName(myTimer));

Figure 5 shows the effect of the alert function.

Figure 5 Checking Type Information with AJAX
Figure 5 Checking Type Information with AJAX(Click the image for a smaller view)
Figure 5 Checking Type Information with AJAX
Figure 5 Checking Type Information with AJAX (Click the image for a larger view)

Of all the native JavaScript objects, only RegExp, the object behind built-in support for regular expressions, is not extended in any way in the Microsoft AJAX Library. Once the library is installed, the RegExp object you get to use is exactly the same as before the Microsoft AJAX Library.

Where's XMLHttpRequest?

Although primarily designed to provide a richer and more object-oriented JavaScript programming environment, the Microsoft AJAX Library is primarily perceived as an AJAX library. So a good question would be, where's XMLHttpRequest? If you take a closer look at the Microsoft AJAX Library class hierarchy, you'll see that XMLHttpRequest is buried in the network stack in the classes of the Sys.Net namespace.

In the Microsoft AJAX Library, all Web requests pass through a Web request manager object. A unique instance of the Sys.Net_WebRequestManager object is created upon loading and used for the lifetime of the page. Requests are fired asynchronously, but the manager can't accept more than one request at a time. It's unlikely in a Web page, but should the same instance of the request manager be asked to serve requests simultaneously, then an exception would be raised for the second request. It's the responsibility of the developer to plan a second attempt at a later time.

The Web request manager maintains a list of event handlers and defines the timeout and executor type for requests. You set the default timeout through the defaultTimeout property and you can change the default executor type using the defaultExecutorType property, a string. By default, the executor is an instance of the Sys.Net.XMLHttpExecutor class and is instantiated just before firing the request.

The executor is the code that physically connects to the remote URL and sends the HTTP packet. In AJAX, the most obvious way of accomplishing this is via the browser's XMLHttpRequest object. The Sys.Net.XMLHttpExecutor class just wraps the XMLHttpRequest object and uses its services to send an HTTP packet to its destination.

As you can see, the executor is a separate module and the manager uses it only through a known interface made of the executeRequest method and the invokingRequest and completedRequest events. Let's delve deeper into the mechanics of Web requests in the Microsoft AJAX Library.

Details of a Web Request

The Sys.Net.WebRequest class defines the HTTP packet sent to a server URL for processing. The public properties of the object are url, httpVerb, headers, body, timeout, userContext, and executor. Timeout and executor, if not set, default to the values defined in the Web request manager. The completed event and the invoke method complete the programming interface of the WebRequest object. To trigger a request, you call the invoke method. As you can see, only one request at a time is supported and it is passed on to the request manager for execution:

function Sys$Net$WebRequest$invoke() {
    if (arguments.length !== 0) throw Error.parameterCount();
    if (this._invokeCalled) {
        throw Error.invalidOperation(Sys.Res.invokeCalledTwice);
    }

    Sys.Net.WebRequestManager.executeRequest(this);
    this._invokeCalled = true;
}

The request manager checks to see whether there is an explicit demand for an executor in the packet. If not, it uses the default executor. Next, the request manager fires the invokingRequest method. If the event is not canceled by the user code, the final step takes place: the executor physically sets up the connection and sends the packet.

The executor derives from a base class named WebRequestExecutor, which defines the expected programming interface. As mentioned, the default executor is XMLHttpExecutor. This class internally creates an instance of the XMLHttpRequest browser object and uses it to carry the request.

A different executor can be specified for each Web request, as it is a property of the Sys.Net.WebRequest object. Only one actual executor is defined in the Microsoft AJAX Library—XMLHttpExecutor—but others could be created by developers as well. The point is, what other techniques and alternatives exist to justify the creation of a custom executor?

An additional executor could use a dynamically created, invisible IFRAME to download any response sent back by any URL. You should notice that, when using XMLHttpRequest, you're limited to the so-called same-origin-policy that most browsers implement. In practice, it means that browsers do not allow scripted calls to URLs located outside the domain of the current page. When using XMLHttpRequest, you can't call into, say, contoso1.com from contoso2.com. You can accomplish this, instead, using an IFRAME to grab the response and then parse it to any suitable JavaScript object.

To write an IFRAME-based executor, you should start with a Microsoft AJAX Library object that inherits from Sys.Net.WebRequestExecutor and implements all of its abstract members. As for parsing the response into a usable memory object, you might want to define a request-specific parser component that provides a custom behavior under a common programming interface.

Proxy Objects for Script Services

Scriptable services need some JavaScript wrapper code in order to be invoked. A script-based proxy class can therefore be generated that provides a public programming interface that is nearly identical to that of the original service—be it a classic ASP.NET Web service or a Windows Communication Foundation (WCF) service. The proxy class is linked to the page via a script tag that is bound to a specific URL:

http://www.yourserver.com/service.asmx/js

The name of the proxy object is the same as the fully qualified name of the service class. The object also has the same set of methods. Methods, though, have an extended signature that encompasses additional parameters in addition to the standard set of input arguments. In particular, each method adds two callback functions to allow user code the ability to handle a successful or failed call, plus an optional object that represents the context of the call. The proxy object derives from the Sys.Net.WebServiceProxy Microsoft AJAX Library class.

Figure 6 shows a fragment of a service proxy class. For performance reasons, a proxy class is a singleton that executes each of its methods asynchronously in a callback model. In this code, the _invoke method ends up creating a Web request and—not a minor point—sets the content type to application/json, a required setting for the ASP.NET AJAX runtime. Here's a sample fragment:

Figure 6 Web Service Proxy Class (Close Figure 6)
Type.registerNamespace('MsdnMag');
MsdnMag.SimpleService = function()
{
   MsdnMag.SimpleService.initializeBase(this);
   this._timeout = 0;
   this._userContext = null;
   this._succeeded = null;
   this._failed = null;
}
MsdnMag.SimpleService.prototype =
{
    DoSomething : function(succeededCallback,
        failedCallback, userContext)
    {
        return this._invoke(MsdnMag.SimpleService.get_path(),
            'DoSomething', false, {}, succeededCallback,
            failedCallback, userContext);
    }
}
MsdnMag.SimpleService.registerClass(
       'MsdnMag.SimpleService', Sys.Net.WebServiceProxy);

MsdnMag.SimpleService._staticInstance =
    new MsdnMag.SimpleService();

MsdnMag.SimpleService.DoSomething =
    function(onSuccess,onFailed,userContext)
{
    MsdnMag.SimpleService._staticInstance.DoSomething(
        onSuccess, onFailed, userContext);
}

var request = new Sys.Net.WebRequest();
request.get_headers()['Content-Type'] =
'application/json; charset=utf-8';

A timeout is automatically set and controlled through an internal timer. Furthermore, the proxy registers a handler for the completed event of the Web request. When the event fires, the system-provided code checks the response. If all went well, the proxy invokes the user-defined success callback. Otherwise, it invokes the failure callback and passes it information about the error that occurred, as in this code snippet:

var error = response.getResponseHeader("jsonerror");
var errorObj = (error === "true");
if (errorObj)
    err = new Sys.Net.WebServiceError(false,
    result.Message, result.StackTrace, result.ExceptionType);
...
onFailure(err, userContext, methodName);

If the execution of the service method results in an unhandled exception, the message that is associated with the exception, its stacktrace, and its type are carried back to the client and composed into a WebServiceError JavaScript object that the failure callback can work with.

The Partial Rendering Engine

The key object for partial rendering is PageRequestManager, in the Sys.WebForms namespace. The code that initializes the object is transparently inserted in any client page that includes at least one UpdatePanel control. If you view the source of such a page, you'll find a piece of script like the following:

Sys.WebForms.PageRequestManager._initialize(
    'ScriptManager1', document.getElementById('form1'));

The ID of the ScriptManager control as well as the ID of the form are arbitrary. The _initialize method on the PageRequestManager does two main things. First, it creates the unique instance of the object to serve all needs in the page lifecycle. Second, it registers a script handler for the form's submit event. In this way, you end up having a common piece of code to intercept any type of postback—regular postbacks done via the browser, cross-page postbacks, and postbacks commanded through the DOM.

The goal of the submit handler is stopping the browser's default action and replacing the form submission with an AJAX request managed through the default Web request executor.

The submit handler starts its job by composing the body of the request. It does that looping through the input fields in the form and creating a proper string. The body is nearly identical to the body the browser would generate itself except for an extra parameter:

ScriptManager1=UpdatePanel1

Again, the IDs here are arbitrary. What matters is that you assign to the ID of the page's script manager the ID of the updatable panel triggering the call.

Once the request is ready, the PageRequestManager first fires the client-side initializeRequest event. The event gives developers a chance to cancel the operation. If the operation can proceed, the manager kills any pending operation—only one partial rendering operation can be active at a time—and fires the beginRequest event to notify that the request is going to leave. Next, it calls invoke on the corresponding WebRequest object. When the response is back, it is parsed to look for errors and the pageLoading event is fired if all is fine.

When the pageLoading event arrives, the response has been parsed but not yet merged with the current DOM of the page. Next, the panels involved with the operation are refreshed and any other data coming from the server is processed, including data items and postback scripts. Finally, the pageLoaded event is fired followed by the endRequest event. Each PageRequestManager event has its own class and carries specific information.

Send your questions and comments for Dino to cutting@microsoft.com.

Dino Esposito is a mentor at Solid Quality Learning and the author of Programming Microsoft ASP.NET 2.0 (Microsoft Press, 2005). Based in Italy, Dino is a frequent speaker at industry events worldwide. You can get in touch with Dino at cutting@microsoft.com or join the blog at weblogs.asp.net/despos.


Test Run: UI Automation with Windows PowerShell

Test Run
UI Automation with Windows PowerShell
Dr. James McCaffrey

Code download available at: TestRun2007_12.exe (164KB)
Contents
Using Windows PowerShell
Overview of My Test Script
The Application Under Test
Creating a Custom Cmdlet Library
Using the Custom Cmdlet Library
Using, Modifying, and Extending the UI Cmdlet Library

T
hough it has only been around for a relatively short amount of time, Windows PowerShellTM is already one of my favorite tools. I recently discovered that Windows PowerShell has all the features you need for creating a tiny library that will enable you to write ultralightweight UI automation.

In this month's column, I will show you how to create a small collection of custom Windows PowerShell cmdlets that perform Windows® UI automation tasks. These include obtaining handles to applications and controls, manipulating controls, and checking application state. In this discussion, I will assume you already have a basic familiarity with Windows PowerShell and with calling Win32® API functions using the Microsoft® .NET Framework P/Invoke mechanism with the C# language. But even if these are new to you, you should still be able to follow this column with a little bit of effort.

Using Windows PowerShell

The easiest way for me to illustrate the key points of using Windows PowerShell for ultralightweight UI automation is with a screenshot, as shown in Figure 1. The first couple of lines of output in the shell simply indicate that I'm using Windows PowerShell. The next few lines then tell me that my custom cmdlets are being registered; this is done by a custom startup script that runs every time a new instance of the Windows PowerShell shell is launched. (My startup script also sets the current working directory to C:\UIautomationWithPowerShell.) In this example, I've written and registered eight custom cmdlets for ultralightweight UI automation: get-window, get-control, get-controlByIndex, send-chars, send-click, get-listBox, send-menu, and get-textBox.

Figure 1 UI Automation with Windows PowerShell
Figure 1 UI Automation with Windows PowerShell(Click the image for a smaller view)
Figure 1 UI Automation with Windows PowerShell
Figure 1 UI Automation with Windows PowerShell (Click the image for a larger view)

Next, I issue the following command to display the names of all items in the current directory that begin with the letter "t":

PS C:\UIautomationWithPowerShell> get-childitem t* | select-object Name | format-table -auto

In Figure 1, the output tells me that I have a directory named TheAppToTest and a file named testScenario.ps1. This file is my Windows PowerShell test script.

The get-childitem command is one of approximately 130 built-in Windows PowerShell cmdlets. Many of these cmdlets also have aliases. The get-childitem cmdlet, for instance, has a few aliases—it can be given as "dir" (for those most familiar with the old cmd.exe command shell and .bat files), as "ls" (for those engineers used to a UNIX environment), and "gci" (a simple abbreviation for convenience at the command line).

My command pipes the output of get-childitem to the select-object cmdlet, which I use to filter the results down to just file and directory name properties. Then I pipe that result to the format-table cmdlet with an -auto switch to produce a condensed display. Of course, I could have typed just "get-childitem" (or one of its aliases) without any piping to accept all the default arguments for the command.

I can invoke my test script by entering the command .\testScenario.ps1 on the command line. Unlike most scripting languages, note that in Windows PowerShell, even if your script is in the current working directory, you still have to specify the path to the script using either a relative path (as I've done) or a full path. This is for security purposes. Additionally, Windows PowerShell does not allow script execution by default, so you must explicitly enable execution if you want to run scripts. You can check the current script execution policy by entering the command "get-executionpolicy" and modify the policy by entering "set-executionpolicy" with an argument of "remoteSigned" or "unrestricted".

Once set, the execution policy on a particular system will remain in effect for all new instances of the Windows PowerShell shell and user sessions. The fact that my startup script, which is just an ordinary Windows PowerShell script, was actually able to execute when I launched a new shell indicates that my current execution policy allows script execution.

Overview of My Test Script

My UI test automation script has quite a few logging messages, so you should be able to follow its progress without too much trouble. The script first launches the application under test. As you can see in Figure 1, I'm testing a simple Windows form-based application. While this particular application is built on the .NET Framework, these Windows PowerShell UI automation techniques will also work with Win32 applications. The techniques I'm showing you, however, are not designed to work with Web applications.

After launching the application under test, my script obtains a handle to the application itself and the child controls on the application. It then simulates a user-click on the application's Search button. The application responds to the button click with an error message box. My script locates the message box and simulates a click on the OK button in the message box.

The next part of the automation sends text "Product Name" to the ComboBox control (to specify how the application should search) and "ad" to the TextBox control (to specify a search target). The script clicks the search button again and this time the application searches through a store of dummy data, displaying matches in a ListBox control.

My script finishes by checking the state of the application (in this case, looking at the contents of the ListBox and TextBox controls). It determines whether the test scenario passed or failed, and then performs a user-click on the application File | Exit menu item.

The Application Under Test

As I mentioned, the dummy application under test is .NET-based. It has basic user controls, including a TextBox control, a ComboBox control, a Button control, two Label controls, and a ListBox control. I often use this dummy application to explore different UI automation techniques. Note that the complete code for this application, along with all the other code referenced in this column, is available in the download that accompanies this column.

When performing UI automation, you must know the name or index control number for any control you want to manipulate or examine programmatically. Bear with me for a moment because you may not be familiar with this concept. All user controls are windows. To manipulate a control you need a handle to it. Some user controls have window names, but many don't. In my dummy application, the window name of the application itself is Form1, while the TextBox control has no window name. If a control has a window name, you can use that name to programmatically get a handle to the control. However, when a control does not have a window name, you can use the control index number.

Control index numbers are implied rather than explicit, and they are determined by the order in which they are added to the main Form in the InitializeComponent method. If you look at the source code of my dummy application, you'll see:

this.Controls.Add(this.listBox1);
this.Controls.Add(this.button1);
this.Controls.Add(this.textBox1);
this.Controls.Add(this.comboBox1);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);

The control index for the parent Form window ("this") is 0, so the ListBox control has control index 1, the Button has index 2, the TextBox has index 3, the ComboBox has index 4, Label2 has index 5, and Label1 has index 6. It's important to note that the implied control index value of a control is not the same as the tab order property of the control.

In this scenario, I can determine the index values for all the controls since I have access to the application source code. If the source code is not available, you can still determine control index values using the Spy++ tool that ships with Visual Studio®.

Realistically, a search application would most likely retrieve search results from a database server. For simplicity, however, I use a local data store in the form of a simple Product class:

public class Product {
  public readonly string ID, Name, Price;
  public Product(string id, string name, string price) {
    ID = id; Name = name; Price = price;
  }
}

Data for Product instances is stored in a collection and supplied when the main Form object loads:

private List<Product> list = new List<Product>();

private void Form1_Load(object sender, System.EventArgs e) {
  list.Add(new Product("111", "Widget", "$11.11"));
  list.Add(new Product("222", "Gadget", "$22.22"));
  list.Add(new Product("333", "Thingy", "$33.33"));
}

Since I'm performing UI automation, the data source is to some extent irrelevant. Regardless of where the underlying result data comes from, the results are reflected in the application's UI and the UI state can be examined in order to determine a test scenario pass/fail result.

All the application logic occurs in the search button control's click method. The application begins by performing a rudimentary check to see if the user has specified a search criterion and a search target. If either item is missing, the application displays a MessageBox window with an error message:

if (comboBox1.Text == "" || textBox1.Text == "") {
  MessageBox.Show("Please enter search criteria and term", "Error");
}
listBox1.Items.Clear();

MessageBox windows are not part of an application's Form object and dealing with them is a basic UI test automation technique. After the application clears the ListBox control (which holds the search results), the application fetches the search criteria and targets string from the TextBox and ComboBox controls and then traverses through the List data store looking for matches:

string target = textBox1.Text, criterion = comboBox1.Text;

foreach (Product p in list) {
  if (criterion == "Product ID") {
    if (p.ID.IndexOf(target) >= 0) {
      listBox1.Items.Add(p.ID + " " + p.Name + " " + p.Price);
    }
  }
  else if (criterion == "Product Name") {
    // search for by product name similarly
  }
}

In the interest of keeping my code short and understandable for this column, I am not following every best coding practice. Of course, crude code such as this approximates the unrefined nature of an application in its early stages of development, which is often the situation when you should be starting to test the application.

Creating a Custom Cmdlet Library

Now I will step you through the creation of a small set of custom cmdlets. In particular, I will implement the eight UI-related cmdlets listed in Figure 1. The approach I am using here is just one of several possible design options you can take. (I will discuss a few of the alternatives in a moment.)

First, I launch Visual Studio and create a new C# Class Library project named CustomUICmdletsLib, which creates by default a namespace with the same name. The choice of namespace is arbitrary, but the Windows PowerShell documentation suggests naming custom cmdlet libraries as XXX.Commands, where the XXX represents your organization or the library functionality. The overall structure of my custom UI cmdlet library is shown in Figure 2.

Figure 2 Custom UI Cmdlet Library Structure (Close Figure 2)
using System;
using System.Management.Automation;
using System.ComponentModel;
using System.Configuration.Install;
using System.Runtime.InteropServices;

namespace CustomUICmdletsLib
{
  [Cmdlet(VerbsCommon.Get, "Window")]
  public class GetWindowCommand : Cmdlet { . . . }

  [Cmdlet(VerbsCommon.Get, "Control")]
  public class GetControlCommand : Cmdlet { . . . }

  [Cmdlet(VerbsCommon.Get, "ControlByIndex")]
  public class GetControlByIndexCommand : Cmdlet { . . . }

  [Cmdlet(VerbsCommunications.Send, "Chars")]
  public class SetCharsCommand : Cmdlet { . . . }

  [Cmdlet(VerbsCommunications.Send, "Click")]
  public class SetClickCommand : Cmdlet { . . . }

  [Cmdlet(VerbsCommon.Get, "ListBox")]
  public class GetListBoxCommand : Cmdlet { . . . }

  [Cmdlet(VerbsCommon.Get, "TextBox")]
  public class GetTextBoxCommand : Cmdlet { . . . }

  [Cmdlet(VerbsCommunications.Send, "Menu")]
  public class SetMenuCommand : Cmdlet { . . . }

  [RunInstaller(true)]
  public class LibPSSnapIn : PSSnapIn { . . . }
}

My library has a project reference to the assembly contained in the System.Management.Automation.dll file. This DLL is not one of the normal .NET Framework DLLs that ship with Visual Studio; instead, it's typically available as part of the Windows Software Development Kit (available at microsoft.com/downloads), or in some cases the System.Management.Automation.dll is included with the Windows PowerShell download itself.

The System.ComponentModel namespace and the System.Runtime.InteropServices namespace are available by default to a C# Class Library project so all you have to do is add the corresponding using statements. These namespaces are required for the LibPSSnapIn class and P/Invoke code, respectively.

You must add a project reference to the System.Configuration.Install.dll assembly; this is part of the standard .NET Framework assembly set so you will find it listed in the Add | Project Reference | .NET GUI dialog box. You may have noticed that there are nine classes in Figure 2—one for each custom cmdlet and one special snap-in class that is used to register the custom cmdlets.

My first custom cmdlet, get-window, accepts the name of a top-level window (such as an application Form) and returns a handle to the window/application. The code is shown in Figure 3.

Figure 3 Custom get-window Cmdlet (Close Figure 3)
[Cmdlet(VerbsCommon.Get, "Window")]
public class GetWindowCommand : Cmdlet
{
  [DllImport("user32.dll", CharSet=CharSet.Auto)]
  static extern IntPtr FindWindow(
    string lpClassName, string lpWindowName);

  private string windowName;

  [Parameter(Position = 0)]
  public string WindowName {
    get { return windowName; }
    set { windowName = value; }
  }

  protected override void ProcessRecord()
  {
    IntPtr wh = FindWindow(null, windowName);
    WriteObject(wh);
  }
}

The first line of code uses the Windows PowerShell Cmdlet attribute to implicitly name my custom cmdlet. Windows PowerShell cmdlets follow a verb-noun naming convention and use verbs from a standard list. So I name my cmdlet get-window. The class itself inherits from a base Cmdlet class that does most of the plumbing for you, making it very easy to write custom cmdlets. (The name of the implementation class does not need to be related to the name of the cmdlet that is being implemented, but it's a good idea to use a consistent class-naming scheme.)

Next, I specify a single parameter named windowName for my custom cmdlet. I do not use the Class Name parameter of the FindWindow function. The Class Name of a window object is an internal Windows category—it's not related to the C# "class" language feature—and therefore it is not useful for identifying the window.

I provide get and set properties for my custom cmdlet input parameter, decorating the properties with a required Parameter attribute and specifying the zero-based index position of the parameter. (I could also supply a Mandatory argument to add some error-checking.) All the actual work in a custom cmdlet is performed in a special ProcessRecord method. Here I simply call my C# FindWindow alias, which in turn calls the underlying Win32 FindWindow function and fetches the handle of the window with the specified name. Since Windows PowerShell uses the pipeline architecture, I use the special WriteObject method to return the handle value (rather than returning the window handle value with a "return" keyword).

The get-ControlByIndex cmdlet is slightly tricky—the code is shown in Figure 4. Here I use the FindWindowEx Win32 function to get a handle to a control based on the control's implied index value. Note that FindWindowEx accepts four arguments—the second argument is a handle that tells FindWindowEx at which window/control to begin looking. By repeatedly passing in the return value from the previous call to FindWindowEx, I effectively advance one window handle on each iteration through the do...while loop in Figure 4. I stop iterating when the local ct variable reaches the value of index, which is passed in as an argument.

Figure 4 Custom get-controlByIndex Cmdlet (Close Figure 4)
[Cmdlet(VerbsCommon.Get, "ControlByIndex")]
public class GetControlByIndexCommand : Cmdlet
{
  [DllImport("user32.dll", CharSet = CharSet.Auto)]
  static extern IntPtr FindWindowEx(IntPtr hwndParent,
    IntPtr hwndChildAfter, string lpszClass, string lpszWindow);

  private IntPtr handleParent;
  private int index;

  [Parameter(Position = 0)]
  public IntPtr HandleParent {
    get { return handleParent; }
    set { handleParent = value; }
  }

  [Parameter(Position = 1)]
  public int Index {
    get { return index; }
    set { index = value; }
  }

  protected override void ProcessRecord()
  {
    if (index == 0) {
      WriteObject(handleParent);
    }
    else {
      int ct = 0;
      IntPtr result = IntPtr.Zero;
      do {
        result = FindWindowEx(handleParent, result, null, null);
        if (result != IntPtr.Zero) ++ct;
      } while (ct < index && result != IntPtr.Zero);
      WriteObject(result);
    }
  }
} // class

My remaining six custom cmdlets follow the same general design pattern as the get-window and get-controlByIndex cmdlets. All eight custom cmdlets are summarized in Figure 5 (the code download includes complete source code for all of these).

Figure 5 Summary of Custom Cmdlets for UI Automation (Close Figure 5)

Cmdlet Name | Input Parameters | Return Value / Effect
get-window | windowName | Handle to a top-level window.
get-control | handleParent, controlName | Handle to a named control.
get-controlByIndex | handleParent, index | Handle to a non-named control.
send-chars | handleControl, s | Sends string s to a control.
send-click | handleControl | Clicks a control.
get-listBox | handleControl, target | Zero-based location of target or -1.
get-textBox | handleControl | Contents of TextBox as a string.
send-menu | mainIndex, subIndex | Fires application menu command.

Admittedly, my choice of cmdlet names is rather terse—most of my colleagues prefer more descriptive cmdlet names. For example, you might want to rename the get-window to something like get-automationWindow or get-uiAutoWindowHandle. (Fortunately, you can provide aliases for custom cmdlets with long names.) Every custom cmdlet library in Windows PowerShell must implement a special snap-in class that enables the custom library to be registered with Windows PowerShell. My snap-in class is as follows:

[RunInstaller(true)]
public class LibPSSnapIn : PSSnapIn
{
  public LibPSSnapIn() : base() { }
  public override string Name {
    get { return "LibPSSnapIn"; }
  }
  public override string Vendor {
    get { return "James McCaffrey"; }
  }
  public override string Description {
    get { return " Provides cmdlets for lightweight UI automation"; }
  }
}

A detailed discussion of Windows PowerShell snap-ins is outside the scope of this column. For now, you can just take this code and replace the string values as appropriate.

After writing my custom cmdlet library, I build the Visual Studio project, which creates a single CustomUICmdletLib.dll file in my output directory. Then I must register and enable the library. There are several ways to do this. My preferred method is to create a small Windows PowerShell registration function and add it to a startup script that automatically executes every time a new instance of Windows PowerShell is launched. (Actually there are four different Windows PowerShell startup scripts.)

You can edit or create the startup script by entering "notepad $profile" on the Windows PowerShell command line. Figure 6 shows the startup script I used when capturing the screen displayed in Figure 1.

Figure 6 Registering the Custom Cmdlet Library (Close Figure 6)
# file: Microsoft.PowerShell_profile.ps1

function RegisterUILib
{
  write-host "registering custom cmdlets for UI automation`n"
  $env:path = $env:path +=
    ";C:\Windows\Microsoft.NET\Framework\v2.0.50727"
  sl 'C:\UIautomationWithPowerShell\CustomUICmdletsLib\bin\Debug'
  installutil.exe CustomUICmdletsLib.dll | out-null
  add-pssnapin LibPSSnapin
  write-host "get-window, get-control, get-controlByIndex,
    send-chars, send-click"
  write-host "get-listBox, send-menu, and get-textBox custom
    cmdlets are enabled `n"
}
RegisterUILib # invoke function

set-location C:\UIautomationWithPowerShell

# end startup script

In the script, I create a function to encapsulate my custom Windows PowerShell cmdlet registration code (I could have just as easily used individual Windows PowerShell statements). I like to place descriptive write-host statements in my startup scripts because I often change my startup profile and the messages let me know exactly what special functionality my current shell has available.

Next, I use the built-in Windows PowerShell $env:path variable to add the location of a special utility file named installutil.exe to my shell path variable. I then set the current directory location to the location of my custom cmdlet DLL file. I invoke the installutil.exe program and pipe that command to out-null to eliminate noisy progress messages.

The last key command uses the built-in add-pssnapin cmdlet to call my LibPSSnapIn class defined inside my custom library. Note that this code merely defines a function. After defining the function, I invoke it in the startup script by its name.

Using the Custom Cmdlet Library

With my custom Windows PowerShell cmdlets in place, using the library is very easy. Now I will walk you through the script that generated the output shown in Figure 1. The first few lines of my test automation script are as follows:

# testScenario.ps1
write-host "`nBegin UI automation with PowerShell test"
$pass = $true
write-host "`nLaunching application to automate"
invoke-item '.\TheAppToTest\bin\Debug\TheAppToTest.exe'
[System.Threading.Thread]::Sleep(2000)

After I begin with a Windows PowerShell comment, I use the write-host cmdlet to print a message to my shell. The `n is a Windows PowerShell escape sequence for an embedded newline character.

Next, I set a variable named $pass to true. My logic here is that I assume the test scenario will pass, and I will set $pass to false if some application state does not meet an expected value. Windows PowerShell variable names begin with the "$" character, making them easy to distinguish inside scripts. The $true variable is a special built-in Windows PowerShell variable.

After displaying a progress message using write-host, I use the built-in invoke-item cmdlet to launch the application under test. Note that Windows PowerShell uses both single quotes and double quotes (single-quote strings are literals while double-quoted strings allow evaluation of embedded escape sequences and variables).

Next, I directly call the .NET Sleep static method of the Sys- tem.Threading.Thread namespace. (Windows PowerShell provides full access to the .NET Framework. Very cool!) Here I pause my script for two seconds (2000 milliseconds) to give the application time to launch.

Now I obtain handles to the main application Form and three of the Form's child controls, like so:

$app = get-window "Form1"
$cb = get-controlByIndex $app 4
$btn = get-control $app "Search"
$tb = get-controlByIndex $app 3

In the first line, I use the custom get-window cmdlet to get a handle to a top-level window. And I use the custom get-controlByIndex cmdlet to get handles to controls that have no Window Name property ($cb for the ComboBox and $tb for the TextBox). To get a handle to a named control ($btn for the search button), I use the custom get-control cmdlet.

Next, I display my window handles to make sure I don't have any null or duplicate values, and then use my custom send-click cmdlet to simulate a click on the search button:

write-host "`nHandle to Application is " $app
write-host "Handle to ComboBox is" $cb
write-host "Handle to Button is " $btn
write-host "Handle to TextBox is " $tb

write-host "`nClicking on search button"
send-click $btn

(In a production scenario, you should probably check for bad handle values programmatically.) The initial click on the search button will generate an error message, as shown in Figure 7, because both the search criterion on the ComboBox control and the search target in the TextBox control are empty.

Figure 7 Application Error MessageBox
Figure 7 Application Error MessageBox

The next few commands in my script deal with the modal MessageBox window:

write-host "Finding Error Message box"
[System.Threading.Thread]::Sleep(2000)
$mbox = get-window "Error"

write-host "Clicking Message Box away"
$ok = get-control $mbox "OK"
send-click $ok

I put a two-second delay in my script to make sure the MessageBox is fully available then I get a handle to the window using my custom get-window cmdlet. (Remember that MessageBox windows are separate top-level windows and not child windows of the application that generated them.) I use get-control to get a handle to the OK button control on the MessageBox and then click the MessageBox away.

My next few statements send strings to the ComboBox and TextBox controls:

  write-host
    "`nSending 'Product Name' to   ComboBox"
  send-chars $cb "Product Name"
  write-host "Sending 'ad' to TextBox"
  send-chars $tb "ad"
  write-host "Re-clicking search button"
  send-click $btn

After supplying search criterion and target values, I re-click the search button to cause the application under test to search for all products where the product name contains "ad" and to display results in the ListBox control.

I pause two seconds and then examine the ListBox control:

write-host "`nChecking contents of ListBox for '222'"
[System.Threading.Thread]::Sleep(2000)
$lb = get-controlByIndex $app 1
write-host "Handle to ListBox is " $lb
$result = get-listBox $lb "222"

My custom get-listBox cmdlet returns -1 if a target string is not found, or the zero-based index of the location of the target if it is found. So, I can check the value in the $result variable like this:

if ($result -ge 0) {
  write-host "Found '222' in ListBox!"
}
else {
  write-host "Did NOT find '222' in ListBox"
  $pass = $false
}

A slightly unusual quirk of Windows PowerShell is its use of Boolean comparison operators, such as -ge ("greater than or equal to") and -eq ("equal to"), rather than lexical tokens, such as ">=" and "==".

Now I use the custom get-textBox cmdlet to fetch the contents of the search target:

write-host "Checking contents of TextBox for 'ad'"
[System.Threading.Thread]::Sleep(2000)
$text = get-textBox $tb

Then I check to see if the application correctly left the TextBox control alone:

if ($text -eq "ad") {
  write-host "Found 'ad' in TextBox!"
}
else {
  write-host "Did NOT find 'ad' in TextBox"
  $pass = $false
}

Now I can examine my $pass variable to see if it is still set to true or if it has been set to false due to an incorrect application state:

if ($pass) {
  write-host "`nTest scenario result = Pass" -foregroundcolor green
}
else {
  write-host "`nTest scenario result = * FAIL *" -foregroundcolor red
}

I finish by calling my custom send-menu cmdlet to simulate a File | Exit to close the application:

write-host "`nClicking File -> Exit in 5 seconds . . ."
[System.Threading.Thread]::Sleep(5000)
send-menu $app 0 0
write-host "`nEnd UI automation with PowerShell test`n"
# end script

The two 0 arguments to the send-menu cmdlet mean to use the zero-indexed main menu item (File) and then the zero-indexed sub-item (Exit).

Using, Modifying, and Extending the UI Cmdlet Library

The techniques I've presented here are most appropriate for use in very lightweight automation situations. When you want some quick and easy UI automation, using Windows PowerShell with a small custom UI cmdlet library is a great approach. These techniques are also appropriate for when you want to perform interactive style UI automation from the Windows PowerShell command line. But when writing relatively complex UI automation, you are generally better off using Visual Studio to create C# programs that use the P/Invoke mechanism.

The custom cmdlet library I've presented in this article was specifically designed to allow you to easily modify and extend my code. In particular, I've removed all error-checking to keep my code short and easy to understand. As it stands, my library has custom cmdlets that will allow you to perform quite a bit of common UI automation, but there's no reason why you can't add cmdlets that work with other user controls, such as CheckBox and RadioBox controls. Just don't get too carried away here—complete UI automation libraries that take into account the majority of UI scenarios are very large. Lightweight UI automation is for performing mundane, repetitive tasks, freeing you up to perform manual testing on more interesting and complex UI scenarios.

You may also want to extend my UI automation by creating Windows PowerShell functions that call the core custom cmdlets so that you can provide error checking and additional functionality. For example, my sample uses a very crude approach to deal with waiting for the application under test to launch—I just use a Thread.Sleep call before calling the get-window cmdlet. A much more robust approach is to write a short Windows PowerShell function that contains a loop that calls get-window with a short delay until the return value to get-window is not null or once some maximum number of attempts has been reached.

Send your questions and comments for James to testrun@microsoft.com.

Dr. James McCaffrey works for Volt Information Sciences, Inc., where he manages technical training for software engineers who work for Microsoft. He has worked on several Microsoft products, including Internet Explorer and MSN Search. James is the author of .NET Test Automation Recipes (Apress, 2006). He can be reached at jmccaffrey@volt.com or v-jammc@microsoft.com.


Service Station: Extending WCF with Custom Behaviors

Service Station
Extending WCF with Custom Behaviors
Aaron Skonnard

Code download available at: ServiceStation2007_12.exe (165KB)
Contents
WCF Extensibility
Dispatcher/Proxy Extensions
Implementing Custom Extensions
Parameter Inspectors
Message Inspectors
Operation Invokers
Applying Custom Extensions with Behaviors
Adding Behaviors to the Runtime
Adding Behaviors with Attributes and Configuration
Behavior Validation and Binding Configuration
Sharing State between Extensions
Take-Aways

W
indows® Communication Foundation (WCF) provides numerous extensibility points that allow developers to customize the runtime behavior for service dispatching and client proxy invocation. You can tap into these extensibility points by writing custom behaviors that can be applied declaratively to your services. This month I'll show you how this process works.

WCF Extensibility

In my last column I focused on the WCF concept of a binding, which you can specify for each endpoint on your WCF service. The binding controls the messaging details (what happens on the wire) for that endpoint. It's essentially the recipe WCF follows to build a channel stack capable of transforming between a stream of bytes (the message on the wire) and a WCF message. There are countless extensibility points found throughout the WCF channel layer.

WCF also provides a higher-level runtime that sits on top of the channel layer, targeted more at the application developer. It is often referred to throughout the WCF documentation as the service model layer. This higher-level runtime consists primarily of a component called the dispatcher (within the context of a service host) and a component called the proxy (within the context of a client).

The dispatcher/proxy combination serves one primary purpose—to translate between WCF Message objects and Microsoft® .NET Framework method calls (see Figure 1). These components follow a well-defined sequence of steps to perform this process, and at each step along the way they provide extensibility points that you can plug into. You can use these extensibility points to implement a wide variety of custom behaviors including message or parameter validation, message logging, message transformations, custom serialization/deserialization formats, output caching, object pooling, error handling, and authorization, to name a few. Here I'll focus on the implementation of these types of custom behaviors.

Figure 1 WCF Runtime Architecture
Figure 1 WCF Runtime Architecture

Dispatcher/Proxy Extensions

Both the dispatcher and proxy provide numerous extensibility points where you can inject your own code; these extensions are often referred to as interceptors because they allow you to intercept the default execution behavior of the runtime. However, I usually refer to them as runtime extensions.

Figure 2 shows the client-side proxy architecture and the extensibility points it makes available. The main responsibility of the proxy is to transform the caller-supplied objects (parameters) into a WCF Message object, which can then be supplied to the underlying channel stack for transmission on the wire.

Figure 2 Proxy (Client) Extensions
Figure 2 Proxy (Client) Extensions

The first extensibility point available to you during this process allows you to perform custom parameter inspection, as you see in the first step. You could use this hook to perform custom validation, value modification, or special filtering. Next, the proxy leverages a serializer to transform the supplied parameters into a WCF Message object (Step 2 in the diagram). You can customize the serialization process here by using a custom formatter object.

Once the proxy has produced a Message object, it makes use of a final extensibility point for inspecting the resulting Message object (shown in Step 3) before it's submitted to the channel stack. As illustrated in Figure 2, this extension takes effect regardless of which operation was invoked. You might use this hook to implement cross-cutting messaging features such as message logging, validation, or transformations—functionality that wouldn't necessarily be specific to a single operation.

You configure these extensions on a proxy through the ClientOperation and ClientRuntime objects. You'll find one ClientOperation object for each service operation and a single ClientRuntime object for configuring the proxy as a whole. ClientOperation provides properties for managing the parameter inspection and message formatting extensions, while ClientRuntime provides a property for managing message inspection extensions.

Figure 3 shows the dispatcher extensibility points. You'll notice it looks very similar to Figure 2, but in this case the extensibility points are executed in reverse order and there are a few more points that didn't exist on the client side in the previous figure.

Figure 3 Dispatcher Extensions
Figure 3 Dispatcher Extensions

When the dispatcher receives a Message object from the channel stack, the first extensibility point encountered is message inspection. Then the dispatcher must select an operation to invoke (Step 2) before it can continue—there's an extensibility point here for overriding the default operation selection behavior. Once the target operation has been identified, the dispatcher deserializes the message into objects that can be supplied as parameters when invoking the target method. At this point (Step 3), the dispatcher provides extensibility points for message formatting (deserialization) and parameter inspection (Step 4). The final step for the dispatcher is to invoke the target method supplying the prepared parameters. You can even override this step by providing a custom operation invoker object.

You configure these extensions on the dispatcher through the DispatchRuntime and DispatchOperation objects, as per Figure 3. I'll show you how to gain access to these objects shortly, but first let's discuss how to implement them.

Implementing Custom Extensions

Each of the extensibility points described above is modeled by a .NET interface definition (see Figure 4). Note that in some cases the same logical extension type requires a different interface between the dispatcher and proxy sides. Let's take a closer look at how to implement a few of these interfaces.

Figure 4 Dispatcher/Proxy Extension Summary (Close Figure 4)

Stage | Interceptor Interface | Description
Parameter Inspection | IParameterInspector | Called before and after invocation to inspect and modify parameter values.
Message Formatting | IDispatchMessageFormatter IClientFormatter | Called to perform serialization and deserialization.
Message Inspection | IDispatchMessageInspector IClientMessageInspector | Called before send or after receive to inspect and replace message contents.
Operation Selection | IDispatchOperationSelector IClientOperationSelector | Called to select the operation to invoke for the given message.
Operation Invoker | IOperationInvoker | Called to invoke the operation.

Let's assume you're building a ZIP code lookup service with the following contract:

[ServiceContract]
public interface IZipCodeService
{
    [OperationContract]
    string Lookup(string zipcode);
}

The Lookup method takes a single parameter, zipcode, of type string and returns a string to the caller. The caller is expected to supply a ZIP code value and the service will return the location (in city, state format). What's not obvious to the user is that the supplied ZIP code must be in the official ZIP + 4 format: #####-####. For example, my home ZIP code is 84041-1501. Given this requirement, the service implementation must validate each incoming ZIP code value.

Parameter Inspectors

It wouldn't be hard to implement the ZIP + 4 validation logic within the Lookup method itself, but if the result is a large number of operations that accept ZIP codes, it would be better to implement the validation logic as an IParameterInspector extension that can be declaratively applied to any operation.

In order to do this, you must write a class that implements IParameterInspector, which defines two methods: AfterCall and BeforeCall. As their names suggest, the runtime invokes BeforeCall before calling the target method on the service instance and AfterCall after having made the call. This gives you pre- and post-interception points for inspecting the parameters and return values, which are supplied to these methods as arrays of objects.

Figure 5 shows a complete implementation of IParameterInspector that performs the necessary ZIP + 4 validation. The ZipCodeInspector class implements IParameterInspector, but I only implemented BeforeCall since I only need input validation. BeforeCall validates the supplied ZIP code value against the ZIP + 4 regular expression ("\d{5}-\d{4}"), and if it doesn't match, it goes ahead and throws a FaultException.

Figure 5 ZIP Code Validation Parameter Inspector (Close Figure 5)
public class ZipCodeInspector : IParameterInspector
{
    int zipCodeParamIndex;
    string zipCodeFormat = @"\d{5}-\d{4}";

    public ZipCodeInspector() : this(0) { }
    public ZipCodeInspector(int zipCodeParamIndex)
    {
        this.zipCodeParamIndex = zipCodeParamIndex;
    }

    ... // AfterCall is empty

    public object BeforeCall(string operationName, object[] inputs)
    {
        string zipCodeParam = inputs[this.zipCodeParamIndex] as string;
        if (!Regex.IsMatch(
            zipCodeParam, this.zipCodeFormat, RegexOptions.None))
            throw new FaultException(
                "Invalid zip code format. Required format: #####-####");
        return null;
    }
}

With ZipCodeInspector in place, you can easily apply this validation logic to any operation that accepts a ZIP code value. And you can use the implementation on both sides of the wire (within the client or service). I'll show you how to wire up this parameter inspector after a few more examples.

Message Inspectors

Instead of inspecting parameters, let's assume you want to inspect the messages flowing in and out of a service regardless of the operation. This is where you'd use the message inspection extensibility point. Unlike parameter inspection, the interfaces for message inspection are different for the dispatcher and proxy (IDispatchMessageInspector versus IClientMessageInspector). However, you can always implement both interfaces when you want to support both sides.

IDispatchMessageInspector has two methods: AfterReceiveRequest and BeforeSendReply, which means you get pre- and post-interception points for inspecting the WCF Message object. IClientMessageInspector also has two methods that provide the converse points: AfterReceiveReply and BeforeSendRequest.

Say you want to implement a diagnostic utility that prints all incoming and outgoing messages to the console window. Figure 6 provides a complete sample that does this. Notice ConsoleMessageTracer implements both message inspector interfaces, so it can be used on both sides of the wire. Each method simply copies the incoming message and prints it to the console window.

Figure 6 Console Tracing Message Inspector (Close Figure 6)
public class ConsoleMessageTracer : IDispatchMessageInspector,
    IClientMessageInspector
{
    private Message TraceMessage(MessageBuffer buffer)
    {
        Message msg = buffer.CreateMessage();
        Console.WriteLine("\n{0}\n", msg);
        return buffer.CreateMessage();
    }
    public object AfterReceiveRequest(ref Message request,
        IClientChannel channel,
        InstanceContext instanceContext)
    {
        request = TraceMessage(request.CreateBufferedCopy(int.MaxValue));
        return null;
    }
    public void BeforeSendReply(ref Message reply, object
        correlationState)
    {
        reply = TraceMessage(reply.CreateBufferedCopy(int.MaxValue));
    }

    public void AfterReceiveReply(ref Message reply, object
        correlationState)
    {
        reply = TraceMessage(reply.CreateBufferedCopy(int.MaxValue));
    }
    public object BeforeSendRequest(ref Message request,
        IClientChannel channel)
    {
        request = TraceMessage(request.CreateBufferedCopy(int.MaxValue));
        return null;
    }
}

If you're curious about why I've employed the message copying technique in Figure 6, see my previous column, "WCF Messaging Fundamentals," and review the section on message lifetime (see msdn.microsoft.com/msdnmag/issues/07/04/ServiceStation).

Operation Invokers

For a final example, let's consider the operation invoker extensibility point. This hook allows you to override the default process with a custom invoker object. In the ZIP code example, you could use the operation invoker to implement a simple output caching feature. For a given ZIP code, the result is always going to be the same, so if you cache the results, you'll never have to invoke the service instance more than once for a particular ZIP code value. This could greatly improve performance and response times in certain situations where the service logic is either costly or takes a long time to complete.

Figure 7 shows a complete example. Here, the ZipCodeCacher class implements IOperationInvoker and the ZIP code caching functionality is found in the Invoke method. The Invoke method first attempts to look up the ZIP code location in its cache, and if it's not found, it invokes the method on the service instance (using the default invoker). It stores new results in the cache for future invocations. The user of this extension must supply the dispatcher's default invoker object upon construction as the remaining methods delegate to it.

Figure 7 ZIP Code Caching Operation Invoker (Close Figure 7)
public class ZipCodeCacher : IOperationInvoker
{
    IOperationInvoker innerOperationInvoker;
    Dictionary<string, string> zipCodeCache =
        new Dictionary<string, string>();

    public ZipCodeCacher(IOperationInvoker innerOperationInvoker)
    {
        this.innerOperationInvoker = innerOperationInvoker;
    }

    public object Invoke(object instance, object[] inputs,
        out object[] outputs)
    {
        string zipcode = inputs[0] as string;
        string value;

        if (this.zipCodeCache.TryGetValue(zipcode, out value))
        {
            outputs = new object[0];
            return value;
        }
        else
        {
            value = (string)this.innerOperationInvoker.Invoke(
                instance, inputs, out outputs);
            zipCodeCache[zipcode] = value;
            return value;
        }
    }

    ...

    // remaining methods elided
    // they simply delegate to innerOperationInvoker
}

I've shown you some common examples of building custom extensions. There are others I didn't have time to cover, but I'll leave those for your own personal study. A more important matter I need to discuss is how you wire up these extensions to the dispatcher/proxy. This is where behaviors enter the picture.

Applying Custom Extensions with Behaviors

A behavior is a special type of class that extends runtime behavior during the ServiceHost/ChannelFactory initialization process. There are four types of behaviors: service, endpoint, contract, and operation. Each type allows you to apply extensions at different scopes (see Figure 8). Each type of behavior is also modeled by a different interface definition, but they all share the same set of methods (see Figure 9). One exception is that IServiceBehavior doesn't have an ApplyClientBehavior method because service behaviors can't be applied to clients.

Figure 9 Behavior Interface Methods (Close Figure 9)

Method | Description
Validate | Called just before the runtime is built—allows you to perform custom validation on the service description.
AddBindingParameters | Called in the first step of building the runtime, before the underlying channel is constructed—allows you to add parameters to influence the underlying channel stack.
ApplyClientBehavior | Allows behavior to inject proxy (client) extensions. Note that this method is not present on IServiceBehavior.
ApplyDispatchBehavior | Allows behavior to inject dispatcher extensions.
Figure 8 Types of Behaviors (Close Figure 8)

Scope | Interface | Potential Impact
 |  | Service | Endpoint | Contract | Operation
Service | IServiceBehavior | ✗ | ✗ | ✗ | ✗
Endpoint | IEndpointBehavior |  | ✗ | ✗ | ✗
Contract | IContractBehavior |  |  | ✗ | ✗
Operation | IOperationBehavior |  |  |  | ✗

As illustrated in Figure 8, service behaviors are used for applying extensions across the entire service; you can apply them to the service itself or to specific endpoints, contracts, and operations. Endpoint behaviors, on the other hand, are used to apply extensions to a particular endpoint (or perhaps to the endpoint's contract or operations). Contract and operation behaviors are used to apply extensions to contracts and operations. Endpoint, contract, and operation behaviors can be applied to both services and clients, but service behaviors can only be applied to services.

Although the methods are the same for each behavior interface, the signatures are indeed different. They are tailored to provide the appropriate runtime objects for that particular scope. ApplyDispatchBehavior and ApplyClientBehavior are the core methods that allow you to apply custom extensions to the dispatcher and proxy, respectively. When the runtime calls these methods, it provides the DispatchRuntime, DispatchOperation, ClientRuntime, and ClientOperation objects to you for injecting your extensions (refer back toFigures 2 and 3).

Figure 10 shows how to implement a few operation behaviors. It makes sense to use an operation behavior to apply the ZipCodeInspector and ZipCodeCacher extensions because you only want to use them on methods that deal with ZIP codes. As you can see, ZipCodeValidation adds an instance of ZipCodeInspector to the ParameterInspectors collection found on the supplied DispatchOperation and ClientOperation objects. ZipCodeCaching assigns the custom ZipCodeCacher to the Invoker property on the supplied DispatchOperation object.

Figure 10 Sample Operation Behaviors (Close Figure 10)
public class ZipCodeValidation : Attribute, IOperationBehavior
{
    public void ApplyClientBehavior(OperationDescription
        operationDescription, ClientOperation clientOperation)
    {
        ZipCodeInspector zipCodeInspector = new ZipCodeInspector();
        clientOperation.ParameterInspectors.Add(zipCodeInspector);
    }
    public void ApplyDispatchBehavior(
        OperationDescription operationDescription,
        DispatchOperation dispatchOperation)
    {
        ZipCodeInspector zipCodeInspector = new ZipCodeInspector();
        dispatchOperation.ParameterInspectors.Add(zipCodeInspector);
    }

    ... // remaining methods empty
}

public class ZipCodeCaching : Attribute, IOperationBehavior
{
    public void ApplyDispatchBehavior(
        OperationDescription operationDescription,
        DispatchOperation dispatchOperation)
    {
        dispatchOperation.Invoker =
            new ZipCodeCacher(dispatchOperation.Invoker);
    }

    ... // remaining methods empty
}

Now it's time to decide what type of behavior to use for the ConsoleMessageTracer message inspector. I could apply it using a contract, endpoint, or service behavior, depending on the desired usage. Figure 11 shows an example of how to implement a class that serves as both a service and endpoint behavior for applying ConsoleMessageTracer into the appropriate MessageInspectors collection.

Figure 11 Sample Endpoint and Service Behaviors (Close Figure 11)
public class ConsoleMessageTracing :
    Attribute, IEndpointBehavior, IServiceBehavior
{
    void IEndpointBehavior.ApplyClientBehavior(ServiceEndpoint endpoint,
        ClientRuntime clientRuntime)
    {
        clientRuntime.MessageInspectors.Add(new ConsoleMessageTracer());
    }
    void IEndpointBehavior.ApplyDispatchBehavior(ServiceEndpoint endpoint,
        EndpointDispatcher endpointDispatcher)
    {
        endpointDispatcher.DispatchRuntime.MessageInspectors.Add(
            new ConsoleMessageTracer());
    }

    ... // remaining methods empty

    void IServiceBehavior.ApplyDispatchBehavior(
        ServiceDescription desc, ServiceHostBase host)
    {
        foreach (
            ChannelDispatcher cDispatcher in host.ChannelDispatchers)
            foreach (EndpointDispatcher eDispatcher in
                cDispatcher.Endpoints)
                eDispatcher.DispatchRuntime.MessageInspectors.Add(
                    new ConsoleMessageTracer());
    }
    ... // remaining methods empty
}

Now that you've seen how to implement a few behaviors, you're ready to learn how to add behaviors to the WCF runtime.

Adding Behaviors to the Runtime

When you construct a ServiceHost or client-side ChannelFactory, the runtime reflects over the service types, reads the configuration file, and starts building an in-memory description of the service. Within ServiceHost, this description is made available to you via the Description property (of type ServiceDescription). Within ChannelFactory, it's made available via the Endpoint property (of type ServiceEndpoint); the client-side description is limited to the target endpoint.

The ServiceDescription contains a full description of the service and each endpoint (ServiceEndpoint), including contracts (ContractDescription) and operations (OperationDescription). ServiceDescription provides a Behaviors property (a collection of type IServiceBehavior) that models a collection of service behaviors. Each ServiceEndpoint also has a Behaviors property (a collection of type IEndpointBehavior) that models the individual endpoint behaviors. Likewise, ContractDescription and OperationDescription each have an appropriate Behaviors property.

These behavior collections are automatically populated during the ServiceHost and ChannelFactory construction process with any behaviors that are found in your code (via attributes) or within the configuration file (more on this shortly). You can also add behaviors to these collections manually after construction. The following example shows how to add the ConsoleMessageTracing to the host as a service behavior:

ServiceHost host = new ServiceHost(typeof(ZipCodeService));
host.Description.Behaviors.Add(new ConsoleMessageTracing());

This example iterates through all the ServiceEndpoint objects and adds ConsoleMessageTracing as an endpoint behavior:

ServiceHost host = new ServiceHost(typeof(ZipCodeService));
foreach (ServiceEndpoint se in host.Description.Endpoints)
    se.Behaviors.Add(new ConsoleMessageTracing());

It's similar on the client side, but, as I mentioned, the description is focused on a single endpoint and there are no service behaviors. The following example illustrates how to add the ConsoleMessageTracing as a client-side endpoint behavior:

ZipCodeServiceClient client = new ZipCodeServiceClient();
client.ChannelFactory.Endpoint.Behaviors.Add(
    new ConsoleMessageTracing());

You could use similar techniques to manually add behaviors to specific contracts (use ServiceEndpoint.Contract.Behaviors) or to individual operations on a contract (iterate over the ServiceEndpoint.Contract.Operations collection and access the Behaviors collection on OperationDescription).

Once you open the ServiceHost/ChannelFactory (via ICommunicationObject.Open), the runtime walks through the ServiceDescription and gives each behavior a chance to apply its dispatcher/proxy extensions by calling ApplyDispatchBehavior and ApplyClientBehavior (see Figure 12). Once this process is complete, you don't have the ability to add additional behaviors or extensions to the runtime.

Figure 12 Adding Behaviors to the Runtime
Figure 12 Adding Behaviors to the Runtime(Click the image for a smaller view)
Figure 12 Adding Behaviors to the Runtime
Figure 12 Adding Behaviors to the Runtime (Click the image for a larger view)

Adding Behaviors with Attributes and Configuration

During the ServiceHost/ChannelFactory construction process, the runtime reflects over the service types and configuration file and automatically adds any behaviors it finds to the appropriate behavior collections in the ServiceDescription.

The runtime first looks for .NET attributes on your service code that derive from one of the behavior interfaces listed in Figure 8. Whenever the runtime finds one of these attributes, it automatically adds that attribute to the appropriate collection. For example, here I've annotated my service with three attributes that correspond to the behaviors I defined earlier:

[ServiceContract]
public interface IZipCodeService
{
    [ZipCodeCaching]
    [ZipCodeValidation]
    [OperationContract]
    string Lookup(string zipcode);
}

[ConsoleMessageTracing]
public class ZipCodeService : IZipCodeService
{
    ...
}

When I defined these behavior classes, I made sure to derive them from Attribute (in addition to IServiceBehavior and IOperationBehavior) so I'd be able to configure them this way. When constructing a ServiceHost for the ZipCodeService class shown above, the runtime automatically adds one service behavior (ConsoleMessageTracing) and two operation behaviors (ZipCodeCaching and ZipCodeValidation) to the ServiceDescription.

Contract behavior attributes can be applied to service contract interfaces or to the service class. When applied to a service class, you may want to restrict the contract behavior to take affect only when an endpoint uses a particular contract. You can control this by implementing IContractBehaviorAttribute on your contract behavior attribute class and specifying the desired contract via the TargetContract property.

After the reflection process is complete, the runtime also inspects the application configuration file and loads the information found in the <system.serviceModel> section into the ServiceDescription. WCF provides a <behaviors> section for configuring service and endpoint behaviors. Any service/endpoint behaviors found in this section are automatically added to the ServiceDescription.

In order to place custom behaviors within this configuration section, you must first write a class that derives from BehaviorElementExtension, like this one:

public class ConsoleMessageTracingElement : BehaviorExtensionElement
{
    public override Type BehaviorType
    {
        get { return typeof(ConsoleMessageTracing); }
    }
    protected override object CreateBehavior()
    {
        return new ConsoleMessageTracing();
    }
}

Then you must register your BehaviorExtensionElement in the <extensions> section and map it to an element name. With that in place, you can use your registered element name within the <behaviors> section in order to configure the behavior. Figure 13 provides a complete example showing how to configure the ConsoleMessageTracing behavior.

Figure 13 Configuring Behaviors (Close Figure 13)
<configuration>
  <system.serviceModel>
    <services>
      <service name="ZipCodeServiceLibrary.ZipCodeService"
          behaviorConfiguration="Default">
        <endpoint binding="basicHttpBinding"
          contract="ZipCodeServiceLibrary.IZipCodeService"/>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="Default">
          <serviceMetadata httpGetEnabled="true"/>
          <consoleMessageTracing/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <extensions>
      <behaviorExtensions>
        <add name="consoleMessageTracing" type="Extensions.
            ConsoleMessageTracingElement, Extensions, Version=1.0.0.0,
            Culture=neutral, PublicKeyToken=null"/>
      </behaviorExtensions>
    </extensions>
  </system.serviceModel>
</configuration>

You can add service, contract, or operation behaviors using attributes, but you can't use them to add endpoint behaviors. You can add service and endpoint behaviors via the configuration file, but you can't use it to add contract or operation behaviors. Finally, you can manually add any type of behavior to the ServiceDescription. Figure 14 summarizes these differences.

Figure 14 Behavior Configuration Options (Close Figure 14)

Behavior Type | Configuration Options
 | Attribute | Configuration | Explicit
Service | ✗ | ✗ | ✗
Endpoint |  | ✗ | ✗
Contract | ✗ |  | ✗
Operation | ✗ |  | ✗

Also, it's important to note that you can leverage contract and operation behaviors on the client side by applying attributes to your proxy types, but endpoint behaviors are the only type you can apply to clients via configuration.

Behavior Validation and Binding Configuration

In addition to adding custom runtime extensions, behaviors are also designed to let you perform two additional tasks: custom validation and binding configuration. Notice the Validate and AddBindingParameters in Figure 9.

The Validate method gives you a chance to perform custom validation on the ServiceDescription after it has been initialized but before the rest of the runtime has been built. This is your chance to traverse the ServiceDescription tree (or ServiceEndpoint on the client side) and validate it against your own criteria. If something doesn't meet your requirements, you can throw an exception to prevent the ServiceHost/ChannelFactory from opening.

The following service behavior validates the ServiceDescription to ensure that no endpoints use BasicHttpBinding:

public class NoBasicEndpointValidator : Attribute, IServiceBehavior
{
    #region IServiceBehavior Members
    public void Validate(ServiceDescription desc, ServiceHostBase host)
    {
        foreach (ServiceEndpoint se in desc.Endpoints)
            if (se.Binding.Name.Equals("BasicHttpBinding"))
                throw new FaultException(
                    "BasicHttpBinding is not allowed");
    }

    ... //remaining methods empty
}

Once you've applied this behavior to a service, the runtime will no longer allow you to use BasicHttpBinding when configuring endpoints, and it will force you to choose a secure binding instead.

AddBindingParameters gives you a chance to add additional binding parameters during runtime initialization. The binding parameters are supplied to the underlying channel layer in order to influence the creation of the channel stacks. Custom binding elements have access to these binding parameters and can be designed to look for them (see my column, "WCF Bindings In-Depth," in the July 2007 issue at msdn.microsoft.com/msdnmag/issues/07/07/ServiceStation for more information on custom bindings). This is a more advanced extensibility point that you're not as likely to use as the others I've discussed.

Sharing State between Extensions

Once you begin employing multiple extensions within the dispatcher/proxy, you will need to learn how to share state across them. Thankfully, WCF provides extension objects that can be used to store user-defined state.

Where you store an extension object determines how long it will stick around. You can store it globally on the ServiceHost, on an InstanceContext, or on an OperationContext. Each of these classes provides an Extensions collection that manages objects derived from IExtension<T> (where T is ServiceHostBase, InstanceContext, or OperationContext, depending on the collection).

ServiceHost extension objects remain in memory for the lifetime of the ServiceHost while InstanceContext and OperationContext extension objects only remain in memory for the lifetime of the service instance or operation invocation. Your custom dispatcher/proxy extensions can use these collections to store (and look up) user-defined state throughout the pipeline.

Take-Aways

WCF provides a powerful extensibility architecture that allows for significant runtime customizations. It provides some key extensibility stages throughout the dispatcher/proxy for performing tasks such as parameter inspection, message formatting, message inspection, operation selection, and invocation. You write these custom extensions by implementing the appropriate extension interface, and then you can apply your extension to the dispatcher/proxy via a custom behavior.

There are some more advanced extensibility points made available on the dispatcher that I didn't have space to cover here. They deal with matters such as instancing, concurrency, addressing, and, of course, security. Although the built-in [ServiceBehavior] and [OperationBehavior] behaviors do supply most of what you'll need in these areas, you can write custom behaviors to extend those aspects of the runtime when they don't provide everything you need.

Be sure to download the sample code associated with this column so that you're able to study the examples in more detail and see them in action.

Send your questions and comments for Aaron to sstation@microsoft.com.

Aaron Skonnard is a cofounder of Pluralsight, a Microsoft .NET training provider. Aaron is the author of Pluralsight's Applied Web Services 2.0, Applied BizTalk Server 2006, and Introducing Windows Communication Foundation courses. Aaron has spent years developing courses, speaking at conferences, and teaching professional developers. Reach him at pluralsight.com/aaron.


Netting C++: EEK!—Time to Design the Mouse

Netting C++
EEK!—Time to Design the Mouse
Stanley B. Lippman

Contents
Where to Begin
Sentient Entities

I
n a series of recent columns, I have been working through the design of a software simulation of a mouse (EEK!) and its environment using the resources of the Microsoft® .NET Framework and C++/CLI, the revised C++ language binding to .NET that was introduced in Visual Studio® 2005. In my last column (msdn.microsoft.com/msdnmag/issues/07/10/nettingC), I looked at how EEK! can be initialized using an XML world description file and the facilities of the System::XML and System::Data namespaces. This time, I'll show you the beginnings of the actual Mouse class design.

Where to Begin

In EEK!, a mouse is a kind of sentient entity. All of us working in .NET recognize that an is-a relationship is represented using class inheritance, which provides the first type/subtype class relationship I'll create:

public ref class SentientEntity abstract { ... };
public ref class Mouse : SentientEntity { ... };

Unlike native C++, under .NET, a class is distinguished by its projected use within your design. Inheritance is supported only by reference classes, indicated in C++/CLI by the contextual keyword, ref, followed by the reserved keyword, class. (Unlike a reserved keyword, which a programmer may never use as an identifier, a contextual keyword is treated as a keyword only in certain program contexts; otherwise, the program is free to use it.) In the case of ref, it is treated as a keyword only if it occurs before the class keyword.

A reference class is a garbage-collected, heap-allocated entity manipulated by a named handle. The C++/CLI reserved keyword, gcnew, is used to allocate memory from the .NET managed heap and construct an instance of the reference class:

Mouse ^milkyWhite = gcnew Mouse;

The hat (^) is a C++/CLI token indicating that milkyWhite is a handle to a reference class object of type Mouse residing on the managed heap.

What this is-a relationship between the Mouse and SentientEntity class implies is that I need to design the abstract SentientEntity base class before I tackle the Mouse class. I'll begin by considering how a SentientEntity functions within the simulation.

Sentient Entities

All entities within the simulation, other than sentient entities, are fully described within the XML world file, as I described in my previous column. Sentient entities are different. The simulation manipulates them through a set of four functions:

  1. void onInit performs all the necessary initialization of the sentient entity with the simulated environment, such as binding the mouse's internal map to the initial world tile where the mouse is released into the simulation.
  2. void onTick performs the actual behavior of the sentient entity on each tick of the simulation. The simulator iterates across each sentient entity within the environment, invoking its onTick method in turn.
  3. void onTerminate performs all cleanup behavior at the close of the simulation, such as persisting the internal state of the mouse to a database.
  4. void onLog provides the sentient entity's logging service at the indicated level of detail. (Recall that default arguments are not supported in .NET.)

In order to generalize these methods, I am going to factor them into a separate interface class:

interface class ISimulation
{
    void onInit();
    void onTick();
    void onTerminate();
    void onLog( int levelOfDetail );
};

Interfaces represent a refinement of the original object-oriented model as supported by C++. They resolve the complexity associated with multiple inheritance. It is considerably more lightweight and simpler to manage. In EEK!'s design, any class—be it a rock or a rodent, a tree or a titmouse—that wishes to be an active participant in the simulation need only implement the ISimulation interface. In fact, that becomes the definition of agency within EEK!

Because the SentientEntity class does not represent an actual entity in the simulation, but represents the base class from which all actual entities are derived, it must be declared as abstract and declare the methods associated with ISimulation. In C++/CLI, you write that code as shown in Figure 1.

Figure 1 The SentientEntity Abstract Base Class (Close Figure 1)
public ref class SentientEntity abstract
    : ISimulation {
public:
    // ...
    virtual void onInit() abstract;
    virtual void onTick() abstract;
    virtual void onTerminate() abstract;
    virtual void onLog( int levelOfDetail ) abstract;
};

As you can see, the abstract keyword does double duty, both as a class and as a virtual method specifier. Within the class head, it serves to indicate that instances of the class may not be instantiated. SentientEntity is an incomplete class that serves as the root of a hierarchy of specialized derived classes. EEK! manipulates specific kinds of sentient entities, such as cats and mice, through this common abstract base class. It does not need to know the actual nature of its entities: that is the essential quality of a successful object-oriented design.

Within each function prototype, following the signature of the virtual method, the abstract keyword indicates that this is a pure virtual function. That is, there is no implementation associated with this method, and all concrete classes derived from SentientEntity, such as the Mouse, must provide their own implementations.

These uses of the keyword abstract highlight the intention of the class designer in a much clearer fashion than the equivalent ISO-C++ declaration that's pictured here:

class SentientEntity : public ISimulation {
public:
    // ...
    virtual void onInit() = 0;
    virtual void onTick()= 0;
    virtual void onTerminate()= 0;
    virtual void onLog( int levelOfDetail ) = 0;
};

In Stroustrup's original design of what came to be called Release 2.0 of C++ back in the late 1980s, he actually introduced a keyword called abstract in the final weeks prior to release of cfront 2.0 (Stroustrup's C++ compiler on which I worked). However, it was decided that it was too close to the release date to establish a reserved keyword that could potentially break a great deal of code.

A non-contextual keyword, recall, functions as a reserved word and therefore may not be used within the client's program. This is why keywords such as ref and value in C++/CLI were made contextual rather than reserve keywords.

For some reason, the original ISO-C++ standard chose not to reintroduce the abstract keyword. I hope the revised ISO-C++ standard currently underway revisits this issue. Its uses within C++/CLI clearly demonstrate its elegance over the ISO-C++ syntax for the specification of a pure virtual function.

The last methods necessary to complete the implementation of SentientEntity are a constructor and destructor, together with a data member to store a handle to the constructor's one argument, and a property declaration to retrieve it (see Figure 2).

Figure 2 SentientEntity Constructor (Close Figure 2)
public ref class SentientEntity abstract
    : ISimulation {
public:
    ~SentientEntity();
    property Genome^ genome {
        Genome^ get(){ return _genome; }
    }
    // ... ISimulation methods from Figure 1
protected:
    SentientEntity( Genome^ );
private:
    Genome^ _genome;
};

A fundamental design pattern of an object-oriented class hierarchy is the declaration of its root abstract base class constructor as protected (rather than public) and its destructor as both public and virtual.

Because I manipulate the various sentient entities within EEK! indirectly through a SentientEntity handle, to free up the resources associated with that entity the destructor needs to be able to be invoked within the general program. This is why it needs to be public.

Because my handle refers not to an instance of a SentientEntity but to an instance of a concrete derived class, in order to correctly invoke that unknown derived class destructor, the destructor must be virtual. Of course, the astute reader will object, "you do not declare it as virtual in Figure 2."

True, but I did that to make the following point: because it is so necessary for a ref class destructor to be virtual, and because every ref class is implicitly derived from Object, whose destructor is virtual, all ref class destructors within C++/CLI are always treated as virtual. You cannot have a non-virtual ref class destructor.

There are other things that need to be said about destructors with regard to both .NET and C++/CLI, but I will defer those discussions until I design the derived Mouse class.

Although the only handles I manipulate within EEK! are SentientEntities, I am not actually permitted to create an instance of a SentientEntity since it is an abstract class:

// illegal: SentientEntity is abstract
Genome^ getGenome( System::String^ );
SentientEntity^ mw = gcnew SentientEntity( getGenome( "Mouse" ));

Therefore, to prevent such an illegal call, I declare the constructor to be protected. A protected member, recall, is inaccessible to the general program but fully accessible to its derived classes. The only place where the SentientEntity constructor is invoked is within the initialization list of its derived class constructors, such as that of the derived Mouse class.

A private member of a base class is not directly accessible to its derived class. I make the genome member private so that all access is forced to go through the (hopefully) inlined get property (see Figure 2). This way, if I want to instrument its access, it's nicely encapsulated and all the code can go in one easily identified place.

In C++/CLI, there is a convenient shorthand notation for properties that implement get as a field return and set as a field value assignment; it's called a trivial property, and it's shown here:

public value class SmallInt {
public:
    // trivial property
    property int Value;
};

Internally, this is expanded to a non-accessible backing store field of the property type together with the simple get/set accessor pair, as seen here:

public value class SmallInt {
    int backingStore;
public:
    // pseudo-internal expansion of trivial property
    property int Value{
        int get{ return backingStore; }
        void set( int value ){ backingStore = value; }
    }
};

However, it is not possible to restrict a trivial property to generating only a get accessor. (To support that, it would be necessary to extend the declaration syntax to allow for an initial value to be specified. Currently, there is no way to indicate a value to the compiler with which to initialize the backing store.) Therefore, you would only use a trivial property if a set accessor were safe for your application. In this case, it is not.

Finally, there is the question of the relationship between the constructor and onInit, and the destructor and onTerminate. Recall that both onInit and onTerminate are virtual functions. The question I wish to address is whether or not it is a good idea for onInit to be invoked from within the constructor.

When Stroustrup first designed C++, the invocation of a virtual function within a base class constructor caused the derived class instance to be invoked. After all, this reflected the normal semantics of virtual function behavior. In addition, it is trivial to implement. What he missed, at first, is that class initialization does not reflect normal semantics. If it did, we could have virtual constructors.

The problem is that in C++, classes are constructed from the bottom up. When the SentientEntity constructor is executing, the Mouse constructor has not yet been invoked. Therefore, invoking the Mouse's onInit within the SentientEntity constructor results in undefined behavior. In Release 2.0, Stroustrup redefined the language behavior: a virtual function invoked within a base class constructor (or destructor) calls the instance associated with the base class rather than the derived class.

In contrast, in .NET, the virtual instance invoked is that of the derived class. This means that if a virtual method from a base class is invoked from a constructor of a derived class, it's possible that the virtual method could execute before the instance has been fully constructed, and thus before state relied on by the virtual method has been fully initialized. For that reason, the CLR team within Microsoft recommends that we never invoke a virtual function within either a constructor.

The other distinction between onInit and the constructor (or onTerminate and the destructor) is that the two ISimulation methods initialize (or terminate) the class instance to the simulation while the constructor (or destructor) sets up or tears down the internal state of the class instance.

The benefit of this loosely coupled design strategy is that we can implement the simulation engine independently of the Sentient Entities that it manipulates. If we had a group of developers, this would represent a functional partition of the design space. The UI interface would represent a third functional partition.

Send your questions and comments for Stanley to purecpp@microsoft.com.

Stanley B. Lippman is best known for working on C++ in the 1980s and 1990s with its inventor, Bjarne Stroustrup, at Bell Laboratories, and for his introductory textbook, C++ Primer (Addison-Wesley, 2005), now in its fourth edition. Stan also worked with the Visual C++ team at Microsoft on the invention of C++/CLI, which is the focus of this column.


.NET Matters: IFileOperation in Windows Vista

.NET Matters
IFileOperation in Windows Vista
Stephen Toub

Code download available at: NetMatters2007_12.exe (160KB)

Q I have a bunch of file operations I'd like to execute as a batch, and I'd like to get the standard Windows® progress UI for my operations. I know I could use classes from the System.IO namespace to do all of the file operations one after the other, but then I would need to create my own progress UI, which is way more work than I wish to undertake. I noticed that Windows Vista® includes a new IFileOperations interface, but none of the samples I've seen demonstrate how to use this from managed code. How can it be done?

A Windows Vista does indeed include a new copy engine that supports exactly what you're looking to do. However, it's possible that previously existing functionality may meet your needs. For example, if you want to copy, move, rename, or delete an individual file or directory, you can take advantage of SHFileOperation (exposed from shell32.dll), which is already wrapped by the Visual Basic® runtime. If you're using Visual Basic 2005, you can simply use functionality from the My namespace, for example:

My.Computer.FileSystem.CopyDirectory(
    sourcePath, destinationPath, UIOption.AllDialogs)

Accomplishing the same thing in C# involves only a little more work, adding a reference to Microsoft.VisualBasic.dll (from the Microsoft® .NET Framework installation directory) and using code such as the following:

using Microsoft.VisualBasic.FileIO;
...
FileSystem.CopyDirectory(
    sourcePath, destinationPath, UIOption.AllDialogs);

When run, this will result in the same progress UI you'd see if you were doing the same file operations from Windows Explorer. In fact, when running on Windows Vista, you automatically get the new Window Vista progress UI, as shown in Figure 1.

Figure 1 Windows Vista Progress Dialog
Figure 1 Windows Vista Progress Dialog

However, SHFileOperation and the wrappers provided by .NET are limited to operating on one file system object (which could be a directory) and one operation (copy, move, rename, or delete) per call; you can't mix and match or operate on disparate files within the same operation. That's where IFileOperation comes in. IFileOperation is the successor to SHFileOperation and provides a wealth of new functionality, including the ability to batch a bunch of operations, which can be a mix of copies, renames, moves, deletes, and even the creation of new items. It can also operate on any set of files, not just those in the same directory. These are just a few of the cool features provided through IFileOperation; for a more in-depth look, see shellrevealed.com/blogs/shellblog/archive/2007/04/16/IFileOperation-_1320_-Part-1_3A00_-Introduction.aspx.

Unfortunately, as you point out, at the time of this writing there are currently no managed wrappers provided by Microsoft to expose this functionality from managed code. As such, I've built one. You can download the code from the MSDN® Magazine Web site, and I'll spend the rest of this answer walking through how to use it and how it was implemented. Note that my wrapper is not meant to expose all of IFileOperation's functionality, nor is it meant to completely abstract away all of the underlying bits and bytes and provide a perfect .NET wrapper that follows all of the standard .NET design guidelines; it's simply meant to easily expose IFileOperation's functionality so you can get up and running with the basics in your Windows Vista-targeting applications today.

Let's begin with the IFileOperation interface itself, for which I've shown my managed definition in Figure 2 (this is based on the IDL from the ShObjIdl.idl file in the Windows SDK). The interface is designed such that you schedule with it all of the operations you want performed, and then when you're ready, you tell it to execute them all. At that point, it'll begin execution, and if it senses that the operations may take a noticeable amount of time, it'll present the standard Windows Vista progress dialog, complete with descriptions about everything it is doing. To schedule actions, you use the IFileOperation methods CopyItem, MoveItem, RenameItem, DeleteItem, and NewItem. (The interface also exposes plural versions of those methods, but since their behavior can be simulated as necessary with the singular versions and because they're more difficult to work with from managed code, I won't be discussing them further.) Each of these methods merely expresses your intent for the operation to happen, but the actions aren't actually executed at that time. Once all of the operations have been scheduled, you use the PerformOperations method to run all of the operations previously scheduled. Details about how the operations are performed can be configured with several methods (such as SetOwnerWindow and SetOperationFlags) on IFileOperation before calling PerformOperations. IFileOperation also supports a nice callback notification system, whereby you can register an IFileOperationProgressSink and receive callbacks for all sorts of events, including pre- and post-operation notification, progress updates, and the like. You can register a sink using IFileOperation's Advise method (unregistering it using Unadvise when you're done) that will cause the sink to be used for all of the operations performed, or you can register a sink for individual operations using the last parameter to each of the singular operation methods. My managed definition for IFileOperationProgressSink is shown in Figure 3.(This is also based on the ShObjIdl.idl file from the Windows SDK.)

Figure 3 IFileOperationProgressSink (Close Figure 3)
[ComImport]
[Guid("04b0f1a7-9490-44bc-96e1-4296a31252e2")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IFileOperationProgressSink
{
    void StartOperations();
    void FinishOperations(uint hrResult);

    void PreRenameItem(uint dwFlags, IShellItem psiItem,
        [MarshalAs(UnmanagedType.LPWStr)] string pszNewName);
    void PostRenameItem(uint dwFlags, IShellItem psiItem,
        [MarshalAs(UnmanagedType.LPWStr)] string pszNewName,
        uint hrRename, IShellItem psiNewlyCreated);

    void PreMoveItem(uint dwFlags, IShellItem psiItem, IShellItem
        psiDestinationFolder,
        [MarshalAs(UnmanagedType.LPWStr)] string pszNewName);
    void PostMoveItem(uint dwFlags, IShellItem psiItem,
        IShellItem psiDestinationFolder,
        [MarshalAs(UnmanagedType.LPWStr)] string pszNewName,
        uint hrMove, IShellItem psiNewlyCreated);

    void PreCopyItem(uint dwFlags, IShellItem psiItem,
        IShellItem psiDestinationFolder,
        [MarshalAs(UnmanagedType.LPWStr)] string pszNewName);
    void PostCopyItem(uint dwFlags, IShellItem psiItem,
        IShellItem psiDestinationFolder,
        [MarshalAs(UnmanagedType.LPWStr)] string pszNewName,
        uint hrCopy, IShellItem psiNewlyCreated);

    void PreDeleteItem(uint dwFlags, IShellItem psiItem);
    void PostDeleteItem(uint dwFlags, IShellItem psiItem, uint hrDelete,
        IShellItem psiNewlyCreated);

    void PreNewItem(uint dwFlags, IShellItem psiDestinationFolder,
        [MarshalAs(UnmanagedType.LPWStr)] string pszNewName);
    void PostNewItem(uint dwFlags, IShellItem psiDestinationFolder,
        [MarshalAs(UnmanagedType.LPWStr)] string pszNewName,
        [MarshalAs(UnmanagedType.LPWStr)] string pszTemplateName,
        uint dwFileAttributes, uint hrNew, IShellItem psiNewItem);

    void UpdateProgress(uint iWorkTotal, uint iWorkSoFar);

    void ResetTimer();
    void PauseTimer();
    void ResumeTimer();
}
Figure 2 IFileOperation (Close Figure 2)
[ComImport]
[Guid("947aab5f-0a5c-4c13-b4d6-4bf7836fc9f8")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
internal interface IFileOperation
{
    uint Advise(IFileOperationProgressSink pfops);
    void Unadvise(uint dwCookie);

    void SetOperationFlags(FileOperationFlags dwOperationFlags);
    void SetProgressMessage(
        [MarshalAs(UnmanagedType.LPWStr)] string pszMessage);
    void SetProgressDialog(
        [MarshalAs(UnmanagedType.Interface)] object popd);
    void SetProperties(
        [MarshalAs(UnmanagedType.Interface)] object pproparray);
    void SetOwnerWindow(uint hwndParent);

    void ApplyPropertiesToItem(IShellItem psiItem);
    void ApplyPropertiesToItems(
        [MarshalAs(UnmanagedType.Interface)] object punkItems);

    void RenameItem(IShellItem psiItem,
        [MarshalAs(UnmanagedType.LPWStr)] string pszNewName,
        IFileOperationProgressSink pfopsItem);
    void RenameItems(
        [MarshalAs(UnmanagedType.Interface)] object pUnkItems,
        [MarshalAs(UnmanagedType.LPWStr)] string pszNewName);

    void MoveItem(
        IShellItem psiItem,
        IShellItem psiDestinationFolder,
        [MarshalAs(UnmanagedType.LPWStr)] string pszNewName,
        IFileOperationProgressSink pfopsItem);
    void MoveItems(
        [MarshalAs(UnmanagedType.Interface)] object punkItems,
        IShellItem psiDestinationFolder);

    void CopyItem(
        IShellItem psiItem,
        IShellItem psiDestinationFolder,
        [MarshalAs(UnmanagedType.LPWStr)] string pszCopyName,
        IFileOperationProgressSink pfopsItem);
    void CopyItems(
        [MarshalAs(UnmanagedType.Interface)] object punkItems,
        IShellItem psiDestinationFolder);

    void DeleteItem(
        IShellItem psiItem,
        IFileOperationProgressSink pfopsItem);
    void DeleteItems(
        [MarshalAs(UnmanagedType.Interface)] object punkItems);

    uint NewItem(
        IShellItem psiDestinationFolder,
        FileAttributes dwFileAttributes,
        [MarshalAs(UnmanagedType.LPWStr)] string pszName,
        [MarshalAs(UnmanagedType.LPWStr)] string pszTemplateName,
        IFileOperationProgressSink pfopsItem);

    void PerformOperations();

    [return: MarshalAs(UnmanagedType.Bool)]
    bool GetAnyOperationsAborted();
}

Before I dive into how to use these interfaces directly from managed code, I'll first show you the wrapper I've implemented around them. The outline for my FileOperation class is shown in Figure 4. As an example of using this class, let's say I have a directory C:\test that contains two files and a directory: test1.txt, test2.txt, and SampleDir. I want to copy the first file to copy.text, delete the second file, and move the directory to NewDir; I can accomplish that with code like the following:

Figure 4 Managed FileOperation Wrapper (Close Figure 4)
class FileOperation : IDisposable
{
    public FileOperation() : this(null);
    public FileOperation(FileOperationProgressSink callbackSink) :
        this(callbackSink, null);
    public FileOperation(
        FileOperationProgressSink callbackSink, IWin32Window owner);

    public void CopyItem(
        string source, string destination, string newName);

    public void MoveItem(
        string source, string destination, string newName);

    public void RenameItem(string source, string newName);

    public void DeleteItem(string source);

    public void NewItem(
        string folderName, string name, FileAttributes attrs);

    public void PerformOperations();

    public void Dispose();
}

using(FileOperation fileOp = new FileOperation())
{
    fileOp.CopyItem(@"C:\test\test1.txt", @"C:\test", @"copy.txt");
    fileOp.DeleteItem(@"C:\test\test2.txt");
    fileOp.MoveItem(@"C:\test\SampleDir", @"C:\test", @"NewDir");
    fileOp.PerformOperations();
}

If I want to receive callback notifications for various actions, I can register a progress sink through FileOperation's constructor. (The FileOperationProgressSink class used here is a default implementation of IFileOperationProgressSink I've provided that traces all events, but more on that shortly.)

using(FileOperation fileOp =
    new FileOperation(new FileOperationProgressSink()))
{
    ...
}

And if this is running in a Windows Forms application and I want to associate the progress window with my current Form, I can use code like this:

Form form = this;
using(FileOperation fileOp = new FileOperation(null, form))
{
    ...
}

Easy, right? Let's take a look at how this is implemented. IFileOperation is a COM interface, implemented in Windows Vista by the COM component identified by CLSID_FileOperation, and we can create an instance of one with code like the following:

private static readonly Guid CLSID_FileOperation =
    new Guid("3ad05575-8857-4850-9277-11b85bdb8e09");
private static readonly Type FileOperationType =
    Type.GetTypeFromCLSID(CLSID_FileOperation);

public FileOperation(
    FileOperationProgressSink callbackSink, IWin32Window owner)
{
    _fileOperation = (IFileOperation)
        Activator.CreateInstance(FileOperationType);
    ...
}

The created IFileOperation stored in the _fileOperation member variable can then be used for all of the operations in the class. For example, the CopyItem method is implemented as follows:

public void CopyItem(string source, string destination, string newName)
{
    ThrowIfDisposed();
    using (ComReleaser<IShellItem> sourceItem =
        CreateShellItem(source))
    using (ComReleaser<IShellItem> destinationItem =
        CreateShellItem(destination))
    {
        _fileOperation.CopyItem(
            sourceItem.Item, destinationItem.Item, newName, null);
    }
}

Ignoring ThrowIfDisposed and ComReleaser for a moment, CopyItem first creates an IShellItem instance for the source path and an IShellItem for the destination folder, and then passes these (along with the new name for the item) to the IFileOperation's CopyItem method. All of the operation methods on FileOperation are implemented just like this, and PerformOperations simply delegates to the same method on the underlying IFileOperation:

public void PerformOperations()
{
    ThrowIfDisposed();
    _fileOperation.PerformOperations();
}

The ComReleaser class shown in the CopyItem implementation is a little helper class that makes it easy to dispose of a COM object using Marshal.FinalReleaseComObject (the objects would be cleaned up eventually by the garbage collector, but, given the frequency with which these may be created, I've opted to ensure they're disposed of as soon as they're no longer being used). The type's constructor accepts a COM object and caches it in a private member variable; its Dispose method then releases that object. It also exposes an Item property that returns the object, making it easy to use in situations such as the one demonstrated. ComReleaser is shown in Figure 5.

Figure 5 Helper Class for Releasing COM Objects (Close Figure 5)
class ComReleaser<T> : IDisposable where T : class
{
    private T _obj;

    public ComReleaser(T obj)
    {
        if (obj == null) throw new ArgumentNullException("obj");
        if (!obj.GetType().IsCOMObject)
            throw new ArgumentOutOfRangeException("obj");
        _obj = obj;
    }

    public T Item { get { return _obj; } }

    public void Dispose()
    {
        if (_obj != null)
        {
            Marshal.FinalReleaseComObject(_obj);
            _obj = null;
        }
    }
}

In order to get a ComReleaser<IShellItem> for a given file or directory path, I take advantage of the SHCreateItemFromParsingName function exported in Windows Vista from shell32.dll. You can provide this function with the path to the file system object and have it provide you back the appropriate IShellItem; my CreateShellItem method then simply wraps this IShellItem with a ComReleaser<IShellItem>:

private static ComReleaser<IShellItem> CreateShellItem(string path)
{
    return new ComReleaser<IShellItem>((IShellItem)
        SHCreateItemFromParsingName(path, null, ref _shellItemGuid));
}

[DllImport("shell32.dll", SetLastError=true,
           CharSet=CharSet.Unicode, PreserveSig=false)]
[return: MarshalAs(UnmanagedType.Interface)]
private static extern object SHCreateItemFromParsingName(
    [MarshalAs(UnmanagedType.LPWStr)] string pszPath,
    IBindCtx pbc, ref Guid riid);

All that leaves now is the FileOperationProgressSink implementation. The FileOperationProgressSink class implements the IFileOperationProgressSink interface shown in Figure 3:

public class FileOperationProgressSink : IFileOperationProgressSink
{
   ...
}

It does so by providing virtual method implementations for each of the interface's methods. That way, if you want to provide custom behavior to respond to events, you only need to derive from FileOperationProgressSink. As an example, PreRenameItem is the method called before an item is renamed:

public virtual void PreRenameItem(
    uint dwFlags, IShellItem psiItem, string pszNewName)
{
#if DEBUG
    string message = string.Format("Renaming {0} to {1}",
        item.GetDisplayName(SIGDN.SIGDN_NORMALDISPLAY), pszNewName);
    Debug.WriteLine(message);
#endif
}

However, you can override it in your own progress sink in whatever manner you choose:

public class MyProgressSink : FileOperationProgressSink
{
    public virtual void PreRenameItem(
        uint dwFlags, IShellItem psiItem, string pszNewName)
    {
        Console.WriteLine("Goodbye, {0}", item.GetDisplayName(0));
    }
}

Then, when you instantiate your FileOperation class, you just provide an instance of your sink:

using(FileOperation fileOp = new FileOperation(new MyProgressSink()))
{
    ...
}

All in all, the Windows Vista team did a very nice job of designing the IFileOperation API in a way that makes it easily usable from managed code. Note, however, that there is a lot of functionality I haven't exposed here. For example, IFileOperation in Windows Vista not only supports per operation progress sinks (which I haven't exposed), but it also supports providing multiple sinks for all operations by calling IFileOperation::Advise multiple times, once per sink to be registered. Moreover, IFileOperation supports a bunch of operation flags that I haven't exposed for controlling things like directory recursion, what and how dialogs are displayed, how collisions are handled, and so on. It also exposes additional operations I didn't provide wrappers for, such as ones to modify item properties (through the SetProperties, ApplyPropertiesToItem, and ApplyPropertiesToItems methods). Additionally, IFileOperation provides detailed error information about each operation performed. If you need any of that functionality, it should be a straightforward process for you to download the code I've provided from the MSDN Magazine Web site and modify it to suit your needs.

One final note about using FileOperation: IFileOperation and its supporting types (and thus my managed FileOperation class) can only be used in a single-threaded apartment (STA). If you're using this in a Windows Forms application, this shouldn't be an issue, since much of Windows Forms itself requires threads to be STA. If you must run in a multithreaded apartment (MTA) situation, you can still use SHFileOperation. Alternatively, you can spin up a new thread with the appropriate threading model to execute the IFileOperation code; for an example of that, see the ApartmentStateSwitcher class from my December 2004 .NET Matters column at msdn.microsoft.com/msdnmag/issues/04/12/NETMatters.

Send your questions and comments for Stephen to netqa@microsoft.com.

Stephen Toub is a Senior Program Manager on the Parallel Computing Platform team at Microsoft. He is also a Contributing Editor for MSDN Magazine.


Windows with C++: Windows Template Library 8.0

Windows with C++
Windows Template Library 8.0
Kenny Kerr

Code download available at: WindowsWithC++2007_12.exe (175KB)
Contents
Task Dialogs
Aero Wizards
New File Dialogs
Other WTL Features to Explore

T
he developers responsible for the Windows® Template Library (WTL) recently released a new version of this excellent library that fully supports virtually all of the new user interface features and enhancements introduced with Windows Vista®. This month I'll be highlighting some of the new features in this latest release. In case you are not familiar with WTL and how it fits into the Visual C++® development landscape, I will start with a brief introduction.

The Visual C++ team developed the Active Template Library (ATL) to allow COM clients and servers to be easily created while producing the smallest and fastest code possible. Unlike MFC, which is focused on developing user interface applications and which only later added support for COM, ATL was designed from the start to enable COM development and it does this well. It supported only rudimentary user interface development but the classes it provided were incredibly useful as lightweight building blocks and base classes for a more sophisticated user interface library.

WTL extends ATL with a very rich set of class templates for building applications as simple or as complex as you can imagine. WTL is not included with Visual C++ but is made available both on the SourceForge Web site at sourceforge.net/projects/wtl and through the Microsoft Download Center.

WTL 8.0 was primarily developed with a focus on Windows Vista UI support so I will cover some topics that I've covered in previous columns and articles. But the focus this month is on how the new WTL improvements will accelerate adoption of these new features and ensure that you have even less code you need to write yourself! The download for this column, available from the MSDN® Magazine Web site, provides examples of most of the features described here.

Task Dialogs

Task dialogs are one of my favorite new features in Windows Vista, providing a powerful API for developing both simple and sophisticated dialogs with very little effort.

The TaskDialogIndirect function and the TASKDIALOGCONFIG structure can be quite daunting and well-deserving of some help from C++ to simplify its use. WTL provides support for task dialogs via the CTaskDialogImpl base class template. Internally the CTaskDialogImpl class uses the CTaskDialogConfig class to wrap the TASKDIALOGCONFIG structure. This is exposed through the public m_tdc variable should you need to access it directly, but you're unlikely to need it as the CTaskDialogImpl class does an excellent job of providing suitable member functions that wrap it up for you. The CTaskDialog concrete class that derives from CTaskDialogImpl is also provided for very simple scenarios, but it is usually best simply to derive a class from CTaskDialogImpl to implement your task dialog.

Figure 1 illustrates a number of task dialog features and how you can make use of them from WTL. The example sets the window title, main instruction and content text captions. Task dialogs support a number of additional text captions, though they require different mechanisms to set them depending on whether the task dialog has been constructed or not. Unfortunately, the CTaskDialogImpl class does not abstract these differences and this can make updating these text captions a bit confusing. Figure 2 summarizes how to set the various text captions using the CTaskDialogImpl class depending on whether it is set before or after the task dialog is constructed. Typically you use the pre-construction method in your task dialog's constructor and the post-construction method in various event handlers to update the captions.

Figure 2 Updating Task Dialog Captions (Close Figure 2)

Text Caption | Pre-Construction | Post-Construction
Window title | SetWindowTitle() | ::SetWindowText()
Main instruction | SetMainInstructionText() | SetElementText(TDE_MAIN_INSTRUCTION, ...
Content | SetContentText() | SetElementText(TDE_CONTENT, ...
Footer | SetFooterText() | SetElementText(TDE_FOOTER, ...
Expanded information | SetExpandedInformationText() | SetElementText(TDE_EXPANDED_INFORMATION, ...
Expanded control text | SetExpandedControlText() | N/A
Collapsed control text | SetCollapsedControlText() | N/A
Verification | SetVerificationText() | N/A
Figure 1 Task Dialog Example (Close Figure 1)
enum
{
    Button_Save = 101,
    Button_DontSave,
};

class SampleDialog : public CTaskDialogImpl<SampleDialog>
{
public:

    SampleDialog()
    {
        SetWindowTitle(L"Title");
        SetMainInstructionText(L"Main instruction");
        SetContentText(L"Content");

        ModifyFlags(0, TDF_ALLOW_DIALOG_CANCELLATION |
                       TDF_USE_COMMAND_LINKS);

        static TASKDIALOG_BUTTON buttons[] =
        {
            { Button_Save, L"&Save", },
            { Button_DontSave, L"Do&n't save", },
        };

        SetButtons(buttons,
                   _countof(buttons));
    }
};
Figure 1 Task Dialog Example
Figure 1 Task Dialog Example

The code in Figure 1 also makes use of the ModifyFlags method. Various flags are documented in the Windows SDK for customizing the task dialog's appearance. The ModifyFlags method's first parameter indicates the flags to remove and the second parameter indicates the flags to add. The SetButtons method is also used to specify the buttons that will be displayed on the task dialog. The example declares the array of TASKDIALOG_BUTTON structures static to ensure that its storage lives beyond the constructor. This is critical since the button definitions are referenced later on when the DoModal method is called that finally passes all of the information to the TaskDialogIndirect function to actually display the task dialog.

Calling the DoModal method is straightforward and can optionally provide the identifier of the button the user selected. Figure 3 shows an example of how you might display the task dialog from Figure 1 and handle the various button identifiers.

Figure 3 Using DoModal to Call the Dialog Function (Close Figure 3)
SampleDialog dialog;
int selectedButtonId = 0;

COM_VERIFY(dialog.DoModal(
    0,                   // parent window
    &selectedButtonId));

switch (selectedButtonId)
{
    case Button_Save:
    {
        // TODO: save
        break;
    }
    case Button_DontSave:
    {
        // TODO: don't save
        break;
    }
    case IDCANCEL:
    {
        // TODO: cancel
        break;
    }
    default:
    {
        ASSERT(false);
    }
}

Alternatively, you can use the task dialog's callback facility that the CTaskDialogImpl class exposes through a set of public methods that you can optionally implement in your derived class to handle different events. For example, in order to handle button clicks within your dialog class, you can add the public method shown in Figure 4.

Figure 4 Handling Click Events (Close Figure 4)
class SampleDialog : public CTaskDialogImpl<SampleDialog>
{
public:

...

    bool OnButtonClicked(int buttonId)
    {
        bool closeDialog = false;

        switch (buttonId)
        {
            // Do something here
        }

        return !closeDialog;
    }
};

Handling this event is especially useful when you want to handle a particular button click but don't necessarily want the task dialog to close. There are also quite a few other events that can be handled in a similar way. Figure 5 shows a list of overrides that the CTaskDialogImpl class provides. Remember that these are ATL-style overrides that are evaluated at compile-time, so they do not require virtual function calls.

Figure 5 CTaskDialogImpl Overrides (Close Figure 5)

Method | Description
void OnDialogConstructed() | Task dialog created but not yet visible. Window handle is valid.
bool OnButtonClicked(int buttonId) | Button or command link clicked. Return true to prevent dialog from closing.
void OnRadioButtonClicked(int buttonId) | Radio buttons clicked.
void OnHyperlinkClicked(PCWSTR url) | Hyperlink clicked.
void OnExpandoButtonClicked(bool expanded) | Expando button clicked.
void OnVerificationClicked(bool checked) | Verification checkbox was checked or cleared.
void OnHelp() | F1 key pressed.
bool OnTimer(DWORD milliseconds) | Sent every 200 milliseconds. Parameter indicates milliseconds since timer started or reset.
void OnNavigated() | Navigation has occurred.
void OnDestroyed() | Task dialog destroyed. Window handle no longer valid.

Task dialogs provide many more features. The sample code for this column provides a comprehensive set of examples illustrating virtually all of the task dialog's capabilities with the help of WTL. For a detailed look at the extensive capabilities of the task dialog API, read my blog posting "Windows Vista for Developers–Part 2–Task Dialogs in Depth" (weblogs.asp.net/kennykerr/archive/2006/07/18/Windows-Vista-for-Developers-_1320_-Part-2-_1320_-Task-Dialogs-in-Depth.aspx).

Aero Wizards

Windows Vista introduces a new, modern look for the common wizard. As with the older wizard standard, AeroTM wizards are just a variation on the property sheet API. This makes it quite simple to update your applications to take advantage of the new user interface. WTL makes it even easier by providing a set of class templates that wrap all of the new functionality for you.

Simply derive your wizard window class from the CAeroWizardFrameImpl class template and it will take care of applying the appropriate flags to instruct Windows to use the new Aero user interface. CAeroWizardFrameImpl derives from CPropertySheetImpl, which provides all of the common functionality. The wizard pages simply derive from the CAeroWizardPageImpl class, which provides methods to control the new Aero wizard buttons. Figure 6 provides a simple yet complete example of an Aero wizard with a single page.

Figure 6 Simple Aero Wizard (Close Figure 6)
class SamplePage :
    public CAeroWizardPageImpl<SamplePage>
{
public:

    BEGIN_MSG_MAP(SamplePage)
        MESSAGE_HANDLER_EX(WM_INITDIALOG, OnInitDialog)
        CHAIN_MSG_MAP(__super)
    END_MSG_MAP()

    enum { IDD = IDD_SAMPLE_PAGE };

    SamplePage()
    {
        VERIFY(m_title.LoadString(IDS_PAGE_TITLE));
        SetHeaderTitle(m_title);
    }

private:

    LRESULT OnInitDialog(UINT /*message*/,
                         WPARAM /*wParam*/,
                         LPARAM /*lParam*/)
    {
        ShowWizardButtons(
            PSWIZB_BACK | PSWIZB_NEXT | PSWIZB_FINISH | PSWIZB_CANCEL,
            PSWIZB_BACK | PSWIZB_NEXT | PSWIZB_CANCEL);

        EnableWizardButtons(PSWIZB_BACK,
                            0);

        SetButtonText(PSWIZB_NEXT,
                      L"&Run");

        return TRUE;
    }

    CString m_title;
};

class SampleWizard :
    public CAeroWizardFrameImpl<SampleWizard>
{
public:

    BEGIN_MSG_MAP(SampleWizard)
        CHAIN_MSG_MAP(__super)
    END_MSG_MAP()

    SampleWizard() :
        CAeroWizardFrameImpl<SampleWizard>(IDS_WIZARD_TITLE)
    {
        VERIFY(AddPage(m_page));
    }

private:

    SamplePage m_page;
};
Figure 6 Simple Aero Wizard
Figure 6 Simple Aero Wizard

As you can see, there isn't much new to talk about and moving to Aero wizards is pretty painless. The main task will be to manage the standard buttons that adorn an Aero wizard. The example demonstrates the ShowWizardButtons, EnableWizardButtons, and SetButtonText methods. ShowWizardButtons shows or hides one or more buttons. The first parameter indicates the buttons to operate on and the second parameter indicates which of those buttons to show. Similarly, the first parameter of EnableWizardButtons indicates the buttons to operate on and the second parameter indicates which of those buttons to enable. The rest will be disabled. Finally, SetButtonText updates the text captions for the Next, Finish, and Cancel buttons.

New File Dialogs

The new file dialogs provided by Windows Vista present a fresh appearance consistent with the new Windows Explorer user interface and are a welcome replacement for the aging GetOpenFileName and GetSaveFileName functions that have been in use for years. The new file dialogs are exposed through a set of COM interfaces so they are not particularly hard to use directly. However, it can get tedious to write the same boilerplate COM code every time you need to pop up a file dialog, especially if you need to respond to the various file dialog events that require you to provide COM interface implementations. Fortunately, WTL does a good job of wrapping it all up in an easy-to-use set of classes. Figure 7 provides an example of a file open dialog implementation.

Figure 7 File Open Dialog (Close Figure 7)
class SampleOpenDialog :
    public CShellFileOpenDialogImpl<SampleOpenDialog>
{
public:

    SampleOpenDialog()
    {
        COMDLG_FILTERSPEC fileTypes[] =
        {
            { L"Text Documents", L"*.txt" },
            { L"All Files", L"*.*" }
        };

        COM_VERIFY(GetPtr()->SetFileTypes(_countof(fileTypes),
                                          fileTypes));
    }
};
Figure 7 File Open Dialog
Figure 7 File Open Dialog(Click the image for a smaller view)
Figure 7 File Open Dialog
Figure 7 File Open Dialog (Click the image for a larger view)

The COMDLG_FILTERSPEC structure provides a much more manageable format for specifying the file types compared to the multi-string approach used by GetOpenFileName. An array of these structures is then passed to the SetFileTypes method to specify the file types the dialog will provide filtering for. The GetPtr method returns the dialog's IFileOpenDialog interface pointer used to customize the dialog appearance and behavior.

The new file dialogs also let you respond to a variety of events. However, you need to implement the IFileDialogEvents interface to receive these events. Fortunately, WTL implements this for you via the CShellFileDialogImpl base class template so you only need to provide the handlers for the events you are interested in.

Figure 8 presents a list of overrides that the CShellFileDialogImpl class provides. You might, for example, want to validate that the selected file is acceptable before closing the file open dialog. This is easily achieved by the OnFileOk event called just before the dialog is about to return the result, by preventing the dialog from closing should you decide that the selected file is not acceptable. The example in Figure 9 illustrates this technique.

Figure 9 Handling an OnFileOk Event (Close Figure 9)
class SampleOpenDialog :
    public CShellFileOpenDialogImpl<SampleOpenDialog>
{
public:

    // ...

    HRESULT OnFileOk()
    {
        CString filePath;
        COM_VERIFY(GetFilePath(filePath));

        // Validate file here...
        bool acceptable = ...

        return acceptable ? S_OK : S_FALSE;
    }
};
Figure 8 CShellFileDialogImpl Overrides (Close Figure 8)

Method | Description
HRESULT OnFileOk() | User made a selection.
HRESULT OnFolderChanging(IShellItem*) | Enables preventing folder navigations.
HRESULT OnFolderChange() | Called after OnFolderChanging indicating user navigated to new folder.
HRESULT OnSelectionChange() | User changes selection in dialog's view.
HRESULT OnShareViolation(IShellItem*, FDE_SHAREVIOLATION_RESPONSE*) | Enables handling of sharing violations.
HRESULT OnOverwrite(IShellItem*, FDE_OVERWRITE_RESPONSE*) | User chose to overwrite a file.

Finally, the file open dialog can be used as follows:

SampleOpenDialog dialog;

if (IDOK == dialog.DoModal())
{
    CString filePath;
    COM_VERIFY(dialog.GetFilePath(filePath));

    // Use file here...
}

The new file dialogs provide many more features through the various COM interfaces. For a detailed look at the capabilities offered by the new file dialogs API, read my blog post "Windows Vista for Developers–Part 6–The New File Dialogs" (weblogs.asp.net/kennykerr/archive/2006/11/10/Windows-Vista-for-Developers-_1320_-Part-6-_1320_-The-New-File-Dialogs.aspx).

Other WTL Features to Explore

In a previous Windows with C++ column, I introduced many of the control enhancements in Windows Vista (msdn.microsoft.com/msdnmag/issues/07/08/WindowsCPP). WTL now wraps most all of the new features with updated classes and macros for handling the new messages and notifications. There is even more to love about the latest version of WTL, from improvements to the resizable dialog support, a new tab view class as well as many other helpful additions and improvements. Take a few minutes to explore the WTL headers. If you're new to WTL you will be pleasantly surprised at the powerful classes and minimalist design of this excellent C++ library for Windows.

Send your questions and comments for Kenny to mmwincpp@microsoft.com.

Kenny Kerr is a software craftsman specializing in software development for Windows. He has a passion for writing and teaching developers about programming and software design. Reach Kenny at weblogs.asp.net/kennykerr.


{ End Bracket }: You Should Learn SharePoint

{ End Bracket }
You Should Learn SharePoint
Robert Bogue

For a developer, there's never a shortage of material to learn. Whether you're exploring the backwoods of Sys- tem.DirectoryServices.Protocols, spending quality time with generics, or delving into the fine art of garbage collection, there is always something clamoring for your attention. So why should you make learning Microsoft® SharePoint® technologies a priority? Here are five good reasons:

  1. There are many SharePoint features that can easily be extended to your application. How about a set of navigation controls? SharePoint has it. Need to build a security system? SharePoint has it. You'll find that many things that aren't built into ASP.NET are built into SharePoint out of the box.
  2. We talk all the time about reusable software. Since the late 1980s, we've talked about reusability from the perspective of back-end components. However, in all this time, little has been done to improve reusability of elements in the user interface aside from basic common controls. SharePoint provides a framework for user interface components, Web Parts, that can be recomposed with different configurations and in different orders such that one Web Part can serve a multitude of needs. For instance, in SharePoint you can use the DataView Web Part and XML Web Parts to get data from numerous sources and display it. Using only a small number of built-in components, you can create a great number of solutions. When you add your own components and connect them to existing Web Parts, you create even more powerful solutions that can connect with existing systems while leveraging out-of-the-box functionality.
  3. Microsoft Office SharePoint Server (MOSS) contains a search engine that can be used to index and find information in your application. You don't have to write your own search engine. You've got a ready-made platform built right in—all you have to do is expose it. Users will get great relevancy and a friendly user interface that even allows them to be alerted when content changes.
  4. Every app is simple until that one requirement that makes it difficult. The invoicing system is straightforward until the users need to store images of the documents. The CRM system is easy until the user wants to attach spreadsheets. SharePoint offers a complete set of tools for managing documents accessible via the Web site, WebDAV, or directly from the Microsoft Office clients. Why build document support by hand when you can get it for free with your platform?
  5. Nearly every application today has some form of workflow included in it. Why code your own providers for the workflow service—including ways to interact with users—when you can get SharePoint to do the heavy lifting? SharePoint can manage the state of workflows through properties of the item or as properties of a task; either way, taking advantage of SharePoint support for the Windows® Workflow Foundation means less work for you.

However, the picture with SharePoint isn't all rosy. Here are a few of the hurdles:

Cumbersome Tools Whether you're bothered by the lack of F5 run support, the fact that SharePoint solutions must be developed on a server OS, or all of the hand coding of XML files, you'll agree that SharePoint development isn't as streamlined or "shiny" as developing for ASP.NET. Work on the Visual Studio® Extensions for Windows SharePoint Services and the VSTO work in the next version of Visual Studio, code-named "Orcas," isn't quite there yet either.

Cycle Time Because there's more going on, your "cycle time" (the amount of time between editing and testing code) is much longer. This can be a serious hit on your productivity.

Everything and the Kitchen Sink It seems like you get everything with SharePoint—and you'll have to learn quite a bit of it to create a solution. If you haven't picked up Master Page skills, you'll need to do so. If you don't quite understand the whole modular user interface through Web Parts or user controls, you'll need to learn that, too. If you're rusty on XML/XPath/XSLT, prepare for a crash course. SharePoint has a cornucopia of new terms to learn. This includes the capital F feature and the capital S solution, both of which have special meanings in SharePoint. You have a lot to learn even for a simple SharePoint solution.

SharePoint brings a great new way to build applications. It's not quite the leap from Windows Forms to Web Forms, but it's close. You've got a new level of infrastructure under you and a new set of great capabilities—if you can hang on for the ride.

Robert Bogue, Microsoft MVP for Microsoft Office SharePoint Server, MCSE, and MCSA:Security, has contributed to more than 100 book projects and numerous other publishing projects. Robert blogs at www.thorprojects.com/blog, and you can e-mail him at Rob.Bogue@thorprojects.com.