 MSDN Magazine May 2005

MSDN Magazine May 2005

JIT and Run: Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects

There's lots to explore in the .NET Framework 2.0, and plenty of digging to be done. If you want to get your hands dirty and learn some of the internals that will carry you through the next few years, you've come to the right place. This article explores CLR internals, including object instance layout, method table layout, method dispatching, interface-based dispatching, and various data structures.

Hanu Kommalapati and Tom Christian Around the Horn: Engineer a Distributed System Using .NET Remoting for Process Intensive Analysis

Before the Microsoft .NET Framework, creating a distributed cluster of computers to perform scientific analysis was expensive in terms of hardware, programming and debugging time, and maintenance. You had to purchase expensive servers, spend time debugging network communication, design a distributed system completely different from a system deployed locally, and maintain a melting pot of error handling, data acquisition, networking, and analysis code. In this article, the author shows you how he was able to engineer a distributed computing system in C# to perform analysis of real-world data continuously acquired at high sampling rates, thanks to the .NET Framework.

Nate D'Anna Draft a Rich UI: Ground Rules for Building Enhanced Windows Forms Support into Your .NET App

In this article, the winning Windows Forms duo of Chris Sells and Michael Weinhardt team up again to explore lots of new features and additions to Windows Forms 2.0 that will let you build more flexible, feature-rich controls, get better resource management, more powerful data-binding abilities, and make your development life a whole lot more fun.

Michael Weinhardt and Chris Sells Safe!: Repel Attacks on Your Code with the Visual Studio 2005 Safe C and C++ Libraries

When Visual Studio 2005 ships, it will include a major upgrade to the Visual C++ Libraries that was the result of a complete security review of the functions contained in the C Runtime Library, Standard C++ Library, ATL, and MFC. From that extensive review came the Safe C and C++ Libraries, which can improve the security and robustness of your apps.

Martyn Lovell Touch All the Bases: Give Your .NET App Brains and Brawn with the Intelligence of Neural Networks

Pattern recognition is an increasingly complex field. Every day technologies like handwriting recognition, spam filters, and search engines must identify ever more complicated patterns. One way these complex relationships can be represented is through the use of neural networks. Find out what neural networks can do for your pattern recognition and prediction tasks.

Christopher M. Frenz Editor's Note: Under the Covers and Through the Words

There are some people who have little curiosity about the world around them. They drive to work without asking why their airbag light is on. They work all day, never once wondering why their computer goes through periods where it's slow as a snail.

New Stuff: Resources for Your Developer Toolbox

Visual Build Professional 5. 6 by Kinook Software is a management solution for developers writing programs for Microsoft® Windows® and the Web that lets you create an automated, repeatable process for building your software.

Marnie Hutcheson Web Q&A: ASP.NET Performance, Notification, Keeping Sort Order, and More

Edited by Nancy Michell Cutting Edge: Collections and Data Binding

When it's time to design the Data Access Layer (DAL) of your distributed Microsoft® . NET Framework-based app, one of the key decisions you'll make is how you'll pass data to and from methods of DAL classes.

Dino Esposito Data Points: Data Access Strategies Using ADO.NET and SQL

When your goal is a scalable and efficient enterprise solution, you need to develop an efficient data-access strategy. You can't just do some testing on your production machines and rely on the results.

John Papa Service Station: Contract-First Service Development

In one of my previous columns on Service Orientation (SO), I introduced the concept of "contract-first" service development (see Service Station: SOA: More Integration, Less Renovation). Over the next two installments of this column, I'm going to cover the topic in depth.

Aaron Skonnard Extreme ASP.NET: A Little Bit of Control for Your Controls

Having worked for so many years designing and developing ASP. NET while at Microsoft, it's exciting now to have a venue in which to talk about it. In this new column, Extreme ASP. NET, I'll discuss and demonstrate time-tested techniques and approaches to implementing high-performance, reliable, secure, and user-friendly Web applications with ASP.

Rob Howard Basic Instincts: Designing With Custom Attributes

One of the most powerful aspects of the Microsoft® . NET Framework is its support for attributes. Attribute-based programming is extremely powerful because it adds a declarative dimension to designing and writing software.

Ted Pattison Security Briefs: Customizing GINA, Part 1

Over the years I've had many people ask me to write about GINA, the Graphical Identification and Authentication component that serves as the gateway for interactive logons. This month I'll begin my coverage of this topic to help you get started if you're tasked to build such a beast.

Keith Brown .NET Matters: ICustomTypeDescriptor, Part 2

In last month's . NET Matters column, I answered a question concerning the PropertyGrid control, specifically about using it with classes that expose fields instead of properties. I demonstrated how the ICustomTypeDescriptor interface in the Microsoft® .

Stephen Toub C++ at Work: Unreferenced Parameters, Adding Task Bar Commands, and More

Paul DiLascia { End Bracket }: On Being Crew for Fantasia 2000

I was asked to reminisce about my experience as a software technical director on the Firebird segment of Disney's Fantasia 2000 in the special effects group. They're the group that puts the twinkle in Mickey's eyes, literally.

Stanley B. Lippman


JIT and Run: Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects

JIT and Run

Drill Into .NET Framework Internals to See How the CLR Creates Runtime Objects

Hanu Kommalapati and Tom Christian
This article discusses:
  • SystemDomain, SharedDomain, and DefaultDomain
  • Object layout and other memory specifics
  • Method table layout
  • Method dispatching
This article uses the following technologies:
.NET Framework, C#
Contents
Domains Created by the CLR Bootstrap
System Domain
SharedDomain
DefaultDomain
LoaderHeaps
Type Fundamentals
ObjectInstance
MethodTable
Base Instance Size
Method Slot Table
MethodDesc
Interface Vtable Map and Interface Map
Virtual Dispatch
Static Variables
EEClass
Conclusion

S
ince the common language runtime (CLR) will be the premiere infrastructure for building applications in Windows® for some time to come, gaining a deep understanding of it will help you build efficient, industrial-strength applications. In this article, we'll explore CLR internals, including object instance layout, method table layout, method dispatching, interface-based dispatching, and various data structures.

We'll be using very simple code samples written in C#, so any implicit references to language syntax should default to C#. Some of the data structures and algorithms discussed will change for the Microsoft® .NET Framework 2.0, but the concepts should largely remain the same. We'll use the Visual Studio® .NET 2003 Debugger and the debugger extension Son of Strike (SOS) to peek into the data structures we discuss in this article. SOS understands CLR internal data structures and dumps out useful information. See the "Son of Strike" sidebar for loading SOS.dll into the Visual Studio .NET 2003 debugger process. Throughout the article, we will describe classes that have corresponding implementations in the Shared Source CLI (SSCLI), which you can download from msdn.microsoft.com/net/sscli. Figure 1 will help you navigate the megabytes of code in the SSCLI while searching for the referenced structures.

Figure 1 SSCLI Reference (Close Figure 1)

Item | SSCLI Path
AppDomain | \sscli\clr\src\vm\appdomain.hpp
AppDomainStringLiteralMap | \sscli\clr\src\vm\stringliteralmap.h
BaseDomain | \sscli\clr\src\vm\appdomain.hpp
ClassLoader | \sscli\clr\src\vm\clsload.hpp
EEClass | \sscli\clr\src\vm\class.h
FieldDescs | \sscli\clr\src\vm\field.h
GCHeap | \sscli\clr\src\vm\gc.h
GlobalStringLiteralMap | \sscli\clr\src\vm\stringliteralmap.h
HandleTable | \sscli\clr\src\vm\handletable.h
InterfaceVTableMapMgr | \sscli\clr\src\vm\appdomain.hpp
Large Object Heap | \sscli\clr\src\vm\gc.h
LayoutKind | \sscli\clr\src\bcl\system\runtime\interopservices\layoutkind.cs
LoaderHeaps | \sscli\clr\src\inc\utilcode.h
MethodDescs | \sscli\clr\src\vm\method.hpp
MethodTables | \sscli\clr\src\vm\class.h
OBJECTREF | \sscli\clr\src\vm\typehandle.h
SecurityContext | \sscli\clr\src\vm\security.h
SecurityDescriptor | \sscli\clr\src\vm\security.h
SharedDomain | \sscli\clr\src\vm\appdomain.hpp
StructLayoutAttribute | \sscli\clr\src\bcl\system\runtime\interopservices\attributes.cs
SyncTableEntry | \sscli\clr\src\vm\syncblk.h
System namespace | \sscli\clr\src\bcl\system
SystemDomain | \sscli\clr\src\vm\appdomain.hpp
TypeHandle | \sscli\clr\src\vm\typehandle.h

A word of caution before we start—the information provided in this article is only valid for the .NET Framework 1.1 (it's also mostly true for Shared Source CLI 1.0, with the most notable exceptions being some interop scenarios) when running on the x86 platform. This information will change for the .NET Framework 2.0, so please do not build software that relies on the constancy of these internal structures.

Domains Created by the CLR Bootstrap

Before the CLR executes the first line of the managed code, it creates three application domains. Two of these are opaque from within the managed code and are not even visible to CLR hosts. They can only be created through the CLR bootstrapping process facilitated by the shim—mscoree.dll and mscorwks.dll (or mscorsvr.dll for multiprocessor systems). As you can see in Figure 2, these are the System Domain and the Shared Domain, which are singletons. The third domain is the Default AppDomain, an instance of the AppDomain class that is the only named domain. For simple CLR hosts such as a console program, the default domain name is composed of the executable image name. Additional domains can be created from within managed code using the AppDomain.CreateDomain method or from unmanaged hosting code using the ICORRuntimeHost interface. Complicated hosts like ASP.NET create multiple domains based on the number of applications in a given Web site.

Figure 2 Domains Created by the CLR Bootstrap
Figure 2 Domains Created by the CLR Bootstrap

System Domain

The SystemDomain is responsible for creating and initializing the SharedDomain and the default AppDomain. It loads the system library mscorlib.dll into SharedDomain. It also keeps process-wide string literals interned implicitly or explicitly.

String interning is an optimization feature that's a little bit heavy-handed in the .NET Framework 1.1, as the CLR does not give assemblies the opportunity to opt out of the feature. Nonetheless, it saves memory by having only a single instance of the string for a given literal across all the application domains.

SystemDomain is also responsible for generating process-wide interface IDs, which are used in creating InterfaceVtableMaps in each AppDomain. SystemDomain keeps track of all the domains in the process and implements functionality for loading and unloading the AppDomains.

SharedDomain

All of the domain-neutral code is loaded into SharedDomain. Mscorlib, the system library, is needed by the user code in all the AppDomains. It is automatically loaded into SharedDomain. Fundamental types from the System namespace like Object, ValueType, Array, Enum, String, and Delegate get preloaded into this domain during the CLR bootstrapping process. User code can also be loaded into this domain, using LoaderOptimization attributes specified by the CLR hosting app while calling CorBindToRuntimeEx. Console programs can load code into SharedDomain by annotating the app's Main method with a System.LoaderOptimizationAttribute. SharedDomain also manages an assembly map indexed by the base address, which acts as a lookup table for managing shared dependencies of assemblies being loaded into DefaultDomain and of other AppDomains created in managed code. DefaultDomain is where non-shared user code is loaded.

DefaultDomain

DefaultDomain is an instance of AppDomain within which application code is typically executed. While some applications require additional AppDomains to be created at runtime (such as apps that have plug-in architectures or apps doing a significant amount of run-time code generation), most applications create one domain during their lifetime. All code that executes in this domain is context-bound at the domain level. If an application has multiple AppDomains, any cross-domain access will occur through .NET Remoting proxies. Additional intra-domain context boundaries can be created using types inherited from System.ContextBoundObject. Each AppDomain has its own SecurityDescriptor, SecurityContext, and DefaultContext, as well as its own loader heaps (High-Frequency Heap, Low-Frequency Heap, and Stub Heap), Handle Tables (Handle Table, Large Object Heap Handle Table), Interface Vtable Map Manager, and Assembly Cache.

LoaderHeaps

LoaderHeaps are meant for loading various runtime CLR artifacts and optimization artifacts that live for the lifetime of the domain. These heaps grow by predictable chunks to minimize fragmentation. LoaderHeaps are different from the garbage collector (GC) Heap (or multiple heaps in case of a symmetric multiprocessor or SMP) in that the GC Heap hosts object instances while LoaderHeaps hold together the type system. Frequently accessed artifacts like MethodTables, MethodDescs, FieldDescs, and Interface Maps get allocated on a HighFrequencyHeap, while less frequently accessed data structures, such as EEClass and ClassLoader and its lookup tables, get allocated on a LowFrequencyHeap. The StubHeap hosts stubs that facilitate code access security (CAS), COM wrapper calls, and P/Invoke.

Having examined the domains and LoaderHeaps at a high level, we'll now look at the physical details of these in the context of the simple app in Figure 3. We stopped the program execution at "mc.Method1();" and dumped the domain information using the SOS debugger extension command, DumpDomain (see the "Son of Strike" sidebar for SOS loading information). Here is the edited output:

!DumpDomain
System Domain: 793e9d58, LowFrequencyHeap: 793e9dbc,
HighFrequencyHeap: 793e9e14, StubHeap: 793e9e6c,
Assembly: 0015aa68 [mscorlib], ClassLoader: 0015ab40

Shared Domain: 793eb278, LowFrequencyHeap: 793eb2dc,
HighFrequencyHeap: 793eb334, StubHeap: 793eb38c,
Assembly: 0015aa68 [mscorlib], ClassLoader: 0015ab40

Domain 1: 149100, LowFrequencyHeap: 00149164,
HighFrequencyHeap: 001491bc, StubHeap: 00149214,
Name: Sample1.exe, Assembly: 00164938 [Sample1],
ClassLoader: 00164a78

Figure 3 Sample1.exe (Close Figure 3)
using System;

public interface MyInterface1
{
    void Method1();
    void Method2();
}
public interface MyInterface2
{
    void Method2();
    void Method3();
}

class MyClass : MyInterface1, MyInterface2
{
    public static string str = "MyString";
    public static uint   ui = 0xAAAAAAAA;
    public void Method1() { Console.WriteLine("Method1"); }
    public void Method2() { Console.WriteLine("Method2"); }
    public virtual void Method3() { Console.WriteLine("Method3"); }
}

class Program
{
    static void Main()
    {
        MyClass mc = new MyClass();
        MyInterface1 mi1 = mc;
        MyInterface2 mi2 = mc;

        int i = MyClass.str.Length;
        uint j = MyClass.ui;

        mc.Method1();
        mi1.Method1();
        mi1.Method2();
        mi2.Method2();
        mi2.Method3();
        mc.Method3();
    }
}

Our console program, Sample1.exe, is loaded into an AppDomain which has a name "Sample1.exe." Mscorlib.dll is loaded into the SharedDomain but it is also listed against the SystemDomain as it is the core system library. A HighFrequencyHeap, LowFrequencyHeap, and StubHeap are allocated in each domain. The SystemDomain and the SharedDomain use the same ClassLoader, while the Default AppDomain uses its own.

The output does not show the reserved and committed sizes of the loader heaps. The HighFrequencyHeap initial reserve size is 32KB and its commit size is 4KB. LowFrequencyHeap and StubHeaps are initially reserved with 8KB and committed at 4KB. Also not shown in the SOS output is the InterfaceVtableMap heap. Each domain has a InterfaceVtableMap (referred to here as IVMap) that is created on its own LoaderHeap during the domain initialization phase. The IVMap heap is reserved at 4KB and is committed at 4KB initially. We'll discuss the significance of IVMap while exploring type layout in subsequent sections.

Figure 2 shows the default Process Heap, JIT Code Heap, GC Heap (for small objects) and Large Object Heap (for objects with size 85000 or more bytes) to illustrate the semantic difference between these and the loader heaps. The just-in-time (JIT) compiler generates x86 instructions and stores them on the JIT Code Heap. GC Heap and Large Object are the garbage-collected heaps on which managed objects are instantiated.

Type Fundamentals

A type is the fundamental unit of programming in .NET. In C#, a type can be declared using the class, struct, and interface keywords. Most types are explicitly created by the programmer, however, in special interoperability cases and remote object invocation (.NET Remoting) scenarios, the .NET CLR implicitly generates types. These generated types include COM and Runtime Callable Wrappers and Transparent Proxies.

We'll explore .NET type fundamentals by starting from a stack frame that contains an object reference (typically, the stack is one of the locations from which an object instance begins life). The code shown in Figure 4 contains a simple program with a console entry point that calls a static method. Method1 creates an instance of type SmallClass which contains a byte array used in demonstrating the creation of an object instance on a Large Object Heap. The code is trivial, but will serve for our discussion.

Figure 4 Large Objects and Small Objects (Close Figure 4)
using System;

class SmallClass
{
    private byte[] _largeObj;
    public SmallClass(int size)
    {
        _largeObj = new byte[size];
        _largeObj[0] = 0xAA;
        _largeObj[1] = 0xBB;
        _largeObj[2] = 0xCC;
    }

    public byte[] LargeObj
    {
        get { return this._largeObj; }
    }
}

class SimpleProgram
{
    static void Main(string[] args)
    {
        SmallClass smallObj = SimpleProgram.Create(84930,10,15,20,25);
        return;
    }

    static SmallClass Create(int size1, int size2, int size3,
        int size4, int size5)
    {
        int objSize = size1 + size2 + size3 + size4 + size5;
        SmallClass smallObj = new SmallClass(objSize);
        return smallObj;
    }
}

Figure 5 shows snapshot of a typical fastcall stack frame stopped at a breakpoint at the "return smallObj;" line inside the Create method. (Fastcall is the .NET calling convention which specifies that arguments to functions are to be passed in registers, when possible, with all other arguments passed on the stack right to left and popped later by the called function.) The value type local variable objSize is inlined within the stack frame. Reference type variables like smallObj are stored as a fixed size (a 4-byte DWORD) on the stack and contain the address of object instances allocated on the normal GC Heap. In traditional C++, this is an object pointer; in the managed world it's an object reference. Nonetheless, it contains the address of an object instance. We'll use the term ObjectInstance for the data structure located at the address pointed to by the object reference.

Figure 5 SimpleProgram Stack Frame and Heaps
Figure 5 SimpleProgram Stack Frame and Heaps

The smallObj object instance on the normal GC Heap contains a Byte[] called _largeObj, whose size is 85000 bytes (note that the figure shows 85016 bytes, which is the actual storage size). The CLR treats objects with sizes greater than or equal to 85000 bytes differently than the smaller objects. Large objects are allocated on a Large Object Heap (LOH), while smaller objects are created on a normal GC Heap, which optimizes the object allocation and garbage collection. The LOH is not compacted, whereas the GC Heap is compacted whenever a GC collection occurs. Moreover, the LOH is only collected on full GC collections.

The ObjectInstance of smallObj contains the TypeHandle that points to the MethodTable of the corresponding type. There will be one MethodTable for each declared type and all the object instances of the same type will point to the same MethodTable. This will contain information about the kind of type (interface, abstract class, concrete class, COM Wrapper, and proxy), the number of interfaces implemented, the interface map for method dispatch, the number of slots in the method table, and a table of slots that point to the implementations.

One important data structure MethodTable points to is EEClass. The CLR class loader creates EEClass from the metadata before MethodTable is laid out. In Figure 4, SmallClass's MethodTable points to its EEClass. These structures point to their modules and assemblies. MethodTable and EEClass are typically allocated on the domain-specific loader heaps. Byte[] is a special case; the MethodTable and the EEClass are allocated on the loader heaps of the SharedDomain. Loader heaps are AppDomain-specific and any data structures already mentioned here, once loaded, will not go away until an AppDomain is unloaded. Also, the default AppDomain can't be unloaded and hence the code lives until the CLR is shut down.

ObjectInstance

As we mentioned, all instances of value types are either inlined on the thread stack or inlined on the GC Heap. All reference types are created on the GC Heap or LOH. Figure 6 shows a typical object instance layout. An object can be referenced from stack-based local variables, handle tables in the interop or P/Invoke scenarios, from registers (the this pointer and method arguments while executing a method), or from the finalizer queue for objects having finalizer methods. The OBJECTREF does not point to the beginning of the Object Instance but at a DWORD offset (4 bytes). The DWORD is called Object Header and holds an index (a 1-based syncblk number) into a SyncTableEntry table. As the chaining is through an index, the CLR can move the table around in memory while increasing the size as needed. The SyncTableEntry maintains a weak reference back to the object so that the SyncBlock ownership can be tracked by the CLR. Weak references enable the GC to collect the object when no other strong references exist. SyncTableEntry also stores a pointer to SyncBlock that contains useful information, but is rarely needed by all instances of an object. This information includes the object's lock, its hash code, any thunking data, and its AppDomain index. For most object instances, there will be no storage allocated for the actual SyncBlock and the syncblk number will be zero. This will change when the execution thread hits statements like lock(obj) or obj.GetHashCode, as shown here:

SmallClass obj = new SmallClass()
// Do some work here
lock(obj) { /* Do some synchronized work here */ }
obj.GetHashCode();

Figure 6 Object Instance Layout (Close Figure 6)

In this code, smallObj will use zero (no syncblk) as its starting syncblk number. The lock statement causes the CLR to create a syncblk entry and update the object header with the corresponding number. As the C# lock keyword expands to a try-finally that makes use of the Monitor class, a Monitor object is created on the syncblk for synchronization. A call to the GetHashCode method populates the syncblk with the object hash code.

There are other fields in the SyncBlock that are used in COM interop and for marshaling delegates to unmanaged code, but which are not relevant for a typical object usage.

TypeHandle follows the syncblk number in the ObjectInstance. In order to maintain continuity, I will discuss TypeHandle after elaborating on the instances variables. A variable list of instance fields follows the TypeHandle. By default, the instance fields will be packed in such a way that memory is used efficiently and padding is minimized for alignment. The code in Figure 7 shows a SimpleClass that has a bunch of instance variables with varying sizes contained in it.

Figure 7 SimpleClass with Instance Variables (Close Figure 7)
class SimpleClass
{
    private byte b1 = 1;                // 1 byte
    private byte b2 = 2;                // 1 byte
    private byte b3 = 3;                // 1 byte
    private byte b4 = 4;                // 1 byte
    private char c1 = 'A';              // 2 bytes
    private char c2 = 'B';              // 2 bytes
    private short s1 = 11;              // 2 bytes
    private short s2 = 12;              // 2 bytes
    private int i1 = 21;                // 4 bytes
    private long l1 = 31;               // 8 bytes
    private string str = "MyString"; // 4 bytes (only OBJECTREF)

    //Total instance variable size = 28 bytes

    static void Main()
    {
        SimpleClass simpleObj = new SimpleClass();
        return;
    }
}

Figure 8 shows an example of a SimpleClass object instance in the Visual Studio debugger memory window. We set a breakpoint on the return statement in Figure 7 and used the address of the simpleObj contained in the ECX register to display object instance in the memory window. The first 4-byte block is the syncblk number. As we didn't use the instance in any synchronizing code (or access its HashCode), this is set to 0. The object reference, as stored in the stack variable, points to 4 bytes starting at offset 4. The Byte variables b1, b2, b3, and b4 are all packed side by side. Both of the short variables, s1 and s2, are packed together. The String variable str is a 4-byte OBJECTREF that points to the actual instance of the string located on the GC Heap. String is a special type in that all instances containing the same literal will be made to point to the same instance in a global string table during the assembly loading process. This process is called string interning and is designed to optimize memory usage. As we mentioned previously, in the .NET Framework 1.1, an assembly cannot opt out of this interning process, although future versions of CLR may provide this capability.

Figure 8 Debugger Memory Window for Object Instance (Close Figure 8)

So the lexical sequence of member variables in the source code is not maintained in memory by default. In interop scenarios where lexical sequence has to be carried forward into memory, the StructLayoutAttribute can be used, which takes a LayoutKind enumeration as the argument. LayoutKind.Sequential will maintain the lexical sequence for the marshaled data, though in the .NET Framework 1.1 it will not affect the managed layout (however, in the .NET Framework 2.0, it will). In interop scenarios where you really need to have extra padding and explicit control of the field sequence, LayoutKind.Explicit can be combined with FieldOffset decoration at the field level.

Having looked at the raw memory contents, let's use SOS to look at the object instance. One useful command is DumpHeap, which allows listing of all the heap contents and all the instances of a particular type. Instead of relying on the registers, DumpHeap can show the address of the only instance we created:

!DumpHeap -type SimpleClass
Loaded Son of Strike data table version 5 from
"C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\mscorwks.dll"
 Address       MT     Size
00a8197c 00955124       36
Last good object: 00a819a0
total 1 objects
Statistics:
      MT    Count TotalSize Class Name
  955124        1        36 SimpleClass

The total size of the object is 36 bytes. No matter how large the string is, instances of SimpleClass contain only DWORD OBJECTREF. SimpleClass's instance variables only occupy 28 bytes. The remaining 8 bytes are comprised of the TypeHandle (4 bytes) and the syncblk number (4 bytes). Having found the address of the instance simpleObj, let's dump the contents of this instance using the DumpObj command, as shown here:

!DumpObj 0x00a8197c
Name: SimpleClass
MethodTable 0x00955124
EEClass 0x02ca33b0
Size 36(0x24) bytes
FieldDesc*: 00955064
      MT    Field   Offset                 Type       Attr    Value Name
00955124  400000a        4         System.Int64   instance      31 l1
00955124  400000b        c                CLASS   instance 00a819a0 str
    << some fields omitted from the display for brevity >>
00955124  4000003       1e          System.Byte   instance        3 b3
00955124  4000004       1f          System.Byte   instance        4 b4

As noted, the default layout generated for classes by the C# compiler is LayoutType.Auto (for structs, LayoutType.Sequential is used); hence the class loader rearranged the instance fields to minimize the padding. We can use ObjSize to dump the graph that includes the space taken up by the instance, str. Here's the output:

!ObjSize 0x00a8197c
sizeof(00a8197c) =       72 (    0x48) bytes (SimpleClass)
Son of Strike

The SOS debugger extension is used to display the contents of CLR data structures in this article. It's part of the .NET Framework installation and is located at %windir%\Microsoft.NET\Framework\v1.1.4322. Before you load SOS into the process, enable managed debugging from the project properties in Visual Studio .NET. Add the directory in which SOS.dll is located to the PATH environment variable. To load SOS.dll, while at a breakpoint, open Debug | Windows | Immediate. In the immediate window, execute .load sos.dll. Use !help to get a list of debugger commands. For more information on SOS, see the June 2004 Bugslayer column.

If you subtract the size of the SimpleClass instance (36 bytes) from the overall size of the object graph (72 bytes), you should get the size of the str—that is, 36 bytes. Let's verify this by dumping the str instance. Here's the output:

!DumpObj 0x00a819a0
Name: System.String
MethodTable 0x009742d8
EEClass 0x02c4c6c4
Size 36(0x24) bytes
If you add the size of the string instance str (36 bytes) to the size of SimpleClass instance (36 bytes), you get a total size of 72 bytes, as reported by the ObjSize command.

Note that ObjSize will not include the memory taken up by the syncblk infrastructure. Also, in the .NET Framework 1.1, the CLR is not aware of the memory taken up by any unmanaged resources like GDI objects, COM objects, file handles, and so on; hence, they will not be reported by this command.

TypeHandle, a pointer to the MethodTable, is located right after the syncblk number. Before an object instance is created, the CLR looks up the loaded types, loads the type if not found, obtains the MethodTable address, creates the object instance, and populates the object instance with the TypeHandle value. The JIT compiler-generated code uses TypeHandle to locate the MethodTable for method dispatching. The CLR uses TypeHandle whenever it has to backtrack to the loaded type through MethodTable.

MethodTable

Each class and interface, when loaded into an AppDomain, will be represented in memory by a MethodTable data structure. This is a result of the class-loading activity before the first instance of the object is ever created. While ObjectInstance represents the state, MethodTable represents the behavior. MethodTable binds the object instance to the language compiler-generated memory-mapped metadata structures through EEClass. The information in the MethodTable and the data structures hanging off it can be accessed from managed code through System.Type. A pointer to the MethodTable can be acquired even in managed code through the Type.RuntimeTypeHandle property. TypeHandle, which is contained in the ObjectInstance, points to an offset from the beginning of the MethodTable. This offset is 12 bytes by default and contains GC information which we will not discuss here.

Figure 9 shows the typical layout of the MethodTable. We'll show some of the important fields of the TypeHandle, but for a more complete list, look at the figure. Let's start with the Base Instance Size as it has direct correlation to the runtime memory profile.

Figure 9 MethodTable Layout (Close Figure 9)

Base Instance Size

The Base Instance Size is the size of the object as computed by the class loader, based on the field declarations in the code. As discussed previously, the current GC implementation needs an object instance of at least 12 bytes. If a class does not have any instance fields defined, it will carry an overhead of 4 bytes. The rest of the 8 bytes will be taken up by the Object Header (which may contain a syncblk number) and TypeHandle. Again, the size of the object can be influenced by a StructLayoutAttribute.

Look at the memory snapshot (Visual Studio .NET 2003 memory window) of a MethodTable for MyClass from Figure 3 (MyClass with two interfaces) and compare it with SOS-generated output. In Figure 9, the object size is located at a 4-byte offset and the value is 12 (0x0000000C) bytes. The following is the output of DumpHeap from SOS:

!DumpHeap -type MyClass
 Address       MT     Size
00a819ac 009552a0       12
total 1 objects
Statistics:
    MT  Count TotalSize Class Name
9552a0      1        12    MyClass

Method Slot Table

Embedded within the MethodTable is a table of slots that point to the respective method descriptors (MethodDesc), enabling the behavior of the type. The Method Slot Table is created based on the linearized list of implementation methods laid out in the following order: Inherited virtuals, Introduced virtuals, Instance Methods, and Static Methods.

The ClassLoader walks through the metadata of the current class, parent class, and interfaces, and creates the method table. In the layout process, it replaces any overridden virtual methods, replaces any parent class methods being hidden, creates new slots, and duplicates slots as necessary. The duplication of slots is necessary to create an illusion that each interface has its own mini vtable. However, the duplicated slots point to the same physical implementation. MyClass has three instance methods, a class constructor (.cctor), and an object constructor (.ctor). The object constructor is automatically generated by the C# compiler for all objects having no constructors explicitly defined. Class constructor is generated by the compiler as we have a static variable defined and initialized. Figure 10 shows the layout of the method table for MyClass. The layout shows 10 methods because of the duplication of Method2 slot for IVMap, which will be covered next. Figure 11 shows the edited SOS dump of MyClass's method table.

Figure 11 SOS Dump of MyClass Method Table (Close Figure 11)
!DumpMT -MD 0x9552a0
  Entry  MethodDesc  Return Type       Name
0097203b 00972040    String            System.Object.ToString()
009720fb 00972100    Boolean           System.Object.Equals(Object)
00972113 00972118    I4                System.Object.GetHashCode()
0097207b 00972080    Void              System.Object.Finalize()
00955253 00955258    Void              MyClass.Method1()
00955263 00955268    Void              MyClass.Method2()
00955263 00955268    Void              MyClass.Method2()
00955273 00955278    Void              MyClass.Method3()
00955283 00955288    Void              MyClass..cctor()
00955293 00955298    Void              MyClass..ctor()
Figure 10 MyClass MethodTable Layout (Close Figure 10)

The first four methods of any type will always be ToString, Equals, GetHashCode, and Finalize. These are virtual methods inherited from System.Object. The Method2 slot is duplicated, but both point to the same method descriptor. The explicitly coded .cctor and .ctor will be grouped with static methods and instance methods, respectively.

MethodDesc

Method Descriptor (MethodDesc) is an encapsulation of method implementation as the CLR knows it. There are several types of Method Descriptors that facilitate the calls to a variety of interop implementations, in addition to managed implementations. In this article we will only look at the managed MethodDesc in the context of the code shown in Figure 3. A MethodDesc is generated as a part of the class loading process and initially points to Intermediate Language (IL). Each MethodDesc is padded with a PreJitStub, which is responsible for triggering JIT compilation. Figure 12 shows a typical layout. The method table slot entry actually points to the stub instead of the actual MethodDesc data structure. This is at a negative offset of 5 bytes from the actual MethodDesc and is part of the 8-byte padding every method inherits. The 5 bytes contain instructions for a call to the PreJitStub routine. This 5-byte offset can be seen from the DumpMT output (of MyClass in Figure 11) of SOS, as MethodDesc is always 5 bytes after the location pointed to by the Method Slot Table entry. Upon the first invocation, a call to the JIT compilation routine is made. After the compilation is complete, the 5 bytes containing the call instruction will be overwritten with an unconditional jump to the JIT-compiled x86 code.

Figure 12 Method Descriptor
Figure 12 Method Descriptor

Disassembly of the code pointed to by the Method Table Slot entry in Figure 12 will show the call to the PreJitStub. Here's an abridged display of the disassembly before JIT for Method 2:

!u 0x00955263
Unmanaged code
00955263 call        003C3538        ;call to the jitted Method2()
00955268 add         eax,68040000h   ;ignore this and the rest
                                     ;as !u thinks it as code
Now let's execute the method and disassemble the same address:
!u 0x00955263
Unmanaged code
00955263 jmp     02C633E8        ;call to the jitted Method2()
00955268 add     eax,0E8040000h  ;ignore this and the rest
                                 ;as !u thinks it as code
Only the first 5 bytes at the address is code; the rest contains data of Method2's MethodDesc. The "!u" command is unaware of this and generates gibberish, so you can ignore anything after the first 5 bytes.

CodeOrIL before JIT compilation contains the Relative Virtual Address (RVA) of the method implementation in IL. This field is flagged to indicate that it is IL. The CLR updates this field with the address of the JITed code after on-demand compilation. Let's pick a method from the ones listed and dump the MethodDesc using DumpMT command before and after JIT compilation:

!DumpMD 0x00955268
Method Name : [DEFAULT] [hasThis] Void MyClass.Method2()
MethodTable 9552a0
Module: 164008
mdToken: 06000006
Flags : 400
IL RVA : 00002068
After compilation, MethodDesc looks like this:
!DumpMD 0x00955268
Method Name : [DEFAULT] [hasThis] Void MyClass.Method2()
MethodTable 9552a0
Module: 164008
mdToken: 06000006
Flags : 400
Method VA : 02c633e8
The Flags field in the method descriptor is encoded to contain the information about the type of the method, such as static, instance, interface method, or COM implementation.

Let's see another complicated aspect of MethodTable: Interface implementation. It's made to look simple to the managed environment by absorbing all the complexity into the layout process. Next, we'll show how the interfaces are laid out and how interface-based method dispatching really works.

Interface Vtable Map and Interface Map

At offset 12 in the MethodTable is an important pointer, the IVMap. As shown in Figure 9, IVMap points to an AppDomain-level mapping table that is indexed by a process-level interface ID. The interface ID is generated when the interface type is first loaded. Each interface implementation will have an entry in IVMap. If MyInterface1 is implemented by two classes, there will be two entries in the IVMap table. The entry will point back to the beginning of the sub-table embedded within the MyClass method table, as shown in Figure 9. This is the reference with which the interface-based method dispatching occurs. IVMap is created based on the Interface Map information embedded within the method table. Interface Map is created based on the metadata of the class during the MethodTable layout process. Once typeloading is complete, only IVMap is used in method dispatching.

The Interface Map at offset 28 will point to the InterfaceInfo entries embedded within the MethodTable. In this case, there are two entries for each of the two interfaces implemented by MyClass. The first 4 bytes of the first InterfaceInfo entry points to the TypeHandle of MyInterface1 (see Figure 9 and Figure 10). The next WORD (2 bytes) is taken up by Flags (where 0 is inherited from parent, and 1 is implemented in the current class). The WORD right after Flags is Start Slot, which is used by the class loader to lay out the interface implementation sub-table. For MyInterface1, the value is 4, which means that slots 5 and 6 point to the implementation. For MyInterface2, the value is 6, so slots 7 and 8 point to the implementation. ClassLoader duplicates the slots if necessary to create the illusion that each interface gets its own implementation while physically mapping to the same method descriptor. In MyClass, MyInterface1.Method2 and MyInterface2.Method2 will point to the same implementation.

Interface-based method dispatching occurs through IVMap, while direct method dispatch occurs through the MethodDesc address stored at the respective slot. As mentioned earlier, the .NET Framework uses the fastcall calling convention. The first two arguments are typically passed through ECX and EDX registers, if possible. This first argument of the instance method is always a this pointer, which is passed through the ECX register as shown by the "mov ecx, esi" statement:

mi1.Method1();
mov    ecx,edi                 ;move "this" pointer into ecx
mov    eax,dword ptr [ecx]     ;move "TypeHandle" into eax
mov    eax,dword ptr [eax+0Ch] ;move IVMap address into eax at offset 12
mov    eax,dword ptr [eax+30h] ;move the ifc impl start slot into eax
call   dword ptr [eax]         ;call Method1

mc.Method1();
mov    ecx,esi                 ;move "this" pointer into ecx
cmp    dword ptr [ecx],ecx     ;compare and set flags
call   dword ptr ds:[009552D8h];directly call Method1

These disassemblies show that the direct call to MyClass's instance method does not use offset. The JIT compiler writes the address of the MethodDesc directly into the code. Interface-based dispatch happens through IVMap and requires a few extra instructions than the direct dispatch. One is used to fetch the address of the IVMap, and the other to fetch the start slot of the interface implementation within the Method SlotTable. Also, casting an object instance to an interface merely copies the this pointer to the target variable. In Figure 2, the statement "mi1 = mc;" uses a single instruction to copy the OBJECTREF in mc to mi1.

Virtual Dispatch

Let's look now at Virtual Dispatch and compare it with direct and interface-based dispatch. Here is the disassembly for a virtual method call to MyClass.Method3 from Figure 3:

mc.Method3();
Mov    ecx,esi               ;move "this" pointer into ecx
Mov    eax,dword ptr [ecx]   ;acquire the MethodTable address
Call   dword ptr [eax+44h]   ;dispatch to the method at offset 0x44
Virtual dispatch always occurs through a fixed slot number, irrespective of the MethodTable pointer in a given implementation class (type) hierarchy. During the MethodTable layout, ClassLoader replaces the parent implementation with the overriding child implementation. As a result, method calls coded against the parent object get dispatched to the child object's implementation. The disassembly shows that the dispatch occurs through slot number 8 in the debugger memory window (as seen in Figure 10) as well as the DumpMT output.

Static Variables

Static variables are an important constituent part of the MethodTable data structure. They are allocated as a part of the MethodTable right after the method table slot array. All the primitive static types are inlined while the static value objects like structs and reference types are referred through OBJECTREFs created in the handle tables. OBJECTREF in the MethodTable refers to OBJECTREF in the AppDomain handle table, which refers to the heap-created object instance. Once created, OBJECTREF in the handle table will keep the object instance on the heap alive until the AppDomain is unloaded. In Figure 9, a static string variable, str, points to OBJECTREF on the handle table, which points to MyString on the GC Heap.

EEClass

EEClass comes to life before the MethodTable is created and, when combined with MethodTable, is the CLR version of a type declaration. In fact, EEClass and MethodTable are logically one data structure (together they represent a single type), and were split based on frequency of use. Fields that get used a lot are in MethodTable, while fields that get used infrequently are in EEClass. Thus information (like names, fields, and offsets) needed to JIT compile functions end up in EEClass, however info needed at run time (like vtable slots and GC information) are in MethodTable.

There will be one EEClass for each type loaded into an AppDomain. This includes interface, class, abstract class, array, and struct. Each EEClass is a node of a tree tracked by the execution engine. CLR uses this network to navigate through the EEClass structures for purposes including class loading, MethodTable layout, type verification, and type casting. The child-parent relationship between EEClasses is established based on the inheritance hierarchy, whereas parent-child relationships are established based on the combination of inheritance hierarchy and class loading sequence. New EEClass nodes get added, node relationships get patched, and new relationships get established as the execution of the managed code progresses. There is also a horizontal relationship with sibling EEClasses in the network. EEClass has three fields to manage the node relationships between loaded types: ParentClass, SiblingChain, and ChildrenChain. Refer to Figure 13 for the schematics of EEClass in the context of MyClass from Figure 4.

Figure 13 shows only a few of the fields relevant to this discussion. Because we've omitted some fields in the layout, we have not really shown the offsets in this figure. EEClass has a circular reference to MethodTable. EEClass also points MethodDesc chunks allocated on HighFrequencyHeap of the default AppDomain. A reference to a list of FieldDesc objects allocated on the process heap provides field layout information during MethodTable construction. EEClass is allocated on the LowFrequencyHeap of the AppDomain so that the operating system can better perform page management of memory, thereby reducing the working set.

Figure 13 EEClass Layout
Figure 13 EEClass Layout

Other fields shown in Figure 13 are self-explanatory in the context of MyClass (Figure 3). Let's look now at the real physical memory by dumping the EEClass using SOS. Run the program from Figure 3 after setting a breakpoint on the line, mc.Method1. First obtain the address of EEClass for MyClass using the command Name2EE:

!Name2EE C:\Working\test\ClrInternals\Sample1.exe MyClass

MethodTable: 009552a0
EEClass: 02ca3508
Name: MyClass
The first argument to Name2EE is the module name that can be obtained from DumpDomain command. Now that we have the address of the EEClass, we'll dump the EEClass itself:
!DumpClass 02ca3508
Class Name : MyClass, mdToken : 02000004, Parent Class : 02c4c3e4
ClassLoader : 00163ad8, Method Table : 009552a0, Vtable Slots : 8
Total Method Slots : a, NumInstanceFields: 0,
NumStaticFields: 2,FieldDesc*: 00955224

      MT    Field   Offset  Type           Attr    Value    Name
009552a0  4000001   2c      CLASS          static 00a8198c  str
009552a0  4000002   30      System.UInt32  static aaaaaaaa  ui

Figure 13 and the DumpClass output look essentially the same. Metadata token (mdToken) represents the MyClass index in the memory mapped metadata tables of the module PE file, and the Parent class points to System.Object. Sibling Chain (Figure 13) shows that it is loaded as a result of the loading of the Program class.

MyClass has eight vtable slots (methods that can be virtually dispatched). Even though Method1 and Method2 are not virtual, they will be considered virtual methods when dispatched through interfaces and added to the list. Add .cctor and .ctor to the list, and you get 10 (0?a) total methods. The class has two static fields that are listed at the end. MyClass has no instance fields. The rest of the fields are self-explanatory.

Conclusion

That concludes our tour of the some of the most important internals of the CLR. Obviously, there's much more to be covered, and in much more depth, but we hope this has given you a glimpse into how things work. Much of the information presented here will likely change with subsequent releases of the CLR and the .NET Framework. But although the CLR data structures covered in this article may change, the concepts should remain the same.

Hanu Kommalapati is an Architect at Microsoft Gulf Coast District (Houston). In his current role at Microsoft, he helps enterprise customers in building scalable component frameworks based on the .NET Framework. He can be reached at hanuk@microsoft.com.

Tom Christian is an Escalation Engineer with Developer Support at Microsoft, working with ASP.NET and the .NET debugger extension for WinDBG (sos/psscor). He is based in Charlotte, NC and can be contacted at tomchris@microsoft.com.

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


Around the Horn: Engineer a Distributed System Using .NET Remoting for Process Intensive Analysis

Around the Horn

Engineer a Distributed System Using .NET Remoting for Process Intensive Analysis

Nate D'Anna
This article discusses:
  • Using .NET Remoting for distributed processing systems
  • Processor-intensive scientific analysis
  • Data acquisition fundamentals
  • Virtual instrumentation for measurement and automation
This article uses the following technologies:
C# and .NET Remoting
Code download available at: DistributedNETRemoting.exe (161KB)
Contents
Use Case and Data Acquisition Fundamentals
Virtual Instrumentation
Is .NET the Right Choice?
Declaring and Publishing the Remote Analysis Object
Activating the Analysis Object
Passing Data Between the Client and Server
Using the Remote Object From a Client
Performance Improvements
A Natural Evolution
Conclusion

I
magine you're testing an F-16 Falcon fighter plane in a wind tunnel to identify structural defects. You are continuously acquiring 2.5 million measurements per second of time-sensitive wind tunnel data from this fighter plane into your PC. Yet this raw data is meaningless without processor-intensive analysis. With this analyzed data, you can pinpoint dangerous micro-fractures in the skin of a multimillion dollar plane. You need to perform this test in a wind tunnel that does not charge by the day or by the hour, but by the minute. To make an on-the-fly risk assessment of the plane, you need to be able to change the plane's pitch, yaw, and roll, and immediately see how these changes stress different parts of the plane. Because of the significant processing time required for analysis, processing the data locally can be too slow for the tester's needs.

In the past, CPU limitations required that all data had to be logged to file and then analyzed offline. If the analysis showed that changes needed to be made to the test, the entire system would need reworking and testing at significant cost. A better solution is to distribute the analysis functions to multiple servers, allowing nearly real-time computation and display of results.

Before the advent of the Microsoft® .NET Framework, creating a distributed cluster of computers to perform scientific analysis was often expensive in terms of hardware cost, programming and debugging time, and maintenance. You had to purchase expensive servers, spend time debugging network communication, design a distributed system completely different from a system deployed locally, and maintain a melting pot of error handling, data acquisition, networking, and analysis code. Now with .NET, you are promised the performance and power of C++ and DCOM, with the ease of programming in Visual Basic®.

In this article, I'll show you how I engineered a distributed computing system in C# to perform analysis of real-world data continuously acquired at high sampling rates. This architecture can be modified for other processor-intensive applications such as complicated database queries. First, I'll look at the real-world case of testing an F-16 for structural defects. Then, I'll discuss how and why .NET is a good fit for these systems. I'll implement a basic client-server system using .NET Remoting technology. Finally, I'll show you how to scale this system to use multiple servers. From there, you can use your imagination to add load balancing and more advanced distribution schemes.

Use Case and Data Acquisition Fundamentals

In the fighter plane example, 2.5 million samples per second (S/s) of sound data are acquired from an F-16 fighter plane in a wind tunnel. In a real-world situation, it wouldn't be just sound data from a single microphone, but from an entire array of sensors, like those shown in Figure 1. This data helps engineers identify critical aerodynamics and structural defects in the plane. For this example, the 2.5 million S/s are actually divided among 100 sensors each sampled at 25,000 S/s. Each sample consists of an 8-byte double, which means 20MB/s stream across the PCI bus—well under the 132MB/s maximum transfer rate of a 32-bit PCI bus. After streaming, the data will arrive in the PC RAM via direct memory access (DMA), completely bypassing the CPU.

Figure 1 Data Acquisition and Analysis
Figure 1 Data Acquisition and Analysis

Before this data can be sent to RAM, it needs to be converted from analog to digital. A series of PC-based data acquisition (DAQ) modules digitize the analog signals into zeros and ones (see Figure 1). Each sensor is connected to a separate channel on the DAQ module, with samples digitized by the DAQ module's analog-to-digital (A/D) converter. A circular buffer in the RAM stores the raw acquired data. As each new block of data is transferred from the DAQ module via the PCI bus, it is copied to the next contiguous block of RAM. After this data has been transferred to the RAM, it still needs to be transferred from the DAQ driver memory to the memory allocated by application software. This second transfer is a software-timed operation initiated by the DAQ client application. After this operation, the raw data is available for analysis, data logging, and presentation. But if the CPU is busy doing other processing or handling interrupts, consecutive reads might be processed at unpredictable times. If consecutive transfers are delayed too much, previously acquired data is overwritten by new data before the app has transferred the original data to application memory. As a result, data is lost and the F-16 needs to be retested.

If analysis is performed locally, it must fully execute between transfers, leaving enough time to transfer more data out of the circular buffer. Yet, for more complicated analysis routines, such as octave analysis, each analysis operation might take too long to complete—meaning the streaming DAQ data will overflow the circular buffer and data will be lost.

There are several options for preventing the loss of data:

Option 1 Buy a faster computer or more processors, but this is an expensive option, even as hardware costs decrease.

Option 2 Decrease the sampling rate of the data acquisition to reduce the amount of data to analyze, thus reducing the processor load. Unfortunately, this option is equivalent to losing data because acquiring too few data points results in less accurate measurements.

Option 3 Use the time between buffer transfers to pass the raw data to remote machines instead of performing the processor-intensive analysis locally. Each remote machine can then process the data locally, freeing processor time on the acquisition machine. As a result, the analysis algorithm's computational complexity can increase without requiring a correspondingly decreased sampling rate on the acquisition machine.

Virtual Instrumentation

The key component of the system discussed here is virtual instrumentation. With virtual instrumentation, powerful software technologies such as .NET are combined with modular, off-the-shelf hardware such as plug-in DAQ modules. This is drastically different from traditional measurement systems that consist of box instruments such as oscilloscopes and function generators. Traditional instrumentation systems limit you to the specifications of the vendor. If you need to implement a new analysis algorithm or more acquisition channels, you are forced to buy a completely new system—assuming that such a system exists.

With virtual instrumentation, you can use a standard PC or cluster of PCs to create measurement and automation applications. Virtual instrumentation empowers you to use software technologies, such as .NET, to define your hardware's behavior. As software and hardware technology continues to improve, you can change or expand to suit different application needs without entirely reimplementing your setup or buying a new system.

Another great feature of virtual instrumentation that makes it especially well-suited for use with high-channel count acquisitions is the ability to synchronize hardware channels within picoseconds (ps). You augment the PC with external buses like Real-Time System Integration (RTSI) and PCI Extensions for Instrumentation (PXI) to synchronize timing clocks between individual acquisition modules and different PC systems.

Is .NET the Right Choice?

The next question is whether .NET is the right option for processor-intensive analysis. In "Harness the Features of C# to Power Your Scientific Computing Projects,", Fahad Gilani argued that managed code is ideal for scientific computing applications. Gilani noted that the common language runtime (CLR) provides fast performance, language interoperability, just-in-time (JIT) compilation, automatic memory management, object-oriented language constructs, and high-precision floating point operations that are suited perfectly for many scientific applications. For applications that require distributed computing, such as wind tunnel testing of F-16s, the following three major benefits make .NET Remoting and managed code a great choice.

Performance Despite the overhead of the common language runtime (CLR), C# and Visual Basic .NET stand up surprisingly well to C++, as shown by Gilani in his article. The .NET Framework also features several communication protocols. Because performance is crucial here, I will use a TCP protocol as opposed to HTTP or Web services. However, changing from an efficient TCP channel to a more scalable HTTP protocol is as simple as changing a line in the configuration file.

Ease of implementation Implementation of a remotable object is accomplished by inheriting from MarshalByRefObject. With this implementation, it is easy to migrate code engineered for a single application domain on a single machine to one that can span multiple servers at once.

Flexibility and robustness The .NET Framework offers a wide variety of behaviors and programming paradigms, such as multiple activation models, events, asynchronous method calls, and one-way method calls.

Creating a distributed analysis system begins with a client and server. The client acquires DAQ data and displays the analyzed data. The server contains an object that performs the analysis and an application that hosts the remote object. Each time the DAQ client acquires new data, the client asynchronously invokes an analysis method on the server, passing the raw data in a serializable data packet. When the analysis completes, the server passes another serializable data packet containing the response analysis back to the client, as shown in Figure 2.

Figure 2 Distributed Analysis
Figure 2 Distributed Analysis

For the scenario described in this article, analysis is happening in an intranet environment, not an Internet environment, so for performance reasons, securing the network channel is not actually required. However, if you really want better security, you would need to ensure encryption and data integrity for the communication channel, possibly using the SSPI sample available in the MSDN® Library at .NET Remoting Authentication and Authorization Sample – Part II. In scenarios such as testing an F-16 fighter, security would obviously be of prime importance.

Declaring and Publishing the Remote Analysis Object

Architecting a distributed analysis system begins with an object that needs to be available from other AppDomains. In this case, the AppDomains are running on separate computers, but remoting can also be used to communicate between AppDomains in the same process. In the F-16 testing use case, the object that performs remote scientific computing, AnalysisClass, is made remotable by inheriting from System.MarshalByRefObject (see the code in Figure 3). AnalysisClass performs a fast Fourier transform (FFT) to extract frequency domain data, but this class could just as easily be replaced with your own remotable class.

Figure 3 Remote Analysis Class (Close Figure 3)
public class AnalysisClass : MarshalByRefObject
{
    public AnalyzedDataPacket RunAnalysis(RawDataPacket rawPacket)
    {
        // The frequency component between each sample
        // returned from the analysis function
        double df;

        // Perform FFT analysis on raw data
        double[] temp = Analyze(rawPacket.GetRawData(),
            1.0/rawPacket.SamplingFrequency, out df);

        // Create a data packet with the analyzed data
        AnalyzedDataPacket analyzedPacket =
            new AnalyzedDataPacket(rawPacket.TimeStamp, df,
                                   temp);

        // On the server, display packet information
        Console.Write("Time: {0}\n",
            analyzedPacket.TimeStamp.ToString());

        return analyzedPacket;
    }

    // Override InitializeLifetimeService to never expire
    public override object InitializeLifetimeService()
    {
        return null;
    }
}

When marshal by reference (MBR) is used, an instance of AnalysisClass is referenced on the server via a proxy of this object on the client's AppDomain. To the client, this proxy seems to be a local instance of the remote object, but it simply references the actual object on the server, as shown in Figure 2. The proxy communicates with the remote object, but it allows the programmer to be oblivious to this intermediate layer. Think of MBR as passing a pointer of the remotable object between the client and server. In reality, a pointer-like object called an ObjRef is passed between AppDomains. The ObjRef does not contain a memory address; instead it contains the server location (IP address or server name), the object that it references, and the registered channels.

To make AnalysisClass available to remote clients, this class is published by another application called AnalysisServer (see Figure 4). AnalysisServer listens for incoming calls and processes client requests. It's a simple console app that must be run on the server. Simply place AnalysisServer.exe in the Startup folder, so that it runs as soon as a user logs in. For further robustness, create AnalysisServer as a Windows® Service project, thus running without requiring a user to log in on the server machine.

Figure 4 Analysis Server (Close Figure 4)
class AnalysisServer
{
    static void Main(string[] args)
    {
        // Create a channel to exchange data between the
        // server and client

        TcpChannel channel = new TcpChannel(4000);
        ChannelServices.RegisterChannel(channel);

        // On the server, create an instance of AnalysisClass
        AnalysisClass myRemoteObject = new AnalysisClass();

        // After the channel is registered, the object needs to be
        // registered with the remoting infrastructure.  So,
        // Marshal is called
        RemotingServices.Marshal(myRemoteObject, "AnalysisServer");

        Console.WriteLine
            ("AnalysisClass has been published on the server\n");
        Console.WriteLine("Press enter to exit.");
        Console.ReadLine();

        RemotingServices.Disconnect(myRemoteObject);
        ChannelServices.UnregisterChannel(channel);
    }
}

Activating the Analysis Object

With MBR, there are three major choices for the behavior of the remote object. The two best known are server-activated objects (SAOs) and client-activated objects (CAOs). The activation model chosen can greatly affect the responsiveness of the system. If the analysis routine (RunAnalysis) is called synchronously from the client, the activation model chosen could also drastically affect data loss on the DAQ client. Server-activated objects are not instantiated on the server until the first time a method of the remote object is called, rather than when the new keyword is used. SAOs offer two unique modes: single call and singleton. With single call, a new object is created each time a client calls a method on the remote object. With singleton, one object services all requests from clients when the first client calls a method on the remote object.

Under both modes, the first time the RunAnalysis method is called, the server must deal with the overhead of creating a new instance of AnalysisClass. Using singleton, this overhead occurs only once, regardless of the number of clients. Under single call, this overhead is incurred during every method call. Knowing that RunAnalysis needs to be called multiple times per second, the added time consumed using single-call mode to instantiate AnalysisClass seems pointless and will reduce the responsiveness of the system. Singleton seems like the logical choice in that it only creates one instance of AnalysisClass for all clients, but the first method call will be slowed by the time required to instantiate the remote object. Regardless of whether you have chosen single call or singleton, these activation models aren't optimal, especially because SAOs do not allow activation of objects with non-default constructors.

An alternative to SAOs are CAOs, which do not wait to instantiate the remote object until the first method call. CAOs are instantiated when the client calls new to create the remote object. Thus, the first time a method is called, the remote object has already been instantiated. This means a shorter round-trip for the data to the server and back to the client. CAOs also allow you to use non-default constructors when instantiating remote objects. Another CAO characteristic is that a unique instance is activated by each client, which means that state information can be stored between successive calls to the remote object. This feature might be a requirement for some analysis routines, especially statistical analyses, which need to store results from previous analysis runs. Yet, for the analysis routines used in this example, such as FFTs and octave analyses, state information is not required. CAOs introduce unneeded memory and overhead for each additional client that is activated.

So you're probably wondering at this point how you can activate your remote object if neither server-activated nor client-activated objects are a perfect match? Luckily, a third technique exists that provides only one instance of the remote object for all clients, like a server-activated singleton combined with the performance of a CAO. As shown in Figure 4, an instance of the remote object is declared on the server. Then the object is made available to clients through marshaling to an ObjRef using the RemotingServices.Marshal method. Remember, an ObjRef is essentially a network pointer that can be passed across a registered channel to another AppDomain, even if it exists on a different computer.

Passing Data Between the Client and Server

In this example, I used MBR to declare AnalysisClass because the actual computation needed to occur remotely. However, simply passing references to the data between the client and the server is not sufficient—instead, the actual values, in addition to information that identifies each data packet, must be exchanged. In contrast to MBR, marshal by value (MBV) allows AppDomains to exchange actual data—simply add the [Serializable] attribute to the MBV class or structure. Thus, MBV fits perfectly for creating a DataPacket class to share data between the client and server.

The code in Figure 5 declares an abstract base class named DataPacket, which stores identifying information such as a time stamp. Two classes inherit from this base class, representing the actual data being passed between the client and server—RawDataPacket and AnalyzedDataPacket. These classes store information that uniquely identifies the DataPacket and an array of doubles that stores the sensor data. As shown in Figure 3, the RunAnalysis method accepts an object of type RawDataPacket and returns an object of type AnalyzedDataPacket to the caller.

Figure 5 Marshal by Value Data Packet Objects (Close Figure 5)
[Serializable]
abstract public class DataPacket
{
    private DateTime _timeStamp;

    protected DataPacket(DateTime timeStamp)
    {
        _timeStamp = timeStamp;
    }

    public DateTime TimeStamp
    {
        get{ return _timeStamp;}
    }
}

[Serializable]
public class RawDataPacket : DataPacket
{
    private double[] _rawData;
    private double _samplingFrequency;

    public RawDataPacket(DateTime timeStamp,
        double samplingFrequency, double[] data) : base(timeStamp)
    {
        _samplingFrequency = samplingFrequency;
        _rawData = data;
    }

    public double SamplingFrequency
    {
        get{ return _samplingFrequency; }
    }

    public double[] GetRawData()
    {
        return _rawData;
    }
}

[Serializable]
public class AnalyzedDataPacket : DataPacket
{
    private double[] _analyzedData;
    private double _df;

    public AnalyzedDataPacket(DateTime timeStamp, double df,
        double[] data) : base(timeStamp)
    {
        _df = df;
        _analyzedData = data;
    }

    public double Df
    {
        get{ return _df;}
    }
    public double[] GetAnalyzedData()
    {
        return _analyzedData;
    }
}

Using the Remote Object From a Client

The final step to engineering this distributed analysis system requires creating a client that continuously acquires sensor data from a DAQ module, passes this data to the remote server, then displays the analyzed data. You can begin by obtaining a proxy from the remote analysis server:

_analysisClass = (AnalysisClass)Activator.GetObject(
  typeof(AnalysisClass), String.Format("tcp:/
  /{0}:4000/AnalysisServer", computerName));
From the client application, _analysisClass can be treated as a local instance of AnalysisClass, but it is really only just a proxy to the remote instance of this class on the server.

The next step is to configure the data acquisition. In Figure 6, the Configure method creates an instance of a DAQ module with the specified sampling rate and number of samples to read. The sampling rate (measured in samples per second) indicates how fast the DAQ module acquires sensor data and transfers it to the circular buffer. Samples to read indicates how many samples to transfer from the circular buffer to the application memory of the client. In order to transfer data from the circular buffer, I create an event callback called DAQBoard_SampleSetComplete that is invoked each time additional DAQ data is available. When this event is fired from the DAQ module, raw DAQ data is transferred into application memory. You can retrieve the DAQ data with a call to e.GetData, which is the equivalent of transferring data from the circular buffer to application memory.

Figure 6 Data Acquisition Client (Close Figure 6)
public class DAQViewer : System.Windows.Forms.Form
{
    //Remote analysis class and delegate for remote analysis
    private AnalysisClass _analysisClass;
    private delegate AnalyzedDataPacket AnalysisDelegate(
        RawDataPacket rawDataPacket);
    private AnalysisDelegate _runAnalysisDelegate;

    // Delegates to marshal data to UI thread
    private delegate void RawDataUIDelegate(
        double[] data, int counterSent,
        DAQSampleSetStatus daqStatus, double timeBetweenReads);
    private delegate void AnalyzedDataUIDelegate(
        double[] data, int counterReceived, double df);

    // DAQ settings
    private int _samplingRate, _samplesToRead;
    private DAQBoard _daqBoard;

    // Network packet counters
    private int _counterSent;
    private int _counterReceived;

    public DAQViewer()
    {
        InitializeComponent();
        _counterReceived = 0;
        _counterSent = 0;
    }

    protected override void Dispose(bool disposing)
    {
        if( disposing )
        {
            if(components != null) components.Dispose();
            if(_daqBoard != null) _daqBoard.Dispose();
            base.Dispose( disposing );
        }
    }

    // Configure remoting and DAQ board
    public void Configure(string computerName, bool remotingEnabled,
        int samplingRate, int samplesToRead)
    {
        _samplesToRead = samplesToRead;
        _samplingRate = samplingRate;

        // If remoting is enabled, retrieve an ObjRef of an
        // instance of AnalysisClass on the analysis server.
        // Else, create an instance of AnalysisClass locally
        if(remotingEnabled)
        {
            _analysisClass = (AnalysisClass) Activator.GetObject(
                typeof(AnalysisClass), String.Format(
                    "tcp://{0}:4000/AnalysisServer", computerName));
            this.Text = "Connected to " + computerName;
        }
        else
        {
            _analysisClass = new AnalysisClass();
            this.Text = "Connected to localhost";
        }

        // Create an instance of a DAQ board with the specified
        // sampling rate and samples to read
        _daqBoard = new DAQBoard(_samplingRate, _samplesToRead);

        // Configure the SampleSetComplete event to call
        // DAQBoard_SampleSetComplete when new DAQ data is
        // ready on the circular buffer
        _daqBoard.SampleSetComplete +=
            new DAQSampleSetCompleteEventHandler(
                DAQBoard_SampleSetComplete);

        // Configure delegate to call the RunAnalysis
        // method on the remote machine
        _runAnalysisDelegate = new AnalysisDelegate(
           _analysisClass.RunAnalysis);
    }

    // This method is called when new DAQ data is ready.
    // It then sends the raw data to the analysis server
    private void DAQBoard_SampleSetComplete(object sender,
        DAQSampleSetCompleteEventArgs e)
    {
        // Get data from the DAQ board
        double[] data = e.GetData();

        // Safely update the number of packets sent
        System.Threading.Interlocked.Increment(ref _counterSent);

        // Create a data packet to send to the analysis server
        RawDataPacket rawDataPacket = new RawDataPacket(
            DateTime.Now, _samplingRate, data);

        // Asynchronously invoke the remote analysis
        // method called RunAnalysis on remote machine
        _runAnalysisDelegate.BeginInvoke(rawDataPacket,
            new AsyncCallback(AnalysisComplete), null);

        // Marshal data to UI thread
        object[] args = new object[]{e.GetData(),
            _counterSent, e.Status, e.TimeBetweenReads};
        this.Invoke(new RawDataUIDelegate(UpdateUIRawData), args);
    }

    // This method is called when analysis of the
    // data on the analysis server has completed
    private void AnalysisComplete(IAsyncResult ar)
    {
        // Retrieve a packet of analyzed data
        AnalyzedDataPacket analyzedDataPacket =
            _runAnalysisDelegate.EndInvoke(ar);

        // Safely update the number of packets received
        System.Threading.Interlocked.Increment(ref _counterReceived);

        // Marshal data to the UI thread
        object[] args = new object[]{analyzedDataPacket.GetAnalyzedData(),
            _counterReceived, analyzedDataPacket.Df};
        this.Invoke(
            new AnalyzedDataUIDelegate(UpdateUIAnalyzedData), args);
    }

    // Update UI for raw data display
    private void UpdateUIRawData(double[] data, int counterSent,
        DAQSampleSetStatus daqStatus, double timeBetweenReads)
    {
        // Display raw data on the raw graph
        rawDataCtrl.PlotY(data, 0.0, 1.0/_samplingRate);

        // Update the number the number of packets
        // sent on the sent control
        sentCtrl.Value = (double) counterSent;

        if(daqStatus == DAQSampleSetStatus.Overflow)
            overflowCtrl.Value = true;

        // Update the Delay Between DAQ reads control
        delayCtrl.Value = timeBetweenReads;
    }

    // Update UI for analyzed data display
    private void UpdateUIAnalyzedData(
        double[] data, int counterReceived, double df)
    {
        // Display analyzed data on the analyzed graph
        analyzedDataCtrl.PlotY(data, 0.0, df);

        // Update the number of packets
        // received on the received control
        receivedCtrl.Value = (double)counterReceived;
    }

    // Start acquisition on the DAQ board
    public void Start()
    {
        _daqBoard.StartAcquisition();
    }

    // Stop acquisition on the DAQ board
    public void Stop()
    {
        _daqBoard.StopAcquisition();
    }
}

Within the DAQBoard_SampleSetComplete callback, the raw data is encapsulated, along with a time stamp, in an instance of RawDataPacket. The instance of RawDataPacket is sent to the remote server with a call to the _analysisClass.RunAnalysis method on the remote object. You could simply call this method synchronously with:

AnalyzedDataPacket analyzedDataPacket =
  myAnalysisClass.RunAnalysis(myRawDataPacket);
However, if this blocking call to the remote server takes too long to complete, data loss could result because the client machine will not be able to service subsequent DAQBoard_SampleSetComplete events. Thus, you will invoke the RunAnalysis method asynchronously, freeing the DAQ client to process subsequent events to prevent data loss. To accomplish this, use a delegate that asynchronously calls RunAnalysis with BeginInvoke:
_runAnalysisDelegate = new AnalysisDelegate(_analysisClass.RunAnalysis);
_runAnalysisDelegate.BeginInvoke(rawDataPacket,
  new AsyncCallback(AnalysisComplete), _runAnalysisDelegate);
When the analysis has completed on the analysis server, AnalysisComplete is called on the client. From this AnalysisComplete callback, you call EndInvoke to obtain the analyzed data:
AnalyzedDataPacket analyzedDataPacket =
  _runAnalysisDelegate.EndInvoke(ar);

AnalyzedDataPacket contains timing and identification information, in addition to the analyzed data that can then be displayed. The GetAnalyzedData method is called to obtain the analyzed data from the AnalyzedDataPacket:

double[] data = analyzedDataPacket.GetAnalyzedData();

Figure 7 Displaying the Data
Figure 7 Displaying the Data

With this array of analyzed data, you can now display the data however you see fit (see Figure 7). Remember though, that all user interface updates must happen from the UI thread, so you must marshal the analyzed data to the UI thread like so:

object[] args = new object[]{
  analyzedDataPacket.GetAnalyzedData(), _counterReceived,
  analyzedDataPacket.Df};
this.Invoke(new AnalyzedDataUIDelegate(UpdateUIAnalyzedData), args);
Figure 8 shows the architecture for the DAQ client.

Figure 8 DAQ Client Architecture
Figure 8 DAQ Client Architecture

Performance Improvements

In this example, I used two 1.2MHz Pentium III machines, each with 256MB of RAM running version 1.1 of the .NET Framework. Figure 9 shows the maximum number of samples per second that could be acquired before data loss resulted on the DAQ module. I compared the maximum sample rate when the analysis was performed locally versus remotely, and for consistency, the number of samples read was kept at 10 percent of the sampling rate. You can see that the benefits of remoting become clearer as the computation complexity increases.

Figure 9 Analysis of Performance
Figure 9 Analysis of Performance

So far, I have only used one analysis server to distribute the load of time-consuming analysis. Extending this architecture to multiple analysis servers allows even faster sampling from the DAQ client and a more responsive display. Faster sampling allows you to ensure that the phenomena that you are measuring are more accurately represented. Increasing responsiveness allows analysis results to be viewed sooner as changes are made to the F-16.

To quickly scale this application to multiple servers, you can create a top-level application that instantiates one instance of DAQViewer for each available analysis server, as in Figure 10. DAQViewer was the object created that represents the DAQ client (refer back to Figure 6). Each element in the array of DAQViewers represents a different DAQ module that passes its data to a separate server. From this top-level application, you can pass the IP address of each analysis server.

Figure 10 Top-Level Application (Close Figure 10)
public class MainForm : System.Windows.Forms.Form
{
    private System.Windows.Forms.TextBox remoteComputersCtrl;

    // Create an array of DAQViewers to represent each available DAQ board
    // and analysis server
    private DAQViewer[] _daqViewers;

    ...

    [STAThread]
    static void Main()
    {
        Application.Run(new MainForm());
    }

    private void startAcquisition_Click(
        object sender, System.EventArgs e)
    {
        int numComputers, samplingRate, samplesToRead;

        // Get form values for sampling rate, samples to read,
        // and number of computers
        samplingRate = Convert.ToInt32(samplingRateCtrl.Value);
        samplesToRead = Convert.ToInt32(samplesToReadCtrl.Value);
        numComputers = remoteComputersCtrl.Lines.Length;

        // For each analysis server name, create a new
        // DAQViewer to acquire, analyze, and display
        // DAQ data.
        _daqViewers = new DAQViewer[numComputers];
        for(int i=0; i<numComputers; i++)
        {
            _daqViewers[i] = new DAQViewer();
            _daqViewers[i].Show();
            _daqViewers[i].Configure(remoteComputersCtrl.Lines[i],
                enableRemoting.Checked, samplingRate, samplesToRead);
            _daqViewers[i].Start();
        }
    }

    private void stop_Click(object sender, System.EventArgs e)
    {
        // Stop data acquisition for all instances of DAQViewers
        if(_daqViewers != null)
            foreach(DAQViewer daqViewer in _daqViewers)
                daqViewer.Stop();
    }
}

A Natural Evolution

Asynchronous method calls work well to pass raw data to the remote object and return the analyzed data to the same application. In this case, the client acquires the data and displays the analyzed results—meaning a user has to be logged on locally to view the results. In a wind tunnel or other harsh environment like a nuclear plant, you can imagine the need for an acquisition system that is physically separate from the data viewers. Also, some applications require multiple data viewers. For these cases, asynchronous method calls quickly reveal their limitations. Let's now expand this basic server-client architecture.

Figure 11 Multitier System
Figure 11 Multitier System

Events could replace asynchronous method calls. If you want the data to be displayed locally, you can register the AnalysisComplete event on the DAQ client. Or, the AnalysisComplete event could just as easily be registered on another machine. If multiple analysis servers were available, the AnalysisComplete events for each server could be registered to one display machine. This machine would be responsible for displaying and logging the results, thus leaving the acquisition machine free to acquire data faster. To create multiple viewers, use ASP.NET or a Web service on the server to publish the analyzed results.

Thus far, I have known the network address of all available machines in advance. Another logical progression would be to use a load-balancing server that receives all acquired DAQ data. This server would then dynamically poll for available servers, possibly using remoting, and pass raw data to available servers. Each server would then pass the analyzed data to a central display machine (see Figure 11).

Conclusion

Managed code promises to combine the power of C++ and DCOM with the ease of use of Visual Basic. I put C# and the .NET Framework to the test to see if it could back up this bold claim in a real-world application. C# and .NET successfully met this challenge, proving that off-the-shelf hardware combined with flexible software can save you programming time and money. In the future, I look forward to technologies like PCI Express, dual-core processors, and the .NET Framework 2.0, all of which will allow even faster sampling and greater processing speeds.

Nate D'Anna is a product manager for Measurement Studio at National Instruments in Austin, TX. Nate graduated with a B.S. in Computer Science from Vanderbilt University and can be reached at nate.danna@ni.com.


Draft a Rich UI: Ground Rules for Building Enhanced Windows Forms Support into Your .NET App

[ Editor's Update - 1/20/2006: This article refers to a beta version of Visual Studio 2005. An updated version of the article, reflecting features found in the final release of Visual Studio 2005, can be found at Smart Clients: Craft A Rich UI For Your .NET App With Enhanced Windows Forms Support.]
Draft a Rich UI

Ground Rules for Building Enhanced Windows Forms Support into Your .NET App

Chris Sells and Michael Weinhardt
This article is based on prerelease versions of Visual Studio 2005 and the .NET Framework 2.0. All information contained herein is subject to change.
This article discusses:
  • Productivity-focused updates in Visual Studio 2005
  • Resource and settings management improvements
  • Flexible control layout and new strip controls
  • Data binding
This article uses the following technologies:
.NET Framework 2.0, Visual Studio 2005
Contents
Project Configuration
Improved Resource Management
Improved Settings Management
System.Windows.Forms.Form
Form and Control Layout
Strip Controls
ToolStripContainer
Strip Control Settings Management
MaskedTextBox Control
Data Binding
Binding to a Data Source
New Data-Bound Components
Where Are We?

S
ince we last looked at Windows® Forms 2.0 in May 2004, Microsoft has renamed, improved, and added a considerable number of features. The features discussed in this article are from the December 2004 Community Technology Preview, so you can definitely expect some things to change in the final release. (For our first article on Windows Forms, see Craft a Rich UI for Your .NET App with Enhanced Windows Forms Support.)

A Windows Forms project is the gateway to exploring all the new features, and even the gate has been updated in the latest drop. The new default template-generated layout is shown in Figure 1.

Figure 1 Default Generated Windows Forms Projects
Figure 1 Default Generated Windows Forms Projects

Much of the new project layout is focused on removing unnecessary code from the default wizard-generated forms, leaving a trimmed-down version that looks like this for C#:

// Form1.cs
partial class Form1 : Form {
  public Form1() {
    InitializeComponent();
  }
}
Visual Basic® spent a little more time at the code-trimming gym:
' Form1.vb
Public Class Form1
End Class
If you've used previous versions of Visual Studio®, you might be wondering where the InitializeComponent implementation went. All Windows Forms designer-managed form code such as the InitializeComponent implementation has been migrated to a new file, FormName.Designer.cs or FormName.Designer.vb, as shown in Figure 1. To split the class across two physical files, the designer takes advantage of new language support for partial classes:
' Form1.vb (Developer-Managed)
Public Class Form1
End Class

' Form1.Designer.vb (Designer-Managed)
Partial Public Class Form1
    Inherits System.Windows.Forms.Form
  ...
  Private Sub InitializeComponent()
    ...
  End Sub
  ...
End Class
Here's the C# equivalent:
// Form1.cs (Developer-Managed)
partial class Form1 : Form {...}

// Form1.Designer.cs (Designer-Managed)
partial class Form1 {
  ...
  private void InitializeComponent() {...}
  ...
}

If you're used to C#, you might also wonder where the application entry point (static Main method) has gone. Because Main isn't related to any specific form, it's also been moved to its own file, Program.cs, as shown here:

// Program.cs
static class Program {
  /// <summary>
  /// The main entry point for the application.
  /// </summary>
  [STAThread]
  static void Main() {...}
}
By default, Visual Basic 2005 projects are configured to launch Form1 on application startup. If necessary, a custom Shared Sub Main can be specified as the application entry point. For developers, this code separation means you only have to write the code needed to address the specific problem. From a Windows Forms Designer point of view, separating InitializeComponent protects you from inadvertently tampering with it and potentially causing irreparable damage to your form.

Project Configuration

Windows Application projects also include new folders to manage project configuration. They are My Project for Visual Basic and Configuration for C#. Duncan Mackenzie covers My Project in his detailed article at Navigate the .NET Framework and Your Projects with "My", so we'll just take a look at the C# side of things here. Figure 2 shows the result of double-clicking a project's Configuration folder.

Figure 2 C# Project Property Editor (Close Figure 2)

Most of the configuration settings from previous versions of Visual Studio can be found here, although Resources and Settings are new. Exposing project configuration via a designer-managed editor instead of a modal dialog makes them more accessible; project configuration can be kept open constantly and accessed with only a single click of a Visual Studio 2005 file tab.

Improved Resource Management

A good deal more has happened to project resources than simply exposing them via a tab. For starters, the Resource Designer has been given a much needed facelift, the major benefits being improved management and the WYSIWYG ability to see what your resources look like at design time, as illustrated in Figure 3.

Figure 3 Visual Studio 2005 Resource Editor
Figure 3 Visual Studio 2005 Resource Editor

The new Resource Designer allows you to add new and existing files through a variety of mechanisms, including the dropdown list shown in Figure 3, pasting from the Clipboard, and dragging and dropping. You can even drag strings from sources like Microsoft Word, saving you extra typing where string data already exists.

Whichever mechanism you use to add resources, they are automatically added to one of several categories, including Strings, Images, Icons, Text Files and Sound Files. Another category, Other, holds extra resource data such as component-defined serialization of design-time data. You can view each category by changing the value of the aptly named Categories selection. Each category offers more granular views. For example, you can view all resource types other than strings through Details, List, or Thumbnail view.

Beneath the surface, the Resource Designer persists resource data to the new default Resources resource (.resx) file contained within the Configuration project folder, and works hard to make your resource programming experience as simple as resource management is now. Consider what was previously required to retrieve a string resource from your code:

// Load string resource from project resource file
Assembly assem = Assembly.GetExecutingAssembly();
ResourceManager resman = new ResourceManager("MyApp.Resource1", assem);
string myStringResource = resman.GetString("MyString");

Now consider the simplified and strongly typed approach enabled by Visual Studio 2005:

// Load strongly typed string resource
string myString = Configuration.Resources.MyString;
The Resource Designer exposes every resource you add to your project's resources as a strongly typed, static (shared) property from a resource editor-generated managed class called Resources that lives within the ProjectName.Configuration namespace. These properties are generated after either the project is rebuilt or after you run the custom tool associated with the .resx file to generate the class: ResXFileCodeGenerator. Now any type of resource including icons, images, and sounds is as easy to retrieve as a string:
// Strongly typed resources of all kinds
Icon icon = Configuration.Resources.MyIcon;
Image image = Configuration.Resources.MyImage;
MemoryStream sound = new MemoryStream(Configuration.Resources.MySound);

Currently, only a default Windows Forms project's resource file generates a strongly typed resource class by default. If you want the same support for additional .resx files, you'll need to set their Custom Tool property to ResXFileCodeGenerator.

Improved Settings Management

Like resources, settings have a custom editor that allows you to add typed settings to one of two scopes: read/write user settings and read-only application settings. User settings can change frequently, while application settings, like database connection strings, are expected not to change once an application is deployed. Application settings are persisted to an application's configuration file (applicationName.exe.config). Figure 4 shows how you can use the Settings Designer to set both user and application settings.

Figure 4 Visual Studio 2005 Settings Editor
Figure 4 Visual Studio 2005 Settings Editor

The Settings Designer generates a Settings class (located in the ProjectName.Configuration namespace), from which it exposes each setting as a per-instance property of the type specified. Application settings are exposed as read-only properties while User settings are exposed as read/write properties. Either way, properties can be programmatically inspected or updated via a generated static (shared) Value property:

// Inspect an Application-scoped setting
string appSetting = Configuration.Settings.Default.MyAppSetting;

// Update a User-scoped setting
Configuration.Settings.Default.MyUserSetting = "myNewUserSetting";

This support offers the same great benefits you would get by using strongly typed resources, especially when ad hoc settings processing is all you need.

Since users expect their settings changes to be saved from one application session to the next, settings need to be loaded at the beginning of each new session and saved at the end of each. Half of this job is taken care of for you: application and user settings are automatically loaded when first used in a new session. To save them at the end of each session, you have to make a single call to the Save method, which is exposed to your code indirectly via the Settings class:

void OptionsForm_FormClosing(object
  sender, FormClosingEventArgs e) {
  // Save user settings for next time
  Configuration.Settings.Default.Save();
}
A suitable location for this code would be the FormClosing event handler. User settings are persisted to a Windows Logo-compliant location within the file system on a per user basis in a file named user.config, where user is the current user's profile name. On a typical system, here's the path to that file:
c:\documents and settings\username\Local
Settings\Application Data\company name\product
name_hash\product version\

Sometimes users change settings to values they are not happy with and then can't remember what the previous defaults were. Fortunately, there are two simple recovery options. First, you can provide a mechanism for users to revert to the last saved settings by calling the Settings object's Reload method, like so:

void reloadSettingsButton_Click(object sender, EventArgs e) {
  // Revert back to last saved user settings
  Configuration.Settings.Default.Reload();
}
This loads the settings from the same persistent store used when the application was first started. If user settings are changed beyond all recognition, you can revert to the default user settings, which are the initial values entered into the Settings Designer. To retrieve them, simply call the Settings object's Reset method:
private void resetButton_Click(object sender, EventArgs e) {
  // Revert back to default installed user settings
  Configuration.Settings.Default.Reset();
}

User settings are managed in such a way that if the user.config file is deleted the default values for those settings are loaded into the next application session, which is the same as calling the Reset method. The settings subsystem comes with more exotic capabilities, including upgrade support, settings profiles, and even the ability to create custom settings providers—a feature that would allow you to save user settings to a Web service, for example.

System.Windows.Forms.Form

It's also worth your time to explore the full gamut of enhancements to System.Windows.Forms.Form, the number one class in Windows Forms app development. The changes to System.Windows.Forms.Form have been made in accordance with the prime objective of the .NET Framework 2.0: productivity. Figure 5 lists key new members that will simplify your life. Of particular interest are the enhancements to layout capabilities.

Figure 5 System.Windows.Forms.Form Key Enhancements (Close Figure 5)

Member | Type | Description
Flash, StopFlash | Methods | Flashes your form's taskbar button, window title bar, and window StopFlash border. Inactive forms flash to notify the user that something has happened. By default, calling Form.Activate will flash both taskbar button and window title three times.
ValidateAll | Method | Validates all controls within a scope specified by the ValidateFlags enum, including All, Enabled, ImmediateChildren, Selectable, TabStop, and Visible. Calling ValidateAll passing in a ValidateFlags value defaults to Selectable.
AutoValidate | Property | Can be one of three values: Yes (default), No, or Inherits. No prevents any controls with CausesValidation set to true from firing their Validating and Validated events, which avoids focus being retained in an invalid control.
FormClosing, FormClosed | Events | Use instead of Closing and Closed when a form is closed; both offer more detailed information.
ResizeBegin, ResizeEnd | Events | If handling the Resize event is not granular enough, ResizeBegin and ResizeEnd should give more flexibility.

Form and Control Layout

Both System.Windows.Forms.Form and the Windows Forms Designer have been enhanced to offer richer support for form and control layout management. The whole process has been simplified dramatically.

Snap-lines, Beta 1 Style In our previous article, we showed how snap-lines simplify the task of aligning controls with horizontal and vertical edges; the designer redlines snap-lines with a third snap-to feature that helps align controls to common text baselines. Figure 6 shows top-edge, text baseline, and bottom-edge snap-lines in action.

Figure 6 Alignment
Figure 6 Alignment

Padding and Margins Snap-lines allow you to quickly align your controls horizontally and vertically as you drag them around a form. Besides being aligned to one another, controls also need to be nicely spaced out. That's the job of two new properties: Padding and Margin. Padding is the internal distance in pixels from the edge of a form or control's client area that child controls (and also text, images, and so on) can't intrude on. A margin is the external distance in pixels from the edge of a control that other controls can't intrude on. When you drag controls around a form, the Windows Forms Designer will display a dotted line between controls and the form when the distance between them is the sum of both their Margins and Padding (where relevant). Figure 6 shows the dotted line between Label and TextBox controls. Figure 7 shows how margins and padding are used to determine when controls are correctly spaced. Both Padding and Margin properties can be specified at once for all edges, or specified edge by edge.

Figure 7 Margins and Padding
Figure 7 Margins and Padding

Complex Layout Scenarios For more complex layout scenarios, such as resizable dialogs with nontrivial numbers of controls, you'll need an advanced layout engine, an infrastructure for building specialized layout controls to handle more elaborate layout needs. Two layout control implementations come with Windows Forms 2.0, TableLayoutPanel and FlowLayoutPanel, both of which borrow from HTML-style layout capabilities. Both are panel-like controls designed to host other controls while simultaneously applying their particular layouts at both design time and run time.

TableLayoutPanel TableLayoutPanel allows you to define rows and columns that can be configured to size automatically in one of three ways: AutoSize, Percent, and Absolute. Rows and columns can also be merged and physically resized through the Windows Forms Designer. Each cell can only contain one control whose position within the cell is determined by docking and anchoring. Figure 8 shows an example of how the TableLayoutPanel might be used.

Figure 8 TableLayoutPanel Control
Figure 8 TableLayoutPanel Control

Figure 8 also shows both the OK and Cancel buttons hosted in a nested TableLayoutPanel. Since TableLayoutPanel can host only a single control in each cell, you might find nested tables a useful technique. Alternatively, you could utilize TableLayoutPanel's support for merged columns and rows.

FlowLayoutPanel If tabular layout is not what you're after, the FlowLayoutPanel might be more suitable, especially when you want a form and its controls to resize the way HTML Web pages do. You can tell a FlowLayoutPanel the direction in which the controls should be moved during form resize by setting its FlowDirection property to one of four values: LeftToRight, TopDown, RightToLeft, or BottomUp.

As with Web pages, a FlowLayoutPanel may be resized to be so small that controls simply cannot be moved any more, leaving them partially or fully covered by either vertical or horizontal edges of the FlowLayoutPanel, or both. Setting its AutoScroll property to True provides vertical and horizontal scrollbars as needed.

Strip Controls

Besides the layout panels, some of the new controls you saw in our previous article have undergone significant enhancements. One of the most significant is reminiscent of the movie Gremlins; someone at Microsoft obviously fed the WinBar after midnight because it has since multiplied into a complete suite of "strip" controls, including MenuStrip, ToolStrip, StatusStrip, and ContextMenuStrip (see Figure 9).

Figure 9 Strip Controls
Figure 9 Strip Controls

The new strip controls improve on MainMenu, ToolBar, StatusBar, and ContextMenu in several key ways, including:

  • A consistent API to all strip controls
  • Improved design-time experience
  • Allowing adjacent strip controls to be repositioned with respect to one another, as well as repositioned to the top, right, bottom, and left of the host form's client area
  • Contemporary look of Office 2003 and Windows themes
  • Ability to host a rich set of child controls, such as dropdown lists, labels, menus, and textboxes
  • Rendering support that simplifies custom painting coding

While MainMenu, ToolBar, StatusBar, and ContextMenu are not included by default in the Visual Studio Toolbox, they are still included in the .NET Framework, primarily for backwards compatibility. Of course, the strip controls are located on the Toolbox so you can easily drag them onto your form. Through the Designer, you can add a variety of controls to each strip control (see Figure 10).

Figure 10 Controls Hosted by Strip Control (Close Figure 10)

Strip Control | Designer-Allowed Strip Item Controls
MenuStrip | ToolStripMenuItem (for both top menu and submenu items) ToolStripComboBox ToolStripTextBox ToolStripSeparator (for submenus)
ContextMenuStrip | Same as MenuStrip
ToolStrip | ToolStripButton ToolStripComboBox ToolStripSplitButton ToolStripLabel ToolStripSeparator ToolStripDropDownButton ToolStripTextBox ToolStripProgressBar
StatusStrip | ToolStripStatusLabel ToolStripProgressBar ToolStripDropDownButton ToolStripSplitButton

Strip controls offer a number of flexible configuration options. Both strip controls and strip item controls can be wholly configured from the Property Browser, while smart tags support configuration of the key properties for each specific control. If that's not enough, you can add, update, and delete strip items on the visual design surface directly, or from an Edit Items dialog, which can also be accessed from both Property Browser and smart tags, as appropriate.

Both MenuStrip and ToolStrip provide an Insert Standard Items option to prepopulate strip items for typical commands such as Open, Save, Save As, Cut, Copy, and Paste. Interesting configuration options you can apply are listed in Figure 11.

Figure 11 Key Strip Control Property Configurations (Close Figure 11)

Property | Description
AllowItemReorder | Users can reposition strip items by setting this property to True. The user needs to press Alt before repositioning (not for ContextMenuStrip).
ShowItemToolTips | Sets whether strip items can also display tool tips (not for MenuStrip or Context MenuStrip).
Dock | Specifies the form edge to line the strip control up against or fill.
RenderMode | Strip controls support three render modes: System: rendered with colors from the system palette. Professional: rendered according to the current Windows Color Scheme—Blue, Olive Green, or Silver. ManagerRenderMode: rendered using the Windows Theme sensitive ToolStripRenderer and is the default. Custom is currently unsupported.
SizingGrip | Specifies whether the form sizing grip should be shown or hidden (StatusStrip only).
GripStyle | Specifies whether a MenuStrip or ToolStrip displays a grip allowing it to be repositioned.
CanOverflow | When the form becomes narrower than a strip control, this option specifies how to visually represent strip items that are hidden by the overlap, either hidden if False or accessible via a dropdown at the end of the strip control if True.

Apart from standard properties you would typically find on controls, including Enabled, Visible, Checked, and BorderStyle, tool strip item controls also have several unique properties which are described in Figure 12.

Figure 12 Key Strip Item Control Property Configurations (Close Figure 12)

Property | Description
DisplayStyle | Specifies what to display; values include None, Text, Image, and ImageAndText.
TextImageRelation | Sets the position of text in relation to an image. Applied when DisplayStyle is ImageAndText.
ImageScaling | Specifies how an image is rendered, either ShrinkToFit or None.
Alignment | Determines which end of a strip control a strip item control will align to, either Head or Tail (Left or Right sides, respectively, when rendering left-to-right).
Overflow | Determines how each strip item control responds to overflow. Can be either Never, Always, or AsNeeded, which leaves the decision up to the host strip control.

ToolStripContainer
Figure 13 Tool Strip
Figure 13 Tool Strip

By default, strip controls sit where they lay at design time and run time. However, additional support for strip controls can be employed to enable developers and users to drag them from one form edge to another. If this is a feature you need to provide, you can use the new ToolStripContainer control. ToolStripContainer comprises four tool strip panels, shown in Figure 13 as blue, that surround a content panel, shown in gray.

The content panel is for the controls from which you'd normally compose your form. Tool strip panels are designed to house strip controls. The four buttons you can see on the edges of the tool strip panels expand and collapse the size of each tool strip panel by the height or width required for a single tool strip control. When you drag your strip controls onto the tool strip panels at design time, they are stacked horizontally or vertically next to one another. To change their position within the stacks, simply drag them to the new desired location. Z-Order is used to store and apply a strip control's stack position, with higher Z-Ordered strip controls residing closer to the form edge.

At run time, strip controls appear in their default tool strip panels when a form loads. Figure 14 illustrates how users can subsequently drag them from one form edge to another, as long as a ToolStripContainer was used.

Figure 14 Dragging a Strip
Figure 14 Dragging a Strip

When a strip control is relocated to another tool strip panel, the tool strip container is shifted to make room. The benefit here is that all controls contained within the tool strip container are also shifted, saving you the trouble of doing the repositioning manually.

Strip Control Settings Management

Strip controls can have a lot happen to them at run time, with all the repositioning and strip item reordering going on. Since these are user changes, a good application should ensure that they are persisted across application sessions. Strip controls provide two properties that will make sure this happens automatically: SaveSettings and SettingsKey. SaveSettings is a Boolean that by default is set to False. When set to True by the developer, the strip controls persists values including raft position, location, size, and strip item control arrangement to the user settings subsystem that you saw earlier. SettingsKey is a String value that identifies the set of persisted values in the settings file:

FormName.StripControlName
You can change this as necessary, but once enabled, those values are automatically retrieved from user settings and applied to the strip control to make sure it is positioned correctly each time the application loads. Only strip controls and strip control items provide this feature currently, although you can add settings support in your own custom controls.

MaskedTextBox Control

Another new control, MaskedTextBox, is used for data entry. You use mask characters at design time to specify the type of data that can be entered and its format. For example, the mask character "9" specifies that a user can enter either a space or any number from 0 to 9, while the mask character "0" requires a number be entered. Of course, there is a wide range of mask characters, covering numbers, letters, punctuation, and spacing. Please refer to the evolving Visual Studio documentation for specific details. Developers can build a mask by setting the MaskedTextBox's Mask property with the appropriate mask value. The Input Mask editor, shown in Figure 15, lets you choose an existing common mask or construct your own, and it allows you to test them dynamically.

Figure 15 Input Mask Editor
Figure 15 Input Mask Editor

Non-mask characters, such as parentheses, hyphens, and spaces, are treated as literals and are shown at run time to help build a stronger visual cue about the type of data that's being entered. On the other hand, all mask characters are replaced by a prompt character at run time (underscore by default), highlighting where the user should key in the data. At run time, the prompt characters are shown only when a MaskedTextBox has the focus.

MaskedTextBox implements a wide variety of properties (see Figure 16). It's a highly configurable control that you should utilize to give users a more informed data entry experience.

Figure 16 Key MaskedTextBox Properties (Close Figure 16)

Property | Description
HidePromptOnLeave | True by default but, if set to False, ensures that prompt characters are always visible.
PromptChar | Used to change the prompt character. Underscore by default.
AllowPromptAsInput | In some cases, your prompt character may also be a valid value and setting AllowPromptAsInput to True allows this when neither can be reasonably changed.
IncludeLiterals | Specifies whether to include literal character values in MaskedTextBox's Text property.
IncludePrompts | Specifies whether to include prompt character values in MaskedTextBox's Text property.
ResetOnPrompt | If True, when a prompt is entered, a prompt is inserted and values on the right-hand side are shifted right.
ResetOnSpace | If True, when a space is entered, a space is inserted and values on the right-hand side are shifted right.

Data Binding

In our previous article, we looked at two new controls in the data binding space, GridView and DataContainer. Just as WinBar underwent substantial changes, so too have both data controls. GridView is now known as DataGridView, and DataContainer has been split into two new components, BindingSource and BindingNavigator. In fact, there have been many changes across the entire data binding subsystem; we'll explore the most notable of these changes.

Figure 17 Data Sources
Figure 17 Data Sources

A data binding needs a data source. You'll find it much easier to create in Visual Studio 2005. The simplest way is to select Data | Add New Data Source, which opens the Data Source Configuration Wizard. Your choice of data source has increased somewhat to include databases (as you'd expect), Web services, and other objects. If you were to create a data source from a database, not only would a new typed DataSet be added to your project, but you could also view it from the new Data Sources tool window (Tools | Data | Show Data Sources), shown in Figure 17.

The Data Sources tool window allows you to edit your data source with the typed DataSet designer, reconfigure the selected data source, refresh an existing data source, or add a new one. If you haven't already created a data source using these techniques, controls like DataGridView will prompt you to select or create an existing one when they are dropped on the form.

Binding to a Data Source

Just as there are several ways to create a data source, there are also several ways to bind controls to them. First, you can drag tables and fields from the Data Source tool window directly onto your forms. Behind the scenes, this simple act makes several important changes to your form, including:

  • Adding a typed DataSet component to which your controls will bind
  • •Adding a typed data adapter, if the datasource is a database, which assists with the loading and updating of your data
  • •Adding a BindingSource component to expose the data source (discussed in the following text)
  • •Adding a BindingNavigator component to the form for data browsing (discussed in the following text)
  • •Creating or updating the form's Load event handler to automatically load data from the data source
  • •And, of course, adding a control that's automatically bound to the field or table you dragged

How does the designer determine which type of control to create and bind to? In Figure 17, you'll notice that both the Employees table and fields have an icon next to them. This icon represents the type of control that will be created when either a table or field is dragged onto the form. Figure 18 shows how you can configure the control type from the Data Source tool window.

Figure 18 Set Control Type
Figure 18 Set Control Type

Most options are obvious; "[None]" prohibits a table or field from being dragged onto the form at all. "Customize..." allows you to specify which controls you can create for each possible data type, or which listing style for tables. When a field is dragged onto a form, both the specified control type and a Label control are created. If the source field's name is reasonably well formatted, the designer uses simple heuristics to convert it into a properly spaced and cased text value the new Label control displays. For example, "EmployeeID" becomes "EmployeeID:".

New Data-Bound Components

When a data source is dragged onto a form as a DataGridView, BindingSource and BindingNavigator components are also added to the form. Between a data-bound control and its data source exists a BindingManager whose jobs include:

  • Managing the set of bindings between a bound control and its data source
  • Keeping bound controls up to date when underlying data sources change (and vice versa)
  • Tracking the current record (or currency)

Complex data bound controls, such as a DataGridView, know how to access and use a BindingManager to help perform all of these functions. However, in simple binding scenarios such as binding a TextBox's Text property to a list's data field, there is no such support. Instead, you had to manually write your own code to access and use a BindingManager. For example, the following code moves to the next record of a list data source such as a database table:

void nextButton_Click(object sender,
  EventArgs e) {
  // Move to the next record
  Binding binding = nameTextBox.DataBindings["Text"];
  BindingManagerBase manager = binding.BindingManagerBase;
  manager.Position = manager.Position + 1;
}
To save you all this effort, the BindingSource was created to wrap binding managers and expose them through a single component-based interface. Much like other complex bound controls, you bind a BindingSource to a data source by setting its DataSource and DataMember properties. This ultimately allows the BindingSource to access the binding manager and, through the BindingSource's interface, allows you to much more easily utilize it. The BindingSource uses reflection to determine which fields are exposed by the underlying data source and exposes them through its own IBindingList implementation. This means that you can bind the Text property (or any property) of a TextBox control (or any control) directly to the BindingSource. This effort reduces the code from the previous sample to just one line, as shown here:
void nextButton_Click(object sender, EventArgs e) {
  // Move to the next record
  this.employeesBindingSource.MoveNext();
}

Figure 19 Binding Navigator
Figure 19 Binding Navigator

If it's VCR-style navigation you're after, you can use a BindingNavigator component, shown in Figure 19, to avoid having to write any code at all. A BindingNavigator is a specialized strip control that binds to a BindingSource via its BindingSource property, which is used by default BindingNavigator buttons to manage data source navigation. Because BindingNavigator is a strip control, you can reconfigure it by either altering the default tool strip item configuration or extending it with new tool strip items, for example to load and save bound data.

Where Are We?

In this article, we covered a subset of new and improved Windows Forms features found in the December 2004 CTP release of both the .NET Framework 2.0 and Visual Studio 2005. We started by exploring some new Visual Studio 2005 features, including improved resource and application settings management, which also generates strongly typed interfaces. We moved on to look at productivity-focused updates to the System.Windows.Forms.Form class. The design-time experience has also gotten productivity-enhancing improvements that include a wide variety of layout support. The same applies to the tantalizing new strip controls, MaskedTextBox control, and a slew of improvements across the data-binding infrastructure, including the DataGridView, BindingSource, and BindingNavigator.

Windows Forms 2.0 contains significant productivity and feature updates well over and above its .NET Framework 1.x predecessors. Consequently, you should seriously consider it as your Windows Forms development platform for the next several years.

Chris Sells is a Content Strategist for MSDN online, currently focused on "Longhorn," the next version of Windows and smart clients. More information is available at www.sellsbrothers.com.

Michael Weinhardt is currently co-updating Chris Sells's book, Windows Forms Programming in C#, and writes a monthly column for MSDN online called "Wonders of Windows Forms." Visit www.mikedub.net for further information.


Safe!: Repel Attacks on Your Code with the Visual Studio 2005 Safe C and C++ Libraries

Safe!

Repel Attacks on Your Code with the Visual Studio 2005 Safe C and C++ Libraries

Martyn Lovell
This article is based on a prerelease version of Visual C++ 2005. All information contained herein is subject to change.
This article discusses:
  • The Safe C and C++ Libraries in Visual C++ 2005
  • Security principles that drove their design
  • Which functions are affected
  • Tips, tricks, and examples of using the new Safe Libraries
This article uses the following technologies:
C, C++, Visual Studio
Contents
Guiding Principles to Safe Libraries
Safe Libraries Design
The Library in Practice
The Safe C Library
Safe C++ Library
Other Safety Improvements in Visual Studio 2005
Conclusion

W
hen Visual Studio® 2005 ships, you'll notice that it includes a major upgrade to the Visual C++® Libraries. This upgrade resulted from a complete security review of the functions contained in the C Runtime (CRT) Library, Standard C++ Library (SCL), Active Template Library (ATL) and Microsoft® Foundation Classes (MFC). This extensive review mandated substantial changes that can improve the security and robustness of your apps.

Changes include deprecation of functions that are known to be risky, such as strcpy. New, safer functions have been added to supersede them. Many functions have gained extra error checking and validation. Extra debugging support has been added where it can help the most.

This article describes the Safe C and C++ Libraries available in Visual C++ 2005. I'll cover the architecture and design principles that guided the development of these libraries, and I have included specific examples of various ways to use them to write more secure code. I will also briefly discuss how other security-related libraries work. Finally, I'll present a migration guide to help you manage the transition of your code from earlier versions of Visual C++ to Visual C++ 2005.

The code samples here have been tested with a prerelease build of Visual Studio 2005. I encourage you to get a copy of the latest Beta and follow along with your own applications. That's probably the best way for you to get a handle on the important changes.

Guiding Principles to Safe Libraries

When the Visual C++ team was designing the Safe Libraries, we used several principles to guide the design development. Familiarity with these principles will help you understand the choices we made, but remember that there is no library, development process, document, or review that ensures completely secure code. The use of these libraries can help make your existing code more secure with relatively small changes, but nothing can stop you from doing something risky in your own code, or block you completely from misusing our code. Ultimately, even after you enable all of the new Safe Libraries, you still need to follow the other best practices at the heart of a secure development process—such as threat modeling, code review, and penetration testing. These topics are well covered in Writing Secure Code by David LeBlanc and Michael Howard (Microsoft Press®, 2002).

"Secure by default" is one of the Trustworthy Computing principles that helped guide this effort. We know that deprecating strcpy and generating a warning at each usage will cause significant migration cost. But we wanted to make sure that out of the box, you'd automatically get the safest behavior, even for upgraded projects. Of course, you can always disable the warnings until you are ready to deal with them.

In designing the libraries, we tried to reduce the number of paradigm shifts. There are as many opinions on the best and safest way to code as there are software developers. I've found that the safest way to write string manipulation code is to use a C++ string class like std::string or MFC/ATL's CString. The Visual C++ team knows that there is a lot of C-style code out there that is in a maintenance or incremental evolution phase and which might not bear the cost and risk of a rewrite to use C++ and a string class. As a result, we've provided Safe Libraries that work the way your code works today. This allows you to apply these libraries even to your older, less malleable code.

Some of the security work we did in the libraries for the Visual C++ 7.1 releases included full reviews and a large number of changes and fixes. A main objective, however, was to not break source compatibility, since we were only working on a point release. In order to move our safety work to the next level, the team had to take a different approach for the new release. While we still try to avoid breaking compatibility, when we found a problem that absolutely required a source compatibility break, we did that. For example, there is no way for strcpy to be safe in C code without taking an extra parameter to indicate the size of the destination buffer. The library code also marks every function that has been deprecated in this way in the documentation for easy reference. We couldn't provide you with the safest libraries without making some changes to your code, so with the new libraries we're doing that. And because the threats today are very different from those that existed way back when the C Standard Library was designed, some changes of emphasis are necessary.

Despite these reasons for changing your code, we recognize that there are many costs. Each time an upgrade of Visual Studio forces you to make modifications, you must pay, face the risk of introducing new bugs, and perform testing to ensure continued quality. In most cases, we've been able to make improvements to the safety of the libraries without requiring you to make any change at all, and where changes are required, we've tried to make them easy to perform by designing the new functions to closely match the old ones and to use consistent patterns.

We considered it our responsibility to the development community to extend beyond our own product, so I was delighted to get a chance to work with the C standards committee on the proposals for the Safe Libraries. The committee has provided lots of useful suggestions and feedback to help us evolve the functions. We're hoping that we'll soon be able to issue the technical report on this subject from the C committee. Keep a look out for the current draft of this technical report at www.open-std.org/jtc1/sc22/wg14.

Safe Libraries Design

My team owns the Visual C++ Libraries, which includes some of the newest code in the developer division (such as ATL Server) as well as some of the oldest code in the product (such as the CRT). When we looked at the code, we saw some large differences in coding standards as common practice has improved in the last 20 years. One thing that stood out was that some of the older code was written at a time when every extra byte of code was precious, and thus lacked full validation of all parameters.

The newer code is littered with assertions and checks, and we've found that these really help debugging. Increasingly, we've focused on making sure that the retail code is just as robust as our debug code in the face of unexpected conditions. These checks aren't just for debugging any more—they also make the library code safer. A key part of the Safe Libraries initiative has been to add validation code to most of the library functions that has impact on both debug and retail code. For example, if you pass the libraries invalid flags, they will now assert and tell you about your bug.

A lesson we've learned from looking at lots of security attacks is that attackers often exploit the failure to perform appropriate error checking, or an application's "tolerance" for unexpected situations, such as an error return from a standard function. We took this into consideration when we designed the secure code generation (/GS) feature for Visual C++ 7.0. When code compiled with /GS detects a stack-based buffer overrun, the library code terminates it immediately with a minimum of extra code running in the process, thereby reducing the attack surface. We've reduced this surface even further for Visual C++ 2005 (for more information, see Stephen Toub's article "C++: Write Faster Code with the Modern Language Features of Visual C++ 2005" in the May 2004 issue of MSDN®Magazine). The problem is that any code that happens after the library code detects a failure may help an attacker exploit your process, so all that code has been kept small.

The very same logic was applied to the validation code. If you pass the library code a bad parameter, it will respond in debug by asserting and helping to debug your code. If you take a quick look at the call stack at the time of the failure you will be able to track back from the assert to the place in your code that caused the problem. In the retail build, the library code calls its invalid parameter handler, whose default action is to invoke Windows Error Reporting, which can capture a crash dump and invoke a just-in-time debugger as needed.

In this context, an invalid parameter is one that displays an obvious bug that you could have known about when you wrote the code. These often result from undefined behavior in the C standard. For example, if your code writes a string to a NULL pointer using sprintf, the library code will report an invalid parameter. However, if you ask the library code to allocate 10MB of memory using malloc, and the system only has 5MB of memory free, it will just return NULL as it always has. This is just an expected runtime error, not a programming bug.

One problem with this kind of runtime error is that not all of the functions provide a way to return an error directly to the caller. Some of the C functions rely on setting errno, and some don't even set that. When we added new functions, we tried to make sure that they had an error return path directly from the function.

Though aborting is often the safest thing to do in this scenario, you do have the ability to configure the response to this situation. C code can choose an alternative handler, which can for, example, result in the code returning a C-standard error code. Code that handles errors in portable ways across multiple platforms might choose this model. C++ code might instead choose to throw an exception from this handler.

Much of a developer's work seems to revolve around strings. This is one reason why buffer overruns are such a common cause of problems in code. Moreover, one of the largest sources of problems in the Visual C++ library code is the set of traditional string functions in the C Runtime Library.

Arguments about which kind of string buffer is better or safer will rage forever. The choice between null termination and length prefix has long been a part of the low-level language wars. Since we do not want to force major change, we need to live within the boundaries of existing practice in C. The C language and its libraries are intimately tied to the null-terminated string. Their design philosophy reflects the priorities and constraints of thirty years ago when performance and code size were the primary concerns. But to prevent buffer overruns in the libraries, we're going to have to treat those null-terminated strings with a little more rigor than the current library does.

The simplest rule is also the one that creates the most work for the developer and the library designer: never write to a string buffer without knowing how much space you have to write into. The design of strcpy makes the most basic mistake here. There is no way for strcpy to safely write to its destination because it does not know how much space the caller has allocated.

Following this simple rule requires the addition of many new functions, each of which is characterized by the addition of an integer parameter after the destination buffer: strcpy(dest, src) becomes strcpy_s(dest, destsize, src).

You might wonder why the library code does not also need to pass in a size for the source parameter. This question leads to a key design point of the Safe Libraries. I've already mentioned that we had to stick with null-terminated strings. Remember that a null-terminated string that you are reading from already knows its own length. Passing in a second length value may create new truncation failure modes. Additionally, when analyzing existing code, many strings are stored in fixed size buffers that contain only the string itself. For these strings, adding an input size would not add any information. Finally, we found that passing a size around here caused large code churn in many applications so we reached an unavoidable conclusion—functions must trust their input strings.

This isn't really as surprising as it first sounds. Remember that the library code also trusts that the length you pass as the destination size matches the size you allocated for the destination string. With native C and C++ code, you can always pass the wrong thing to a library and undermine the application.

We apply the same principle in the Safe C++ Libraries in that your input is trusted, but the library code insists that you detail your output. If you pass in a pair of input iterators, the libraries trust you to have not exceeded the bounds of your container. But if you pass an output iterator, it must be a checked iterator that knows how much space it has to output into. Traditional unchecked iterators cannot be used as they could write past the end of a buffer.

There is obviously one major case where you definitely don't want the library code to assume that an incoming string is in good shape—when it comes from an untrusted source. For example, imagine you are parsing a packet of data that came from another process or from across the network. In this case, a specially crafted packet might omit a terminator in the hope that this will cause some problem in your code. We added a special function (strnlen) to find the length of an untrusted string. Unlike the other new functions, you don't use this in place of strlen. Instead, you use it in specific cases when you are dealing with untrusted data.

Strings are the largest and most complex challenge we face in our work, but the team addressed several issues at the same time. For example, in the library's file functions, all the new functions deal correctly with the Windows extra-long paths. The team changed all of them to default to an exclusive sharing mode, reducing the risk of an attack against temporary or intermediate files. We also switched all the global variables to be function-based, so that the libraries could report a problem if you ask for a global variable at a time during startup when it is not yet initialized. Additionally, we changed the code of all the lowest-level implementation functions to use only a small amount of stack space and then to fall back to using heap space. This should make them more robust in constrained environments.

The Library in Practice

Let's now take a look at the code in section A of Figure 1 and see how you might use the Safe Libraries to remediate it. This is just a very short piece of string manipulation based on the CRT, but the different possible changes serve to illustrate the kinds of choices you'll have to make as you modify your code.

Figure 1 Remediation with Safe Libraries (Close Figure 1)

A: Original code
void dotdotdot(const wchar_t * src)
{
    // Original
    wchar_t dest[20];
    wcscpy(dest, src);           // compiler warning
    wcscat(dest, L"...");        // compiler warning

    // ...
}
B: Basic remediation
void dotdotdot2(const wchar_t * src)
{
    wchar_t dest[20];
    wcscpy_s(dest, _countof(dest), src);
    wcscat_s(dest, _countof(dest), L"...");

   // ...
}
C: Truncation remediation
void dotdotdot3(const wchar_t * src)
{
    const wchar_t ellipsis[]= L"...";
    wchar_t dest[20];
    wcsncpy_s(dest, _countof(dest)-_countof(ellipsis)+1,
              src, _TRUNCATE);
    wcscat_s(dest, _countof(dest), ellipsis);

   // ...
}
D: Known size remediation
void dotdotdot4(const wchar_t * src)
{
    const wchar_t ellipsis[]= L"...";
    wchar_t dest[MAX_SRC_SIZE+_countof(ellipsis)];
    wcscpy_s(dest, _countof(dest), src);
    wcscat_s(dest, _countof(dest), ellipsis);

   // ...
}
E: Flexible size remediation
errno_t dotdotdot5(const wchar_t * src)
{
    const wchar_t ellipsis[]= L"...";
    wchar_t *dest=NULL;
    size_t len = wcslen(src) + _countof(ellipsis);
    dest=(wchar_t *)calloc(len, sizeof(wchar_t));
    if(!dest)
    {
        return ENOMEM;
    }
    wcscpy_s(dest, len, src);
    wcscat_s(dest, len, L"...");
    // ...
    free(dest);
    return 0;
}
F: Rewrite remediation
void dotdotdot6(const std::wstring &src) throw(...)
{
    std::wstring dest(src);
    dest+=L" ...";
    // ...
}

Section A shows code with classic buffer overrun risks. The original developer probably thought this string would never exceed 10 characters, so surely 20 was enough. Compiling this code with Visual C++ 2005 will result in function deprecation warnings on each of the lines marked with a warning comment.

Section B shows a very basic fix to the same code. This fix simply reinforces the initial programmer's assumption. If src and "..." are really small enough to fit in dest, then everything will be fine. If not, the library will call the _invalid_parameter_handler and abort the process rather than allow a buffer overrun. This sample uses _countof to determine the size of the buffer. The _countof function is a new addition to the CRT that gets an array element count and, in C++ code only, uses template magic in order to avoid being applied to pointers.

Section C shows an alternative remediation where the developer simply wants to truncate the source string. It's important to understand the difference between wcscpy_s, which assumes you have allocated enough space and aborts if it cannot fit, and wcsncpy_s, which will truncate if it cannot fit into the available space.

Section D assumes that your program has a known absolute maximum size for whatever src represents (for example, a path that cannot exceed MAX_PATH). In this case, you can still use a static buffer on the stack and can return to wcscpy_s. As with Section B, it will abort if the string doesn't fit, but presumably the developer is very sure that nothing is ever longer than MAX_SRC_SIZE.

Section E shows a version of the function that can actually cope with an arbitrarily long string, at the expense of some heap space. This one also aborts in wcscpy_s if there isn't enough space, but that should only occur if you have a logic bug. Note that the code uses calloc, rather than malloc, as doing so avoids the possibility of integer overflow in the multiplication to calculate the size bytes.Migrating Your Code

In the last nine months, we've migrated the Windows® code base and the developer division (Visual Studio and the CLR) code base to use the Safe Libraries. We've learned a tremendous amount and I want to pass on to you some of the tips that we recommend most.

When you first move to the Visual C++ 2005 toolset, you'll want to make sure your application compiles and runs, without making any code changes. To do this, you can briefly disable all the warnings using #defines for _CRT_SECURE_NO_DEPRECATE and _SCL_SECURE_NO_DEPRECATE. You should also set _ATL_SECURE_NO_DEPRECATE if you use ATL and _AFX_SECURE_NO_DEPRECATE if you use MFC. You'll get improved runtime checking, but without compile-time warnings.

Later, you'll want to start fixing your issues by enabling these warnings. I suggest enabling one set at a time and cleaning those up before moving to the next. I strongly recommend use of _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES for C++ developers.

As soon as you're clean on one set of warnings, you'll want to check in so that the efforts of other developers do not set you back. If your project is large, you should break up the work into sub-trees and check them in one at a time. Here are the top 10 tips when moving to the Safe Libraries.

Don't assume strcpy_s and strncpy_s (and other string functions) truncate People often think that strcpy_s will truncate when there is not enough room. It doesn't. Instead, it aborts with the invalid parameter handler. If you need truncation behavior, be sure to use strncpy_s with _TRUNCATE. In general, truncation is unlikely to be the right choice unless your code is producing user-interface or other non-system output. Truncating file paths, for example, can be dangerous.

Know that fopen_s defaults to exclusive mode Use _sopen to open shared files, but remember that shared files require significantly more security analysis.

Don't forget _dupenv_s This is often a lot easier to use correctly than _getenv_s, because it allocates a correct buffer size for you.

Be careful about argument order in scanf_s You'll get runtime errors if you didn't get this right, so you do need to test.

Know that validation was added to printf_s format strings Be sure to test at run time. As an example, the library code will assert if you give us an invalid percent sequence.

Use _countof(x) instead of sizeof(x)/sizeof(element) _countof does the right math, and in C++ it will also warn you if you use it on a pointer.

Know that all sizes are in characters, not bytes Whenever you pass the library code the size of a Unicode string, you'll be passing that in terms of wchar_t count, not byte count.

Know that all sizes include string terminators If you pass the library code a buffer size based on a call to strlen, be sure to add 1 to the value because it won't write any output if you don't give it enough space.

Watch for 0xfd in the debugger This is the fill value used in debug for the end of your buffers. If you see it somewhere unexpected, you probably got a length wrong.

Check for errors Many of the new functions have error returns built in where the old functions didn't. Make sure to check them.

Section F shows what you could do if you have the time to really clean up your code. The design assumes that some of you don't; however, in some cases this kind of change can make sense.

The Safe C Library

When the team examined the existing CRT, we wanted to make some minor changes to its functions. Most of the changes are simple implementation upgrades, such as better parameter validation, improved handling of long filenames, and bounded stack usage. They generally won't require you to change your code, although sometimes you'll find that the increased validation illuminates problems already in your code.

There is, however, a much smaller set of functions where we had to add a parameter or change the behavior significantly (see Figure 2). Even though the list has over a hundred items, you'll notice that many are essentially duplicates. When we deprecate strcpy, we have to make corresponding changes to wcscpy and _mbscpy.

Figure 2 Deprecated Functions (Close Figure 2)

asctime
_cgets
_cgetws
_chsize
_controlfp
_creat
_cscanf
ctime
_ctime64
_cwscanf
_ecvt
_fcvt
fopen
freopen
fscanf
_ftime64
fwscanf
_gcvt
getenv
gets
getw
_getws
gmtime
_gmtime64
_i64toa
_i64tow
_itoa
_itow
localtime
_localtime64
_ltoa
_ltow
_makepath
_mbccpy
_mbscat
_mbscpy
_mbslwr
_mbsnbcat
_mbsnbcpy
_mbsnbset
_mbsncat
_mbsncpy
_mbsnset
mbsrtowcs
_mbsset
_mbstok
mbstowcs
_mbsupr
_mktemp
_open
putw
scanf
_searchenv
setbuf
_snprintf
_snscanf
_snwprintf
_snwscanf
_sopen
_splitpath
sprintf
sscanf
strcat
strcpy
_strdate
strerror
_strlwr
strncat
_strnset
_strset
_strtime
strtok
_strupr
_swprintf
swscanf
tmpnam
_ui64toa
_ui64tow
_ultoa
_ultow
_umask
vsnprintf
_wasctime
_wcreat
wcrtomb
wcscat
wcscpy
_wcserror
_wcslwr
wcsncat
wcsncpy
_wcsnset
wcsrtombs
_wcsset
wcstok
wcstombs
_wcsupr
_wctime64
wctomb
_wfopen
_wfreopen
_wgetenv
_wmakepath
_wmktemp
_wopen
wscanf
_wsearchenv
_wsopen
_wsplitpath
_wstrdate
_wstrtime
_wtmpnam

Each deprecated function is replaced by a safer function with an _s appended to the name. Functions with prefix are Microsoft or POSIX extensions to the C standard. Functions without an underscore are part of the C standard. We've worked with the C standards committee to develop a set of functions that supersede them.

Each of these new functions is marked deprecated. Deprecation is a relatively new Visual C++ compiler mechanism that allows the library code to warn you when you use recommended library features. The function's declaration is marked with __declspec(deprecated), and you see a warning each time you use the function.

We've used macros to group together the deprecations. All the insecure functions are marked _CRT_INSECURE_DEPRECATE in the headers. To prevent the deprecation of these functions, you can set a special macro at compile time—_CRT_SECURE_NO_DEPRECATE—and security deprecation warnings will stop.

These new functions were added to address specific problems when we couldn't fix the problem in the existing function. For most of them, the problem was the lack of a specified buffer size. Almost all string functions, and many other functions involving buffers in the C library, have this buffer size problem. The libraries now always accept the buffer size as the parameter directly after the output buffer.

Lack of a specified buffer size afflicts scanf particularly badly. For scanf_s, the parameters to the format string must include a buffer size after each buffer parameter to the scanf function.

Another common problem is functions that do not terminate their strings correctly. In the Standard C Library, both strncpy and snprintf suffer from this problem. All the new functions always terminate their output and require space for the string terminator within their buffer.

When using the library, we found we needed two models of string function. In most cases, if a string overflows a buffer, it means there is a programming bug, and the library aborts; strcpy_s has this behavior. However, in a smaller set of cases, truncation is the correct behavior. We added a new mode to strncpy_s to support this. When _TRUNCATE is passed as the last parameter, users will get as much of the source string as can fit in the destination while retaining a terminator, and STRUNCATE is returned if truncation occurred.

In a few cases, a function did not need extra parameters but had a critical behavior change. For example, we changed fopen_s to default to opening files in exclusive mode instead of in shared mode. This is a much safer default, but we changed the function name to make sure you explicitly opt into the new behavior.

Figure 3 shows a smaller set of functions where we have added a new function even though its counterpart is not deprecated. In each of these cases, we added a function because there was a way to get extra safety in some situations, but we did not expect them to supersede other functions. For example, qsort_s adds a context variable which can be very useful if you are sorting data and wish to pass extra context information that is not stored within the objects being sorted themselves. In the past, developers often used static variables for this task, which creates the risk of threading and reentrancy issues. Similarly, memcpy_s adds an extra size parameter for the size of the destination buffer. This will often be the same as the size of the source buffer, but some developers find it easier to read if both sizes are provided.

Figure 3 New Functions (Close Figure 3)

_access_s
bsearch_s
_dupenv_s
_lfind_s
_lsearch_s
memcpy_s
memmove_s
_putenv_s
qsort_s
strnlen
_waccess_s
_wdupenv_s
wmemcpy_s
wmemmove_s
_wputenv_s

One major advantage of the new functions is that whenever they write to a buffer, they always know its size. As a result, we've added a new debugging feature to the CRT. Whenever you call a function (such as strcpy_s) in a _DEBUG build, the library code will always fill up the output buffer completely. This helps ensure that if you get the buffer size wrong, you'll see that error much more quickly. It also helps detect other subtle bugs such as the use of variables that are already destructed or out of scope.

Though these changes are in the C library, we added some code specific to C++ to help reduce even further the cost of making your code safer. Figure 4 shows how this can work for you.

Figure 4 Remediation with Safe Libraries and C++ (Close Figure 4)

A: Original code
void dashdashdash(wchar_t *output, const wchar_t * src)
{
    // Original
    wchar_t temp[MAX_SIZE];
    wcscpy(temp, src);        // compiler warning
    wcscat(temp, L" —");      // compiler warning
    wcscpy(output, temp);     // compiler warning
}
B: Simple C remediation
void dashdashdash2(wchar_t *output, size_t size, const wchar_t * src)
{
    wchar_t temp[MAX_SIZE];
    wcscpy_s(temp, _countof(temp), src);
    wcscat_s(temp, _countof(temp), L" —");
    wcscpy_s(output, size, temp);
}
C: Simple C++ remediation
void dashdashdash3(wchar_t *output, size_t size, const wchar_t * src)
{
    wchar_t temp[MAX_SIZE];
    wcscpy_s(temp, src);
    wcscat_s(temp, L" —");
    wcscpy_s(output, size, temp);
}
D: Original code, with a #define
#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
#include <wchar.h>
void dashdashdash4(wchar_t *output, const wchar_t * src)
{
    // Original
    wchar_t temp[MAX_SIZE];
    wcscpy(temp, src);
    wcscat(temp, L" —");
    wcscpy(output, temp);        // compiler warning
}
E: Even simpler C++ remediation
#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES 1
#include <wchar.h>
void dashdashdash5(wchar_t *output, size_t size, const wchar_t * src)
{
    wchar_t temp[MAX_SIZE];
    wcscpy(temp, src);
    wcscat(temp, L" —");
    wcscpy_s(output, size, temp);
}

Section A shows another simple piece of C-style code. Our Safe Libraries will warn on all three lines because buffers are used without sizes. You will notice that the temporary buffer isn't really necessary for this function, but it helps me make my point.

Section B shows the simple remediation discussed already. It uses _countof with wcscpy_s to specify the output buffer size.

Section C shows an even simpler remediation, but this one only works if you are compiling your code for C++. You'll see that this code looks a lot more like the original code. Instead of adding parameters, we just changed the names of the first two calls. We use templates to deduce the buffer size in the first two calls. Because temp is a local fixed-size buffer, the templates can automatically deduce the buffer size and pass it in to wcscpy_s.

Section D returns us to the original code, but we use #define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES as well. This setting is not enabled by default because it can cause problems with some unusual coding patterns. But setting it can dramatically reduce the work required. You can see that with this #define set you don't even get a warning on the first two calls. We've used a template to convert a call to wcscpy into a call to wcscpy_s whenever a fixed size destination is used. Of course, if the buffer was allocated on the heap or outside of the function, you'll still have to tell us the size, as happens on the last line.

Section E shows the minimal work you would have to do to fix the function if you use _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES. You only have to change the case where a dynamic output buffer is used. All the other code compiles without change. We used this option when applying the Safe Libraries to the whole developer division codebase late last year, and it really reduced the work involved.

Safe C++ Library

The new Standard C++ Library is safer and requires a lot less work because it was designed recently and because the encapsulation inherent in C++ classes often provides the library developer with increased flexibility to make changes. The primary problem with the Standard C++ Library is that its designers did a great job of making it as efficient as traditional pointer-based iteration in C. This means that a simple iterator often knows nothing more about its context than does a pointer in the middle of an array.

Unfortunately, this means that an iterator also can't tell if it is participating in a buffer overrun. Since it doesn't know where its bounds are, it can't enforce them.

There is a very explicit tradeoff here between efficiency and bounds checking. In previous versions, the library code defaults to the efficient iteration, but in the new version we have added a safer mode where iterators can be sure that they are being used correctly. Figure 5 shows an example of this.

Figure 5 Iterator Safe Mode (Close Figure 5)

A: Converting destination pointer to iterator
void helloworld(void)
{
    wchar_t text[]=L"Hello world";
    wchar_t buffer[20]=L"";
    std::copy(text, text+_countof(text), buffer);     // compiler warning
    std::wcout << buffer;
}
B: Remediation
void helloworld2(void)
{
    wchar_t text[]=L"Hello world";
    wchar_t buffer[20]=L"";
    stdext::checked_array_iterator<wchar_t *> dest(
        buffer, _countof(buffer));
    std::copy(text, text+_countof(text), dest);
    std::wcout << buffer;
}

Section A shows a simple function that outputs a well-known string. This code works fine, but you will get a warning at compile time telling you that _Copy_opt is deprecated. This is a warning that you used an unsized buffer as a destination.

Section B shows a fixed version of this code, where we've used the new checked iterators to wrap up the pointer with its size. We've been lucky to have a strong implementation of the Standard C++ Library with help from Dinkumware. While we were busy adding safer iterators to the libraries, and switching Dinkumware's code to use the Safe C Runtime functions, Dinkumware was also busy adding some new debug iterators functionality. This is only enabled in _DEBUG, but it can find a plethora of misuses, such as invalidated iterators or using an iterator from the wrong collection.

Figure 6 shows a program that will demonstrate both of these runtime checks, depending on whether it is compiled debug or release. When an error occurs at run time in the release build code, you have two rational choices in C++. You can abort, as you do in C, or throw an exception, as is normal in C++ code. Both options are available, and if you set _SECURE_SCL_THROWS to 1, you'll get an exception rather than the aborting behavior when you go past the end of your container.

Figure 6 Debug Functionality (Close Figure 6)
// Bug caught at run time
void pointless(wchar_t *output, const wchar_t * src)
{
    std::wstring str(L"Hello world");
    std::wstring::iterator iter=str.begin();

    for(int i=0; i<20; i++)
    {
        *iter=L'a';        // bug, runtime check invoked
        iter++;
    }
}

Other Safety Improvements in Visual Studio 2005

There are a few other safety improvements in the Safe Libraries that are worth mentioning here. I don't have space to go into detail on each one, but this should give you an indication of the work going on and serve as a starting point for you to learn more.

We've done lots of work in MFC and ATL, but because of their higher-level nature, they didn't require nearly as much change to have all their string buffers treated correctly. We did work to switch them over to use the Safe C Library functions instead of the older ones. In a few cases, an MFC or ATL function had an output buffer with no size, and as a result we deprecated the function and added a new overload with the extra parameter.

We've also made a significant change to the deployment and servicing model in this version. It is important that software developers ship updated copies of the libraries to customers with their applications. When we ship a Visual Studio service pack, we don't automatically update every customer machine with a new copy of the DLLs. However, in Visual C++ 2005 we have switched the libraries to install and run using the Windows side-by-side execution technology. Each executable built with Visual C++ needs a manifest so that it can find its copy of MSVCR80 and other library DLLs. Side-by-side allows DLLs to be installed centrally (in %systemroot%\WinSxS) or in an app-local directory.

Use of this technology has several benefits, including elimination of DLL Hell problems. However, a key benefit for safety is that, in an emergency, we directly service the libraries on your machine via Windows Update. This central servicing affects all applications, including those that install the libraries app-local. Therefore, this would only be used as a last resort. We work very hard to ensure that the library code does not contain any problematic code. But in the case of a serious security problem in a redistributable component, we have a way to help customers directly.

Conclusion

Working on the Visual C++ team gives me the opportunity to influence code for the better. With the Safe Libraries, you and your teams can build much safer apps with minimal code change. They give you an opportunity to expend little effort to realize a significant increase in the safety and robustness of your application. See the sidebar "Migrating Your Code" for tips on migrating your code base to Visual C++ 2005.

Martyn Lovell is a development lead on the Visual C++ team at Microsoft. He is responsible for libraries and the integration of managed and native code. In his nine years at Microsoft he has worked on the Visual Studio IDE, Team Development, and Enterprise Debugging. You can contact him at martynl@microsoft.com.


Touch All the Bases: Give Your .NET App Brains and Brawn with the Intelligence of Neural Networks

Touch All the Bases

Give Your .NET App Brains and Brawn with the Intelligence of Neural Networks

Christopher M. Frenz
This article discusses:
  • The use of neural networks to solve complex problems
  • Pattern recognition and predictive systems
  • Implementing neural networks with Visual Basic .NET
  • Training neural network connections
This article uses the following technologies:
.NET, Visual Basic
Code download available at: NeuralNetworks.exe (128KB)
Contents
Neural Networks
Programming Neural Networks
Back-Propagation of Error with Momentum
Putting the Network to Work
Evaluating Unknown Patterns
Conclusion
Suggested References

P
attern recognition is an increasingly complex field. Every day technologies such as handwriting recognition software, spam filters, and search engines are required to identify ever more complicated patterns. The difficulty that arises when these tasks are attempted through traditional programming is that they involve a multitude of variables and, more often than not, the relationships between these variables cannot be explicitly defined. For example, the differences between spam and legitimate e-mail are often fuzzy, so hardcoding a set of criteria to differentiate between the two can be difficult. To deal with these and similar issues, programmers are beginning to move away from such approaches and are adopting nonlinear programming techniques such as neural networks.

Neural Networks

Artificial neural networks are designed much like biological neural networks. Both comprise a series of simple information processing units that operate in parallel. In both artificial and biological networks, these simple units are called neurons. Signals can be passed between neurons through a series of weighted connections. The pattern of these connections defines the architecture of the neural network and influences the functionality for which the neural net is best suited (pattern recognition, classification, and so on). Neural networks are able to "learn" by adjusting the strengths of these connections until they can approximate a function that computes the proper output for a given input pattern.

In this article I'll examine one of the most common types of neural networks, the feed-forward neural network, which is often used for pattern recognition and predictive purposes. I'll provide a small example program from medical informatics. The premise is that you have been hired by a group of doctors who are trying to predict their patients' risk for developing heart disease. Over the years they have monitored changes in potential risk factors of past patients, such as blood pressure, weight, and so on, and recorded whether these patients developed heart disease. The neural network under development will be trained with this information so that the doctors can predict the heart disease risk for their patients and take appropriate preventative action.

The typical architecture of a feed-forward neural network contains three layers: an input layer, a hidden layer, and an output layer (see Figure 1). The input layer transfers the array of input values into the neural network. The input layer data is then multiplied by a weight matrix (wij) and passed into the hidden layer neurons. Every possible interconnection possesses its own weight. Therefore the weight matrix has n × m dimensions, where n is the number of input layer neurons and m is the number of hidden layer neurons. Note that not every interconnection must actually exist, a case that can be modeled using a weight of zero for that interconnection.

Figure 1 Feed-Forward Neural Net
Figure 1 Feed-Forward Neural Net

The hidden layer neurons allow the network to represent how the elements of a complex pattern work together to produce a given output. The hidden layer increases the number of weighted interconnections. This means that the neural network can approximate more complex functions. In fact, the most basic neural network architectures, such as single-layer perceptions, lack the ability to even approximate the XOR function. A multilayer neural network like the one shown in Figure 1 can readily approximate the XOR function (such that specifying binary input values at the input layer will yield the correct XOR value of these input values at the output layer). For solving highly complex patterns, some neural networks will even employ some additional hidden layers.

A similar weight matrix (wjk) connects the hidden layer neurons to the output neurons. A bias is also added to each neuron in the hidden and output layers, which scales the neurons' input values before they pass through the neurons' transfer function. The transfer function, sometimes known as the activation function, takes the sum of all the neurons' weighted inputs and uses the value to calculate the neurons' output.

Programming Neural Networks

Now that you have an idea of what neural networks are and how they operate, let's start a new Windows® Application project in Visual Studio®. First you need to specify the number of neurons in each layer. To determine the number of input neurons, look at the DataSet provided by the physicians. The doctors provided three variables of interest: change in cholesterol, change in weight, and a family history of the disease. This means that you'll need a total of three input neurons. The number of hidden layer neurons is generally determined during the training process, which I'll discuss later, but for now pick an initial value of 3. Furthermore, since the physicians only want to know whether or not the individual is at risk for the disease, you'll only need a single output neuron, yielding a 3-3-1 architecture for the network.

Once you've established the number of neurons necessary for your network, you need to add the biases and weighted connections between these neurons. Since there's no way of knowing the appropriate weights and biases prior to training, randomly initialize each neuron with a weight between –0.5 and 0.5 (see Figure 2). For larger neural networks it may be advisable to employ a more sophisticated initialization procedure, such as Nguyen-Widrow (a simple modification to the common random weight initialization algorithm that can provide for faster training times).

Figure 2 Randomizing Neuron Weights (Close Figure 2)
Private Sub Init(ByVal n As Integer, ByVal m As Integer)
    Dim I, J As Integer
    Randomize()
    For I = 0 To n - 1
        For J = 0 To m - 1
            hweight(I, J) = (Rnd() - 0.5)
            hweight2(I, J) = hweight(I, J)
        Next J
    Next I
    For J = 0 To m - 1
       hbias(J) = (Rnd() - 0.5)
       hbias2(J) = hbias(J)
       oweight(J) = (Rnd() - 0.5)
       oweight2(J) = oweight(J)
    Next J
    obias = (Rnd() = 0.5)
    obias2 = obias
End Sub

Within the code in Figure 2, the variables prefixed with "h," such as hweight, refer to the variables used by the hidden layer neurons, whereas those prefixed with "o" refer to output neuron variables. The variables without the numeric suffix are the actual weights and biases used by the network, while those with the suffix 2 will play a role in the training process. The training algorithm requires that you keep track of the current weights and biases as well as the values from the previous training iteration. At this point there have been no previous training iterations, so you can simply initialize both sets of variables to the same values. For the sake of simplicity, many variables in this code were not declared at the procedural level, but rather as private form-level variables, because they will be used by multiple procedures during program execution. Thus, if a variable is used in a subroutine, but not declared within that subroutine, make sure to declare that variable in the declarations section of your project's code.

At this point the architecture of the neural network has been laid out and the strength of the interconnections initialized, but you still lack a means of transferring data between layers. The first step is to establish a way to transfer the input data from the input neurons to the hidden layer neurons. When coding, it's important to remember that each hidden layer neuron is connected to every input neuron and that each individual hidden layer neuron will receive the sum of the weighted input neuron values. Applying the bias of this hidden layer neuron then scales this summation. Thus, for each individual neuron the process should proceed according to the formula

where Xi represents the input neuron values and wij represents the weight connecting the input neuron to the hidden layer neuron. In other words, the input value to a node is the bias for that node added to the sum of each input interconnection, where the value of an interconnection is the input neuron's value multiplied by the weight of the connection. You can see this in the code that follows. The subroutine in this code snippet contains two nested loops. The inner loop of the HiddenInput function carries out the summation process for each individual hidden layer neuron, as shown in the equation. The outer loop ensures that this process is repeated for each hidden layer neuron in the neural network:

Private Sub HiddenInput(ByVal n As Integer, ByVal m As Integer)
    Dim I, J As Integer
    Dim sum As Double
    For J = 0 To m - 1
        sum = 0
        For I = 0 To n - 1
           sum = sum + (InputNeuron(I) * hweight(I, J))
        Next I
        hin(J) = hbias(J) + sum
    Next J
End Sub

Once the hidden layer neuron input values have been determined, it is time for each hidden layer neuron to process its input by passing the value through its transfer function. Hidden layer transfer functions are typically bipolar sigmoid functions that control the excitation state, or value, of the neuron. A bipolar sigmoid will generally yield an output that approaches 1 or –1, although the sigmoid of the output neuron can be scaled to yield a range of output values that is appropriate for the given application. Typically, the equation for this type of sigmoid is as follows:

where x is the neuron's scaled input. Here is how you accomplish this programmatically:

Private Sub HiddenTransfer(ByVal m As Integer)
    Dim J As Integer
    For J = 0 To m - 1
        hout(J) = Trans(hin(J))
    Next J
End Sub

Private Function Trans(ByVal Val As Double) As Double
    Dim f As Double
    f = (2 / (1 + (System.Math.Exp(-Val)))) - 1
    trans = f
End Function

The HiddenTransfer subroutine ensures that the scaled input of each hidden layer neuron is processed, while the Trans function encodes the actual bipolar sigmoid. The hout values yielded by this code now need to be passed over the next layer of weighted connections to the output neuron.

This process is almost identical to the process used to transfer values from the input neurons to the hidden layer neurons, although these connections have their own unique set of weights and the output neuron has its own unique bias. You can accomplish this data transfer with the following code:

Private Sub OutputInput(ByVal m As Integer)
    Dim J As Integer
    Dim sum As Double
    sum = 0
    For J = 0 To m - 1
        sum = sum + (hout(J) * oweight(J))
    Next J
    oin = obias + sum
End Sub
In this segment of code, the nested loop structures that were used before aren't necessary since there is only a single output neuron.

The output neuron also possesses a transfer function just like the hidden layer neurons. When writing a transfer function for your output neuron, it is important to consider the range of output values over which you want your network to make predictions, and scale the bipolar sigmoid accordingly. Some networks employ a more linear function as a transfer function to encompass a wider range of possible output values. In this case, the doctors just want to know whether their patient is at increased or decreased risk, so stick with the same bipolar sigmoid already used and just consider a value of –1 to represent decreased risk and a value of 1 to represent increased risk. Since you are choosing to use the same transfer function, just add the code that will pass the output neuron's scaled input into this transfer function.

Private Sub OutputTransfer()
    oout = Trans(oin)
End Sub

Back-Propagation of Error with Momentum

Now the architecture of the neural network is laid out, but it still lacks the ability to learn. The next step is to develop a method of adjusting the weights and biases so that over time the neural network will be able to accurately predict disease based on the input variables that it is presented. This is accomplished through a process known as back-propagation of error, which utilizes a gradient descent algorithm (a form of hill climbing) that seeks to minimize the error of the values that are output from the neural network.

The first step in this process is to take an output computed by the neural network for a given pattern and compare it to a corresponding target value. Target values are known outcomes for given patterns; they are used as part of the training process so that the network can learn which patterns can be associated with which output values. An error information term, delta (δ), is then calculated by multiplying the difference between these two terms by the derivative of the activation function.

This error term is then used to compute a weight adjustment term as well as a bias adjustment term. The computation of these terms, however, also requires two additional terms to be taken into account. One term is the learning rate (alpha), which limits the size of a weight/bias adjustment step in a single training iteration. The smaller the value of alpha, the longer a network will take to train. If alpha is too large, however, the network may never reach a reasonable solution to the problem; the large step size will result in the algorithm making the network step over the set of weights and biases where the error is minimized.

The simplest way to determine the proper value for a learning rate is trial and error during the training process. Additionally, it may be advantageous to have a value for alpha that is not constant, but rather that adapts as training progresses (for example, large at first to improve speed and then smaller later to improve accuracy). Thus one possible improvement to the method being presented here would be to make alpha an adaptive value by employing the delta-bar-delta rule in which past error values can be used to make educated guesses about future calculated error values. With these rough estimates, the system can make more informed choices when adjusting weights.

The second value is mu (µ), the momentum term. Momentum is an addition to the weight adjustment equation. This enables the weight to change in response to the current gradient step and also to the previous one. It allows the network to find a reasonable solution in fewer training iterations. When both the current step and previous step are in agreement, it allows for a larger step size. This also reduces the effects of anomalous data, since the momentum-dictated change will oppose the learning rate-dictated change. The weight and bias update equations including the momentum terms are as follows:

where t represents the current set of weights/biases, t-1 the previous set, and t+1 the new set being calculated. The corresponding code is found in Figure 3.

Figure 3 UpdateOut Subroutine (Close Figure 3)
Private Sub UpdateOut(ByVal I As Integer, ByVal m As Integer)
    Dim J As Integer
    odelta = dtrans(oin) * (targval(I) - oout)
    For J = 0 To m - 1
        doweight(J) = (alpha * odelta * hout(J)) +
            (mu * (oweight(J) - oweight2(J)))
        oweight2(J) = oweight(J)
        oweight(J) = oweight(J) + doweight(J)
    Next J
    dobias = (alpha * odelta) + (mu * (obias - obias2))
    obias2 = obias
    obias = obias + dobias
End Sub

The process here is to first determine the value of odelta, and then enter a loop structure, which will update all of the weighted interconnections between the output neuron and the hidden layer neurons. Before this update occurs, the previous oweight values are shifted to the oweight2 array. This allows you to keep track of past weights and effectively utilize momentum. A similar update procedure is then carried out for the bias value.

The UpdateOut subroutine back-propagates the error to the interconnections present between the output and hidden layer neurons, but a procedure is still needed to do the same for the interconnections between the hidden layer and the input neurons. The procedure for updating the weights is similar to that used for the weights of the connections between the output and hidden layers, with the difference being that there are no target values that can be used to calculate the error of each neuron. Instead, you can calculate the error term of each hidden layer neuron using the value of odelta multiplied by the weight of the connection between the current hidden layer neuron and the output neuron. This allows for the distribution of the error of the output unit back to all units within the hidden layer. From this point onward, the procedure is the same as the previous UpdateOut method (see Figure 4).

Figure 4 UpdateHidden Subroutine (Close Figure 4)
Private Sub UpdateHidden(ByVal n As Integer, ByVal m As Integer)
    Dim I, J, K As Integer
    For J = 0 To m - 1
        hdelta = (odelta * oweight(J)) * dtrans(hin(J))
        For I = 0 To n - 1
            dhweight(I, J) = (alpha * hdelta * InputNeuron(I)) +
                (mu * (hweight(I, J) - hweight2(I, J)))
            hweight2(I, J) = hweight(I, J)
            hweight(I, J) = hweight(I, J) + dhweight(I, J)
        Next I
        dhbias(J) = (alpha * hdelta) + (mu * (hbias(J) - hbias2(J)))
        hbias2(J) = hbias(J)
        hbias(J) = hbias(J) + dhbias(J)
    Next J
End Sub

The only notable difference between the UpdateHidden subroutine and the UpdateOut subroutine is that since there can be multiple hidden layer neurons as well as multiple input neurons, you need to utilize some additional loops to ensure that you update every weight and bias appropriately.

Putting the Network to Work

Now that all the functional components of the neural network are laid out, you need to properly utilize these components. It's that time to train the neural network using a set of training data. A proper training set will contain a set of input patterns with a corresponding set of target output values.

I use a DataSet to hold values for a patient's change in cholesterol, change in weight, and family history of the disease. For the two change-based values, the neural network will be able to accept floating point or integer values; a positive value indicates an increase in weight or cholesterol and a negative value indicates a decrease. Since family history of the disease is a yes or no situation, the values are –1 if there is no family history or 1 if there is a family history of the disease.

Although there are only three variables in this network, neural networks can successfully process many more variables. This would make the pattern more complex and would likely require a longer training time and/or a larger training set. Long training times, however, are especially problematic with the back-propagation algorithm used here. The code in this article is just a sample to demonstrate neural networks; further reading on more sophisticated techniques should be conducted before production-scale neural networks are considered. A list of useful references can be found in the Suggested References box. Also, you should note that Analysis Services 2005 provides a neural network implementation that you can take advantage of. For more information, see Jamie MacLennan's article in the September 2004 issue of MSDN®Magazine, which can be found at SQL Server 2005: Unearth the New Data Mining Features of Analysis Services 2005.

Sufficient training data is also critical to the success of neural networks, since the network will need to use a diversity of possible patterns to be able to process novel patterns. The more complex the pattern, the larger the training set required. The three input values are then followed by a –1 or 1 value that indicates whether the patient is at increased or decreased risk for developing heart disease. Within the training set there is data for eight such patients.

To incorporate training into the application, add a button control to the form and add the code found in Figure 5. In the TrainError subroutine, specify a learning rate and momentum term as well as the number of neurons in the hidden and output layers. Next instantiate a new StreamReader, and use the ReadLine method of the StreamReader to read the training data into array X1 from a file entitled SampleData.txt (for simplicity of example I've hardcoded both the path to the file and the number of data elements in the file, but in a real application you would obviously parameterize these input values). Then call the previously coded init procedure to lay out the architecture of the neural network as well as randomly initialize the weights and biases of the network.

Figure 5 Adding Training (Close Figure 5)
Private Sub Button1_Click(ByVal sender As System.Object,
        ByVal e As System.EventArgs) Handles Button1.Click

    alpha = 0.3
    mu = 0.8
    n = 4
    m = 3
    Dim TrainError As Double
    Dim I, J, K As Integer

    Dim NumCases As Integer = 25
    Dim TrainSR As New IO.StreamReader("C:\NNData.txt")
    For J = 0 To NumCases - 1
        For I = 0 To n
            X1(J, I) = TrainSR.ReadLine
        Next I
        targval(J) = X1(J, n)
    Next J
    TrainSR.Close()

    Init(n, m)
    J = 0
    Do Until J = 1000
        TrainError = 0
        For I = 0 To NumCases - 1
            For K = 0 To n - 1
                InputNeuron(K) = X1(I, K)
            Next
            HiddenInput(n, m)
            HiddenTransfer(m)
            OutputInput(m)
            OutputTransfer()
            UpdateOut(I, m)
            UpdateHidden(n, m)
            Debug.WriteLine(I & " " & oout)
            TrainError = TrainError + (targval(I) - oout) ^ 2
        Next I
        TrainError = System.Math.Sqrt(TrainError / NumCases)
        If TrainError < 0.01 Then
        Exit Do
        End If
        J = J + 1
    Loop
    End Sub

Next, you see a nested loop structure in which the outer loop controls the maximum number of training iterations. The "For I" loop controls the actual training process. A training set input pattern is transferred from the X1 array to the input neurons. The HiddenInput subroutine uses these input neuron values to calculate the input into each hidden layer neuron. The HiddenTransfer function then calculates the outputs of the hidden layer neurons, and the OutputInput subroutine uses these to determine the value that will be sent into the output neuron. The OutputTransfer subroutine then calculates the value output from the neural network. You can use the inserted Debug statement to write the output values to the Output window. Watch how they start off being fairly inaccurate and then increase in accuracy.

Of course, given random initialization of weights and biases, the initial output value is most likely far from the actual target value. So you can call the UpdateOut and UpdateHidden subroutines to update the weights and biases between the output and hidden layer and between the hidden layer and input layer, respectively. At this point the next training pattern is transferred to the input neurons from X1 and the process repeated.

Once all eight training patterns have been used, the first training iteration (or epoch) is complete and the next epoch can begin. Training will continue until the value of J reaches its specified cutoff or the root mean square error stored in the variable TrainError drops below the specified point.

When training a neural network, especially for more complex patterns, it is possible that the first attempt never reasonably approximates the output values for the given input patterns. Then it's time to adjust some parameters. The first things to consider adjusting are the learning rate and the momentum term. Since these values control the degree of weight adjustment with each training step, an inappropriate value could cause the training algorithm to be either unable to converge upon a solution in the number of iterations specified or unable to converge at all. This is the trial-and-error approach.

The second parameter you could consider adjusting is the number of training epochs, since it is possible that the number of iterations allotted was insufficient for the network to converge upon a solution. The remaining parameter that you could consider adjusting is the number of hidden layer neurons, since the more hidden layer neurons within the network, the more sophisticated the internal representations of the network can become. Avoid using more input neurons than needed, since too many promotes a condition known as over-training.

An over-trained neural network will generally be able to output highly accurate values for the training set input patterns, but it loses the ability to predict novel patterns. In other words, the network is only able to create accurate predictions for sets it is familiar with. Losing the ability to deal with novel patterns greatly diminishes the usefulness of neural networks.

Luckily, over-training can be easily tested for using a set of validation data. Validation data is similar to training data in that you are aware of what the output should be for each input pattern in the set, but it should not repeat patterns contained in the training set. A validation set is basically a set of known unknowns, in that the patterns are novel to the neural network, but you know what the answers should be, and as such can accurately assess the performance of the network. The validation set can then be input into the neural network and the predicted results compared to the expected results. If the results match to within a predetermined degree of accuracy (90 percent, for instance) the neural network can be considered properly trained and used to make predictions for true unknowns (patterns that neither you nor the neural network have seen before). If the network fails to predict the validation set to within the specified degree of accuracy, you can assume that the network has been improperly trained and therefore discard it. A new network can then be trained and validated, and the process repeated until a network that successfully passes validation results.

If a given set of network parameters continues to train successfully but continually fails to validate, then it may be beneficial to try modifying the training parameters somewhat, since the training routine is likely converging on a local minima rather than the global minima.

Figure 7 Unsuccessful Validation
Figure 7 Unsuccessful Validation

Let's assume that you now have a successfully trained neural network. You can begin to examine the validation process by adding a button control and the code in Figure 6 to the application. You can see from the code that the validation procedure has much in common with the training aspect of the neural network. It reads in a set of data, computes an output value for each pattern in the data set, and then compares the output value to a target value. Through this comparison it determines how many members of the validation set were correctly predicted, and then determines if at least two out of three data set members were predicted correctly. If at least two out of three members were correctly predicted, the network is properly validated and can be used for unknown evaluation. If fewer than two members were correctly predicted (see Figure 7), then you'll need to discard this network, retrain another, and repeat the validation process once again.

Figure 6 Validation Procedure (Close Figure 6)
Private Sub Button3_Click(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles Button3.Click

    Dim NumCases As Integer
    NumCases = 3
    ReDim X1(NumCases, n + 1)
    ReDim targval(NumCases)
    Dim I, J, K As Integer
    Dim Correct As Integer

    Dim TrainSR As New IO.StreamReader(("C:\NNValid.txt")
    For J = 0 To NumCases - 1
        For I = 0 To n
            X1(J, I) = TrainSR.ReadLine
        Next I
        targval(J) = X1(J, n)
    Next J
    TrainSR.Close()

    For I = 0 To NumCases - 1
        For K = 0 To n - 1
            InputNeuron(K) = X1(I, K)
        Next
        HiddenInput(n, m)
        HiddenTransfer(m)
        OutputInput(m)
        OutputTransfer()
        If targval(I) = System.Math.Round(oout) Then
            Correct = Correct + 1
        End If
    Next I

    TextBox5.Text = Correct & " out of " & NumCases & " Match. "
    If Correct >= 2 Then
        TextBox5.Text = TextBox5.Text & "Validation Successful."
    Else
        TextBox5.Text = TextBox5.Text & "Validation Unsuccessful."
     End If
End Sub

In this case I chose to validate by determining the overall number of predictions that are correct, which is an acceptable method for binary outputs. For nonbinary outputs, validation is usually performed by considering the numeric discrepancies between the output values and the target values.

Evaluating Unknown Patterns

To make the trained neural network accessible to the physicians, you'll need to add some textboxes for inputting the variables for each patient, as well as a third command button for launching the evaluation of the input pattern. You'll also need to add a fourth textbox that will indicate to the physicians whether the patient is at increased or decreased risk. Add the code found in Figure 8 to the click event of the command button just created. The code in Figure 8 simply reads in the values entered into each of the textboxes and sequentially calls the hiddeninput, hiddentransfer, outputinput, and outputtransfer subroutines to obtain the neural network's prediction for the provided input pattern. A simple conditional then translates the neural network's numeric output into a written statement about the patient's risk (see Figure 9).

Figure 8 Evaluating Risk (Close Figure 8)
Private Sub Button2_Click(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles Button2.Click
    InputNeuron(0) = TextBox1.Text
    InputNeuron(1) = TextBox2.Text
    InputNeuron(2) = TextBox3.Text
    HiddenInput(n, m)
    HiddenTransfer(m)
    OutputInput(m)
    OutputTransfer()
    If oout < 0 Then
        TextBox5.Text = "The patient is at a reduced risk for disease"
    Else
        TextBox5.Text = "The patient is at an increased risk for disease"
    End If
End Sub
Figure 9 Sample Output
Figure 9 Sample Output

It is important to note that in order to make a prediction using a trained neural network, the weights and biases do not need to be modified. If this application was being designed for the real world, it would be beneficial to add code that could save the weights and biases of a trained network to a file. An alternate initialization procedure should also be provided, where rather than randomly initializing values, previously saved weights and biases could be loaded to allow the neural network to immediately make effective predictions without further training.

Conclusion

I have briefly examined the operations behind one of the most common types of neural networks. Even this simple example proved that neural networks can provide a highly useful methodology for dealing with pattern matching and predictive tasks. Neural networks are a diverse field; in addition to the feed-forward network discussed here, numerous other types of networks can be employed, depending on the task at hand. There are even variants of the feed-forward network such as networks with multiple hidden layers or networks that also provide direct weighted interconnections between the input and output layers, in addition to the typical hidden layer connections.

Many advances have been made in training algorithms as well. While they all still apply the same basic principles as the back-propagation variant discussed, many of these newer algorithms are able to converge on a solution in far fewer iterations, which can be highly advantageous for patterns with a large number of values.

All in all, the neural network coded in this article only demonstrates a fraction of the power of a modern implementation, but it should have provided you with a glimpse of the evolving and robust arena that is the world of neural networks.

Suggested References

  • Bishop, Christopher M. Neural Networks for Pattern Recognition (Oxford University Press, 1995)
  • Faussett, Laurene V. Fundamentals of Neural Networks (Prentice Hall, 1994)
  • Reed, Russell D. and Marks, Robert J. II. Neural Smithing: Supervised Learning in Feedforward Artificial Neural Networks (MIT Press, 1999)

Christopher M. Frenz is a bioinformaticist and uses neural networks to model biological systems. He is the author of Visual Basic and Visual Basic .NET for Scientists and Engineers (Apress, 2002). He can be reached at cfrenz@gmail.com.


Editor's Note: Under the Covers and Through the Words

Editor's Note
Under the Covers and Through the Words

There are some people who have little curiosity about the world around them. They drive to work without asking why their airbag light is on. They work all day, never once wondering why their computer goes through periods where it's slow as a snail. (It was because their IT department was pushing new virus definitions down to their machine.) Then they get home and settle in for a night of television—again, never wondering about the workings of the miracle that beams Hollywood's hottest stars onto a 35-inch screen in their living room.

But developers need to take an interest in how things work under the hood. That's not to say that only programmers care about things at the micro level—far from it. But the core of any development project is breaking a problem up into parts. Sometimes the parts can be reasonably large—concatenating two strings is a complex operation made simple by any decent string library. But sometimes you need to peel off even more layers to better understand how memory allocation works or how that string library operates.

This month, we've decided to peel away some of those layers. In "JIT and Run: Drill into .NET Framework Internals to See How the CLR Creates Runtime Objects" we take a look at how the JITing engine of the CLR works internally, and how it handles object layout and method dispatching. This isn't for beginners, but if you need to know what happens to your code when you run it, and don't get squeamish when you see a stack heap, this is for you.

On the next level up from the runtime, there is a new element of Visual Studio 2005—the Safe C and C++ Libraries. If you've been assuming that strcpy would provide your retirement income, think again. The fact that your code may be waiting for a null-terminated string that never terminates could leave your heap drained and your code bankrupt, to misuse just two analogies. The Safe Libraries are designed to provide stronger security by stopping buffer overrun attacks in their tracks, among other benefits.

Finally, we have interesting coverage of Windows Forms in the .NET Framework 2.0, a demonstration of how to build neural networks with .NET, and even a new column! Extreme ASP.NET is written by long-time ASP.NET designer and guru Rob Howard. In this first installment, Rob talks all about server controls and paging.

This month we have a special Editor's Note treat: a new section called "People Complaining About Previous Editor's Notes." It seems that we touched off a bit of discussion in March when we took issue with invented words like "performant." Some people were firmly behind our stance, while others rejoiced in the trashing of the English language in a quest to invent yet more words.

One dev in the Microsoft Mobile group, who will remain nameless because we accidentally deleted the e-mail, wrote:

Keyboardable. Need I say more?
Actually you do, because "Keyboardable." isn't a complete sentence. Dennis Voss filled us in from Germany:
I just wanted to point out that "performant" is a German word that can be translated to what you said ("highly performing"). And yes, it's an adjective, not a noun.
So the plot thickens. Not only are people using a word that's not a word, they didn't even make it up like they thought they did! David Knapman wrote to us from the UK:
Your first point of needing to create new words used the examples of boxing and unboxing. What was wrong with the words reference and de-reference?
At the risk of inconsistency, we're going to give "boxing" a pass because it sounds sort of cool. "De-reference" sounds like the act of taking something out of the dictionary—which is something you don't have to do with the word "performant," because it's not in there.

Finally, we heard from Dave Edson, who used to grace these pages as a technical editor for Microsoft Systems Journal. Dave wrote:

I wrote an ednote WayBackWhen where I got into a hissyness state of delusionism over the abundatage of bad words, including the word "nervousing". Ingesticate a chill pill and stop your plagiarizationism.
Sure, Dave talks a good game. But let's see him show up here and settle this issue where it counts, in meatspace.

—J.T.

Thanks to the following Microsoft technical experts for their help with this issue: Mark Boulter, Shawn Burke, Susan Chory, Kit George, Matthew Gibbs, Manish Godse, John Justice, Peter Kim, Ronald Laeremans, Steve Lasker, Gerrard Lindsay, Martyn Lovell, Jim Miller, Bradley Millington, Vance Morrison, Polita Paulus, Brian Pepin, Eric Perlin, Matt Powell, Joe Stegman, and Ted Tanner.

ActiveX, BizTalk, IntelliSense, JScript, Microsoft, MSDN, MSN, Microsoft Press, Visual Basic, Visual Studio, Windows, Windows NT, Win32, and Windows Server are either registered trademarks or trademarks of Microsoft Corporation. Other trademarks or tradenames mentioned herein are the property of their respective owners.

MSDN Magazine does not make any representation or warranty, express or implied with respect to any code or other information herein. MSDN Magazine disclaims any liability whatsoever for any use of such code or other information.


New Stuff: Resources for Your Developer Toolbox

New Stuff
Resources for Your Developer Toolbox
Marnie Hutcheson

Contents
Cut Down Your Software Build Time
Generate Sequence Diagrams
Convert Visual Basic to C#
The Installer That Can Do It All
Quick Autorun App Builder for CDs
Add Flash Charts to Your Web Project
Keep Your Site Safe from Automated Tools
The Bookshelf

Cut Down Your Software Build Time

Visual Build Professional 5.6 by Kinook Software is a management solution for developers writing programs for Microsoft® Windows® and the Web that lets you create an automated, repeatable process for building your software. Visual Build Pro creates a permanent build record of XML project files and log files that become part of your project's source code (and can be maintained in source control), to provide a persistent, documented record of how builds are performed.

New features include full support for building Microsoft Visual Studio® 2005 projects and solutions with MSBuild (without installing Visual Studio on the build box), custom actions for Subversion version control and code signing of executables, support for Borland Delphi 2005 and JBuilder 2005, enhancements to the FTP, Replace in File, and Zip actions.

Visual Build Professional 5.6 runs on Windows NT® 4.0, Windows 2000, Windows XP, and Windows Server™ 2003.

Price: $295 for a single-user license.

www.visualbuild.com

Generate Sequence Diagrams

EventStudio 2.5 by EventHelix.com is a sequence diagram-based system design and architecture modeling tool for the Windows platform. EventStudio 2.5 supports generation of message sequence charts and call flow diagrams used in telecom and embedded system development. It also produces process workflow diagrams for business process reengineering applications.

EventStudio 2.5 has been designed to handle diagram generation for systems with a large number of interacting objects. Objects in the system can be classified into groups and subgroups. This classification is used to generate diagrams that focus on a particular aspect of the system, enabling better understanding of a complicated system.

Some of the supported document types are: interface documents that focus on the interactions involving an object or an object-group, a diagram focusing only on the intergroup interfaces and a document that only lists messages that match a specified regular expression.

EventStudio lets you model a system by describing the interactions in the system using a simple declarative modeling language. EventStudio then analyzes the model for design errors, inconsistent operations, resource leaks, and invalid timer operations. Once the design issues have been addressed, EventStudio automatically generates sequence diagrams and collaboration diagrams.

EventStudio automatically handles diagram layout, formatting, scaling, and page breaks for sequence diagrams and collaboration diagrams. The documents are generated in the Adobe PDF and Microsoft Word Picture formats. Therefore, you can explore any number of success and failure scenarios and focus on system architecture and design, without getting bogged down by document layout issues.

EventStudio 2.5 runs under Windows NT 4.0, Windows 2000, and Windows XP.

Price: $99 for a single-user license.

www.EventHelix.com

Convert Visual Basic to C#

C-Sharpener For Visual Basic by Elegance Technologies Inc. typically converts greater than 99 percent of your Microsoft Visual Basic® .NET projects to C# accurately and reliably. It converts Windows-based applications, class libraries, Windows control libraries, ASP.NET Web applications, Web control libraries, console applications, and Windows services.

C-Sharpener For Visual Basic is a full parser that emits C# instead of Microsoft intermediate language. It does full syntactic and semantic analysis, including a full symbol tree of all symbols in the code and referenced assemblies, to ensure accuracy in the converted code and to minimize conversion problems.

It is a Visual Studio .NET add-in so selecting the C-Sharpener For Visual Basic menu option opens the conversion wizard. A command-line interface lets you easily convert multiple projects and you can use this feature as part of an automated build process.

As with any conversion, there will be issues that the tool can't resolve by itself. The conversion report window shows the number of translation errors and warnings encountered. These issues have to be resolved before you can compile successfully, but tracking them down is easy; you just search your project for the "TRANSERROR" and the "TRANSWARNING" comments inserted during translation.

C-Sharpener For Visual Basic also has a Quick-Resolver feature that gives users the option to automatically send problem descriptions, intermediate files, log files, or entire projects to the company's support group for help with the conversion process.

Price: $99 for a single-user license.

www.elegancetech.com

The Installer That Can Do It All

Ghost Installer Studio 4.1 by Ethalone Solutions Inc. is a complete installation solution that creates installation CDs and Web-based distribution packages. The advanced Web deployment options let the user decide which components to install, so they can reduce the time required for download and installation.

You can create the installation package for your Visual Studio .NET 2003, Borland Delphi, JBuilder, and C++Builder projects with as few as five mouse clicks, or you can customize it using the built-in XML-based scripting and conditional processing options. There is a built-in scripter plug-in that lets you insert scripts written in VBScript, JavaScript, C++, and Object Pascal.

Ghost Installer Studio 4.1 provides RSA encryption and personalized installation key options to protect your application from piracy. It offers skinning and customization options so you can create just the branded installation you want. Ghost Installer Studio 4.1 will install the .NET Framework onto the user's computer and it can perform supplemental actions for your assemblies, such as registering for COM interop, creating COM+ applications, and installing assemblies into the Global Assembly Cache.

Price: $299 for a single-user license.

www.ethalone.com

Quick Autorun App Builder for CDs

Sometimes you don't want to install a program, you just want to distribute something. If all you want is to have the user put the CD in the drive and "autorun" into your launch screen menu, instructions, or site map, without a big time investment on your part, then Visual Autorun by Digitope might be a solution for you.

Visual Autorun lets you create interactive, multimedia, autorun interfaces for your CD in just a few mouse clicks with no programming required. You add the programs you want to have on your CD to the Visual Autorun Project folder, create your autorun interface, and when you are ready, Visual Autorun burns the entire image to your CD.

You can create menus with captions, sounds, music, buttons, and splash screens, and you can link your buttons to documents or programs on the CD. The program gives you a variety of configurable options. For example, if you are distributing a document that requires a viewer application, such as a PDF, Visual Autorun can check to see if the viewer is installed and then install it if necessary.

Visual Autorun runs under Windows 98, Windows 2000, Windows Me, and Windows XP.

Price: $25 for a single-user license.

www.digitope.com

Add Flash Charts to Your Web Project

AnyChart 2.0 by Anychart is a Macromedia Flash charting component that lets you add static or dynamic Flash-based charts to your Web pages, without having to modify any Flash source.

AnyChart 2.0 gives you a Flash charting component and a set of industry-standard XML configuration files. Set up your chart with the XML configuration tool that comes with Anychart, or use your favorite XML editor.

There are a variety of animated, hint-enabled 2D and 3D pie, bar, column, candlestick, and donut charts available. The XML configuration files let you control transparency, color, URL linking, captions, legends, backgrounds, animations, and sound along with the data that is displayed.

Price: $79 for a single-user license.

www.AnyChart.com

Keep Your Site Safe from Automated Tools

Here is an inexpensive solution for keeping bots from harvesting your e-mail addresses, running amuck in your registration-required pages, and skewing your survey results.

Lanap BotDetect Web Control by Lanap Software is a server-side control that generates images with textual code rendered as part of the image, so people can read it, but automated tools can't. When you set the image on a form and have the user key in the embedded characters, the server-side control validates the input they have entered.

Drop the control onto a Web Form, write a few lines of code to get it started, and the control will do the rest by itself. The help file has lots of step-by-step information and samples are available on the Web site.

Price: $79 for a single-user license.

www.lanapsoft.com

The Bookshelf

Programmer's Ultimate Security DeskRef by James C. Foster (Syngress) is a comprehensive guide to security flaws in code writing in the most popular programming languages, including C++, C#, Visual Basic for Applications, VBScript, ColdFusion, JavaScript, and JScript®.

Each function or method documented is followed by a series of subjects created to help describe each function's purpose, risk, origin, resources, and so on. The description provides a detailed explanation of how the function should be used and when it should not be used. It also has explanations for any parameters the function or method may accept as input, in addition to providing details on any returned values.

The risk section describes any security threat posed when implementing the function or method. It recommends more secure alternatives if they exist. Topics include secure usage, bolt-on alternatives, and other types of developer-focused solutions. The impact section indicates the potential result that a poor implementation of the function might have on the application.

The additional resources section covers the programmatic particulars of the language, function, or method. There is also a list of cross references to similar functions and methods that are available for use in the language. For example, the C language printf may have cross references of sprintf and snprintf.

Price: $49.95, 608 pages.

www.syngress.com

All prices were confirmed at press time and are subject to change.

Send your New Stuff to newstuff@microsoft.com.

Marnie Hutcheson is president of Internet Development Associates (Ideva), a firm in Ocala, Florida, that specializes in Internet and intranet Web application design and development. She has published technical papers and books on various computing topics. You can reach her at marnie@ideva.com.


Web Q&amp;A: ASP.NET Performance, Notification, Keeping Sort Order, and More

Web Q&A
ASP.NET Performance, Notification, Keeping Sort Order, and More
Edited by Nancy Michell

Q I am developing ASP.NET sites, and I need to measure the performance of the pages. Will adding performance counters impact the performance? What about monitoring them? I've read that it slows down the application.

A ASP.NET and the Microsoft® .NET Framework will publish to performance counters whether you read them or not. However, the act of retrieving those values will take up CPU cycles, introduce additional context switching, and so forth. In most situations, you are going to want to monitor performance once you hit production, so this should be considered part of the production environment you would want to measure anyway.

Another alternative is to use a tool that measures TTLB (the number of milliseconds that passed before the last byte of the response was received), which is a client-side measurement. This could be used in conjunction with your server-side processing times to understand what percentage of the time is consumed on the server and what is caused by network latency.

For more information, read the following articles: "Analyze ASP.NET Web Application Performance by Using the Performance Administration Tool" and "Measuring .NET Application Performance".

Q I'm building a system that provides a stateless Web service to integrate with partners. Basically, the external partner calls my Web service once a day and the Web service forwards the request to my back-end server to handle. Handling the request takes quite a bit of time and, in the meantime, the partner's system wants to know if it's been handled successfully or not. How can I do this?

A Sometimes it seems like the most intuitive way to address this is to make the call synchronized. But the question becomes: is a one or two hour timeout value for a Web service outrageous?

You could return success immediately, and let the partner call later to get the result (based on some context token issued for that call). Or the partner could implement a callback.

Actually, those last two options are better than increasing the timeout. Because the operation is only happening once a day, it's already a batch-style call of sorts. These are best handled by asynchronous calls. If the partner doesn't have a means to provide a notification mechanism (your second option), then returning them a job/task ID in a context token makes complete sense. If they're going to be polling for the results, you may also consider putting a polling interval based on the current system load into the context token.

Q I've placed a Data-Grid on my Web Form which has both sorting and editing functionality. When I click on the Edit button after sorting on a column, rows from the original sort order are selected. How can I resolve this issue?

My Page_Load event looks like this:

private void Page_Load(object sender, System.EventArgs e)
{
    PubAdapter.Fill(publisherDataSet1);
    if (!IsPostBack)
    {
        MyGrid.DataSource = publisherDataSet1.publishers.DefaultView;
        MyGrid.DataBind();
    }
}
My sorting code looks like this:
private void MyGrid_SortCommand(object source,
System.Web.UI.WebControls.DataGridSortCommandEventArgs e)
{
    DataView SortView  = publisherDataSet1.publishers.DefaultView;
    SortView.Sort = e.SortExpression;
    MyGrid.DataSource = SortView;
    MyGrid.DataKeyField = "pub_id";
    MyGrid.DataBind();
}
Finally, the following code is what I use to perform editing:
private void MyGrid_EditCommand(object source,
System.Web.UI.WebControls.DataGridCommandEventArgs e)
{
    MyGrid.DataSource = publisherDataSet1.publishers.DefaultView;
    MyGrid.DataKeyField = "pub_id";
    MyGrid.EditItemIndex = e.Item.ItemIndex;
    MyGrid.DataBind();
}

A The only place where you are setting the Sort in the DataView is in the sort event. This means that next time the user does a postback, the DataView will be filled again from your data store, and then when you call DataBind, the sort is gone because you are rebinding to the unsorted data. So it is up to you to maintain the proper sort in your DataView. The way to do that is to essentially reapply the sort expression and sort order to your DataView. Note that if the amount of data is fairly small, it may be better to use ViewState to store the sorted data rather than Session, since it will work on Web Forms without a state server and in scenarios where session state is disabled. Just note that storing values in the ViewState might be a security risk since ViewState is not encrypted, just encoded. Therefore someone could see the content (although they will not be able to tamper with it).

You could create a property called SortExpression like this:

private string SortExpression {
    get {
        object o = ViewState["SortExpression"];
        return o == null ? "" : (string)o;
    }
    set {
        ViewState["SortExpression"] = value;
    }
}

Then create and use a helper method that actually binds the data (see Figure 1).

Figure 1 Bind the Data (Close Figure 1)
private void Bind() {
    PubAdapter.Fill(publisherDataSet1);
    DataView dataView = publisherDataSet1.publishers.DefaultView;

    string sortExpression = SortExpression;
    if (sortExpression.Length != 0) {
        dataView.Sort = sortExpression;
    }
    MyGrid.DataKeyField = "pub_id";
    MyGrid.DataSource = dataView;
    MyGrid.DataBind();
}

private void Page_Load(object sender, System.EventArgs e) {
    if (!IsPostBack) {
        Bind();
    }
}

private void MyGrid_SortCommand(object source,
  DataGridSortCommandEventArgs e) {
    SortExpression = e.SortExpression;
    Bind();
}

private void MyGrid_EditCommand(object source,
  DataGridCommandEventArgs e) {
    MyGrid.EditItemIndex = e.Item.ItemIndex;
    Bind();
}

Q I've got an ASP.NET application that attempts to connect to an OLAP cube on another machine. If the user is logged onto the Web server (locally or through terminal services), my application can connect. Otherwise the connection fails, even if I explicitly impersonate the user in code before trying the connection. This all worked well when the database and cubes were on the same machine as IIS. What's going wrong here?

A This is a common double-hop problem. You can't just jump off a machine to another machine as an impersonated user. You need to use Kerberos Delegation, or you need to stop impersonating for a bit (falling back to the identity of the process, assuming it has permissions), make the connection, and then pop back. Otherwise you need to log onto the Web Server as another user and make the connection.

So your options are:

  1. Use Kerberos Delegation.
  2. Stop impersonation and connect to the remote resource as the identity of the worker process.
  3. Call LogonUser, passing credentials for a user with permissions to the remote resource, and then impersonate using the returned token.

A great resource on the topic is an MSDN® webcast called Getting Delegation to Work with IIS and ASP.NET: The Ins and Outs of Protocol Transition.

Q My WebMethods randomly disappear from my Web service description page, with the error: "Server did not recognize the value of HTTP Header SOAPAction." This also happens when the application is completely inactive. What should I do?

A Check to see if you're using the same namespace and class in your Web service as in other reference dependencies or bin folders. Do they share the same name? If so, then the duplicate class can be loaded before the real Web service class and you end up without WebMethods.

You should check to see if there's a class defined in a dependent assembly with the same name. This can happen when the Web service project has a reference to some utility assembly, which has a Web reference back to the Web service—so it gets the proxy class for the Web service created with the same class name but different namespace ("localhost"). Then if you change the namespace to the same namespace as the Web service-derived class, you have a conflict. It won't be detected at compile time if the projects are compiled separately, but since they are both loaded into the appdomain (all assemblies in the \bin directory will be), and since the .asmx file just references the class name if it gets the proxy object, you'll have a problem.

Q I am attempting to access an Excel spreadsheet using OLE DB. I can display the data from the spreadsheet using a GridView, but I can't update the data and insert it into the spreadsheet. I always get error message: "Operation must use an updateable query." Is this a problem in Excel?

A You are going to have big problems trying to do this from a Web site. When you open the spreadsheet with OLE DB the result is an exclusive lock, so only one user at a time will be able to write anyway. Plus, you would need to disable pooling because pooling tries to keep the connection open, causing exclusive lock problems as well. Excel is not a multiuser database system.

You are better off switching to SQL Server™ if you can, as it will offer you much more success. But in any case, try the code in Figure 2. It will help if you still want to use Excel. Note that opening the file book1.xls in Excel also puts an exclusive lock on the file, breaking this code, so Excel only works for one user at a time.

Figure 2 Update Spreadsheet (Close Figure 2)
private void button1_Click(object sender, System.EventArgs e)
{
  OleDbConnection conn = new OleDbConnection();
  conn.ConnectionString = @"Provider=Microsoft.Jet.OLEDB.4.0;Data
    Source=C:\Book1.xls;Extended Properties='Excel
    8.0;HDR=Yes;ReadOnly=0;'";

  conn.Open();
  OleDbCommand cmd = conn.CreateCommand();
  cmd.CommandText = "create table [Table1] (f1 long, f2 text)";
  cmd.ExecuteNonQuery();

  for ( int i=1; i<=10; i++ )
  {
    cmd.CommandText = "insert into [Table1] (f1,f2) values (" +
      i.ToString() + ",'Value')";
    cmd.ExecuteNonQuery();
  }

  cmd.CommandText = "update [Table1] set f2 = 'Changed' where f1 = 5";
  cmd.ExecuteNonQuery();

  cmd.Dispose();
  conn.Close();
}

Q I have a DLL that's not unloading when I create an object using JavaScript and the ActiveX® control method. Setting the object to NULL is not causing the class to be unloaded. Why is It only unloaded when I exit Microsoft Internet Explorer?

A Consider what the world would be like if COM actually loaded and unloaded components the way you want it to:

for (x = 1; x < 1000 ; ++x)
{
    var wuwebObj = new ActiveXObject("SoftwareDistribution.WebControl");
    wuwebObj = null;
}
That would load and unload the DLL a thousand times. Aggressive unloading is a performance nightmare, which is exactly why COM doesn't do it. The assumption in COM is that you loaded a DLL because you intended to use the code in it! COM keeps the DLL around until the apartment is torn down or you instruct COM to unload the DLLs.

Also, you're using JScript®, a language with a nondeterministic garbage collector. Even if COM did aggressively tear down DLLs, setting an object to null does not release an object in JScript. It marks it as releasable, which is a completely different thing. That object is not actually going to be released until the garbage collector runs, which might be some time later.

In short, you are using the wrong language. If you require deterministic control over the lifetime of COM DLLs, you need to be using a language such as C++, where you can ensure that all instances of the objects have known lifetimes, and where you can call CoFreeUnusedLibraries whenever you feel like it. (See CoFreeUnusedLibraries for more information on CoFreeUnusedLibraries, and when it releases objects, and for the rest of the COM reference see COM (Component Object Model).)

There are a number of ways that you could solve this problem. One way would be to write two DLLs, one that contains the object and one that contains its class factory. COM creates the class factory DLL, and that DLL is never unloaded. The class factory DLL can then manage the lifetime of the object DLL—when the class factory is asked for an instance, it loads the object's DLL and creates an instance. The object DLL should probably communicate with the class factory DLL so that the class factory DLL knows when the last outstanding object is released. The class factory can then aggressively unload the object DLL.

There are probably some more elegant ways of doing this. One way might be with shadow-copied files. Or, you could call the CollectGarbage function to force a collection—there are very few situations in which you ought to call CollectGarbage, but this might be one of them.

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

Thanks to the following Microsoft developers for their technical expertise: Francisco Arreciado, Sanjiv Banerjee, Jamie Brigden, Todd Carter, Jim Cheshire, Michael Evans, Alan Gao, Mark Haas, Zhaojie Huang, Bas Hugenholtz, Chris Jackson, Rajive Kumar, Eric Lippert, Christer Ljung, David Lovell, Carlos Aguilar Mares, Wade Mascia, Matt Neerincx, Nikola Penkov, Don Smith, Josh Twist, Murugesan Vivekananthan, and Peter Williams.


Cutting Edge: Collections and Data Binding

Cutting Edge
Collections and Data Binding
Dino Esposito

Code download available at: CuttingEdge0505.exe (159KB)
Contents
Collection Basics
Fill the Collection
A Word on Generics
Binding to ASP.NET Controls
Binding to Windows Forms
Binding to Web Services
Wrap Up

When it's time to design the Data Access Layer (DAL) of your distributed Microsoft® .NET Framework-based app, one of the key decisions you'll make is how you'll pass data to and from methods of DAL classes. There are quite a few options for passing business data to and from DAL classes, but in most real-world cases the choices boil down to just two—using DataSet objects or collections of custom business entity objects. The "Patterns and Practices" series of articles and books provide some guidance on the most commonly implemented practices. For instance, issues related to passing data through data tiers are fully dissected in the article at Designing Data Tier Components and Passing Data Through Tiers. For a more complete list, see Microsoft Patterns.

DataSets have built-in support for optimistic concurrency and the ability to define and handle complex relationships between tables. DataSets are also serializable and don't usually require changes to the DAL API should the relevant database schema change. Similarly, collections of custom objects support serialization and don't require any method signature change if the database schemas are modified. Collections also circumvent performance penalties that DataSets can incur. For example, DataSet marshaling can be problematic when thousands of data rows are involved (for details see Cutting Edge in the December 2002 and October 2004 issues of MSDN®Magazine). DataSets are particularly suited to moving small to medium sized sets of data; for methods mostly needing instance data and scalar values, an approach based on custom business entities is perhaps preferable.

Data that DAL classes retrieve will often be bound to Windows® and Web Forms controls and also will often be used to serve Web services requests. I've found out that the majority of books and articles out there (including mine) tend to cover mostly data binding from the DataSet perspective. Here I'll cover data binding with custom collections from all perspectives—Windows Forms, Web Forms, and Web services. But before I go any further, a quick refresher on collections in the .NET Framework 1.x, and a glimpse of generics in the .NET Framework 2.0, are in order.

Collection Basics

A collection is a set of homogeneous objects that are managed through the interface of a container class. A collection is a typed entity; subsequently, all contained objects agree to the same declared type. .NET defines a number of different kinds of collections; each maps to a different kind of data structure—queue, stack, list, hashtable, and more. Let's compare the various collection classes.

The .NET Framework comes with plenty of collection classes, and they may all look alike at first glance. So how can you be sure to get the right one for your task? The more specialized a collection is, the more limited it is, but also the better it is for particular situations. Figure 1 lists the most common collection classes used in the .NET Framework 1.x. For each class, you can see the main interface it implements and the source namespace. Figure 2 details the characteristics of the interfaces behind collections.

Figure 2 Collection Interfaces in the .NET Framework 1.x (Close Figure 2)

Interface | Description
IEnumerable | Defines methods to support a simple iteration over a collection
ICollection | Adds size, copy, and synchronization capabilities to enumerable collections
IList | Defines a collection of objects that can be individually accessed by index
IDictionary | Represents a collection of key/value pairs
Figure 1 Collection Classes in the .NET Framework 1.x (Close Figure 1)

Class | Interface | Namespace | Description
Queue | ICollection | System.Collections | A first-in, first-out (FIFO) collection of objects
Stack | ICollection | System.Collections | A last-in, first-out (LIFO) collection of objects
ArrayList | IList | System.Collections | A weakly typed list of objects
StringCollection | IList | System.Collections.Specialized | A strongly typed list of strings
Hashtable | IDictionary | System.Collections | A collection of key/value pairs that are organized based on the hash code of the key
StringDictionary | IDictionary | System.Collections.Specialized | A hashtable with the key strongly typed as String rather than as Object
SortedList | IDictionary | System.Collections | A sorted collection of key/value pairs
ListDictionary | IDictionary | System.Collections.Specialized | Singly linked list designed for collections that typically contain 10 items or fewer
NameValueCollection | IDictionary | System.Collections.Specialized | A sorted collection of associated string keys and string values that can be accessed either with a key or with an index

A collection is an enumerable list of objects, meaning that it implements the IEnumerable interface. The interface basically exposes an enumerator object to make for-each constructs work, and more specifically to get the members of the enumerable in a generic way. In addition to IEnumerable, collections implement additional interfaces such as ICollection or IList to supply additional capabilities. For example, ICollection adds the Count property and CopyTo method for copying values to an external array. Note that collection classes are generally not thread-safe, though synchronization can optionally be provided through properties defined in the ICollection interface.

IList extends ICollection with Add and Remove methods. IList also adds the Item property, which allows indexed, array-like access to items. The IDictionary interface defines an API that represents a collection of key/value pairs. IDictionary exposes methods similar to those in IList, but with different signatures. All dictionaries feature properties such as Keys and Values.

Array, ArrayList, and StringCollection are examples of collection classes available in the .NET Framework. Typical dictionary classes are ListDictionary, Hashtable, and SortedList. Figure 3 provides some tips for choosing the .NET Framework 1.x collection that will meet your needs.

Figure 3 Choosing Your Collection Class (Close Figure 3)

Class | Description
Queue and Stack | Ideal in a scenario where you receive data and need to process data one element at a time in a specific order.
Arrays | The most efficient tools for fixed-size, indexed collections. Note that for one-dimensional arrays, compilers use built-in Microsoft intermediate language (MSIL) instructions to process them quickly and more effectively.
ArrayList | An array-like growable alternative to fixed-size arrays that can store any kind of object. Note that many publicly available collection classes use an ArrayList internally and just create a more friendly programming interface around it.
StringCollection | A better choice than ArrayList for a dynamic size array of strings, because it provides a strongly typed interface.
Hashtable and ListDictionary | Good options when key-based access is required. You might want to choose ListDictionary to handle a small number of items (20 or fewer) and opt for Hashtable for greater numbers.
HybridDictionary | A wrapper class that begins as a ListDictionary and automatically switches to Hashtable when the size grows beyond the critical threshold. (Used by some internal ASP.NET classes.)
StringDictionary | A better choice than Hashtables as long as your collections contains only strings.
NameValueCollection | The slowest collection. You should use it only if you want to provide both indexed and key-based access to data.
SortedList | A hybrid of a Hashtable and an array. It behaves like a Hashtable when you access an element by key and mimics an array if you use an index. You should use this class only if you need to maintain the sorted order.

How do you build your own collection class for data binding purposes? A custom collection is a strongly typed collection that exposes objects of a particular type. In ASP.NET server controls, there are lots of custom collections. For example, the type of the Items property that many list controls feature is ListItemCollection, which implements ICollection. To create a custom collection, you could write a class that implements ICollection, but a better option is to inherit from CollectionBase, as shown in Figure 4 for the sample OrderCollection class.

Figure 4 The OrderCollection Class (Close Figure 4)
using System;
using System.Collections;

namespace Msdn.CuttingEdge.Samples
{
    public class OrderCollection : CollectionBase
    {
        // Class constructor
        public OrderCollection() {}

        // Add method
        public void Add(OrderInfo o)
        {
            InnerList.Add(o);
        }

        // Indexer property
        public OrderInfo this[int index]
        {
            get { return (OrderInfo) InnerList[index]; }
            set { InnerList[index] = value; }
        }
    }
}

The OrderCollection class groups instances of the OrderInfo class, which in turn collects a bunch of public properties describing an order, as in the SQL Server™ Northwind database. Figure 5 shows a simple implementation of the OrderInfo class. It's simple because it only exposes some of the fields in the Orders table. In a realistic scenario, you probably want to map foreign keys to actual names (employee and customer) as well as optionally drill down in the query to get a child collection of order details.

Figure 5 The OrderInfo Class (Close Figure 5)
using System;
using System.ComponentModel;

namespace Msdn.CuttingEdge.Samples
{
    [Serializable]
    public class OrderInfo
    {
        private int m_id;
        private DateTime m_date;
        private int m_employeeID;
        private string m_customerID;

        public OrderInfo() {}

        public OrderInfo(int id)
        {
            m_id = id;
        }

        public OrderInfo(int id, DateTime date) : this(id)
        {
            m_date = date;
        }

        public int ID
        {
            get {return m_id;}
        }

        public DateTime Date
        {
            get {return m_date;}
            set {m_date = value;}
        }

        public string CustomerID
        {
            get {return m_customerID;}
            set {m_customerID = value;}
        }

        public int EmployeeID
        {
            get {return m_employeeID;}
            set {m_employeeID = value;}
        }
    }
}

Fill the Collection

In Figure 5, the collection supplies no fill method. You can use (optionally static) methods on an accessor class to physically load data into a new instance of the collection or extend the collection's constructor to accept any parameter required to perform data access, as shown in the following line of code:

OrderCollection orders = new OrdersCollection(year);

By incorporating some data awareness into the collection, you get tighter coupling between data containers and data accessors; on the bright side, though, you probably get better code abstraction. Put another way, it would be like having the DataSet class expose a method to accept a query or stored procedure name. As you know, this is not the case in the .NET Framework, where adapters act as decoupled data accessors and provide the fill capabilities.

While building data accessors, it's usually a good idea to employ a set of data access helper components. These components centralize common data access tasks such as the management of database connections, the handling of parameters, the execution of SQL commands, and stored procedures. An excellent example of a similar layer is the Microsoft Data Access Application Block.

A Word on Generics

Collection troubles disappear in the .NET Framework 2.0, because that version introduces generics. Generics offer classes that implement ICollection and other interfaces in a strongly typed way with a type dynamically provided by the programmer. Generic classes share some characteristics with collections; for example, collections behave the same way regardless of the type of contained objects. To build a collection in the .NET Framework 2.0, you need one line of code indicating one of a variety of collection types—enumerable, collection, list, dictionary, or binding list:

public class OrderCollection : Collection<OrderInfo> {}

Collections built on generics provide a unique combination of reusability, type safety, and efficiency in a way that classic .NET Framework 1.x collections could not. The .NET Framework 2.0 supplies a new namespace—System.Collections.Generic—which contains several new generics-based collection classes (see Figure 6). The list doesn't include the most interesting class—BindingList<T>; that's located in the System.ComponentModel namespace. In addition to the basic functionalities of a collection, BindingList<T> supplies a framework for you to code typical binding functions such as sorting, searching, and CRUD (create-read-update-delete) operations. Thus it is the favorite class for binding tasks in Windows and ASP.NET 2.0 applications when ADO.NET containers, like DataSet and DataTable, are not going to be used.

Figure 6 Generics-Powered Collection-Related Classes in .NET (Close Figure 6)

Generic Class | Description | Non-Generic Counterpart
BindingList<T> | List that supports complex data-binding scenarios | None
Collection<T> | Provides the base class for a generic collection | CollectionBase
Comparer<T> | Compares two objects of the same generic type for sorting | Comparer
Dictionary<K, V> | A collection of key/value pairs | Hashtable
IEnumerable<T> | A collection that can be iterated using a for-each statement | IEnumerable
KeyedCollection<T, U> | A keyed collection | KeyedCollection
LinkedList<T> | A doubly linked list | None
List<T> | The base class for a list | ArrayList
Queue<T> | A FIFO collection of objects | Queue
ReadOnlyCollection<T> | The base class for a generic read-only collection | ReadOnlyCollectionBase
SortedDictionary<K, V> | A collection of key/value pairs sorted by key | SortedList
Stack<T> | A simple LIFO collection of objects | Stack

Binding to ASP.NET Controls

Imagine you a have a set of DAL components that can return a collection of orders issued in a given year. You get this information through the following code and bind it to a data-bound control:

OrderCollection orders = Helpers.LoadOrders(1997);
DataGrid1.DataSource = orders;
DataGrid1.DataBind();
The grid defines its columns as follows:
<Columns>
   <asp:BoundColumn DataField="ID" HeaderText="ID" />
   <asp:BoundColumn DataField="Date" HeaderText="Date"
        DataFormatString="{0:d}" />
</Columns>

If you're familiar with the Northwind database, you know that the Orders table doesn't have columns labeled "ID" or "Date". The DataField attribute of the grid's columns reference public properties on the OrderInfo class rather than columns on an Orders table resultset. The type of the data members in the bound collection is OrderInfo.

For binding purposes, it's essential that collection members expose public properties and not public fields. More precisely, if you redefine the OrderInfo class you saw in Figure 5 to look like the following code, you would get an HTTP exception:

public class OrderInfo {
   public int ID;
   public DateTime Date;
   public int EmployeeID;
   public string CustomerID;
}

The error message explains that no field or property named "ID" has been found on the selected data source. This is due to the fact that fields are not supported in .NET data binding, even though fields may be marked as public in the class definition. So you have to resort to properties. The difference between fields and properties is that a field is a public property that provides callers with direct, read/write access to the underlying data, while a property is a public property where access to the underlying data is filtered by get/set accessors. If you're still interested in binding to fields, see the .NET Matters column in this issue and in the April 2005 MSDN Magazine.

Once you build your custom business entities to expose properties, ASP.NET data binding is automatic, and even paging won't pose any significant problems. But what about sorting?

Generally speaking, ASP.NET grids sort their data in one of two ways: either by using a new database query with an ORDER BY clause or by using a DataView object. The former approach doesn't require Web server resources and is probably the fastest technique for sorting. After all, the SQL Server sorting algorithms are very well optimized. However, making a request back to SQL Server for sorted data is not necessarily a smart move because network latency, query execution, and data marshaling time affect the overall performance. On the other hand, if you sort on the Web server using an ADO.NET DataView object, or a sortable array or collection, you may tax the Web server's memory unless you cache sorted views for further use. (See the sidebar "Sorting in the .NET Framework.")

This goes to show that in spite of the effectiveness of the employed algorithm, your sorting decisions shouldn't be taken lightly. The approach to sorting should consider binding and paging. In general terms, caching is usually a good answer to paging and sorting issues. The bottom line is that, for high performance and highly scalable applications, you might want to consider storing presorted copies of the same data, either from SQL Server or sorted once through a DataView or an array sort method. Of course, there can be drawbacks to caching, such as stale data and filling up cache pages with infrequently used data.

To sort through a DataView, you must have data stored in a DataTable object. To sort a custom collection that internally stores its data in an array or in an ArrayList, you'll normally use the Array.Sort static method or the ArrayList.Sort instance method, making use of a custom comparer implementation or relying on the contained objects implementation of the IComparable interface. Figure 7 shows an enhanced version of the OrderCollection class that supports sorting. The new version of this class features a new Sort method which takes a sort expression (typically, a property name) and a value indicating the sort direction. These arguments are used to initialize a comparer class:

public class OrderComparer : IComparer
{
    public OrderComparer(string sortExpr, OrderSortDirection direction)
    {
        ...
    }
    ...
}

Figure 7 Sort-Enabled Version of OrderCollection (Close Figure 7)
public class OrderCollection : OrderCollectionBase
{
    // Class constructor
    public OrderCollection() {}

    // Add sort capabilities
    public void Sort(string sortExpression, OrderSortDirection direction)
    {
        SortInternal(sortExpression, direction);
    }

    protected virtual void SortInternal(
        string sortExpression, OrderSortDirection direction)
    {
        OrderInfo[] rgOrders = new OrderInfo[this.Count];
        this.InnerList.CopyTo(rgOrders);
        Array.Sort(rgOrders,
            new OrderComparer(sortExpression, direction));
        Reload(rgOrders);
    }

    protected void Reload(OrderInfo[] rgOrders)
    {
        this.InnerList.Clear();
        InnerList.AddRange(rgOrders);
    }
}

The IComparer interface features a single method—Compare—which is used to order a pair of elements in the array. In this case, the elements are two instances of OrderInfo. The value of the specified property is retrieved via the PropertyDescriptor class and compared to return an integer value. For more details, see the code download available at the MSDN Magazine Web site.

As a result, you can now handle the SortCommand event on the DataGrid and sort the bound collection. Here's a code snippet:

void SortCommand(object source, DataGridSortCommandEventArgs e)
{
   BindSortedData(e.SortExpression);
}
Sorting in the .NET Framework

Sorting is not a quick operation. Computationally speaking, O(N*LogN) comparison operations is typically the best performance you can get from a general-purpose sort algorithm. Let's delve deeper in the internal implementation of sorting in the ADO.NET DataView object and arrays.

The DataView class works on top of a DataTable and maintains an index of row views optionally filtered by an expression and sorted by a combination of columns. The index is updated each and every time a new filter or sort expression is specified. References to the selected table rows are cached in an array of DataRowView objects. The array is sorted using an implementation of the QuickSort algorithm.

Arrays also sort using the QuickSort algorithm. The implementation of QuickSort is a bit different in the two cases. Arrays attempt to optimize the algorithm on the type of contained objects and do a lot more of exception handling and static checks that the DataView structure and internal design makes unnecessary.

As mentioned, the QuickSort algorithm makes O(N*LogN) comparisons on average to sort N items. In the worst case, though, it makes O(N*N) comparisons. The QuickSort's worst case is when all items are already sorted in reverse order. QuickSort is significantly faster in practice than other O(N*LogN) algorithms because its inner loop can be efficiently implemented on a variety of architectures.

The helper method retrieves the collection from the ASP.NET cache and sorts it on the specified column and direction. Next, it caches the sorted version and binds to the grid, as you can see in the code in Figure 8.

Figure 8 Sorting and Binding (Close Figure 8)
void BindSortedData(string sortExpression)
{
   OrderCollection orders = (OrderCollection) Cache["MyData"];
   if (orders == null) {
      LoadData();
      orders = (OrderCollection) Cache["MyData"];
   }

   orders.Sort(sortExpression, OrderSortDirection.Descending);
   Cache["MyData"] = orders;

   DataGrid1.DataSource = orders;
   DataGrid1.DataBind();
}

Figure 9 shows a fully functional ASP.NET grid bound to an instance of the OrderCollection class.

Figure 2 Sortable Grid
Figure 2 Sortable Grid

To finish the ASP.NET binding discussion, let's review what happens with update and delete statements. Much like core binding, in-place editing is not really affected by the type of the enumerable object you use as the data source. Button columns that you would use for Edit and Delete operations work as expected and there's no other issue to face with ADO.NET objects as far as presentation is concerned.

You have two options for updating the back-end data store: performing an update to the Web server cache or performing a direct update on the database. The first option is appropriate if you're using cached data; it requires that you also supply a Submit button (or a timed mechanism) to let users persist changes periodically. Any Web server failures that occur in the meantime between two successive submissions cause a loss of data. When the user clicks to edit and then update a grid's row, the changes are persisted only in the ASP.NET cache (see Figure 10).

Figure 10 Editing and Updating a Bound Collection (Close Figure 10)
void DataGrid1_UpdateCommand(object source, DataGridCommandEventArgs e)
{
    // Get order being edited
    OrderCollection orders = RetrieveData();
    OrderInfo currentOrder = orders[e.Item.DataSetIndex];

    // Get textboxes
    TextBox txtOrderDate = e.Item.Cells[2].Controls[0] as TextBox;
    TextBox txtEmployeeID = e.Item.Cells[3].Controls[0] as TextBox;
    if (txtOrderDate != null)
        currentOrder.Date = DateTime.Parse(txtOrderDate.Text);
    if (txtEmployeeID != null)
        currentOrder.EmployeeID = Int32.Parse(txtEmployeeID.Text);

    DataGrid1.EditItemIndex = -1;
    BindData();
}

To fire a direct update to the database whenever the user exits edit mode, you might want to call a method on the DAL that takes an OrderInfo filled with fresh values and uses its contents to prepare and execute a stored procedure or a SQL command.

In the end, ASP.NET data binding doesn't change that much if you use custom collections in lieu of classic ADO.NET objects. Binding is nearly identical, even if from a template perspective. Data-bound expressions look the same regardless of the type of data source; the field name parameter of DataBinder.Eval can be either the name of a column or a public property. Sorting requires a bit more effort because you are required to implement a sortable collection. In terms of performance, DataView and arrays do nearly the same, as both use the QuickSort algorithm. Moreover, code based on arrays could even be slightly faster because of the thick layer of code and helper objects that surround and are used by ADO.NET DataViews.

Binding to Windows Forms

When you design a DAL, you normally make no assumptions on the calling application—be it an ASP.NET page, a Windows-based application, or a Web service. On the other hand, a DAL is merely a family of .NET classes with a given programming interface. A DAL, though, could reside on a remote machine. If this is the case, you need to ensure that your collection items (that is, OrderInfo) are serializable for the data transfer to take place successfully. Also, in Windows Forms applications, collections pose no significant problems for binding and paging operations.

What about sorting? Interestingly, if you use the following code snippet to populate a Windows DataGrid everything works fine, except that sorting is disabled:

OrderCollection orders = Helpers.LoadOrders(1997);
dataGrid1.DataSource = orders;
Try clicking on any column header and verify that nothing happens, regardless of the fact that the AllowSorting property is set to True. Now try binding another grid in the same form to any ADO.NET objects. Amazingly, the grid now sorts like a champ. So, what's up?

By design, the Windows Forms DataGrid doesn't expose a public sorting API like the Web DataGrid, and like the DataGridView control in ASP.NET 2.0. (For a sneak preview, see last month's Cutting Edge column.) To customize sorting, you can write some code around the grid, catch user clicks on the header bar, figure out the underlying column, and proceed manually with sorting. Not impossible, but not an easy task either. But the hot question is: why won't the DataGrid sort on a custom collection?

By design, the Windows DataGrid relies on methods on the IBindingList interface for sorting. If the bound collection doesn't implement this interface, the grid will not sort your data. In the .NET Framework 1.x, only two classes implement IBindingList: DataView and DataViewManager. When you bind a DataTable or a DataSet, the control automatically extracts the default view and uses that DataView object for binding.

More often than not, Windows Forms applications use batch update for persisting in-memory changes to a remote database. Along with data adapters, DataSets provide functionality to handle batch updates and implement optimistic forms of concurrency. A similar, fairly complex mechanism isn't available for custom collections and should be built from the ground up when needed. However, with BindingList<T> it's fairly straightforward; see TableAdapters and Object Binding - Bridging the gap for an example.

Binding to Web Services

The DataSet is often used for returning data from a Web service method, although this is not recommended as it leads to tightly coupled designs and provides few interop capabilities.

The DataSet is XML-serializable, meaning that it can be used as an input argument or return value for WebMethods. .NET provides two main mechanisms for data serialization—through formatters and through the XML serializer—and each serves different application scenarios. Formatters can serialize any .NET object that is marked with the [Serializable] attribute; the XML serializer is a specialized component that takes care of marshaling parameters and return values in and out of Web service methods.

Formatters are designed for type fidelity and to preserve the full state of an object. The XML serializer limits you to persisting the public interface of the object being serialized and creating an XML representation of only public properties and fields with no support for circular references. The XML serializer also works for types that implement the IXmlSerializable interface, including the illustrious DataSet type.

Note that the .NET Framework adds two ways to enhance DataSet serialization. First, binary serialization of DataSets provides for a more compact representation, but limits you to .NET-to-.NET communications. Additionally, the new SerializationMode option allows developers to skip schema serialization on typed DataSets. This has a huge impact for developers who are sending small payloads. For small payload DataSets, 90 percent of the data would be the schema, which is already known on both sides.

The DataSet represents a collection of data tables and offers the opportunity to group together multiple resultsets in a single instance. In the context of Web services, the DataSet type is helpful when you need to grab some data, present it to the user, and post back to apply changes. Note that in the .NET Framework 1.x, the DataSet is the only ADO.NET object that can be used with Web service methods, because it's the only one that implements IXmlSerializable. The DataTable has been extended in ADO.NET 2.0 to implement the same interface.

The DataSet is a polymorphic type whose layout is determined only at run time once the object has been filled with data. Schema information can be embedded, but this doesn't normally help Web service toolkits to map it to a platform-specific class. So DataSets are fine as long as the Web service is consumed from within a .NET client. In non-.NET environments, handling DataSet return values is problematic and requires the platform's low-level XML API.

To work around these issues, you often write WebMethods to make them receive and return arrays or collections for better compatibility with other platforms. Arrays and collections of custom classes also significantly reduce the workload for the network as they require much less room to serialize to XML. Here's an example of a bunch of Web methods that wrap the same core functionality in different signatures.

[WebMethod]
public DataSet GetOrdersAsDataSet(...)
[WebMethod]
public OrderInfo[] GetOrdersAsArray(...)
[WebMethod]
public OrderCollection GetOrdersAsCollection(...)
[WebMethod]
public string GetOrdersAsXml(...)

In Windows and Web Forms apps, you can add a reference to a Web service in order to generate a client-side proxy, call a method on that proxy, and bind the resulting value. When the return value is a DataSet, binding poses no problems at all. What if the Web method returns an array or a collection of custom structures?

When you import a Web service reference into your project, Visual Studio® .NET creates a proxy class to manage outbound calls. This class also imports any class declaration that serves for completing the call. There's the rub.

No matter how a custom class like OrderInfo is defined by the Web service source code, it gets rebuilt by the .NET Framework command-line tool—wsdl.exe—using fields, not properties (in the .NET Framework 2.0, however, properties are generated by default). As mentioned, arrays or collections of classes devoid of properties can't be bound to Windows and Web controls.

To fix the problem, you ensure that Windows and Web clients include a correct definition for any custom business entity used by the Web service. A correct definition is a definition that uses public properties in lieu of public fields. You can achieve this in one of two ways. You can edit the autogenerated proxy file to replace fields with properties, or you can remove all the class definitions from the reference file and add it to the project through an additional class file. Note that the proxy implementation file is normally hidden from view in Visual Studio .NET and shows warnings about the potential risks you take by editing it. However, feel free to edit it if you know what you're doing. Any change to the Web method's signature, or the class structure, may require you to edit the proxy implementation file or any other helper class you used.

Wrap Up

Custom collections are a valid alternative to ADO.NET container objects in most data-binding scenarios. The use of custom business entities in lieu of DataSets presents both advantages and disadvantages, but this is definitely an option that's worth exploring. Binding collections to Windows and Web Forms doesn't raise major issues except those involving sorting. For Web services, collections require some coding because of the limitations of the Visual Studio .NET importer tool that uses public fields instead of public properties. In general, DataSets are easy to use, but are not necessarily an optimal solution. Collections are lightweight objects that are also preferable from an interop standpoint.

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

Dino Esposito is a Wintellect instructor and consultant based in Italy. Author of Programming ASP.NET and his newest book Introducing ASP.NET 2.0 (both from Microsoft Press), he spends most of his time teaching classes on ASP.NET and ADO.NET and speaking at conferences. Get in touch with Dino at cutting@microsoft.com or join the blog at weblogs.asp.net/despos.


Data Points: Data Access Strategies Using ADO.NET and SQL

Data Points
Data Access Strategies Using ADO.NET and SQL
John Papa

Contents
ADO.NET Connections
Open Late, Close Early
Returning a Single Column or Row
Inefficient Queries
Commands and Parameters
ADO.NET Transactions
Wrapping Up

When your goal is a scalable and efficient enterprise solution, you need to develop an efficient data-access strategy. You can't just do some testing on your production machines and rely on the results. While an application can exhibit excellent response times when serving a few users, performance problems can surface when the load increases. So when I am asked to perform architectural and code reviews, there are several coding patterns I look for. In this column, I will share some of them as they relate to data access using ADO.NET. I'll discuss efficient ways to implement ADO.NET database connections to take advantage of connection pools and will present tips on how to use various ADO.NET objects to open and close connections and to manage connection state for you. I'll discuss connections and the DataAdapter and DataReader and move on to stored procedures, parameterized queries, and SQL injection attacks.

ADO.NET Connections

Database connections are usually an afterthought, which is surprising considering how essential they are and how they can easily be a source of inefficiencies and bottlenecks. One way to reduce the number of connections and their concomitant resource usage is to reuse connections through connection pooling. To be able to reuse a connection from a pool, the connection string must match that of an available pooled connection. Each connection pool is associated with one distinct connection string, using an exact matching algorithm. If no exact match is found, a new connection is created and later thrown into the pool.

Figure 1 shows three code samples that open a connection against the same database, using two different users. The first connection is accessed by the SQL Server™ user called TestUser1. Assuming there is no connection available in the pool, this connection is created from scratch. Later, this connection is closed and sent to the connection pool. The second connection is accessed by a different SQL Server user. The deactivated connection in the pool doesn't have an exact connection string match since the users are different. Thus, the second connection is created from scratch, closed, and sent to the connection pool. Then a third connection is opened by the SQL Server user called TestUser1. There is a connection in the pool that matches the connection string exactly, so the connection is retrieved from the pool.

Figure 1 Connection Pooling (Close Figure 1)
using(SqlConnection oCn = new SqlConnection(
    "Server=(local);Database=Pubs;User ID=TestUser1;Password=foo1;")) {
    oCn.Open();
    ...
}

using(SqlConnection oCn = new SqlConnection(
    "Server=(local);Database=Pubs;User ID=TestUser2;Password=foo2;")) {
    oCn.Open();
    ...
}

using(SqlConnection oCn = new SqlConnection(
    "Server=(local);Database=Pubs;User ID=TestUser1;Password=foo1;")) {
    oCn.Open();
    ...
}

You can take advantage of connection pooling by using a service account for your application. For example, you can create a single SQL Server user account that your application will use to access the database. This will allow you to take full advantage of connection pooling since the connection strings will always match. This is a good option from a scalability perspective. However, if you want greater control over security, you should consider using Windows® authentication.

Open Late, Close Early

Another connection-related item I look for when performing a code review is whether or not a connection is held open for the shortest period of time that it is required. The basic rule is to keep the connection open only as long as you need it. You should only open a connection immediately before you need to access the database and then close it as soon as you are done accessing the database. Be wary of any nondatabase-related code that executes while the connection is open. Connections hold open valuable resources to the database, consume memory, and can lock data that could cause other queries to slow down. So it's best to open connections late and close them as early as possible.

One of the great features of ADO.NET is that the DataAdapter object's Fill and Update methods can open and close a connection automatically (see Figure 2). The advantage of this is that it is not necessary to open the connection explicitly because the DataAdapter opens it for you at the moment right before it executes its SQL command against the database and then closes it right afterwards.

Figure 2 The Fill Method Opens, Executes, and Closes (Close Figure 2)
using (SqlConnection oCn = new SqlConnection(sConnString))
{
    string sProcName = "prGet_Products";
    using (SqlCommand oSelCmd = new SqlCommand(sProcName, oCn))
{
        oSelCmd.CommandType = CommandType.StoredProcedure;

        oDa.SelectCommand = oSelCmd;
        oDa.Fill(oDs);
    }
}

Also, it is a good idea to avoid passing an open connection between methods where possible. Instead, you may want to take advantage of connection pooling and close the first connection, call the method without passing the connection, and then open a new connection within the called method.

Like the Connection object, a DataReader object should always be closed when you have finished using it. When using a DataReader, I look to see if the Command object's ExecuteReader method has its CommandBehavior set to CloseConnection. This will make sure the connection is automatically closed when the DataReader is closed. If you want to return multiple rowsets, you should omit the CommandBehavior to allow it to use its default setting, which allows multiple rowsets to be returned and accessed. Figure 3 demonstrates that the Connection will automatically be closed when the DataReader is closed.

Figure 3 Automatically Closing the Connection (Close Figure 3)
oCn.Open();
SqlDataReader oDr = oCmd.ExecuteReader(CommandBehavior.CloseConnection);
while (oDr.Read())
{
    Debug.WriteLine(oDr[1].ToString());
}
Debug.WriteLine("Connection is " + oCn.State);
oDr.Close();
Debug.WriteLine("Connection is " + oCn.State);

One sign that connections are being held open longer than needed is that you find code that checks if the connection's ConnectionState is open or closed:

if (oCn.State == ConnectionState.Closed) {
    oCn.Open();
}
The presence of such code indicates that the connection is expected to be open in some cases when it gets to this point. One exception to the guideline that could cause you to keep connections open longer is when you need to implement a transaction that spans multiple commands.

If you do not close a connection explicitly and instead wait for the garbage collector to release the resource, the connection will not be released back to the connection pool until the garbage collector comes for it, and you can't be sure when that will be.

Returning a Single Column or Row

The ADO.NET Command object exposes multiple Execute methods. The ExecuteReader and ExecuteXmlReader methods are capable of returning multiple rows and columns to a DataReader and XmlReader, respectively. The Fill method of the DataAdapter can also return multiple rows and columns to a DataSet.

But when you want to execute an action query and do not want to return any data, use the Command object's ExecuteNonQuery method. This executes its command and doesn't bother with any of the overhead involved in returning data. This is ideal for updates and deletes when you want the SQL statement to be executed.

If you want to retrieve a single value, the ExecuteReader, ExecuteXmlReader, and Fill methods are usually overkill. If you use the DataAdapter's Fill method to fill a DataSet with a single column and a single row, you are also getting all of the metadata and overhead that comes with the more powerful DataSet object. It would be more efficient to use the Command object's ExecuteScalar method. You could execute the same SELECT statement and retrieve the single value into a variable. To demonstrate, notice the two examples in Figure 4 that use both techniques to execute a SQL statement that returns a single value.

Figure 4 Returning a Single Value (Close Figure 4)

Using SqlCommand.ExecuteScalar
private void UseExecuteScalar() {
    string sConnString =
        "Server=(local);Database=Northwind;Integrated Security=True;";
    string sSQL =
        "SELECT CompanyName FROM Customers WHERE CustomerID = 'ALFKI'";
    using (SqlConnection oCn = new SqlConnection(sConnString)) {
        using (SqlCommand oSelCmd = new SqlCommand(sSQL, oCn)) {
            oCn.Open();
            string sCompany = oSelCmd.ExecuteScalar().ToString();
            oCn.Close();
        }
    }
}
Using SqlDataAdapter.Fill
private void UseFill() {
    string sConnString =
        "Server=(local);Database=Northwind;Integrated Security=True;";
    string sSQL =
        "SELECT CompanyName FROM Customers WHERE CustomerID = 'ALFKI'";
    using (SqlConnection oCn = new SqlConnection(sConnString)) {
        using (SqlCommand oSelCmd = new SqlCommand(sSQL, oCn)) {
            SqlDataAdapter oDa = new SqlDataAdapter();
            DataSet oDs = new DataSet();
            oDa.SelectCommand = oSelCmd;
            oDa.Fill(oDs);
        }
    }
}

If you want to return more than one column from a single row, you could use the Fill method of the DataAdapter to fill a DataSet. Alternatively, instead of returning a rowset from a stored procedure you could return the values in output parameters. For example, instead of using this stored procedure

CREATE PROCEDURE prGet_CustomerRowSet
    @sCustomerID NCHAR(5)
AS
    SELECT CompanyName, ContactName, City
    FROM Customers
    WHERE CustomerID = @sCustomerID
GO
you might want to consider using the stored procedure in Figure 5.

Figure 5 Taking Advantage of Output Parameters (Close Figure 5)
CREATE PROCEDURE prGet_Customer
    @sCustomerID NCHAR(5),
    @sCompanyName NVARCHAR(40) OUTPUT,
    @sContactName NVARCHAR(30) OUTPUT,
    @sCity NVARCHAR(15) OUTPUT

AS
    SELECT @sCompanyName = CompanyName,
           @sContactName = ContactName,
           @sCity = City
    FROM Customers
    WHERE CustomerID = @sCustomerID
GO

When called from ADO.NET, the first stored procedure could be used to fill a DataSet from a DataAdapter. The DataSet would contain the data and all of the supporting metadata as overhead. The second stored procedure can be called from ADO.NET using a Command object. Its values can be retrieved using the Command object's parameter collection. The prGet_Customer stored procedure is more efficient since it does not need to pass a rowset nor does the client code have to bear the weight of the extra overhead that comes with the DataSet. The code in Figure 6 could be used to retrieve the output parameters from the second stored procedure (prGet_Customer). The three values can be stored in variables requiring less than 100 bytes. In contrast, if you return the same three values into a DataSet from a DataAdapter using the first stored procedure, the DataSet and its schema will be almost 1,000 bytes in size. If all you need are a handful of values from a single row, using the ExecuteScalar (for 1 value) or output parameters executes faster than retrieving a rowset into a DataSet, and then having to pass that data to another application tier.

Figure 6 Retrieving Values via Output Parameters (Close Figure 6)
private void GetOutputValues()
{
    string sConnString =
        "Server=(local);Database=Northwind;Integrated Security=True;";
    string sProc = "prGet_Customer";
    using (SqlConnection oCn = new SqlConnection(sConnString))
    {
        using (SqlCommand oCmd = new SqlCommand(sProc, oCn))
        {
            oCn.Open();
            oCmd.CommandType = CommandType.StoredProcedure;

            oCmd.Parameters.Add("@sCustomerID", SqlDbType.NChar, 5);
            oCmd.Parameters["@sCustomerID"].Value = "ALFKI";

            oCmd.Parameters.Add("@sCompanyName", SqlDbType.NVarChar, 40);
            oCmd.Parameters["@sCompanyName"].Direction =
                ParameterDirection.Output;

            oCmd.Parameters.Add("@sContactName", SqlDbType.NVarChar, 30);
            oCmd.Parameters["@sContactName"].Direction =
                ParameterDirection.Output;

            oCmd.Parameters.Add("@sCity", SqlDbType.NVarChar, 15);
            oCmd.Parameters["@sCity"].Direction =
                ParameterDirection.Output;

            oCmd.ExecuteNonQuery();
            oCn.Close();

            string sCompanyName =
                oCmd.Parameters["@sCompanyName"].Value.ToString();
            string sContacName =
                oCmd.Parameters["@sContactName"].Value.ToString();
            string sCity = oCmd.Parameters["@sCity"].Value.ToString();
        }
    }
}

Inefficient Queries

So often when I examine SQL and stored procedures I notice that more data is being retrieved than is being used. A SELECT statement should only return the columns that are going to be used, possibly for display purposes or some business logic. For example, suppose I return 100 rows of data and you retrieve two extra integer columns that are not used anywhere. Each integer column is 4 bytes, so I am returning 8 bytes per row or 800 extra unused bytes of data. Then, when inserted into a DataSet, which may be passed to another application layer, the XML and supporting metadata that is needed for the extra columns adds to that total.

So now I am returning superfluous data from the database server to my business logic server and then passing extra data around as XML to services that may very well exist on other machines across the network. Often I hear that the data size is so small that it makes no noticeable difference in performance. Of course that argument may be valid when there is a single user hitting the application. But when you try to scale the application to thousands of users, passing unnecessary data across a network can slam performance.

Another issue is when a SELECT statement retrieves more rows than it needs. For example, returning 100 rows is overkill when you will only display the top 10. It's better to trim the SELECT statement to retrieve only the top 10 records, using the TOP keyword.

The CommandBuilder object is often used in demonstration code to show how it can automatically figure out how to execute INSERT, UPDATE, and DELETE statements. It takes the SELECT statement from a DataAdapter and tries to generate the other commands for you. This is going to be slower than if you coded the commands yourself because the CommandBuilder has to figure it out (which takes time) and generally creates less efficient queries than you would create. I recommend avoiding the CommandBuilder object entirely at run time.

Another key point I look for in reviews of SQL code and stored procedures is excessive joins. They're often found in SELECT statements when a table was needed at one time to retrieve a column, but since that time the column has been removed but the join to the table remains. I also look out for extra joins, particularly when joining a table whose column can already be accessed without having to use a join.

Commands and Parameters

When a SQL statement is executed, the database has to generate an execution plan for it. If that SQL statement is run repeatedly, each time it is executed the database might have to regenerate the query's execution plan. In these cases where SQL is run frequently, moving the SQL to a stored procedure can offer greater performance (not to mention greater security). The first time a stored procedure is executed, the database generates a query execution plan, stores that plan in the procedure cache, and then executes the stored procedure. On subsequent calls of the stored procedure, the database engine only has to grab the query plan from the procedure cache and rerun the stored procedure. Thus, it skips the step of devising the query plan on subsequent calls. For the increased performance, I always suggest using stored procedures when it is an available option in a database's architecture.

Another secondary option is to execute a SQL statement using the SqlCommand object and calling its Prepare method. If the data provider supports it, calling the Prepare method tells the data provider to get ready for this SQL statement to be executed multiple times. The SQL statement's query execution plan will then be stored by the database so that subsequent executions will run faster. However, executing the Prepare method should only be used in situations where the SQL will be executed multiple times. The process of preparing the SQL statement requires a bit of overhead and thus if the statements are only executed once, performance will degrade. Figure 7 demonstrates how an UPDATE statement can be prepared and executed twice.

Figure 7 Executing a Prepared Command (Close Figure 7)
private void CallPreparedCmd() {
    string sConnString =
        "Server=(local);Database=Northwind;Integrated Security=True;";
    string sSQL =
        "UPDATE Customers SET City=@sCity WHERE CustomerID=@sCustomerID";
    using (SqlConnection oCn = new SqlConnection(sConnString)) {
        using (SqlCommand oCmd = new SqlCommand(sSQL, oCn)) {
            oCmd.CommandType = CommandType.Text;

            oCmd.Parameters.Add("@sCustomerID", SqlDbType.NChar, 5);
            oCmd.Parameters.Add("@sCity", SqlDbType.NVarChar, 15);

            oCn.Open();
            oCmd.Prepare();

            oCmd.Parameters["@sCustomerID"].Value = "ALFKI";
            oCmd.Parameters["@sCity"].Value = "Berlin2";
            oCmd.ExecuteNonQuery();

            oCmd.Parameters["@sCustomerID"].Value = "CHOPS";
            oCmd.Parameters["@sCity"].Value = "Bern2";
            oCmd.ExecuteNonQuery();

            oCn.Close();
        }
    }
}

One setting that is often omitted is the Command object's CommandType. This property helps the Command object process the command by telling it ahead of time whether the command it is going to execute is a stored procedure, the name of a table, or a free-form text command. If you do not set this property, the Command object defaults this property to CommandType.Text; while it will still work with stored procedures, it will execute more efficiently if you tell it ahead of time:

oCmd.CommandType = CommandType.StoredProcedure;

I also look for whether the Parameter object could be used in a query instead of hardcoding parameter values in a SQL statement. In Figure 7 the UPDATE statement runs twice, so it makes sense that the Parameter object be used for the values of the City and CustomerID. If this was not a parameterized query, preparing the SQL would not make much sense in this case since the SQL statements would then differ and preparing a statement and only running it once is inefficient. Also, by using a parameterized query you remove the need for nuisances like embedded quotes. Notice that in Figure 5 the values do not have embedded quotes around them, nor do the placeholders for the parameters in the SQL string. If I were to replace the parameter placeholders with the values themselves, I would have to include the embedded quotes, as follows:

string sSQL = "UPDATE Customers SET City = 'Berlin2'
WHERE CustomerID = 'ALFKI'";

Using parameterized SQL statements and stored procedures with Command objects is also good for security as they can deter SQL injection attacks, which occur when a malicious user enters input that is used to perform actions other than what was intended. Parameter objects treat the values of the parameters as literal values and not as executable code. So even if SQL code is injected, it would not be executed but rather used as a parameter's value. For a more in-depth look at SQL injection attacks, see Paul Litwin's article in the September 2004 issue of MSDN®Magazine, available at Data Security: Stop SQL Injection Attacks Before They Stop You.

Using embedded values for the parameters can easily get out of hand as the statement grows in size and the number of parameters increases. Any developer who has run SQL statements by concatenating strings together has likely been frustrated at one time or another by trying to get the embedded quotes in the right places. Imagine the following code:

System.Text.StringBuilder oSb = new System.Text.StringBuilder();
oSb.Append("UPDATE Customers SET City = 'Berlin2', ");
oSb.Append("Address = 'Some address', ContactName = 'Some name' ");
oSb.Append("ContactTitle = 'Some title', CompanyName = 'Some company'");
oSb.Append("WHERE CustomerID = 'ALFKI'");

The StringBuilder can make the operation more efficient in certain scenarios, but you still have to be careful to embed the single quotes in the right places. With parameterized queries, this headache is gone.

This leads me to the topic of concatenating fragments of SQL code together in a string. This is much more efficiently handled by using the StringBuilder object than by actually concatenating string fragments together since the string object is immutable. I recommend using the StringBuilder any time you are concatenating more than two strings. That said, I have to reiterate that you should only build up SQL strings like this when absolutely necessary. If done incorrectly, you can open yourself up to a wide variety of attacks, and Microsoft discourages this practice whenever possible.

ADO.NET Transactions

Data transactions are integral to most enterprise applications. However, transactions are an area that can easily become inefficient when unmanaged. Like connections, transactions should be kept as short as possible. Begin them as late as possible and commit or roll them back as soon as possible. Between the time the transaction begins and the time it ends only the crucial INSERT, UPDATE, and DELETE SQL statements should execute. Non-SQL modifying code should be avoided within the transaction since the transaction holds locks open and reduces concurrency.

Creating explicit transactions should be avoided if only a single UPDATE statement is being executed, for example. In this case an implicit transaction is already surrounding the UPDATE statement so creating an explicit transaction adds additional and unneeded overhead. However, if a second command is executed and needs to be included in the atomic transaction along with the first UPDATE command, then a transaction should be used. Take a look at the code in Figure 8 to see how multiple commands can be assigned to a single transaction.

Figure 8 Multiple Commands One Transaction (Close Figure 8)
SqlTransaction oTran = null;
try {
    oCn.Open();
    oTran = oCn.BeginTransaction();

    oCmd1.Transaction = oTran;
    oCmd1.ExecuteNonQuery();

    oCmd2.Transaction = oTran;
    oCmd2.ExecuteNonQuery();

    oTran.Commit();
}
catch {
    oTran.Rollback();
    throw;
}
finally {
    oCn.Close();
}

Transaction objects are only available if the data provider exposes them. These objects must be explicitly created and then assigned to the appropriate Command objects. The Transaction object can then be committed or rolled back, as shown in Figure 8. Also, make sure you clean up your resources in the finally blocks. The figure shows how a Connection object is closed regardless of whether the transaction succeeds or fails.

Wrapping Up

It has often been said that it's the little things that matter. Of course, that sentiment was never intended to refer to the creation of enterprise software applications, but nevertheless it certainly applies. Taken in isolation, these little examples of inefficiencies barely make a noticeable difference in an application on a developer's computer. However, remember that once you throw dozens, hundreds, or thousands of concurrent users at it, these little inefficiencies can really slow down your application and inconvenience your users. I often create a checklist of these items that apply to an application's architecture and use it as the basis for a code review. In addition to what I have discussed here, the DataSet, XML features, indexes, and batch SQL are all areas that can benefit from a little performance tweaking to improve data access efficiency. I will cover these topics in an upcoming installment of this column.

Stay tuned!

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

John Papa is a baseball fanatic who spends most of his summer nights rooting for the Yankees with his two little girls, wife, and faithful dog, Kadi. He has authored several books on ADO, XML, and SQL Server and can often be found speaking at industry conferences such as VSLive.


Service Station: Contract-First Service Development

Service Station
Contract-First Service Development
Aaron Skonnard

Contents
Lessons Learned from COM
Contract-First Component Development
Fast-Forward to Service Orientation
Code-First ASMX
Contract-First ASMX
Contract-First or Code-First?
Contract-First Tool Support
What's Next?

In one of my previous columns on Service Orientation (SO), I introduced the concept of "contract-first" service development (see Service Station: SOA: More Integration, Less Renovation). Over the next two installments of this column, I'm going to cover the topic in depth. In this first part, I'll build the case for contract-first development and discuss the ASMX programming model. As a follow-up, I'll walk you through a practical contract-first development process using today's tools and illustrate a hybrid technique that can give you a good balance between interoperability and productivity.

Lessons Learned from COM

The shift from object-orientation to component development made it possible to build more loosely coupled and flexible systems, in which individual components could evolve independently without affecting the rest of the system. Component technology made this possible by enforcing strict boundaries and restricting access to objects through explicit interface definitions. An interface-based development approach focuses on sharing explicit interface definitions, or "contracts," expressed in a language easily understood by others, removing dependencies on implementation details and offering programming language interoperability. There are valuable design lessons to be learned from the experience with components as SO evolves. One of the most important of these lessons revolves around contract design.

In order to use a class found inside a COM component, you ask the runtime to activate an instance through a class factory. The runtime then returns an interface reference for communicating with the object instance. All further interactions with the instance occur through the supplied interface reference. This interface-based model shields the consumer from all class implementation details. The only dependency the consumer takes on in this situation is the binding to the interface definition. As long as the interface definition doesn't change, the implementation can be updated with bug fixes or other necessary improvements.

The decoupling provided by interface-based development also makes it possible to distribute components across different contexts without impacting the consumer's programming model. As far as the programming model is concerned, it doesn't matter to the consumer whether the instance is local (on the same machine) or remote (on a separate machine), although there are some obvious design considerations. The DCOM infrastructure handles this through an interception layer that takes care of serializing method calls into the appropriate wire-level messages for transmission between machines. It handles recreating the call stacks on the remote machine, invoking the instance, and returning any values. The interface definition essentially tells the DCOM infrastructure what to do and therefore drives the wire-level contract of the system.

COM relied on a common language for defining interface definitions known as the Interface Definition Language (IDL). IDL can be implemented in a variety of different programming languages, such as C++ and Visual Basic®. In fact, IDL is typically used to generate header files and type libraries that can be used from these different languages during development (see Figure 1).

Figure 1 IDL Contracts and Language Interoperability
Figure 1 IDL Contracts and Language Interoperability

Components authored in different programming languages can successfully interact with each other and achieve interoperability, since they share an understanding of the interface definition. It has been quite common for Visual Basic applications to rely on C++ components and vice-versa. This type of cross-language interoperability was one of COM's greatest strengths.

Contract-First Component Development

While it's true that COM offered cross-language interoperability, it required discipline to achieve it in practice. Despite the interface-based architecture, it was common for problems to arise when trying to achieve interoperability across Visual Basic and C++ components due to the differences in their type systems, their capabilities, and how the IDL contracts were authored by developers. There was a big difference between how the two camps thought about, authored, and used IDL.

Developers using C++ typically authored their IDL interface definitions by hand, focusing on the contract before the implementation—a contract-first approach. After authoring IDL, C++ developers ran it through midl.exe, a code generator that produced header files, type libraries, and other source files needed by the COM interception layer. The header file contained C++-style interface definitions that the developer could implement in a C++ class. C++ clients used the same exact files to write code that consumed the interface.

The Visual Basic support for COM attempted to hide IDL from the developer as much as possible because it was generally considered hard to use. Hence, most developers using Visual Basic didn't start with IDL during design. Instead, they started by authoring Visual Basic classes—a code-first approach. During compilation, Visual Basic 6.0 automatically made the class into a COM object and produced an interface definition behind the scenes. The generated interface became part of the component's type library, which was referenced by clients wanting to use the component. The code-first approach made it much easier for developers using Visual Basic to build COM applications, but it came with its own set of problems.

Although it increased productivity, this code-first approach complicated versioning. Since developers were shielded from the official interface definitions, they became completely dependent on the IDE to manage them properly. The translation layer (from Visual Basic to IDL) made it harder for developers to understand the consequences of modifying their class definitions during development. As a result, the Visual Basic IDE came with several compatibility modes, which provided the needed assistance and warnings when making breaking changes, perhaps unintentionally, during development. Using the compatibility model effectively in real-world long-term projects became one of the more complex areas of Visual Basic COM development.

The other problem with the Visual Basic code-first approach was that it hurt interoperability in mixed-language environments. The self-centered model didn't encourage developers to think about the types they were using in their interface definitions, and the problems that such types might cause in other language environments. Hence, it was common for developers using Visual Basic to naively produce method signatures that became extremely difficult, if not impossible, to work with in C++. Now to be fair, it was also possible for developers using C++ to produce interfaces that were unfriendly to Visual Basic, even though they started with IDL, but in general, they were more aware of the issues given their focus on the contract. An important aspect to designing a shared contract is considering the needs of all possible implementation environments before writing code.

Many developers didn't realize that they too could enjoy the benefits of contract-first development by authoring the IDL first, compiling the IDL into a type library, and then referencing the type library to bring the interface definition into the project. Then it was as simple as implementing the interface explicitly in a new Visual Basic class and, by so doing, avoiding an auto-generated interface and the problematic translation layer. It didn't take long for some organizations to catch on to this secret and to encourage its use, especially in environments with collaborating teams of developers using C++ and Visual Basic.

Now, not all organizations using COM were concerned with mixed-language support or long-term versioning and were more interested in improving short-term development productivity. Such organizations were perfectly content, and even successful, taking the Visual Basic code-first route despite some of the issues I've outlined. Ironically, the new world of SO is heading down the same path, dealing with a very similar set of issues.

Fast-Forward to Service Orientation

A service is very similar to a distributed component, only the component runtime (COM) is no longer required by those who want to consume the service. The service contract shields consumers from all implementation details, including the platform, development framework, and programming language in use. Anyone who understands the service contract—the message formats and transport protocols—can have access to the service; this significantly increases reuse.

As with component development, the interfaces or contracts become the building blocks of an SO application. However, instead of IDL, there are new languages that are used to express service contracts, all of which are now widely understood throughout the industry. XML Schema Definition (XSD) is used to define the data contracts—the precise format of the XML messages that travel on the wire via SOAP. Web Services Description Language (WSDL) is layered over XSD and specifies how the XSD messages are correlated to define operations—the focus here is on sequencing, message exchange patterns, and transport details. These languages together provide an interoperable contract language for services. (In addition to XSD and WSDL, Web service policies can be used to define additional rules and behavior such as security constraints, which can also be considered to be part of the behavioral contract.)

When working with a service, the only dependency you have to enter into is the binding to the XSD and WSDL definitions in order to produce the correct wire-level protocol. And like IDL, XSD and WSDL can be mapped to any programming language and used in a variety of different development environments, promoting great flexibility and a wide range of programming options (see Figure 2).

Figure 2 WSDL Contracts
Figure 2 WSDL Contracts

Like the component days, most of today's Web services frameworks make it possible to design services using either code-first or contract-first techniques; however, most Web service development frameworks push developers towards the former. Frameworks that provide a code-first approach provide a translation layer to map between the native programming language and XSD and WSDL.

Code-First ASMX

Like Visual Basic 6.0, the ASP.NET Web services framework, also known as ASMX, enlists a code-first development process that hides the XSD and WSDL contract from developers. This approach makes it easy to get Web service endpoints up and running by simply annotating classes with predefined Web service attributes and binding the class to an .asmx endpoint.

The code in Figure 3 shows a simple ASMX service for submitting and retrieving purchase orders (po.asmx). You should notice that the OrderService class looks like a regular .NET class with a few extra Web service attributes, specifically [WebService] and [WebMethod]. These attributes tell the ASMX framework to expose the class as a Web service and to expose the marked methods as Web service operations, respectively.

Figure 3 Sample ASMX Endpoint (po.asmx) (Close Figure 3)
<%@ WebService Language="C#"
    CodeBehind="~/Code/Test.cs" Class="POService" %>

using System.Web.Services;

[WebService(Namespace="http://pluralsight.com/purchasing")]
public class POService : WebService {
    [WebMethod]
    public void SubmitPO(string customerId, string customerName,
        string companyName, LineItem[] items) {
        ... // process order here
    }
}

public class LineItem {
    public string productId;
    public string productName;
    public double quantity;
    public double unitPrice;
    public double extendedPrice ;
}

ASMX automatically maps the class and the [WebMethod] signatures to a WSDL contract. The ASMX approach encourages a remote procedure call (RPC ) programming style, where developers are focused more on methods and parameters than XML messages and elements. ASMX uses the method and parameter names to generate the XML message used on the wire. By default, ASMX generates a wrapper element with the same name as the method. It also generates an element for each parameter and nests it within the wrapper element. Other attributes, such as [SoapDocumentService] and [SoapDocumentMethod], can be used to customize the precise WSDL mapping, and to control whether or not the method wrapper element is used.

ASMX also takes care of mapping .NET data types and other user-defined types to an appropriate XSD type within the generated WSDL. You can also use various serialization attributes (like [XmlType], [XmlRoot], [XmlElement], and [XmlAttribute] found in System.Xml.Serialization) to customize the XSD mapping if you need something other than the default transformation.

If you browse to order.asmx you'll see that ASMX automatically generates a human-readable documentation page along with the official WSDL contract that others can use. If you tweak your code or modify the attributes in use and refresh the ASMX page, you'll notice the WSDL contract change, illustrating that the ASMX code is driving the contract details. Figure 4 illustrates what the SOAP request looks like for SubmitPO (that you saw in Figure 3), as taken from the documentation page.

Figure 4 SOAP Request Format for SubmitPO (Close Figure 4)
<soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <SubmitPO xmlns="http://pluralsight.com/purchasing">
      <customerId>string</customerId>
      <customerName>string</customerName>
      <companyName>string</companyName>
      <items>
        <LineItem>
          <productId>string</productId>
          <productName>string</productName>
          <quantity>double</quantity>
          <unitPrice>double</unitPrice>
          <extendedPrice>double</extendedPrice>
        </LineItem>
        ...
      </items>
    </SubmitPO>
  </soap:Body>
</soap:Envelope>

Like Visual Basic, ASMX embraces a code-first development model through a sophisticated translation layer. At run time, the translation layer provides all the necessary dispatching and serialization support to map between the worlds of SOAP and .NET. For many developers, the translation layer is a godsend because the familiar development process increases productivity. However, like before, the translation layer can become troublesome and usually causes developers to lose sight of the real service contract (the XSD and WSDL) they are producing and forcing consumers to use. Like before, this can cause problems with interoperability and long-term versioning.

Experience has shown that services implemented using code-first techniques, like the default ASMX model, are less likely to interoperate cleanly in mixed-language environments. However, the difference with SO is that mixed environments are much more common—practically the norm—making interoperability a much greater design concern. The problem is compounded by the fact there are more programming languages and type systems involved in this new world than before in the component days. Now it's even more important to consider all possible implementation environments during the contract design stage.

Code-first can also complicate versioning because the developer isn't in direct control of the contract, but must rely on the translation layer throughout development. However, unlike Visual Basic 6.0, ASMX doesn't provide any fancy compatibility modes to help the developer know when making changes to the ASMX class may break contract compatibility with existing clients, which makes the situation even worse.

Contract-First ASMX

.NET provides the necessary tool support for contract-first ASMX development today, but it requires more discipline than the convenient code-first approach. To use a contract-first approach, you start by authoring the XSD definitions to model the messages used by the service. Next you author the WSDL to define the service operations, specifying which XSD messages are sent and received. There are a variety of applications you can use to author these documents, including Notepad. These first few steps are the hardest for most developers because they aren't familiar with XSD and WSDL and because there isn't a great deal of tool support.

Once you have the WSDL contract created, you use wsdl.exe (with the "/server" switch) to generate the ASMX service interface, leaving you with the simple task of implementing the operations. The generated service interface contains all the necessary .NET attributes to produce the same WSDL contract from which you started. All you have to do is implement this service interface using your own business logic.

When you generate the WSDL contract up front, numerous developers from different organizations, perhaps working on different platforms and in different languages, can use code generation tools from their frameworks to begin developing against the predefined contract before you've even started writing code. Development work against the predefined contract, where you may have several organizations implementing the same service contract and various client applications being developed to consume them, can happen simultaneously (see Figure 5).

Figure 5 Contract-First Development with WSDL
Figure 5 Contract-First Development with WSDL

Figure 6 summarizes the differences between the ASMX code-first and contract-first development models. With contract-first, you do the heavy lifting in the first few steps, authoring the contract (WSDL), after which you automate the generation of the .NET code. With code-first, you start by authoring the .NET code, and let ASMX generate the contract (WSDL). The choice you'll make will boil down to what you want to author and what you want to automate. Both approaches have their pros and cons, and it makes sense to use each one at certain times.

Figure 6 Code-First Versus Contract-First Development (Close Figure 6)

Code-First Development
1. | Author ASMX classes directly, defining contract and implementing operations simultaneously
2. | Use ASMX to generate XSD and WSDL
3. | Iterate
Contract-First Development
1. | Define XML messages using XSD
2. | Define operations using WSDL
3. | Generate ASMX code from WSDL
4. | Implement operations
5. | Iterate

Contract-First or Code-First?

When designing a service contract, you can always choose to use either code-first or contract-first techniques. Ultimately the decision hinges on whether you care more about ensuring interoperability or improving productivity.

It's interesting that contract-first is generally considered the right model to follow when building clients. Virtually all frameworks start by generating code from WSDL on the client slide. You need to conform to a contract, so you start with the contract and generate the code—that makes sense. But when you start talking about using the same model for implementing services, some developers don't see it as clearly. So when should you definitely consider using the contract-first plan?

The answer is any time there is a strong emphasis on the messages exchanged or you need to ensure conformance to an existing message format. BizTalk® applications are a good example of this because they are typically designed around (existing) message formats from the ground up. Interestingly, BizTalk natively supports a contract-first development experience. When designing a BizTalk orchestration, developers start by designing the schemas that represent the input and output and everything else is designed from that starting point. Thanks to the BizTalk support for contract-first, developers commonly embrace BizTalk (perhaps instead of using ASMX) when they need to conform to existing standards. Contract-first is, without question, the most natural approach in a message-oriented world, such as that of BizTalk.

Contract-first is also especially important when you require conformance to a particular contract from third parties, or when you're collaborating with other groups to define a shared contract throughout an organization. Using contract-first here allows you to solidify the contract up front while numerous parties implement the contract in parallel. All of these situations are common in industry today.

In a real-world SO system, it's likely that you'll have to implement and consume contracts designed by others, and you're also going to design contracts for others to use. And when you're forced to use someone else's contract, all you're going to care about is that it works with the framework you're stuck using. So when you own the contract, it is common sense to strive to make the contract as easy as possible for your consumers to use, which also begs for a contract-first model.

The biggest obstacle to contract-first design today is the lack of tool support, which hurts productivity. When productivity is the overriding concern, code-first can be an acceptable approach, especially when you're not particularly concerned with mixed-environment interoperability.

Contract-First Tool Support

All the tools you need to employ contract-first development are available today, but unfortunately they're not integrated within a single, unified development environment. Visual Studio® .NET provides an intuitive XSD designer that can be used to author your data contracts. And the Microsoft® .NET Framework provides the code generation tools you need, including an XSD code generator (xsd.exe) and a WSDL code generator (wsdl.exe). The main things missing from .NET are a WSDL designer and support for an iterative development process fully integrated with Visual Studio .NET.

There are, however, numerous third-party tools that can help improve the contract-first experience today. There are sophisticated WSDL editors that give you a more user-friendly experience than Notepad. There are also tools like Thinktecture's WsContractFirst that integrate with Visual Studio .NET and fill some of the big holes. So as long as you're willing to use some third-party tools, you can pull together a complete toolset supporting the contract-first model. There are always developers who don't like stepping outside of Visual Studio .NET no matter what, and if you're one of them, you'll simply have to wait for Microsoft to fill the gap.

Microsoft has taken notice of the community's general desire for more tool support in this area and has already begun making improvements to Visual Studio .NET. In Visual Studio 2005 Team System, you'll notice a new Application Connection Designer that allows you to model service contracts and interactions with other applications. The designer allows you to model the service contracts, including the applications that are going to use them, before writing a single line of ASMX code. Once you're happy with the contracts you've authored, the designer will generate all of the service-related code for the applications using them.

The Application Connection Designer is a great step in the right direction and a good teaser for what a fully integrated and iterative contract-first experience could be like. However, it doesn't provide an integrated approach for modeling messages with XSD and operations using WSDL. It still focuses on the contracts from an RPC perspective, focusing on methods and parameters, as opposed to a message perspective, where your focus is on schema types and elements. The good news is that Microsoft plans to provide better support for a full message-oriented contract-first design experience in a future release of Visual Studio.

The reason I say this approach requires more discipline today is the overall lack of tool support for an iterative and integrated contract-first development process. In a follow-up column, I'll walk you through some practical examples of contract-first development techniques using the various tools I've mentioned here.

What's Next?

Component development teaches some important lessons about designing contracts. Embracing a contract-first development process will provide your systems with increased interoperability and better versioning control. However, if you're concerned less with interoperability and more with productivity, code-first may still be a reasonable approach. Future tools will continue to make contract-first development easier, but that doesn't mean you can't begin using it today.

My next column will show you how to get started. In the meantime, check out the following references on this topic. "Contract First Web Services Interoperability between Microsoft .NET and IBM WebSphere", "Web Services Interoperability", and my Service Station columns from February 2005 and November 2004.

Send your questions and comments for Aaron to sstation@microsoft.com.

Aaron Skonnard is a co-founder of Pluralsight, an education and content creation company, where he focuses on XML and Web services technologies. He's a contributing editor to MSDN Magazine and the author of several books, including Essential XML Quick Reference (Addison-Wesley, 2001). Reach him at www.pluralsight.com/aaron.


Extreme ASP.NET: A Little Bit of Control for Your Controls

Extreme ASP.NET
A Little Bit of Control for Your Controls
Rob Howard

Contents
Server Controls
The Need for Data Paging
Using the Pager Control
Implementing the Pager Control

Having worked for so many years designing and developing ASP.NET while at Microsoft, it's exciting now to have a venue in which to talk about it. In this new column, Extreme ASP.NET, I'll discuss and demonstrate time-tested techniques and approaches to implementing high-performance, reliable, secure, and user-friendly Web applications with ASP.NET.

Server Controls

When you're first learning to use ASP.NET, you'll quickly discover that there are various layers to the technology. The HttpRuntime is the lowest layer and has many similarities with the IIS ISAPI programming model. Using the HttpRuntime, you could actually build many technologies such as ASP, PHP, Cold Fusion, and so on.

The HttpRuntime is simply a request/response processing engine. The ASP.NET page model is then an implementation on top of the HttpRuntime—something most developers are more familiar with. Within the ASP.NET page model, you will then quickly find yourself using server controls to compose rich and complex page functionality using simple declarative statements.

ASP.NET controls, better known as server controls because they run on the server, are the developer's toolset for quickly writing Web applications. In fact, server controls are what ASP.NET page development is all about. Using them you can easily get great functionality for database access, calendars, textboxes, dropdown lists, and lots of other common composite Web functionality. Plus you can do it quickly through declarative programming.

The sooner you realize just how essential controls are to the ASP.NET page programming model, the better. For example, in ASP.NET 2.0 there are nearly 60 new server controls. The fact that the Page class is also a control indicates just how important server controls are to ASP.NET.

Both personally and professionally, I'm an advocate of writing as little code in a page as possible by encapsulating user interface functionality into server controls. If you download and examine Community Server (www.communityserver.org) you'll find that 99 percent of the pages are containers for server controls. All UI behavior, layout, and other aspects of the interface are accessed strictly through server controls.

In the January 2005 issue of MSDN®Magazine, I discussed 10 performance tips for writing better ASP.NET applications (see ASP.NET: 10 Tips for Writing High-Performance Web Applications). One of those tips was to reduce the amount of data sent back and forth from the database while another was to batch multiple resultsets together to reduce the number of round-trips. In this month's column I'll look at combining those tips and exposing their functionality through a custom server control that handles paging.

The Need for Data Paging

There are two ways you can create a server control. One way is to take any ASP.NET page, make minor modifications to the code, and rename the extension from aspx to ascx. The user control can then be used declaratively within other pages. The second way is to crack open your favorite code editor and write a class that ultimately derives from the Control base class.

Writing compiled server controls is my preference. Compiled controls give you the greatest flexibility over what your control does. I can't possibly go into all the details and nuances of compiled server controls—for that I'd recommend you pick up the excellent book Developing Microsoft ASP.NET Server Controls and Components by Nikhil Kothari and Vandana Datye (Microsoft Press®, 2002).

One of the tips in my article concerned reducing time spent communicating with the database or "time spent on the wire." The most salient point this tip makes is that going back and forth to the database repeatedly is inefficient. Instead, a more efficient technique is to return multiple resultsets from the database when possible. This is accomplished using a stored procedure, and each resultset is processed using the DataReader's NextResult method:

// read first resultset
reader = command.ExecuteReader();

Supplier.DataSource = reader;
Supplier.DataBind();

// read next resultset
reader.NextResult();

Products.DataSource = reader;
Products.DataBind();

The second tip, which also used the multiple resultset tip, is to be smart about how much data to return from the database. Using a technique known as paging, resultsets are further reduced to only the data required to satisfy the request. However, to successfully page through the data you also have to know how many records are returned in the original query, thus the need to return a second resultset containing the total number of records.

Figure 1 shows a stored procedure that can be used to page through the Orders table in the sample Northwind database. The procedure first calculates the total number of records it has to return to retrieve the requested data. It sets the ROWCOUNT to this value, forcing SQL Server™ to return only this specified number of records. It then does some calculations on lower and upper bounds of the page. Next a temporary #PageIndex table is created with an IndexID identity column and an OrderID column. Note that if I were working with extremely large sets of data I could make some additional optimizations, such as adding a SQL index on the IndexID column.

Figure 1 Paging Stored Procedure (Close Figure 1)
CREATE PROCEDURE northwind_OrdersPaged
(
    @PageIndex int,
    @PageSize int
)
AS
BEGIN
DECLARE @PageLowerBound int
DECLARE @PageUpperBound int
DECLARE @RowsToReturn int

-- First set the rowcount
SET @RowsToReturn = @PageSize * (@PageIndex + 1)
SET ROWCOUNT @RowsToReturn

-- Set the page bounds
SET @PageLowerBound = @PageSize * @PageIndex
SET @PageUpperBound = @PageLowerBound + @PageSize + 1

-- Create a temp table to store the select results
CREATE TABLE #PageIndex
(
    IndexId int IDENTITY (1, 1) NOT NULL,
    OrderID int
)

-- Insert into the temp table
INSERT INTO #PageIndex (OrderID)
SELECT
    OrderID
FROM
    Orders
ORDER BY
    OrderID DESC

-- Return total count
SELECT COUNT(OrderID) FROM Orders

-- Return paged results
SELECT
    O.*
FROM
    Orders O,
    #PageIndex PageIndex
WHERE
    O.OrderID = PageIndex.OrderID AND
    PageIndex.IndexID > @PageLowerBound AND
    PageIndex.IndexID < @PageUpperBound
ORDER BY
    PageIndex.IndexID

END

Next I insert into the temporary #PageIndex table the OrderID values from the Orders table. If I wanted to select only data that matched a specific criteria, I would add my WHERE clause, like I did here to get a specific data range. The number of records added to the #PageIndex table is limited based on the ROWCOUNT set previously. I then retrieve the total number of records available with SELECT COUNT(OrderID) FROM Orders. This is my first resultset returned from the stored procedure.

Finally, I SELECT the data I want from the Orders table by joining on the temporary #PageIndex table and controlling the returned results through the desired upper and lower bounds requests and ordering by the IndexID.

You might be wondering why I don't just use the OrderID or another identity column on the Orders table for accomplishing page selection. Well I can't do this for several reasons: first, there is no guarantee that the initial values in the identity column are contiguous (a row might have been deleted), and second, the numerical order will not be contiguous if a WHERE clause is used. The #PageIndex table builds a contiguous set of numerically ordered values based on how they were selected from the Orders table.

As you can see, this complicates the data access layer quite a bit, although it makes data access more efficient as the database grows. Requests execute through a stored procedure that takes a number of parameters, such as the page index, page number, and any other information required to create the resultset. Once returned, the resultset must be processed and data returned.

So you get the general idea—lots of work to get the desired result!

One thing you can do to simplify this data access functionality is create a server control to expose the UI required to page through data. Such a control would allow you to easily navigate to the first or last record with a single click or to walk through pages one at a time by clicking. The following shows the kind of UI I would expect to see:

Page 1 of 19 (373 items) 1 2 3 4 5 >> ... Last >>
There are two server controls used to generate the UI. The first is a server control called CurrentPage and the second is a control called Pager. Both are custom controls that ship with Community Server, the full source of which is available with the released version. Here I'm going to examine only the Pager control.

Using the Pager Control

The Pager control performs the following actions: it generates UI for easy navigation to the first or last page with a single click, it walks backwards and forwards through pages using numerical page numbers or forward/backward links, it displays the current page as plain text (not a link), and it raises a server-side event when a link button on the pager is clicked. Figure 2 shows a sample use of the Pager control used with a DataGrid.

Figure 2 Pager Control (Close Figure 2)
<%@ Page language="cs" Codebehind="BetterPager.aspx.cs"
    AutoEventWireup="false" Inherits="BlackBelt.Pager" %>
<%@ Register TagPrefix="cc1" Namespace="Controls" Assembly="Controls" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" >
<HTML>
  <HEAD>
    <title>Pager</title>
    <meta name="GENERATOR" Content="Microsoft Visual Studio .NET 7.1">
    <meta name="CODE_LANGUAGE" Content="C#">
    <meta name="vs_defaultClientScript" content="JavaScript">
    <meta name="vs_targetSchema"
        content="http://schemas.microsoft.com/intellisense/ie5">
  </HEAD>
  <body>
    <form id="Form1" method="post" runat="server">
      <P>
      <asp:DataGrid id="DataGrid1" runat="server"></asp:DataGrid></P>
      <P>
      <cc1:Pager id="Pager1" runat="server"></cc1:Pager></P>
    </form>
  </body>
</HTML>

Let's take a look at the Page_Load method and the event raised by the Pager control in this page—remember, ideally you would encapsulate all of this functionality in a separate control too, but for the purpose of this column I'll keep it simple:

public void Page_Load() {
  if (!Page.IsPostBack)
    Bind();
}

In the Page_Load I determine if I've already bound the data. If it's the first request, I'll call Bind to initially bind the DataGrid with default data and also set up the Pager (see Figure 3).

Figure 3 Bind to the Grid (Close Figure 3)
private void Bind() {

  SqlConnection connection =
      new SqlConnection(ConfigurationSettings.AppSettings("Northwind"));
  SqlCommand command =
      new SqlCommand("northwind_OrdersPaged", connection);
  SqlDataReader reader;

  command.CommandType = CommandType.StoredProcedure;
  command.Parameters.Add("@PageIndex", SqlDbType.Int).Value =
      Pager1.PageIndex;
  command.Parameters.Add("@PageSize", SqlDbType.Int).Value =
      Pager1.PageSize;

  try {
    connection.Open();

    reader = command.ExecuteReader();

    reader.Read();
    Pager1.TotalRecords = CType(reader(0), Integer);
    reader.NextResult();

    DataGrid1.DataSource = reader;
    DataGrid1.DataBind();

  } catch (Exception ex){
    ... // log exception
  } finally {
    connection.Close();
  }
}

In the Bind procedure I create a connection to the database and attempt to execute a stored procedure. This particular procedure, as you saw in Figure 3, requires that the page index and the page size be set. These settings tell the procedure which page you want returned and how large each page is.

Next, I open the connection to the database. The first resultset returned tells me how many total records are available that meet the criteria of the clauses in the stored procedure query. In Figure 2 the procedure is relatively simple; a more complex procedure might filter data, sort, or order the resultset through other parameters—this is exactly what my team did for the Forums feature of Community Server.

After I call Bind, the DataGrid and the Pager are both bound and rendered. When the user of the paged grid decides to click on the Pager control to navigate forwards (or backwards) the following event is raised on the server:

void Index_Changed (sender As Object, e As EventArgs) {
    Bind();
}
This calls the Bind method and rebinds the DataGrid using the PageIndex value that the user clicked on or selected from the Pager.

You might be wondering why I'm going through all of this complicated paging functionality when the DataGrid already has built-in paging. What you probably didn't know is that there is a small problem with the way paging works in the DataGrid. Let's say your data layer returns 1,000 records and you enable DataGrid paging with a page size of 25—meaning 25 records displayed at a time. The DataGrid accomplishes this by binding all 1,000 records and then discarding 975 of them. So, as you page through the data you'll first show records 1-25 discarding 26-1,000. When you click Next, records 26-50 would be shown while records 1-25 and 51-1,000 would be discarded. As long as the number of records returned is relatively small this is a fairly insignificant problem.

However, as the resultsets grow, the application will experience more and more serious performance problems because it has to spend more and more time communicating with the database—imagine the earlier case in which five million records are returned! Furthermore, the whole point of paging data is to be able to deal with so much returned data. The Pager control isn't necessary for all data access, but for paging through large sets of data it will make your data access more efficient.

Note that ASP.NET 2.0 provides support for data source-level paging. Using ObjectDataSource, you can write a SelectMethod that accepts parameters for a starting row index and the maximum number of rows to return. If you then enable paging in a connected DataGridView (not a DataGrid), the DataGridView will take advantage of the paging support in the data source. You can also configure a separate method (SelectCountMethod) to return the total number of rows in the resultset (regardless of the page size) so that DataGridView can use this number to calculate how to render pager UI links. In this case, the DataGridView asks the data source for just the data it needs in order to render the current page instead of asking for all of the data and throwing away rows.

Implementing the Pager Control

Now that you've seen how the Pager control works, let's look at its implementation. The Pager control derives from the ASP.NET Label control and uses the INamingContainer marker interface:

public class Pager : Label, INamingContainer {
As shown earlier, there are a couple of properties on the Pager that must be set. The first is the TotalRecords property. This property tells the control how many total records exist in the data source with which the Pager is associated. The second is PageSize. In the control there is a default value of 25. PageSize is required to calculate the total number of pages available in the CalculateTotalPages helper function (see Figure 4).

Figure 4 Calculate Total Pages (Close Figure 4)
public int CalculateTotalPages() {
    int totalPagesAvailable;

    if (TotalRecords == 0)
        return 0;

    // First calculate the division
    totalPagesAvailable = TotalRecords / PageSize;

    // Now do a mod for any remainder
    if ((TotalRecords % PageSize) > 0)
        totalPagesAvailable++;

    return totalPagesAvailable;

}

Next, let's look at how the control renders itself. The Pager overrides both the CreateChildControls and Render methods. All of the event wire-up happens in CreateChildControls when the control tree is created. To ensure that the event is wired correctly, a link button is created for every page available. Later the Render method is called when it is time for the Pager to render its output. Within Render there exists logic to display the correct link buttons based on the current PageIndex and PageSize being used. For example, the RenderLast method is used to Render a link button with the text "Last", but only when the last page is not the current page:

void RenderLast (HtmlTextWriter writer) {

    int totalPages = CalculateTotalPages();

    if (((PageIndex + 3) < totalPages) && (totalPages > 5)) {
        LiteralControl l = new LiteralControl("&nbsp;...&nbsp;");
        l.RenderControl(writer);

        lastButton.RenderControl(writer);
    }

}
The most complex logic is in the RenderPagingButtons method (see Figure 5).

Figure 5 RenderPagingButtons (Close Figure 5)
void RenderPagingButtons(HtmlTextWriter writer) {
    int totalPages;

    // Get the total pages available
    totalPages = CalculateTotalPages();

    // If we have <= 5 pages display all the pages and exit
    if ( totalPages <= 5) {
        RenderButtonRange(0, totalPages, writer);
    } else {

        int lowerBound = (PageIndex - 2);
        int upperBound = (PageIndex + 3);

        if (lowerBound <= 0)
            lowerBound = 0;

        if (PageIndex == 0)
            RenderButtonRange(0, 5, writer);

        else if (PageIndex == 1)
            RenderButtonRange(0, (PageIndex + 4), writer);

        else if (PageIndex < (totalPages - 2))
            RenderButtonRange(lowerBound, (PageIndex + 3), writer);

        else if (PageIndex == (totalPages - 2))
            RenderButtonRange((totalPages - 5), (PageIndex + 2), writer);

        else if (PageIndex == (totalPages - 1))
            RenderButtonRange((totalPages - 5), (PageIndex + 1), writer);
    }
}

As shown earlier, applications that use the Pager control must subscribe to an event raised by the Pager when a new page index is selected by the user:

public event System.EventHandler IndexChanged;
All the LinkButtons added to the PagerControl are wired to an internal PageIndex_Click event handler. Two things happen when an internal click event is raised. First, the PageIndex is set to the page selected by the user. The page index is stored as a CommandArgument of each LinkButton. Second, the IndexChanged event is raised and subscribers can handle the event. Here is the code that handles the internal PageIndex_Click event:
void PageIndex_Click(Object sender, EventArgs e) {

    PageIndex = Convert.ToInt32(((LinkButton) sender).CommandArgument);

    if (null != IndexChanged)
        IndexChanged(sender, e);

}

The biggest shortcoming of the Pager control—and something I'll likely fix in a future version of the Community Server controls—is that it requires postbacks. Due to some logic paths, postbacks are required, and while it's technically possible for a search engine to follow postback links, Google, MSN® Search, and other popular search engines don't currently do so. In a future version we'll likely support standard links in the pager, too.

Using the Pager control combined with some smarter data access tips can help your ASP.NET application be more efficient when using the database. The Pager server control shows how you can encapsulate some complex functionality into a reusable server control that supports events. Controls make writing reusable functionality incredibly easy in ASP.NET.

Send your questions and comments for Rob to xtrmasp@microsoft.com.

Rob Howard is a founder of Telligent Systems, specializing in high-performance Web apps and knowledge management and collaboration systems. Previously, Rob was employed by Microsoft where he helped design the infrastructure features of ASP.NET 1.0, 1.1, and 2.0.


Basic Instincts: Designing With Custom Attributes

Basic Instincts
Designing With Custom Attributes
Ted Pattison

Contents
The Evolution of Attribute-Based Programming
Using Attributes in Software Design
Creating a Custom Attribute Class
Designing Attributes with Parameters
Summing It Up

One of the most powerful aspects of the Microsoft® .NET Framework is its support for attributes. Attribute-based programming is extremely powerful because it adds a declarative dimension to designing and writing software. Declarative programming provides an elegant new way for developers to leverage services and functionality from reusable libraries and frameworks as well as from developer tools, such as the Visual Basic® .NET compiler.

As a user of Visual Basic .NET, it's essential that you understand what attributes are and how to use the syntax necessary to apply them. I will cover these topics in this month's column and will then discuss why and how to create your own custom attribute class.

The Evolution of Attribute-Based Programming

Microsoft support for attribute-based programming can be traced back to the days before the .NET Framework, back to Microsoft Transaction Services (MTS) and COM+. These older runtime environments allowed you to declaratively assign attributes to classes and methods to express your intent to leverage system services. For example, you can declare that a class "requires a transaction" simply by setting a COM+ component attribute. This results in developers being able to take advantage of COM+ services without having to write much code. Reducing the coding burden is certainly a major benefit of attribute-based programming.

The .NET Framework raises the bar with attribute-based programming, taking it much further than either MTS or COM+. I say this because MTS and COM+ offer only a handful of system-defined attributes. In contrast, there are scores of attributes defined in the Framework Class Library. What's more, the .NET Framework introduces extensibility mechanisms that lets you or your development team create your own custom attributes.

Many teams at Microsoft working on the Framework Class Library have created their own attributes as a means to expose their services and functionality. You will likely be applying these system-defined attributes in your code on a regular basis. The .NET Framework support for attributes is especially elegant because you can apply them directly in your source code. Figure 1 is a simple example of applying attributes to an assembly, a class, and a method in Visual Basic .NET.

Figure 1 Applying Attributes (Close Figure 1)
Imports System
Imports System.Reflection
Imports System.Diagnostics

'*** apply AssemblyDescriptionAttribute to an assembly
<Assembly: AssemblyDescription("Bob's component library")>

'*** apply SerializableAttribute to Class1
<Serializable()> _
Public Class Class1
  '*** apply DebuggerStepThroughAttribute to Method1
  <DebuggerStepThrough()> _
  Sub Method1()
    '*** method implementation
  End Sub
End Class

To apply an attribute, you simply supply the name of the attribute, surrounded by the < and > characters, immediately before the definition of the target entity to which you'd like to apply it. You can see in Figure 1 that a line-continuation character has been placed after the attribute to introduce a line break between the attribute and the class it has been applied to. This is commonly used in Visual Basic .NET to improve readability when applying attributes.

If the attribute defines parameters, the parameters are passed inside parentheses. Note that applying an attribute at the assembly level is a special case in which you must prefix the attribute name with the Assembly keyword and a colon. Assembly-level attributes must be defined in a source file after any Imports statements and before any other code. Also keep in mind that it is a common convention to maintain all your assembly-level attributes for a Visual Studio® .NET project in the file named AssemblyInfo.vb.

By convention, the names of attributes end with the suffix "Attribute" as in the case of AssemblyDescriptionAttribute and SerializableAttribute. However, the compilers for both Visual Basic .NET and C# allow you to optionally omit the suffix when applying an attribute. For example, the compiler will accept an attribute name of Serializable or SerializableAttribute. Notice that the IntelliSense® feature of Visual Studio .NET prefers the shorter version, stripping the suffix off the end of an attribute name automatically.

When you compile your code, the attributes you've applied and their parameterized settings are recorded in the component metadata of your assembly (see Figure 2). These attributes can then be read from the assembly image later. The act of inspecting an assembly at run time to read type information and to determine how attributes have been applied is known as reflection.

Figure 2 Attribute Settings in the Assembly
Figure 2 Attribute Settings in the Assembly

When you apply an attribute that is defined with parameters, there are two different styles for parameter passing. Attribute parameters can be passed either by position or by name. The attribute definition itself dictates which style is allowed or required. That means that whoever authors an attribute gets to decide how parameters are passed.

First, let's look at a simple example of an attribute parameter passed by position:

Imports System.Reflection

<Assembly: AssemblyDescription("Bob's component library")>
As you can see, passing parameters by position uses the standard Visual Basic .NET syntax for passing parameters to methods and properties. Now compare that to the next example in which attribute parameters are passed by name:
Imports System.Web.Services

<WebService(Name:="BobSVC", Description:="Bob's web service")> _
Class BobsWebService : Inherits WebService
  '*** web service implementation
End Class
Here named parameters are passed using the special := syntax. You may recall using this syntax in method calls when passing parameters by name in earlier versions of Visual Basic. It is interesting to note that Visual Basic .NET no longer supports passing parameters by name in method calls. Named parameters are only supported if you're passing parameters when applying attributes. However, the support for named parameters with attributes is valuable because, unlike methods and properties, attributes do not support the concept of overloading the parameter list.

Using Attributes in Software Design

Now that I have walked through the basic syntax for applying attributes, let's take a step back and think about attributes at a higher level. This will make it easier to see how attributes are actually used in designing systems and applications.

It all starts when a programmer or development team creates an attribute. Creating an attribute involves defining its semantic meaning and writing an attribute class definition. After the attribute has been created, other programmers can then apply it. Finally, someone (often the creator of the attribute) typically uses reflection to determine how the attribute has been applied and takes a course of action. For example, the common language runtime (CLR) and managed frameworks such as ASP.NET will change the behavior of your code depending on how you have applied their attributes.

Let's look at the SerializableAttribute created by the CLR team. The semantics of this attribute imply that objects created from classes that carry the SerializableAttribute can be serialized by the CLR. The CLR provides a convenient built-in service that can automatically serialize your objects. This service is valuable when you need to persist the contents of an object to a file or write this information into the body of a message to transmit across the network. Applying the SerializableAttribute is the key to taking advantage of the CLR's automatic serialization service:

<Serializable()> _
Public Class Customer
  Protected Name As String
  Protected Phone As String
End Class

Remember that applying an attribute doesn't do any good until the CLR or some other component uses reflection to inspect it. Imagine you attempt to pass a Customer object created from the class definition you just saw across the wire as a parameter in a method call using .NET Remoting. The .NET Remoting layer cannot pass an object across the network unless it is able to serialize its type information and contents into the body of a message.

The .NET Remoting layer and the CLR use reflection to inspect the object's underlying class definition. They do this to see if it has been defined with the SerializableAttribute. If the object is not serializable, the attempt to pass it in a .NET Remoting call fails. In this case the Customer object is serializable. Therefore, .NET Remoting calls upon the CLR's built-in serialization service to construct a network message holding the type information and contents of the object being passed.

This example shows one of the most common usage patterns of attribute-based programming—a development team defines attributes intended for other programmers to apply. This development team then also uses reflection to see how other programmers have applied their attributes. In this usage pattern, attributes provide an elegant means for a development team to expose their services and functionality to other programmers.

One determination you should make when you apply an attribute is when the attribute will be inspected. The answer varies because many different attributes are inspected at different times. For example, the SerializableAttribute from the last example is inspected by the CLR at run time. The AssemblyDescription attribute is inspected by the Visual Basic compiler when building an assembly at compile time.

A second piece of information you need when you apply an attribute is who is inspecting the attribute. The answer might be the CLR, ASP.NET, the Visual Basic .NET compiler, or Visual Studio .NET. The table in Figure 3 provides a few more examples to give you an idea of when attributes are inspected and by whom.

Figure 3 Attributes Defined in the Framework Class Library (Close Figure 3)

Attribute Name | Applied to | Inspected by | Inspected at
AssemblyVersionAttribute | Assemblies | CLR | Run time
SerializableAttribute | Classes | CLR | Run time
WebServiceAttribute | Classes | ASP.NET | Run time
WebMethodAttribute | Methods | ASP.NET | Run time
ObsoleteAttribute | Types/members | Language compilers | Compile time
DefaultValueAttribute | Anything | Visual Studio .NET | Design time
DesignerCategoryAttribute | Classes | Visual Studio .NET | Design time

Creating a Custom Attribute Class

Now that you have a better understanding of what an attribute really is, it's time to create one. The first thing to do when creating a custom attribute is to decide what semantic meaning it should have. In the example, you will create an attribute named TestedAttribute. The semantics will be defined such that any class or assembly to which the attribute is applied has been tested by a specific individual and given a test grade.

Before creating the attribute class, let's create an enumeration named TestGrade with four discrete test grade values to assist you in parameterizing your custom attribute class:

Public Enum TestGrade
    Excellent
    Good
    Poor
    Unacceptable
End Enum

When you want to create a custom attribute, you should follow these steps. First, create a class that inherits from System.Attribute. Next, add fields to store the attribute's parameterized values. Third, add one or more constructors to initialize the attribute's parameterized values. The following code snippet shows a starting point for the attribute class that I've created:

Public Class TestedAttribute
       Inherits System.Attribute

    Public Tester As String
    Public Grade As TestGrade

    Sub New(ByVal Tester As String, _
            ByVal Grade As TestGrade)
        Me.Tester = Tester
        Me.Grade = Grade
    End Sub

End Class

Once you have created your attribute class, you can apply it to a target such as an assembly or a class definition:

'*** apply attribute to assembly
<Assembly: Tested("Fred", TestGrade.Good)

'*** apply attribute to a class
<Tested("Bob", TestGrade.Excellent)> _
Class Customer
    '*** class definition goes here
End Class

When you create an attribute class you must consider which targets are applicable. For example, it doesn't make sense for every attribute to apply to assemblies, classes, methods, and parameters. One attribute might be designed so that it only makes sense to apply it at the assembly level. Another attribute should only apply to classes and structures.

You can specify the possible targets by applying the AttributeUsageAttribute to your attribute class definition. When you apply this attribute, you use a parameter typed to the AttributeTargets enumeration to list the target type. For example, if you wanted to create TestedAttribute so that it could only be applied to classes, you might write its definition like this:

<AttributeUsage(AttributeTargets.Class)> _
Class TestedAttribute : Inherits System.Attribute
  '*** attribute definition goes here
End Class

Once you specify your target in this manner, the compiler will carry out your wishes. For example, another programmer will now experience a compile-time error when trying to apply the TestedAttribute to anything other than a class definition:

'*** compile-time error
'*** attribute not supported at assembly level
<Assembly: Tested("Fred", TestGrade.Good)>

If you want to allow for multiple types of targets, you can Or together values of the AttributeTargets enumeration:

<AttributeUsage(AttributeTargets.Class Or AttributeTargets.Struct Or _
                AttributeTargets.Enum Or AttributeTargets.Delegate)> _
Class TestedAttribute : Inherits System.Attribute
'*** attribute definition goes here
End Class
You can also use the AttributeTargets enumeration value of All to explicitly specify that your attribute class can be applied to any type of target supported by the .NET programming model:
<AttributeUsage(AttributeTargets.All)> _
Class TestedAttribute : Inherits System.Attribute
'*** attribute definition goes here
End Class

Note that a parameter value of AttributeTargets.All provides the same result as the default behavior. In other words, an attribute class defined without the AttributeUsageAttribute can be applied to any type of target. Therefore, you must use the AttributeUsageAttribute whenever you want to restrict the possible target types.

Another important issue to consider when creating a custom attribute is whether it can be applied multiple times to the same target. By default, an attribute can only be applied once. However, the attribute creator can override this and allow the attribute to be applied two or more times to a single target. You can do this by defining your attribute class with the AttributeUsageAttribute and initializing the AllowMultiple parameter with a value of true.

For example, let's say you want to create an attribute class to track the author of a class. However, you would like to apply the attribute more than once when a class has been written by multiple authors. See the following definition for an AuthorAttribute class:

<AttributeUsage(AttributeTargets.Class, AllowMultiple:=True)> _
Public Class AuthorAttribute : Inherits Attribute
  Public Name As String
  Sub New(ByVal Name As String)
    Me.Name = Name
  End Sub
End Class
Now that the attribute class has been defined with the AllowMultiple parameter, the compiler will allow you and other programmers to apply it several times to a class definition:
<Author("Jose"), Author("Doug")> _
Public Class SuperTransactionProcessor
  '*** class definition goes here
End Class

Another consideration is how an attribute class is affected by inheritance. Ask yourself the following question: "If you apply the attribute to a base class, should all derived classes automatically have the attribute applied as part of their definition?"

<Tested("Bob", TestGrade.Excellent)> _
Class Customer
    ' class definition goes here
End Class

'*** Should this class definition include Customer's TestedAttribute?
Class RetailCustomer : Inherits Customer
    ' class definition goes here
End Class

In some designs, it makes sense to have an attribute that is implicitly applied to all derived classes when it is applied to a base class. However, it does not make sense in other cases, including the one here with the TestedAttribute class. Applying the TestedAttribute to the Customer class should not implicitly cause it to be applied to its derived classes, since the derived classes have not been tested yet.

You can control the inheritance behavior when applying the AttributeUsageAttribute to the attribute class definition. In the case of the TestedAttribute class, you definitely do not want inheritance to affect the attribute. No class should be defined with the TestedAttribute unless it is explicitly applied to its class definition. Therefore, the attribute class should be defined like this:

<AttributeUsage(AttributeTargets.All, Inherited:=False)> _
Public Class TestedAttribute
  '*** attribute definition goes here
End Class

Designing Attributes with Parameters

When designing an attribute class, you should consider how you would like other programmers to pass parameters. You can define your parameters so that they can be passed by name or by position. You can also design your attribute class with the flexibility so that other programmers can choose to pass parameters by name or by position. Let's look at an example—the final version of the TestedAttribute class definition in Figure 4.

Figure 4 TestedAttribute Class (Close Figure 4)

Defining an Attribute
Public Enum TestGrade
    Excellent
    Good
    Poor
    Unacceptable
End Enum

<AttributeUsage(AttributeTargets.All, Inherited:=False)> _
Public Class TestedAttribute : Inherits System.Attribute

    Public Tester As String
    Public Grade As TestGrade

    '*** parameterized constructor for passing parameters by position
    Sub New(ByVal Tester As String, ByVal Grade As TestGrade)
        Me.Tester = Tester
        Me.Grade = Grade
    End Sub

    '*** default constructor for passing parameters by name
    Sub New()
    End Sub

End Class
Using the Attribute
'*** applying attribute and passing parameters by position
<Tested("Bob", TestGrade.Excellent)> Class Customer
    ' class definition goes here
End Class

'*** applying attribute and passing parameters by name
<Tested(Tester:="Alice", Grade:=TestGrade.Excellent)> Class Employee
    ' class definition goes here
End Class

Note that there are now two constructors in the TestedAttribute class. The first constructor is parameterized, allowing for the following usage in which parameters are passed by position:

'*** applying attribute and passing parameters by position
<Tested("Bob", TestGrade.Excellent)> _
Class Customer
    ' class definition goes here
End Class
The arguments in a parameterized constructor define how parameters are to be passed by position. However, a default constructor (a no-argument constructor) doesn't define any parameters to be passed by position. You should note that your attribute class will require a default constructor if you want to allow all parameters to be passed by name:
<Tested(Tester:="Alice", Grade:=TestGrade.Excellent)> _
Class Employee
    ' class definition goes here
End Class

Note that you need more than just a default constructor to allow for parameter passing by name. Each named parameter must also be defined in the attribute class as a public field or a public writeable property. The TestedAttribute class definition in Figure 4 exposes public fields named Tester and Grade. These public fields can be passed as named parameters.

As a final note, you should see that constructors dictate which parameters must be passed by position. For example, if you remove the default constructor, you cannot apply the attribute class with only named parameters. You would be forced to pass both parameters by position.

Summing It Up

This month's column has discussed the power and flexibility that attributes bring to the programming model of the .NET Framework. It's critical that you know how to apply attributes because that's how many of the .NET libraries expose their services and functionalities to you.

You also saw how to create your own custom attribute class. Such an undertaking will make it possible for you to incorporate the elegance of attribute-based programming in your own designs. However, this column has not told the complete story. If you really want to embrace designs that make use of attribute-based programming, you are going to have to learn to write code that uses reflection. In particular, you must be able to inspect compiled assemblies and determine if and how your attributes have been applied. Stay tuned, because that will be the subject of the next installment of Basic Instincts.

Send your questions and comments for Ted to instinct@microsoft.com.

Ted Pattison is a cofounder of Barracuda .NET, an education company that assists companies building collaborative applications using Microsoft technologies. Ted is the author of several books including Building Applications and Components with Visual Basic .NET (Addison-Wesley, 2003).


Security Briefs: Customizing GINA, Part 1

Security Briefs
Customizing GINA, Part 1
Keith Brown

Code download available at: SecurityBriefs0505.exe (286KB)
Contents
What is GINA and Why Replace It?
Where to Start
Secure Attention Sequence
Structure of GINA
WinLogon States
Initialization
A Day in the Life of GINA
GINA Modal Dialogs and Threads
Deploying GINA
Debugging GINA

Over the years I've had many people ask me to write about GINA, the Graphical Identification and Authentication component that serves as the gateway for interactive logons. This month I'll begin my coverage of this topic to help you get started if you're tasked to build such a beast. I'll build a sample called KIOSKGNA, which is the simplest possible GINA implementation I could think of. Next time I'll introduce a sample called FULLGINA, a more fully featured GINA. The examples and code snippets presented here are in unmanaged C++, which is the most natural way to do GINA development these days.

What is GINA and Why Replace It?

GINA is the pluggable part of WinLogon that third parties may replace in order to customize the functionality or the UI of the logon experience in Windows®. By replacing GINA, you can choose the authentication mechanism Windows will use for interactive users. This is often useful for smartcard or biometric logons.

Let me state up front that replacing the GINA is an advanced technique that should not be taken lightly. You should only do this if you have no other choice (if, for example, you are implementing a new logon mechanism that Windows does not support).

Think for a moment about what GINA does. It collects credentials from local and remote users (the latter via Terminal Services) who want to establish an interactive logon on the machine. It then establishes a logon session for that user. If GINA were compromised, it could be used to steal plaintext user passwords, user biometric data, smartcard PIN codes, and so on. A compromised GINA could act as a back door, letting a certain normal user log in with administrative privileges. In short, if you replace GINA, be sure to put your best programmers on the job, and then you should carefully review their work.

One other thing: small and simple is best here. This component runs as SYSTEM, accepts input from local and remote users, and runs all the time. Therefore this is one piece of code that must be absolutely bulletproof.

Where to Start

As of this writing, you'll find very little documentation on customizing GINA. There are a couple of samples in the Platform SDK, GINASTUB, and GINAHOOK. GINASTUB simply loads MSGINA.DLL, the default GINA implementation that ships with Windows, and delegates all calls through to it. If you build and install this sample, you'll see no visible change on your system. Because GINASTUB wraps MSGINA, it can pre- and post-process each request. This simple technique might be enough to solve your problem, but it certainly won't help if you need to do something important and nontrivial like replace the login mechanism with biometric authentication.

There is also a second sample called GINAHOOK which looks exactly like GINASTUB, except that it goes one step further by hooking into the dialogs that MSGINA displays. Using this technique you can change the look of any of the default login dialogs, and even change their behavior to some extent. But the danger here is that you'll be relying on internals of MSGINA that are not documented. This includes simple stuff like IDs for dialog controls but also includes behaviors of the underlying dialog procedures. Be wary of going down this road if you need to support many different versions of the operating system, as your GINA may break when the next service pack is installed!

For most nontrivial GINA replacements, writing a custom GINA from the ground up is the best choice. But there aren't any examples of such a beast out in the wild (certainly none for free). I'm going to remedy this right now.

Given the complexity of the subject, I'll be presenting a couple of columns on the topic, and won't have the space to explore every dark corner of GINA development, but my goal is to give you a great head start if you need to build a GINA yourself. For further information, I'm setting up a GINA development Wiki where you can find (and contribute) additional information and sample code. Just look for Keith Brown's Wiki at pluralsight.com/wiki.

Secure Attention Sequence

Before diving in head first, let me explain a concept that you'll often run into. A secure attention sequence (SAS) is something that a user does to get the attention of the real operating system so that she can perform some secure action such as logging on, unlocking her workstation, or changing her password.

The SAS most users are familiar with is Ctrl+Alt+Del, which is trapped by the kernel. This helps thwart malware that may try to spoof the logon dialog and steal credentials from the user because when WinLogon receives a SAS notification, it literally switches away from the normal user desktop onto a secure desktop before prompting the user for credentials. Ever wonder where your taskbar and desktop go when you press Ctrl+Alt+Del? They're still there; they're just not the current desktop anymore. The goal is to train users to type their password only after they press Ctrl+Alt+Del.

Now a SAS isn't restricted to just Ctrl+Alt+Del. If you've got a biometric device that can signal a driver on the computer when it's in use, that could be used as a SAS instead (for example, a user placing her finger on a print scanner). Another common SAS is generated by a smartcard reader when a card is inserted or removed. When you write your own GINA, you get to decide which mechanism(s) will be used to generate a SAS. There's a function (WlxSasNotify) that you call in WinLogon when you detect a SAS or want to simulate one. Of course, you can tell WinLogon to use Ctrl+Alt+Del if you don't have anything more appropriate.

Structure of GINA

GINA is a DLL that WinLogon loads and calls at various times while the computer is running. It's a long-running DLL that is typically unloaded only when the machine reboots, so an absence of memory leaks is a feature!

A custom GINA must expose several entrypoints defined by Microsoft. I have listed these functions in Figure 1. A custom GINA will almost always maintain some internal state, and WinLogon will help you here by passing a void* as the first argument to those functions. You can decide what that pointer points to. In my sample, I will have it point to an instance of a class called Gina, where all the state and behavior of my custom GINA will be defined.

Figure 1 Functions Implemented by GINA (Close Figure 1)

WlxNegotiate
WlxInitialize
WlxDisplaySASNotice
WlxLoggedOutSAS
WlxActivateUserShell
WlxLoggedOnSAS
WlxDisplayLockedNotice
WlxWkstaLockedSAS
WlxIsLockOk
WlxIsLogoffOk
WlxLogoff
WlxShutdown
WlxScreenSaverNotify
WlxStartApplication
WlxNetworkProviderLoad
WlxDisplayStatusMessage
WlxGetStatusMessage
WlxRemoveStatusMessage
WlxGetConsoleSwitchCredentials
WlxReconnectNotify
WlxDisconnectNotify

Now one thing that confuses people who are new to GINA development is that besides the Wlx* functions that the custom GINA must implement, WinLogon also exposes several functions that the GINA may optionally call (see Figure 2). Since these functions all have the same prefix, Wlx, it's easy to get them confused. A quick way to tell the difference is to look at the first parameter to the function. If it is HANDLE hWlx, this is a function that WinLogon exposes for GINA to call. If it is PVOID pWlxContext, that's a function that your GINA is supposed to implement.

Figure 2 Functions Implemented by WinLogon (Close Figure 2)

WlxSasNotify
WlxSetTimeout
WlxMessageBox
WlxDialogBox
WlxDialogBoxParam
WlxDialogBoxIndirect
WlxDialogBoxIndirectParam
WlxSwitchDesktopToUser
WlxSwitchDesktopToWinlogon
WlxGetSourceDesktop
WlxSetReturnDesktop
WlxCreateUserDesktop
WlxChangePasswordNotifyEx
WlxCloseUserDesktop
WlxSetOption
WlxGetOption
WlxWin31Migrate
WlxQueryClientCredentials
WlxQueryInetConnectorCredentials
WlxDisconnect
WlxQueryTerminalServicesData
WlxQueryConsoleSwitchCredentials
WlxQueryTsLogonCredentials

WinLogon States

WinLogon can be in one of three states at any given time, and for the purposes of this column, I'll label them whenever I refer to them so they stand out clearly. The states are: LOGGED_OFF, which means that WinLogon has no current logged on user, LOGGED_ON, which means the user is currently logged on, and LOCKED, which refers to being logged on with workstation locked.

WinLogon will call some of the most important functions in your GINA when it transitions between these various states. Now that you know about SAS and the WinLogon state machine, let me show you GINA in action.

Figure 3 GINA State Diagram
Figure 3 GINA State Diagram

Figure 3 shows a more fleshed-out diagram of GINA states and transitions, and you'll want to refer to it as you read. It includes many of the major functions in your GINA that WinLogon will call, showing the normal sequence of events.

Initialization

When the machine boots up, once the operating system has initialized and started any services marked as automatic, WinLogon loads GINA and calls WlxNegotiate followed by WlxInitialize.

Negotiate is trivial to implement. It simply gives GINA and WinLogon a chance to verify each other's version so that things work smoothly later on. The version you choose determines how much functionality WinLogon will expect from your GINA, and also how much functionality you can expect from WinLogon. As of this writing, the most current version you can implement is 1.4, which provides a bit of extra support for Remote Desktop in Windows XP, so that's the version I'll use in my FULLGINA sample.

In Initialize, you will receive a handle (hWlx) that represents WinLogon, along with a pointer to a table of functions that your GINA may call. These are the Wlx* functions that WinLogon implements, the ones that take a HANDLE as their first argument. In order to call these functions later on, you'll need to store this handle and pointer somewhere. That's where the last argument comes in, pWlxContext, which is an out parameter that you pass back to WinLogon. The simplest approach is to point this at a data structure (class or struct) that you use to hold your GINA's state, which includes the handle and dispatch table pointer given to you by WinLogon. Figure 4 shows a very simple example of this.

Figure 4 Implementing WlxInitialize (Close Figure 4)
BOOL WINAPI WlxInitialize(
    LPWSTR                  /* lpWinsta */,
    HANDLE                  hWlx,
    PVOID                   /* pvReserved */,
    PVOID                   pWinlogonFunctions,
    PVOID *                 pWlxContext)
{
    // first store the stuff WinLogon gave us
    Gina* pGina = new Gina(hWlx, pWinlogonFunctions);
    if (!pGina) return FALSE;

    // now hand a pointer to our state to WinLogon
    *pWlxContext = pGina;
    return TRUE;
}

The actual implementation in my sample is a bit more sophisticated than this, but this clearly demonstrates the idea. From now on, when WinLogon calls any of the functions in your GINA, pWlxContext will be passed as the first argument, so you'll always have access to your state. For example:

BOOL WINAPI WlxIsLogoffOk(PVOID pWlxContext) {
  return ((Gina*)pWlxContext)->IsLogoffOk();
}
By simply dispatching each call to the GINA class, I can simply add member variables to the class to hold any state my GINA needs. It's a very natural way to develop a GINA, and you can reuse the stub code on any project.

A Day in the Life of GINA

Once GINA is initialized, WinLogon will start running its state machine, calling functions exported from GINA as its state changes. After initialization, no user is logged on yet, so WinLogon will ask you to display a logon prompt by calling WlxDisplaySASNotice. A typical GINA pops up a familiar dialog that says "Press Ctrl+Alt+Del to log in" at this point.

When the user presses Ctrl+Alt+Del, or GINA calls WlxSasNotify to generate a SAS of some other type, WinLogon dismisses your dialog (more on this later), and calls the next function in your GINA: WlxLoggedOutSAS.

WlxLoggedOutSAS may seem like a funny name, but it completely describes what's going on in your GINA. You are currently in the LOGGED_OUT state, and you've received a SAS. Your job here is to authenticate the user who is trying to log on. My FULLGINA sample opens a dialog to retrieve the user's name and password, and calls LsaLogonUser to see if the password is valid. If it's not valid, I present an error message, then loop back around and ask for a user name and password once again. Once given a valid set of credentials, LsaLogonUser establishes a logon session for the user and returns a token handle to my code, which I then return to WinLogon.

WinLogon takes the token returned from the GINA and configures the access control list (ACL) on the default desktop to make it private for this user's logon session. No other logged on user is allowed to access this desktop, except for administrators and the operating system itself. Now that the default desktop is ready to go, WinLogon calls back into GINA through WlxActivateUserShell. GINA must now launch the user's shell and return to WinLogon.

It's interesting that in the WlxActivateUserShell call, WinLogon does not provide the user's token handle that your GINA gave it in the last step. This is an example of state that your GINA must maintain internally. Now WinLogon is in the LOGGED_ON state, and the logged on user is in control. Just for kicks, let's say the user presses Ctrl+Alt+Del. Can you guess the name of the function WinLogon will call?

If you guessed WlxLoggedOnSAS, you're right. Here, you will likely give the user a set of options. Figure 5 shows the typical dialog displayed at this point. It should look familiar. This dialog is a lot easier to implement than it looks because WinLogon provides most of the functionality for you. GINA just needs to tell WinLogon which option the user chose by returning one of several predefined constants from WlxLoggedOnSAS. Handling the change password request is the only thing that requires much programming effort, and if you're using passwords, you'll need this functionality elsewhere in the GINA anyway.

Figure 5 WlxLoggedOnSAS
Figure 5 WlxLoggedOnSAS

Let's say the user chooses to lock the workstation. At this point, WinLogon will call GINA's WlxDisplayLockedNotice. This is similar to the WlxDisplaySASNotice. You simply display a modal dialog box and wait for GINA to dismiss it.

When the user presses Ctrl+Alt+Del, WinLogon will call WlxWkstaLockedSAS, at which time you should ask the user to authenticate once again. The tricky part here is that your GINA needs to tell WinLogon whether this is the same user who originally locked the workstation and is returning to unlock it, or if it's someone else entirely. In the latter case, you'll verify that this new user is an administrator and ask if he would like to forcefully log off the current user, in which case you'll eventually see a WlxLogoff call into your GINA followed by a return to WlxDisplaySASNotify, asking the user to log in.

If the user simply unlocks her workstation, WinLogon will return to the LOGGED_ON state and once again the user will be in control of her desktop. Now keep in mind that the user might log off, shut down, or even lock her workstation without necessarily pressing Ctrl+Alt+Del and going through the GINA. For example, the LockWorkstation API or even ExitWindowsEx can be called from any application, including Windows Explorer. Of course, the GINA will be notified if the user does log off or lock the workstation, but it's good to keep in mind that not all of these user actions will be initiated via GINA.

GINA Modal Dialogs and Threads

Any modal dialog you show from GINA needs to be interruptible by WinLogon. There are several reasons WinLogon might need to interrupt GINA's user interface: the user might be idle for several minutes, a SAS could occur, a screen saver might kick in, and so on. WinLogon has a very simple way of dealing with this. Instead of calling the normal Win32® functions DialogBox, DialogBoxParam, and so forth, GINA instead calls equivalent functions exposed through WinLogon's dispatch table. WinLogon can then hook into the dialog box procedure and end the modal dialog whenever it needs to. So if you're using a framework like MFC to implement your dialog boxes, you'll need to tweak it a bit to call through WinLogon's APIs.

But if you're pulling in MFC or even something more sophisticated, you might ask yourself if GINA is the right place for such application frameworks. Remember that a good GINA is small, simple, and bulletproof. Try to keep the amount of code your GINA loads to an absolute minimum.

While I'm on the subject of user interface code, I want to point out that the GINA really is a GUI component. It's designed to be single threaded, at least as far as WinLogon is concerned. WinLogon doesn't expect to be called back on any thread other than the one it used to call into GINA in the first place. For example, if GINA detects a SAS from an external device, it shouldn't call WlxSaSNotify on some random thread. It should instead use WinLogon's thread to make the call. Your best bet is to use the thread that called into WlxDisplaySAS/LockedNotice to let WinLogon know that the user generated a SAS via your device and wants to log in or unlock the workstation. If you do have a secondary thread that receives the device notification, you can simply post a message to your dialog that's displaying the SAS or Locked notice. The UI thread can then safely call WlxSasNotify.

Deploying GINA

Once your GINA is built, you'll want to deploy it and test it out. I strongly recommend that you don't deploy the GINA to your development box. Use a separate machine to host your GINA for testing. My own setup relies on Virtual PC with its undo disk feature. If something goes really wrong and I end up in an infinite reboot loop (yes, this will happen to you at some point), I just close down the Virtual PC and discard changes. Another option is to jump into Safe mode to remove a misbehaving GINA.

To deploy the GINA for testing, just copy the DLL onto the target machine (it is appropriate to place it in the System32 directory), and update that machine's registry by adding a named value under WinLogon's key:

HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WinLogon

The named value should be GinaDLL, of type REG_SZ, and its value should be the name of the DLL. Reboot the machine, and you'll find your GINA running when the machine boots back up. If you are trying to redeploy a new version of your GINA, you may find that the existing version is locked by the operating system and that you cannot overwrite the file. In this case, rename the GINA file on disk before copying the new one. Then you can simply delete the old GINA later.

If you get into trouble and need to restore the old GINA, just delete the GinaDLL key and remember that you can access the registry of any machine remotely, as long as you can be authenticated as an administrator on the target machine. Just bring up REGEDIT.EXE and choose File | Connect Network Registry.

When you have finished all your testing and are finally deploying for real, after copying your GINA DLL onto the user's disk, you should make sure to set the ACL on the GINA DLL to ensure that only administrators are allowed to modify the file. You won't get this level of protection by default simply by installing into the System32 directory.

Debugging GINA

Unless you're someone who regularly uses low-level symbolic debuggers, you won't enjoy debugging a custom GINA while it's running inside WinLogon. That's why I designed my sample so that it can be run outside of WinLogon, and I recommend you design yours the same way. I simply expose one extra entry point, called DebugGINA, into my GINA DLL. In non-debug builds, this function does absolutely nothing. In debug builds, however, I can use this entrypoint to drive the GINA through any scenario that I want to debug. The key to making this possible is to abstract your interface to WinLogon so that when debugging, you can simulate the WinLogon dispatch table by providing your own implementation. The standard way of doing this in unit testing circles is to use interfaces coupled with mock objects.

I use a very simple interface called IWinLogon that has methods for each of the functions my GINA needs to call in WinLogon. WlxDialogBox becomes IWinLogon::wlxDialogBox, for example. Then during normal operation, I use an implementation of IWinLogon that actually calls through WinLogon's dispatch table. During debugging, I substitute a mock implementation that in many cases can get away with doing absolutely nothing. My mock object's implementation of IWinLogon::wlxDialogBox, simply calls DialogBox. It's dirt simple, and it works like a charm.

Of course, you'll need a program to load your GINA and call that debug entry point, but that's easy. Here's a simple snippet of code that calls LoadLibrary and GetProcAddress to call the DebugGINA entrypoint. Just build something like this as an EXE and you can step right into your GINA from the debugger.

void main() {
  GetProcAddress(LoadLibrary("mygina.dll"),
    "DebugGINA")();
}

Now before you do this, you'll want to ensure that you're debugging the program while running as SYSTEM to simulate the WinLogon environment. When I'm doing a lot of GINA debug sessions, I keep a command prompt running as SYSTEM open, and just type DEVENV from there to launch my debugger, load the project, and trace right in.

How do you get a command prompt running as SYSTEM? One way to do it is to schedule an interactive job with CMD.EXE as the target application:

at 7:32pm /interactive cmd
Of course the time you choose should be sometime in the very near future, like one minute from now. This causes the scheduler service (which runs as SYSTEM) to launch the command prompt, which inherits the security context of SYSTEM. Your debugger will inherit that security context as well when launched from your SYSTEM command prompt, which you should title as such (type this into the command prompt after it starts):
title SYSTEM (DANGER, WILL ROBINSON)

The silly title is actually quite serious—you should close this command prompt when you're not debugging because SYSTEM has full control of everything on your machine, and mistyping in this command prompt could have serious consequences.

In the next installment of this column, I'll drill down into some of the more complicated aspects of GINA development such as calling LsaLogonUser to establish a logon, using CreateProcessAsUser to launch the shell, and other important topics. At the end of this series, you'll have a simple but fully featured GINA implementation to start working with.

Send your questions or comments for Keith to briefs@microsoft.com.

Keith Brown is a co-founder of Pluralsight, specializing in developing and delivering high quality training for software developers. Keith's most recent book, The .NET Developer's Guide to Windows Security, is available here. Keith also keeps a web log and contact information at www.pluralsight.com/keith.


.NET Matters: ICustomTypeDescriptor, Part 2

.NET Matters
ICustomTypeDescriptor, Part 2
Stephen Toub

Code download available at: NETMatters0505.exe (163KB)
Contents
TypeDescriptionProvider
Caching
CustomTypeDescriptor
Registering and Using Providers
Data Binding

In last month's .NET Matters column, I answered a question concerning the PropertyGrid control, specifically about using it with classes that expose fields instead of properties. I demonstrated how the ICustomTypeDescriptor interface in the Microsoft® .NET Framework 1.x can be used to craft at run time your own pseudo-properties that wrap each of an object's fields, thus allowing those fields to be displayed and edited in the PropertyGrid. I also showed how this feature can be used with ASP.NET data binding to allow controls to bind to fields on objects.

This month, I'll continue that exploration, delving into changes made for the .NET Framework 2.0 that make my solution much more robust and even easier to implement. If you haven't read last month's column, I suggest you do so before continuing here (see .NET Matters: ICustomTypeDescriptor, Part 1).

TypeDescriptionProvider

System.ComponentModel.ICustomTypeDescriptor is still used and supported in the .NET Framework 2.0. In fact, the .NET Framework 2.0 expands support for it by introducing the new System.ComponentModel.TypeDescriptionProvider feature. This allows you to write a separate class that implements ICustomTypeDescriptor and then to register this class as the provider of descriptions for other types (remember that in the .NET Framework 1.x, the type being described has to implement ICustomTypeDescriptor, or a proxy class that implements it has to be used).

In other words, you can make some minor adjustments to the FieldsToPropertiesTypeDescriptor class shown in last month's .NET Matters column, create a custom provider that derives from TypeDescriptionProvider (and that associates the descriptor class with your settings type), and the PropertyGrid will then use these custom properties without requiring any changes to your type. None. Zero. Pretty cool, huh?

To take advantage of this new functionality, you first need to create a TypeDescriptionProvider. That part's easy, and an example provider for this situation is shown in Figure 1. This custom provider simply derives from TypeDescriptionProvider and overrides its GetTypeDescriptor method. GetTypeDescriptor returns the ICustomTypeDescriptor implementation that TypeDescriptor should use when querying for property descriptors, so this implementation returns an instance of my FieldsToPropertiesTypeDescriptor class.

Figure 1 Implementing a TypeDescriptionProvider (Close Figure 1)
public class FieldsToPropertiesTypeDescriptionProvider :
    TypeDescriptionProvider
{
    private TypeDescriptionProvider _baseProvider;
    private PropertyDescriptorCollection _propCache;
    private FilterCache _filterCache;

    public FieldsToPropertiesTypeDescriptionProvider(Type t) {
        _baseProvider = TypeDescriptor.GetProvider(t);
    }

    public override ICustomTypeDescriptor GetTypeDescriptor(
            Type objectType, object instance) {
        return new FieldsToPropertiesTypeDescriptor(
            this, _baseProvider.GetTypeDescriptor(
                objectType, instance), objectType);
    }
    ...
}

GetTypeDescriptor will be called each and every time that a request is made to TypeDescriptor for information on your type, so it's important that the implementation of ICustomTypeDescriptor that's returned be "flyweight." For example, it should not have a finalizer, and it should have a minimal number of member variables in order to minimize its memory footprint. The classes in the .NET Framework 2.0 that implement this pattern have been profiled extensively, and when it's implemented correctly, this pattern is relatively inexpensive.

You might think it odd that the constructor of this provider calls to the TypeDescriptor.GetProvider method. However, this is a recommended approach when creating providers that are only intended to augment or modify the existing metadata for a type, rather than completely replace it. Unlike in the .NET Framework 1.x, you shouldn't call to the corresponding member of TypeDescriptor from within a method on the ICustomTypeDescriptor returned by a TypeDescriptionProvider.

In the .NET Framework 2.0, TypeDescriptionProviders are the main way that TypeDescriptor gets at metadata, so calling into TypeDescriptor's methods from your provider will almost certainly result in one of those pesky StackOverflowExceptions, for reasons similar to those discussed last month. As such, if you need to create a provider that merely augments metadata rather than creating it from scratch, you'll also need a way to delegate to the previously registered provider for that type. (And if no provider was previously registered, you need access to the default built-in implementation that uses the type's metadata as supplied by the System.Reflection namespace.) So in its constructor, your provider should call to TypeDescriptor.GetProvider to get the current provider for the specified type (if no provider has been added for that type, an internal System.ComponentModel.ReflectTypeDescriptionProvider will be constructed and used). This provider can then be used whenever you need to access the underlying description of the type.

Caching

Note, too, that for this new implementation for the .NET Framework 2.0, the property and attribute caches have been moved from the ICustomTypeDescriptor implementation (where they were in my version targeting the .NET Framework 1.x) into the provider class. In the 1.x version, each instance had its own cache, as the type itself implemented ICustomTypeDescriptor. Since, in this new provider version, GetTypeDescriptor returns a new ICustomTypeDescriptor for each request, caching in the descriptor would be pointless, since the cache would be lost for subsequent requests. Thus, the caching is done at the provider level, with all descriptor instances sharing the provider's cache. To facilitate this, a reference to the provider is also passed to the descriptor in its constructor.

As all of the providers now share access to the same cache, synchronization between multiple threads using TypeDescriptor becomes an issue (it would be a problem in the 1.x version only if the same instance were being described from multiple threads). However, since locking is relatively expensive, the approach I've taken (and that taken by the built-in implementations in the Framework) is to rely on the strong memory model of the common language runtime (CLR) and the fact that reference assignments in the CLR are atomic actions (for more information on memory models, see the "Multithreading and Memory Models" sidebar).

At the beginning of my GetProperties implementation, the method simply copies a reference to the existing cached property sets from the provider. These cached sets are immutable, so when a new set of properties is needed, a new set is generated, and the provider's reference to the cache is simply replaced to point to the new cache. This means that I don't need to take out a lock; however, it means that multiple threads may find it necessary to run the property set generation logic at the same time. If that happens, no errors result, but unnecessary CPU cycles may be consumed as multiple threads duplicate the same work. I don't envision this happening frequently at all (neither does the .NET Framework team), thus making it less expensive to do it this way than to use synchronization primitives around the entire contents of the GetProperties method. However, if you find that this doesn't hold true for your app, you can reexamine this decision and implementation.

CustomTypeDescriptor

TypeDescriptionProvider.GetTypeDescriptor returns the ICustomTypeDescriptor implementation that provides the metadata to the client, and so it must be passed the type of the object being described, the actual instance of the object being described (if one was specified), and the base provider to which it can delegate, as discussed earlier.Multithreading and Memory Models and Vance Morrison

There is little wonder that confusion exists about correctly writing multithreaded programs. Multithreaded programs are inherently nondeterministic. In addition, because threads interact through reads and writes to memory, the behavior of a multithreaded program is much more dependent than a sequential program on exactly when reads and writes happen. Finally, hardware and compilers continually evolve, making it very difficult to specify concisely the optimizations they perform today and those they will do going forward.

One key to cutting through this confusion is a specification between the programmer and the compiler and hardware on exactly what it means to read and write memory. This specification is called the memory model. At first glance, it would seem that that this specification should be straightforward: reads and writes from each thread should happen in program order, and reads and writes from different threads can be interleaved in any order. This memory model is what most programmers have in mind when they write multithreaded programs and has a formal name: sequential consistency.

Unfortunately, sequential consistency is too restrictive for compiler writers and hardware manufacturers. This model disallows modifying any read or write that came from the same thread. This means a compiler optimization as simple as caching a memory fetch in a register and reusing it more than once is now illegal (since now there are fewer memory reads). It also disallows most forms of caching, write buffering, and other hardware techniques to speed memory access. Clearly there is a need to relax sequential consistency to allow for these optimizations.

One such relaxed model is specified in section 12, Partition I of the .NET runtime ECMA standard (see msdn.microsoft.com/net/ecma). In this model, compilers and memory hardware are allowed to reorder ordinary reads and writes even from a single thread as long as the following rules are obeyed:

  1. The behavior of a thread run in isolation isn't changed.
  2. Reads and writes cannot move before a volatile read or the entering of a lock.
  3. Reads and writes cannot move after a volatile write or the leaving of a lock.

This model basically allows compilers and hardware to optimize most ordinary reads exactly as they would have if the program were not multithreaded. This model works very well if programmers always follow a simple (but hard to follow) locking protocol: a lock is associated with every memory location that can be accessed from more than one thread. This lock is entered before any access to that location and must be released before another thread can access the location.

This model is elegant. Even in the sequential consistency model, programs generally need to follow the locking protocol to ensure that other threads only see complete updates to any particular data structure. The relaxed model exploits this by requiring the compilers and memory hardware to respect only the boundaries created by these locks, but no more.

This relaxed model works perfectly if programs follow the locking protocol mentioned earlier. However, there are times when there is a desire to avoid this protocol. Entering locks can be expensive (at least dozens of cycles, and possibly significantly more) and for frequently accessed locks, this overhead can be significant. Unfortunately, once you remove the locks, not only can other threads read or write data structures while they are being updated, but the compiler and memory system can reorder your reads and writes!

As an example of just how subtle things are, consider one of the few places where you can write correct, lock-free code: lazy initialization. In this code, a variable starts out uninitialized, and the first thread that tries to fetch it will initialize it.

private static LazyInitClass myValue = null;
public static LazyInitClass GetValue() {
    if (myValue == null) myValue = new LazyInitClass();
    return myValue;
}

Since this code does not follow the locking protocol (there are no locks), it is dangerous code. Even in the strong sequential consistency model, this code has issues. Two threads can be simultaneously running the "if" statement, both find it null, and both initialize myValue. If this were C++ code, this would cause a memory leak. Even in a garbage-collected environment, there is a problem if it is important that myValue have a unique identity (you use pointer equality).

In a relaxed memory model there are even more subtle problems. The assignment to myValue might be moved before the writes that are in LazyInitClass's constructor. This means other threads can access the fields of the object myValue references before it is completely initialized! Now you may be thinking this won't happen on "real" computers, and you would be partially right. On a uniprocessor, or even a multiprocessor x86 computer, it turns out that this memory reordering can't happen. However, on a multiprocessor IA64 computer, it definitely can (we have the stress failures to prove it).

The write reordering issue can be fixed by a trivial code change. By adding "volatile" to the declaration of myValue, you force the system to keep the writes in the proper order. It turns out that even this change is not strictly needed on the .NET runtime because Microsoft has decided on a memory model that guarantees writes are never reordered (this means the CLR has a stronger memory model than the ECMA specification provides).

Note that the previous example is one of the simplest low-lock code patterns, and it is already filled with subtleties. All of these subtleties are avoided simply by following the locking protocol, so that should always be the design of choice. When you are forced by performance considerations to use low-lock code patterns, you need a more thorough introduction to the issues than is given here (the CLR team will make the exact rules available before the .NET Framework 2.0 ships). It is best to stick closely to patterns that have been carefully reviewed by people with a deep knowledge of multithreading issues.

Figure 2 Using TypeDescriptionProvider in the .NET Framework 2.0
Figure 2 Using TypeDescriptionProvider in the .NET Framework 2.0

The new implementation of FieldsToPropertiesTypeDescriptor I've written for the .NET Framework 2.0 implements the process depicted in Figure 2 and is shown in Figure 3. It should look very familiar. Just like with the 1.x version, this code delegates most of its functionality. However in this case, it's delegating to another descriptor instance rather than to TypeDescriptor. Additionally, decisions are made based on the specified instance rather than on itself, since the type being described no longer derives from this descriptor type (in my 1.x version, this descriptor class was the base type for the type being described). Also, this class no longer directly implements ICustomTypeDescriptor, but instead derives from the new abstract class System.ComponentModel.CustomTypeDescriptor. This class implements all of the boilerplate code so I don't have to.

Figure 3 FieldsToPropertiesTypeDescriptor for the .NET Framework 2.0 (Close Figure 3)
private class FieldsToPropertiesTypeDescriptor : CustomTypeDescriptor {
    private Type _objectType;
    private FieldsToPropertiesTypeDescriptionProvider _provider;

    public FieldsToPropertiesTypeDescriptor(
        FieldsToPropertiesTypeDescriptionProvider provider,
        ICustomTypeDescriptor descriptor, Type objectType) :
        base(descriptor)
    {
        if (provider == null) throw new ArgumentNullException("provider");
        if (descriptor == null)
            throw new ArgumentNullException("descriptor");
        if (objectType == null)
            throw new ArgumentNullException("objectType");
        _objectType = objectType;
        _provider = provider;
    }

    public override PropertyDescriptorCollection GetProperties() {
        return GetProperties(null);
    }

    public override PropertyDescriptorCollection GetProperties(
        Attribute[] attributes)
    {
        // Retrieve cached properties and filtered properties
        bool filtering = attributes != null && attributes.Length > 0;
        FilterCache cache = _provider._filterCache;
        PropertyDescriptorCollection props = _provider._propCache;

        // Use a cached version if we can
        if (filtering && cache != null && cache.IsValid(attributes))
            return cache.FilteredProperties;
        else if (!filtering && props != null) return props;

        // Otherwise, create the property collection
        props = new PropertyDescriptorCollection(null);
        foreach (PropertyDescriptor prop in
            base.GetProperties(attributes)) props.Add(prop);
        foreach (FieldInfo field in _objectType.GetFields()) {
            FieldPropertyDescriptor fieldDesc =
                new FieldPropertyDescriptor(field);
            if (!filtering || fieldDesc.Attributes.Contains(attributes))
                props.Add(fieldDesc);
        }

        // Store the updated properties
        if (filtering) {
            cache = new FilterCache();
            cache.FilteredProperties = props;
            cache.Attributes = attributes;
            _provider._filterCache = cache;
        }
        else _provider._propCache = props;

        // Return the computed properties
        return props;
    }
}

You'll notice that FieldsToPropertiesTypeDescriptor is private. I've actually declared it as a nested class within FieldsToPropertiesTypeDescriptionProvider (private accessibility is only valid on types when they're nested), and I've done this for three reasons. First, there's no need for anyone external to access a strongly typed instance of FieldsToPropertiesTypeDescriptor; all relevant functionality is available through its ICustomTypeDescriptor interface. Second, I wanted to make the property caches inside of the provider private, and a class external to the provider does not have access to a private member, whereas a nested class does. Third, it keeps the entire implementation of this provider in one place and makes it very easy to modify these internal implementation details without requiring external dependencies to change, should a modification be necessary in the future.

Registering and Using Providers

With that in place, forcing the PropertyGrid to show a type's fields as properties (and allowing ASP.NET data binding to work with fields) can now be done without any modifications to the type in question and with only one code statement:

TypeDescriptor.AddProvider(new
    FieldsToPropertiesTypeDescriptionProvider
    (typeof(Person)), typeof(Person));
This snippet registers an instance of my provider for the Person class. From this point on (or until the provider is removed with the TypeDescriptor.RemoveProvider method), any requests for a type description of an instance of Person or for the type Person will go through my custom provider.

Additionally, a new attribute to the .NET Framework 2.0, System.ComponentModel.TypeDescriptionProviderAttribute, allows you to associate a provider with a particular type (by attributing that type) in metadata, thus completely eliminating the need for manual registration. Using that attribute, the declaration of the type in question would look something like the following:

[TypeDescriptionProvider(
    typeof(FieldsToPropertiesTypeDescriptionProvider))]
public class Person {
    public string Name;
    public int Age;
    public string Hobbies;
}

Unfortunately, TypeDescriptor expects that providers used with the TypeDescriptionProviderAttribute have a parameterless public constructor. My FieldsToPropertiesTypeDescriptionProvider was engineered to be totally generic in that it could be used with any type. To get it to work with TypeDescriptionProviderAttribute, the simplest solution is to hardcode the associated type into the provider's constructor and to then change the constructor to be parameterless. As an example, instead of the current constructor which is coded as follows:

public FieldsToPropertiesTypeDescriptionProvider(Type t) {
    _baseProvider = TypeDescriptor.GetProvider(t);
}
I could specify here that this provider will be used only with the Person type, as shown in the following code:
public FieldsToPropertiesTypeDescriptionProvider() {
    _baseProvider = TypeDescriptor.GetProvider(typeof(Person));
}
While TypeDescriptionProviderAttribute can be very handy in certain situations, it's not intended to be used with every kind of provider, and it's probably not appropriate for this situation.

I mentioned last month briefly that in the .NET Framework 2.0 Beta 1 (also true for Beta 2), certain invocations of TypeDescriptor methods will filter for you, and certain invocations won't. It actually depends on the overload of TypeDescriptor.GetProperties that you use. If you pass an instance to TypeDescriptor.GetProperties, then filtering will be performed for you automatically, just as with the .NET Framework 1.x.

If, however, you pass a Type to TypeDescriptor.GetProperties, it won't filter for you automatically. The base set of properties you retrieve for your type from within your ICustomTypeDescriptor (relying on the base provider) will be filtered by the System.ComponentModel.ReflectTypeDescriptionProvider used internally, but any properties you then add (such as the property for each field) will not automatically be filtered after the set is returned from your descriptor. Thus, you must do it manually, and hence why I've chosen to do it in all cases. It's just simpler to do so, and the performance impact should be minimal in most scenarios. Note, however, that this behavior will almost certainly change by the final release of the .NET Framework 2.0 so that all paths will be filtered by the Framework. Implementing filtering in your type descriptors will then be optional and should only be done if there is a good performance reason for doing so (for example, if the expense of the extra filtering step is less than the expense of otherwise creating the additional descriptors, both in running time and in development time).

Data Binding

This type of solution is very useful, and for more scenarios than just the one described here. A handful of Framework classes, including DataRowView, implement ICustomTypeDescriptor in order to provide a custom description of the type. Moreover, PropertyGrid isn't the only class in the Framework that takes advantage of TypeDescriptor. DetailsView, BindingList<T>, and ExpandableObjectConverter, to name just a few, all take advantage of TypeDescriptor and will thus make use of any TypeDescriptionProviders you implement.

But more importantly, I discussed last month how ASP.NET data binding also relies on TypeDescriptor to retrieve property information about an object. In the .NET Framework 1.x, the proxy approach I described (creating a proxy class that implements ICustomTypeDescriptor, wraps your object, and returns information about your object) is often the best solution when working with the PropertyGrid, as it allows you to keep your original classes intact, without modification.

However, often ASP.NET data binding is used with a collection or an array of objects, and each of these would need to be wrapped in a proxy for this approach to be successful with data binding. Type description providers solve that problem. Before binding your collection to a DataGrid, a DataList, or a ListControl (such as ListBox or DropDownList), all of which make use of the Databinder class, which in turn uses TypeDescriptor, simply register a TypeDescriptionProvider for your object's type. That provider (and the type descriptor it supplies) will then be used by the list or grid when determining what data needs to be rendered for an object of that type. Makes you feel powerful, doesn't it?

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

Stephen Toub is the Technical Editor for MSDN Magazine.


C++ at Work: Unreferenced Parameters, Adding Task Bar Commands, and More

C++ At Work
Unreferenced Parameters, Adding Task Bar Commands, and More
Paul DiLascia

Code download available at: CAtWork0505.exe (171KB)

Q I've seen C++ code that uses UNREFERENCED_PARAMETER for parameters that aren't used. For example:

int SomeFunction(int arg1, int arg2)
{
  UNREFERENCED_PARAMETER(arg2)
  ...
}
But I also see code like this
int SomeFunction(int arg1, int /* arg2 */)
{
  ...
}
Can you explain what the difference is and which is better?

Judy McGeough

A Why surely. Let's start with UNREFERENCED_PARAMETER. This macro is defined in winnt.h, like so:

#define UNREFERENCED_PARAMETER(P) (P)
In other words, UNREFERENCED_PARAMETER expands to the parameter or expression passed. Its purpose is to avoid compiler warnings about unreferenced parameters. Many programmers, including yours truly, like to compile with the highest warning level, Level 4 (/W4). Level 4 warnings fall into the category of "things that can be safely ignored." Little infelicities that won't break your code, though they might make you look bad. For example, you might have some line of code in your program like this
int x=1;
but you never use x. Perhaps this line is left over from a time when you did use x, but then you removed the code and forgot to remove the variable. Warning Level 4 can find these minor mishaps. So why not let the compiler help you achieve the highest level of professionalism possible? Compiling with Level 4 is a way to show pride in your work. Level 4 is de rigueur if you're writing a library for public consumption. You don't want to force your developers to use a lower level to compile their code cleanly.

The problem is, Level 4 is really fussy. At Level 4, the compiler complains about such harmless things as—well, unreferenced parameters (unless, of course, you really did mean to use the parameter, in which case it's not so harmless). Say you have a function with two arguments, but you only use one:

int SomeFunction(int arg1, int arg2)
{
  return arg1+5;
}

With /W4, the compiler complains: "warning C4100: 'arg2' : unreferenced formal parameter." To fool the compiler, you can add UNREFERENCED_PARAMETER(arg2). Now your function references arg2 so the compiler will shut up. And since the statement

arg2;
doesn't actually do anything, the compiler doesn't generate any code for it, so there's no loss of either space or efficiency.

Sharp minds might wonder: if you don't use arg2, why declare it in the first place? Usually it's because you're implementing a function to meet some API signature ordained from the heavens. For example, MFC's OnSize handler must have the following signature:

void OnSize(UINT nType, int cx, int cy);

Here cx/cy are the window's new width/height and nType is a code like SIZE_MAXIMIZED if the window is being maximized or SIZE_RESTORED for normal sizing. Usually you don't care about nType; you only care about cx and cy. So you need UNREFERENCED_PARAMETER(nType) if you want to compile with /W4. OnSize is just one function among thousands in MFC and Windows®. It's hardly possible to write a Windows-based program without unreferenced parameters.

So much for UNREFERENCED_PARAMETER. As Judy notes in her question, another trick C++ programmers often use to achieve the same result is to comment the parameter name out of the function signature:

void CMyWnd::OnSize(UINT /* nType */,
  int cx, int cy)
{
}

Now nType is an unnamed parameter, the same as if you'd typed OnSize(UINT, int cx, int cy). So now the $64,000 question is: which method should you use—unnamed parameters or UNREFERENCED_PARAMETER?

Most of the time it makes no difference, it's purely a style thing. (Do you like your java black or with cream?) But I can think of at least one situation where UNREFERENCED_PARAMETER is required. Suppose you've decided to disallow maximizing your window. You disable the Maximize button, remove Maximize from the system menu, and block every other place the user could maximize. Because you're paranoid (and most good programmers are paranoid), you add an ASSERT to ensure that your code is working as you intended:

void CMyWnd::OnSize(UINT nType, int cx, int cy)
{
  ASSERT(nType != SIZE_MAXIMIZE);
  ...   // use cx, cy
}

The QA team runs your program 87 ways and the ASSERT never bombs, so you figure it's safe to compile a Release build. But now without _DEBUG defined, ASSERT(nType != SIZE_MAXIMIZE) expands to ((void)0) and suddenly nType becomes an unreferenced parameter! There goes your clean compile. You can't comment nType out of the parameter list because you need it for the ASSERT. So in this situation—where the only place you use a parameter is within an ASSERT or other _DEBUG-conditional code—only UNREFERENCED_PARAMETER will keep the compiler happy in both Debug and Release builds. Got it?

Before closing, I'd be remiss not to mention that you can suppress individual compiler warnings using a pragma like so:

#pragma warning( disable : 4100 )
4100 is the error code for unreferenced parameter. The pragma suppresses the warning for the rest of the file/module. You can reenable the warning like so:
#pragma warning( default : 4100 )
However, a better approach is to store all of the warning states before disabling the specific warning, and then go back to that configuration when you're done. That way, you get back to the state you were at previously and not just the compiler default.

So you could suppress unreferenced parameter warnings for a single function by surrounding it with pragmas like this:

#pragma warning( push )
#pragma warning( disable : 4100 )
void SomeFunction(...)
{
}
#pragma warning( pop )
Of course, that's way too verbose for unreferenced parameters, but possibly necessary for other kinds of warnings. Library builders use #pragma warning all the time to block warnings so their code can compile cleanly with /W4. MFC is full of such pragmas. There're even more #pragma warning options I haven't mentioned. Check 'em out in the documentation.

Q I've noticed that some apps have special commands when you right-click on their minimized button in the task bar. For example, WinAmp (a popular media player program) has an added "WinAmp" menu item with WinAmp commands. How can I add my own items to my app's task bar button?

Jirair Osygian

Q I've created a simple MFC single document interface (SDI) application with a form view to display a counter. I want to be able to start and stop the counter by right-clicking on the minimized application down on the task bar. The start and stop functions work fine as buttons on my form, and I was able to add the start/stop commands to the system menu. But when I click on them in the system menu, nothing happens. How can I handle these messages from my modified system menu?

Monicque Sharman

A I'll answer both questions in one fell swoop. The answer to Jirair's question is simple: the menu the user sees when you right-click on an application's minimized task bar button is the same as the menu displayed if she clicks on the application icon in the top-left corner of the caption bar or presses Alt+Space. Figure 1 shows what I mean. This menu is called the system menu and has commands like Restore, Minimize, Maximize, and Close.

Figure 1 Sys Menu
Figure 1 Sys Menu

You can call ::GetSystemMenu to get the system menu, then modify it by adding, deleting, or changing items. You can even suppress the system menu entirely by turning off the WS_SYSMENU style in your window creation flags or virtual PreCreateWindow function. But whatever you do, the system menu is also the one that's displayed when users right-click on your application's minimized button in the task bar.Origins of Ctrl+Alt+Del

In my January column, I asked if anyone knew the origins of Ctrl+Alt+Del. Apparently several readers know how to use Google, because they sent me links to the same USA Today article I discovered before posing my challenge (see Thank this guy for 'control-alt-delete'). Ctrl+Alt+Del was invented by a man named David J. Bradley who worked at IBM.

IBM decided it would be good to have a way to reset their new PC without turning off the power. And why those particular keys, Ctrl+Alt+Del? For technical reasons, David needed to use two modifier keys. He also wanted a combination no one was likely to type by accident. So he chose Ctrl+Alt as the modifier keys (less common than Shift) and Delete, which is on the other side of the keyboard, so typing Ctrl+Alt+Del required two hands. At least it did in the old days.

Modern keyboards now have Ctrl and Alt on the right side as well. The reset feature was originally intended to be a secret escape hatch for IBMers, but inevitably the cat got out of the bag. Once developers learned about it, they started telling customers to use it as a last resort when their machines were hung. The rest is history. Ctrl+Alt+Del is affectionately known as the "three finger salute," and survives in Windows even today, where it invokes the Task Manager so you can kill hung tasks or shut your system down (you can learn more about Secure Attention Sequences like Ctrl+Alt+Del in this month's Security Briefs column). And what if Ctrl+Alt+Del fails? Why, just hold the power button down for five seconds, please.

David Bradley was one of the original 12 engineers who built the IBM Personal Computer. He wrote the ROM BIOS. For a brief bio on David, see David J. Bradley.

Which leads to Monicque's question: if you add your own commands to the system menu, how do you handle them in MFC? If you do the normal thing—write an ON_COMMAND handler somewhere and add it to one of your message maps, you'll discover your handler never gets called. How come?

It's because Windows and MFC handle system commands differently from ordinary menu commands. When the user invokes a normal menu command or button in your form, Windows sends your main window a WM_COMMAND message. If you're using MFC, MFC's command-routing mechanism catches this message and routes it through the system to any object in the command route that has an ON_COMMAND handler for that command. (For details on MFC command routing, see my article "Meandering Through the Maze of MFC Message and Command Routing" in the July 1995 MSJ.)

But system commands don't come via WM_COMMAND. They come via a different message called—what else?—WM_SYSCOMMAND. This is true whether the command ID is one of the true system commands like SC_MINIMIZE and SC_CLOSE, or some other command ID you've added. To handle system menu commands, you have to handle WM_SYSCOMMAND explicitly and check for your own command IDs. This requires adding ON_WM_SYSCOMMAND to your main window's message map, with a handler function like this:

CMainFrame::OnSysCommand(UINT nID, LPARAM lp)
{
  if (nID==ID_MY_COMMAND) {
    ... // handle it
    return 0;
  }
  // pass to base class: important!
  return CFrameWnd::OnSysCommand(nID, lp);
}

If the command isn't yours, don't forget to pass it to your base class—which is typically CFrameWnd or CMDIFrameWnd. Otherwise, Windows won't get the message and you'll break the built-in commands.

Handling WM_SYSCOMMAND in your main frame certainly works, but it feels kludgy. Why use a special mechanism to handle commands just because they come via the system menu? What if you want to handle a system command in some other object like your view or document? One common command to put in your system menu is About (ID_APP_ABOUT), and most MFC programs handle ID_APP_ABOUT in the application object:

void CMyApp::OnAppAbout()
{
   static CAboutDialog dlg;
   dlg.DoModal();
}

One of the really cool features of MFC is its command-routing system, which lets non-window objects like CMyApp handle menu commands. Many programmers don't even realize how out of the ordinary this is. If you already handle ID_APP_ABOUT in your application object, why should you have to implement a separate mechanism if you add ID_APP_ABOUT to your system menu?

A better and more MFC-like way to handle add-on system commands would be to pass them through the normal command-routing mechanism. Then you could handle system commands the normal MFC way, by writing ON_COMMAND handlers. You could even use ON_UPDATE_COMMAND_UI to update your system menu items, for example to disable an item or display a checkmark next to it.

Figure 2 shows a little class I wrote, CSysCmdRouter, that turns system commands into ordinary commands. To use it, all you have to do is instantiate CSysCmdRouter in your main frame and call its Init method from OnCreate:

int CMainFrame::OnCreate(...)
{
  // Add my items to system menu
  CMenu* pMenu = GetSystemMenu(FALSE);
  pMenu->AppendMenu(..ID_MYCMD1..);
  pMenu->AppendMenu(..ID_MYCMD2..);
  // Route sys commands through MFC
  m_sysCmdHook.Init(this);
  return 0;
}

Figure 2 CSysCmdRouter (Close Figure 2)
////////////////////////////////////////////////////////////////
// MSDN Magazine May 2005
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//
#include "Subclass.h"

//////////////////
// Class to route user-defined WM_SYSCOMMAND messages through the normal
// MFC command routing system, so you can handle them the normal MFC way
// with ON_COMMAND and ON_UPDATE_COMMAND_UI handlers. The simplest way to
// achieve this is to translate the system commands to ordinary WM_COMMAND
// messages.
//
// To use: instantiate in your CMainFrame and call Init from OnCreate.
//
// You must also link Subclass.cpp in your app.
//
class CSysCmdRouter : public CSubclassWnd {
protected:
    CWnd* m_pMainWnd;    // main window hooked
public:
    CSysCmdRouter() { }
    virtual ~CSysCmdRouter() { }

    // Initialize: hook the main window
    BOOL Init(CWnd* pMainWnd) {
        ASSERT(pMainWnd);
        m_pMainWnd = pMainWnd;
        return HookWindow(pMainWnd);
    }

    // Terminate: unhook. No need to call unless you want to stop hooking
    // for some reason; CSubclassWnd will automatically unhook itself when
    // the hooked window is destroyed.
    void Term() {
        Unhook();
    }

protected:

    // virtual WndProc handler: convert WM_SYSCOMMAND to WM_COMMAND if the
    // command is not a system command: That is, if the command ID is less
    // than SC_SIZE = 0xFFFF.
    //
    virtual LRESULT WindowProc(UINT msg, WPARAM wp, LPARAM lp)
    {
        if (msg==WM_SYSCOMMAND && wp<SC_SIZE) {
            return m_pMainWnd->SendMessage(WM_COMMAND, wp, NULL);
        } else if (msg==WM_INITMENUPOPUP) {
          // Hide system menu flag (= HIWORD(lp)) so even system menu can
          // be initialized through MFC. This is somewhat dangerous
          // because you lose the ability to distinguish between the
          // system menu and other menus—but if you're adding your own
          // commands it shouldn't matter which menu(s) they come from!
          // Just make sure your command IDs don't conflict with the
          // built-in commands like SC_SIZE,and so on, which start at
          // 0xF000. By default, MFC command IDs start at 0x8000 so you'll
          // be safe if you follow MFC.
            lp = LOWORD(lp);
        }
        return CSubclassWnd::WindowProc(msg, wp, lp); // pass along
                                                      //-important!
    }
};

Once you call CSysCmdRouter::Init, you can process ID_MYCMD1 and ID_MYCMD2 the normal way, by writing ON_COMMAND handlers for any object in the MFC command-routing superhighway—view, document, frame, app, or any other command target you've added by overriding OnCmdMsg. CSysCmdRouter also lets you update your system menu using ON_UPDATE_COMMAND_UI handlers. The only caveat is to make sure your command IDs don't conflict with any other menu commands (unless they truly represent the same command) or any of the built-in system commands, which begin at SC_SIZE = 0xF000. Visual Studio® .NET assigns command IDs starting at 0x8000 = 32768, so if you let Visual Studio assign the IDs, you'll be OK as long as you don't have more than 0xF000-0x8000 = 0x7000 commands. That's 28,672 in base 10. If your app has more than 28,000 commands, you need to consult a programming psychiatrist.

How does CSysCmdRouter perform its magic? Simple: it uses the ubiquitous CSubclassWnd from my previous columns. CSubclassWnd lets you subclass MFC window objects without deriving from them. CSysCmdRouter derives from CSubclassWnd and uses it to subclass the main frame. Specifically, it intercepts WM_SYSCOMMAND messages sent to the frame. If the command ID is in the system command range (greater than SC_SIZE = 0xF000), CSysCmdRouter passes it along to Windows; otherwise it eats the WM_SYSCOMMAND and resends it as a WM_COMMAND, whereupon MFC follows its normal routing procedures, calling your ON_COMMAND handlers. Pretty clever, eh?

And what about ON_UPDATE_COMMAND_UI handlers? How does CSysCmdRouter make them work for system menu commands? Simple. Just before Windows displays a menu, it sends your main window a WM_INITMENUPOPUP message. This is your big chance to update the menu items—enable or disable them, add checkmarks, and so on. MFC catches WM_INITMENUPOPUP in CFrameWnd::OnInitMenuPopup and performs its UI-update magic. MFC creates a CCmdUI object for each menu item and passes it to the appropriate ON_UPDATE_COMMAND_UI handlers in your message maps. The MFC function that takes care of this is CFrameWnd::OnInitMenuPopup, which begins like so:

void CFrameWnd::OnInitMenuPopup(CMenu* pMenu,
  UINT nIndex, BOOL bSysMenu)
{
  if (bSysMenu)
    return; // don't support system menu
  ...
}

MFC doesn't do anything to initialize the system menu. Why not? Why should it care? What if somehow you could make bSysMenu FALSE, even for the system menu? That's exactly what CSysCmdRouter does. It intercepts WM_INITMENUPOPUP and clears the bSysMenu flag, which is the HIWORD of LPARAM:

if (msg==WM_INITMENUPOPUP) {
   lp = LOWORD(lp); // (set HIWORD = 0)
}

Now when MFC gets WM_INITMENUPOPUP, it thinks the menu is a normal menu. This all works fine as long as your command IDs don't conflict with true system commands. The only thing you lose is the ability to distinguish the system menu from the main window menu if you override OnInitMenuPopup. Hey, you can't have everything! You can always handle WM_INITMENUPOPUP by overriding CWnd::WindowProc, or compare HMENUs if you need to distinguish. But really, you shouldn't care where the command came from.

Figure 3 Task Bar Menu
Figure 3 Task Bar Menu

To show how all this works in practice, I wrote a little test program called TBMenu. Figure 3 shows the menu displayed when you right-click on TBMenu's minimized button in the task bar. You can see the two extra commands at the bottom of the menu. Figure 4 shows the code for TBMenu's CMainFrame. You can see where it adds the commands in OnCreate and handles them with ON_COMMAND and ON_UPDATE_COMMAND_UI handlers in CMainFrame's message map. TBMenu handles ID_APP_ABOUT within its application class (not shown). CSysCmdRouter makes system commands work like any other commands. Speaking of commands, see the fascinating sidebar "Origins of Ctrl+Alt+Del".

Figure 4 MinFrm.cpp (Close Figure 4)
// MSDN Magazine May 2005
// If this program works, it was written by Paul DiLascia.
// If not, I don't know who wrote it.
//
#include "StdAfx.h"
#include "TbMenu.h"
#include "MainFrm.h"
...
IMPLEMENT_DYNAMIC(CMainFrame, CFrameWnd)
BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)
    ON_WM_CREATE()
    ON_COMMAND(ID_CHECKME, OnCheckMe)
    ON_UPDATE_COMMAND_UI(ID_CHECKME, OnUpdateCheckMe)
END_MESSAGE_MAP()

CMainFrame::CMainFrame() : m_bChecked(0)
{
}

CMainFrame::~CMainFrame()
{
}

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
    ... // normal MFC stuff: call base class OnCreate,
    ... // create toolbar and status bar (not shown here)

    // Append my own items to the system menu
    CMenu* pMenu = GetSystemMenu(FALSE);
    ASSERT(pMenu!=NULL);
    pMenu->AppendMenu(MF_BYPOSITION|MF_SEPARATOR);
    pMenu->AppendMenu(MF_STRING,(UINT_PTR)ID_CHECKME, _T("Chec&k Me"));
    pMenu->AppendMenu(MF_STRING,(UINT_PTR)ID_APP_ABOUT,
        _T("&About TBMenu"));

    // Hook system commands so they're routed through MFC
    // command routing to ON_COMMAND handlers.
    m_sysCmdHook.Init(this);

    return 0;
}

//////////////////
// Handle "Check Me" command
//
void CMainFrame::OnCheckMe()
{
    m_bChecked = !m_bChecked;
    MessageBox(m_bChecked ? _T("Checked") : _T("Unchecked"), _T("TBMenu"));
}

//////////////////
// Set checkmark next to "Check Me" command (or not)
//
void CMainFrame::OnUpdateCheckMe(CCmdUI* pCmdUI)
{
    pCmdUI->SetCheck(m_bChecked);
}

Happy programming!

Send your questions and comments for Paul to cppqa@microsoft.com.

Paul DiLascia is a freelance writer, consultant, and Web/UI designer-at-large. He is the author of Windows++: Writing Reusable Windows Code in C++ (Addison-Wesley, 1992). Paul can be reached at www.dilascia.com.


{ End Bracket }: On Being Crew for Fantasia 2000

{ End Bracket }
On Being Crew for Fantasia 2000
Stanley B. Lippman

I was asked to reminisce about my experience as a software technical director on the Firebird segment of Disney's Fantasia 2000 in the special effects group. They're the group that puts the twinkle in Mickey's eyes, literally. This group was a mix of artists who draw by hand and artists who use computer graphics. I wrote software to help the graphic artists work, mostly in Perl, tkl and tsch under IRIX, the SGI UNIX OS, although I did get to extend the Houdini C++ class framework.

I became pretty good friends with Tim, my officemate, and Michael, our primary CGI animation artist. We used a program called Houdini for the production—my coworkers had used it on the Hydra animation in Hercules. I couldn't make heads or tails out of Houdini from an artistic standpoint, although my boss was nice enough to let me sit in on the training.

The traditional effects people work differently than the character developers do. In the character stream, there are lead animators who establish the look of the character in model sheets that detail every perspective and annotate how each aspect of the character should be drawn. She may draw the keyframe sequence in a very freehand manner. The clean-group turns the freehand drawings into line art, after which another group, called the in-betweeners, fill in the necessary drawings to connect one keyframe to the next. (This has now all been replaced with computers.)

But the effects guys have to provide every frame. I watched one artist draw the lava. He worked at a light table, constantly flipping sheets back and forth to gauge the continuity. All he drew were little circles and wave-like ridges for days and days. We had one artist from Digital Domain who had worked on the CGI lava for Inferno. He was pushing for the Firebird lava sequence to be replaced by a CGI element. There were often such arguments between the traditional advocates and CGI.

There are lots of antagonisms in the industry. Motion capture, in which a person is wired up and her movements captured as a set of coordinates, upset many of the traditional computer animators with the complaint, "where's the art in that?" Each step in automating a previously human-executed procedure seems to always be shadowed by that question.

The Firebird combined traditional drawing and CGI graphics remarkably well. For example, the Elk in the segment was hand animated, but its antlers were CGI elements. To match the two elements, I wrote a Perl script to capture the Disney camera values and translate them into a procedural camera entity that could be made the default camera within Houdini. Using the Houdini particle system, Tim did the grass streaming from the Sprite as she flies through the forest. He also did the butterflies that randomly flit alongside her, as well as the red poppies that pepper the screen. Michael animated the gorgeous bursts of tree blossoms, the sadly sashaying ashes, and the shimmering sunlight in the final scene (that was later discarded).

I was invited to attend the first presentation to the special effects group of the climatic rebirth scene. The directors reviewed the storyboard and the concept art, telling us the story and the general feelings they wanted conveyed by the effects. This was again going to be a mix of hand-drawn and CGI effects. But, and this startled me at first, no one really knew how to make this work. The meeting was intended to kick-start the process. As a programmer, it surprised me that there wasn't already a procedure in place for this. After all, we were already in production. I was interested in how much the folks leading the team relished the absence of structure and the challenge of creating this crazy scene.

I was asked to try out a special tree-growing Houdini module. The tree-growing algorithm lets you set variables for branching and trunk length and direction. I remember walking out of the building and looking at the trees as if I had never seen them before. I needed to see how they grew and the space they occupied. In practical terms, I was a complete failure. But it was a glimpse of another way of being in the world. That was my most special moment in animation.

It's a different kind of life fretting about how to stream grass from out of the Sprite's arms than working out an algorithm for translating C++ and implementing it.

It was a kooky kind of thing to do, all in all, but nothing has ever approached the wonder and camaraderie of being crew. When I was at Siggraph last year, I bumped into some of the Firebird folks; they were making a CGI movie in Toronto and asked if I would like to be on crew. Of course, I do. But I told them no.

Stanley B. Lippman is an architect with the Visual C++ team at Microsoft. He began working on C++ with its inventor, Bjarne Stroustrup, in 1984 at Bell Laboratories. In between, he worked in feature animation at Disney and Dreamworks and was a Distinguished Consultant with JPL. He also writes the Pure C++ column for MSDN Magazine.