 MSDN Magazine February 2003

MSDN Magazine February 2003

Class Templates: Bring the Power of Templates to Your .NET Applications with the CodeDOM Namespace

In the .NET Framework, the CodeDOM object model can represent code in a variety of languages. This article examines how source code templates written with the Framework's System.CodeDom and System.CodeDom.Compiler namespaces allow developers to create reusable boilerplate source code that can be shared between projects. Components designed via templates improve productivity and shorten development time.Here C++-style classes and templates are simulated and code is generated in multiple languages through the creation of CodeDOM object graphs. Compiling object graphs and formatting output code are also explained.

Adam J. Steinert BITS: Write Auto-Updating Apps with .NET and the Background Intelligent Transfer Service API

Both the .NET Framework and Windows have some very interesting APIs for creating applications that are capable of updating themselves automatically over a network. There are many advantages to writing your application to update itself like Windows Update does, including convenience for your users, from easier maintenance to network administration. Automatic updates require attention to factors such as discovery, security, and file replacement. In this article, the author covers the BITS API as well as a number of features of the .NET Framework that take care of these facets of auto-updating using the same facilities that the Windows Update uses.

Jason Clark Printing: Preview and Print from Your Windows Forms App with the .NET Printing Namespace

Printing is an integral part of every complete Windows-based application. Providing robust printing capabilities in these applications has often proved to be a tedious chore. Now, printing from Windows Forms with the .NET Framework means you must adopt a document-centric approach, resulting in cleaner and more manageable code. While the System.Windows.Forms namespace provides seamless integration with all the standard print dialogs (such as Print Preview, Page Setup, and Print), the System.Drawing.Printing namespace offers numerous classes for extensibility and customization. These classes, and how they provide access to printing capabilities, are covered here. Other useful techniques, such as printing in the background to allow the user to continue other tasks, is also explained.

Alex Calvo Windows Forms: Give Your .NET-based Application a Fast and Responsive UI with Multiple Threads

If your application performs any non-UI processing on the thread that controls the user interface, it may make the app seem slow and sluggish, frustrating users. But writing multithreaded apps for Windows has long been restricted to C++ developers. Now with the .NET Framework, you can take advantage of multiple threads in C# to control the flow of instructions in your programs and isolate the UI thread for a fast and snappy user interface. This article shows you how. Also, it discusses the pitfalls of multiple threads and presents a framework for safe concurrent thread execution.

Ian Griffiths Delegates in .NET: A Primer on Creating Type-Safe References to Methods in Visual Basic .NET

Delegates, new in Visual Basic .NET, are type-safe, object-oriented references to methods. By using delegates, you make your methods accessible to others and therefore more extensible. This article provides an introduction to delegates, their benefits, and how they are used.Here the author shows you how to declare, create, and use delegates to invoke instance and shared methods at run time, and how to implement delegates as properties. He then goes on to provide examples of how delegates are used for callback functionality and explains the relationship between delegates and events. Finally, the article provides a glimpse at how delegates are implemented in intermediate language code by the Visual Basic .NET compiler.

Jesse Liberty Web-Aware Apps: Build Hyperlinks into Your Client App with the Windows Forms LinkLabel Control

LinkLabels are Windows Forms controls that enable a user to hyperlink to a URL that points to either the Web or the local directory system. While the SDK documentation discusses the control, it does not demonstrate how linking is accomplished, nor does it outline the power and flexibility the control provides. This article fills those gaps, showing how to link using the LinkLabel control. It also shows the flexibility of this control for the programmer using the .NET Framework. In addition, the author covers the large number of properties that allow you to customize your controls and accurately place them. Their built-in behaviors are also discussed, along with their use in both Visual Basic .NET and C#.

Dan Hurwitz Editor's Note: Rich Clients Count Too

It's the dead of winter here at the MSDN Magazine offices, and we're having a blast. Temperatures are making their daily struggle to stay above zero. The tempting aroma of chestnuts roasting on an open subway ventilation grate fills the city with a wintry mix of excitement and anticipation.

New Stuff: Resources for Your Developer Toolbox

Vektrex Electronic Systems Inc. has announced the launch of a new version of VIVID, a software development toolkit for the generation of IVI-COM-compliant instrument drivers. VIVID links the many IVI Foundation specifications into a full lifecycle environment and produces IVI-COM drivers that are compliant with the latest specifications approved by the IVI Foundation.

Theresa W. Carey Web Q&A: Data Shredding, Updating the Status Bar, and More

Edited by Nancy Michell The XML Files: XPath Selections and Custom Functions, and More

Aaron Skonnard Cutting Edge: The ASP.NET View State

In ASP. NET pages, the view state represents the state of the page when it was last processed on the server. It's used to build a call context and retain values across two successive requests for the same page.

Dino Esposito Advanced Basics: Automatically Generating Proxy Classes

Ken Spencer Basic Instincts: Programming with Events Using .NET

This month's Basic Instincts column builds upon my last two columns in which I talked about concepts and programming techniques associated with delegates. I will assume you have read the last two installments of this column and that you understand the role that delegates play within the Microsoft® .

Ted Pattison The ASP Column: Using the Internet Explorer WebControls

During the lifespan of ASP, there have been many technologies developed for making browser-based user interface development easier. For example, during the early days of classic ASP development, keeping a browser-based UI consistent required many conditional statements into the ASP script.

George Shepherd C++ Q&A: Singleton Class Private Constructor, C# Singleton Class, and More

Paul DiLascia Resource File: Technical Support Options

Development can have its frustrating moments. How many times have you been in the middle of a project and encountered a problem you just couldn't solve alone? In order to help you through this, Microsoft provides quite a wide variety of support options from Web-based chats and newsgroups to paid premier customer support.


Class Templates: Bring the Power of Templates to Your .NET Applications with the CodeDOM Namespace

Class Templates

Bring the Power of Templates to Your .NET Applications with the CodeDOM Namespace

Adam J. Steinert
This article assumes you're familiar with C# and Reflection
Level of Difficulty 1 2 3
Code download available at: CodeDOM.exe (169KB)
SUMMARY

In the .NET Framework, the CodeDOM object model can represent code in a variety of languages. This article examines how source code templates written with the Framework's System.CodeDom and System.CodeDom.Compiler namespaces allow developers to create reusable boilerplate source code that can be shared between projects. Components designed via templates improve productivity and shorten development time.

Here C++-style classes and templates are simulated and code is generated in multiple languages through the creation of CodeDOM object graphs. Compiling object graphs and formatting output code are also explained.

Contents
Creating Object Graphs
Using the Object Graph
Generating Source Code
Formatting the Output
Compiling Object Graphs
Compiler Options
Class Templates and the CodeDOM
Creating the Application
Creating the Template
Conclusion

T
he CodeDOM is used in the Microsoft® .NET Framework to represent source code documents in a language-neutral manner. CodeDOM object graphs can be manipulated and used to generate source code and even compiled assemblies for any number of uses. The System.CodeDom namespaces contain upwards of 70 type definitions that represent characteristic entities of a typical high-level, object-oriented language such as C#, Visual Basic® .NET, Eiffel, or Java. The System.CodeDom.Compiler namespace defines a framework of abstract classes for processing CodeDOM object graphs, and any language that wants to support the CodeDOM may do so through the implementation of the CodeDomProvider base class.

The CodeDOM plays an important behind-the-scenes role in the .NET Framework and Visual Studio® .NET. It's also used by ASP.NET to generate executable code, and in fact it is necessary for a language to implement a CodeDomProvider in order to be used in ASP.NET pages. The CodeDOM is also used for Web Services Description Language proxy generation and in the designers and code wizards in Visual Studio .NET. A number of languages implement CodeDomProvider classes and thus can be used to process CodeDOM object graphs. As you might expect, C# and Visual Basic .NET implement this through the CSharpCodeProvider and VBCodeProvider classes defined in the Microsoft.CSharp and Microsoft.VisualBasic namespaces, respectively. Additionally, there is a JScriptCodeProvider in the Microsoft.JScript namespace, and Visual J#™ .NET exposes the VJSharpCodeProvider in the Microsoft.VJSharp namespace. In addition, a number of Microsoft .NET language partners have, or are in the process of implementing, CodeDomProvider classes as part of their language integration with .NET. Some of these include Eiffel from Interactive Software Engineering, ActivePERL, developed by ActiveState, and Fujitsu COBOL.

After a brief look into the CodeDOM, some practical uses for the object model become apparent. One that I find most beneficial—and which is the main focus of this article—is using the CodeDOM to create templates for common and reusable source code. This is not a new concept. A number of books, articles, language features, and code-generating wizards discuss the topic. Regardless, I feel compelled to extol the virtues and benefits of using the CodeDOM to this end. By taking some time to learn the relatively simple (albeit large and cumbersome) object model, it is possible to create templates using the CodeDOM. Additionally, because a number of languages have implemented CodeDomProviders, the templates can be used to generate code in these languages, extending the usefulness of your templates to other programmers who don't work in your language.

In this article, I will walk through the creation of a CodeDOM object graph that is a template for a simple Windows®-based application. My first of two C# example applications incorporates this code. I will discuss using the object graph to generate source and compiled code and even create and execute the application in memory, and in multiple languages. After covering the basics, my second example will explore using the CodeDOM to mimic C++-style class templates.

Creating Object Graphs

I'll begin by looking at the creation of an object graph template with the CodeDOM to describe some of its central classes and how they represent code. Creating your first object graph with the CodeDOM is akin to writing code in a new language. It is structured in such a way that many object-oriented concepts are represented. As such, to describe its use I'll start with a twist on the epitome of programming examples: Hello World. The HelloGen project in the code download (see the link at the top of this article) is the basis for this examination, and the TemplateConstructor class is responsible for creating the object graph.

When creating templates, it often helps to have an example of the code you are creating as a reference. Figure 1 is the complete C# source that will serve as a reference for the construction of the object graph for the application. There is nothing extraordinary about the application itself. It consists of two classes, HelloApplication, which instantiates and shows the form, and HelloWorldForm, which defines the Windows Form that will be displayed to the user. As you will see later, the template will be modified at run time to replace the TextBox txtSourceCode's Text property when the form is displayed.

Figure 1 Hello World Template Reference (Close Figure 1)
namespace HelloWorld {
    using System;
    using System.Drawing;
    using System.Windows.Forms;

    public class HelloWorldForm : System.Windows.Forms.Form {

        private System.Windows.Forms.TextBox txtSourceCode;

        public HelloWorldForm() {
            this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
            this.Text = "Hello, World!";
            this.Size = new System.Drawing.Size(700, 350);
            txtSourceCode = new System.Windows.Forms.TextBox();
            this.Controls.Add(txtSourceCode);
            txtSourceCode.Multiline = true;
            txtSourceCode.ScrollBars = ScrollBars.Both;
            txtSourceCode.Top = 10;
            txtSourceCode.Width = 10;
            txtSourceCode.Size = new System.Drawing.Size(690, 300);
            txtSourceCode.Anchor = (AnchorStyles.Bottom
                        | (AnchorStyles.Top
                        | (AnchorStyles.Left | AnchorStyles.Right)));
            txtSourceCode.Text = "[Little Recursive Box]";
        }
    }

    public class HelloApplication {

        public static void Main() {
            HelloWorldForm hwApp;
            hwApp = new HelloWorldForm();
            hwApp.ShowDialog();
        }
    }
}

The TemplateConstructor class is where the entire object graph for Hello World is created, beginning with the call to GenerateCCU:

public CodeCompileUnit GenerateCCU() {
    CodeCompileUnit        hwCompileUnit = new CodeCompileUnit();
    CodeNamespace          hwNamespace = BuildNamespace();
    CodeTypeDeclaration    hwFormClass = BuildFormClass();

    hwClassRef = hwFormClass;
    CodeTypeDeclaration hwAppClass = BuildAppClass();

    hwCompileUnit.Namespaces.Add(hwNamespace);
    hwNamespace.Types.Add(hwFormClass);
    hwNamespace.Types.Add(hwAppClass);

    return hwCompileUnit;
}
The CodeCompileUnit class that is exposed in the System.CodeDom namespace is at the root of the Hello World object graph. This class is representative of a project or an assembly, and hwCompileUnit represents the HelloWorld assembly. The CodeCompileUnit is the object that is used by code providers and code generators and all of the code included in a CodeCompileUnit compiles to a single assembly.

Three of the public properties that the CodeCompileUnit exposes are of particular importance. The Namespaces property holds a collection of CodeNamespace objects which, as you would expect, represent namespaces within the assembly. The Referenced Assemblies property is a string collection representing the names of assemblies that the CodeCompileUnit references, and the AssemblyCustomAttributes property is a CodeAttributeDeclarationCollection representing the attributes applied to the assembly.

Populating the CodeCompileUnit means adding the CodeNamespace objects that will compose the assembly to the Namespaces collection hwCompileUnit. As you have likely deciphered by looking at the previous code for the GenerateCCU function, the CodeTypeDeclaration class represents classes within a namespace. The hwFormClass and hwAppClass objects represent the HelloWorld and AppClass classes in Figure 1, respectively, and completing the basic structure of the Hello World application is simply a matter of defining these CodeTypeDeclaration objects and adding them to the CodeNamespace hwNamespace. In order to make this as clear as possible I have moved the code for creating each of these objects into separate functions.

The CodeNamespace object is trivial; creating and defining one takes no more than a few lines of code:

private CodeNamespace BuildNamespace() {
    CodeNamespace    hwNamespace = new CodeNamespace();

    hwNamespace.Name = "HelloWorld";
    hwNamespace.Imports.Add(new CodeNamespaceImport("System"));
    hwNamespace.Imports.Add(new CodeNamespaceImport("System.Drawing"));
    hwNamespace.Imports.Add(new
        CodeNamespaceImport("System.Windows.Forms"));
    return hwNamespace;
}
The BuildNamespace function creates and returns a representation of the HelloWorld namespace. The Imports collection is a collection of CodeNamespaceImport objects that represents the using statement in C# and the Imports statement in Visual Basic .NET. The Namespace property of the CodeNamespaceImport class is the only property of note, and it is set internally by the constructor in this example.

As I mentioned before, the CodeTypeDeclaration class is used to represent classes in the CodeDOM; however, it may also be used to represent enumerations, interfaces, and structs. The type you are representing is noted by setting the appropriate public property—IsClass, IsEnum, IsInterface, or IsStruct—to True. The BuildFormClass function is responsible for creating the CodeTypeDeclaration for the HelloWorld class, as shown in Figure 2.

Figure 2 Creating the HelloWorldForm Class (Close Figure 2)
private CodeTypeDeclaration BuildFormClass() {
    CodeTypeDeclaration    FormClass = new CodeTypeDeclaration();
    CodeEntryPointMethod MainMethod = new CodeEntryPointMethod();

    // Set up Class
    FormClass.Name = "HelloWorldForm";
    FormClass.IsClass = true;
    FormClass.BaseTypes.Add(new CodeTypeReference(
                            typeof(System.Windows.Forms.Form)));

    FormClass.Attributes = MemberAttributes.Public;

    // set up members
    FormClass.Members.Add(new CodeMemberField(
        typeof(System.Windows.Forms.TextBox), "txtSourceCode"));

    // set up constructor
    FormClass.Members.Add(BuildFormConstructor());

    return FormClass;
}

As you can see from the C# source in Figure 1, the HelloWorldForm class represents the visible element of the application. To represent it in the object graph, I create a new CodeTypeDeclaration to represent the class, giving it the name HelloWorldForm and setting its IsClass property to True. Since the HelloWorld class is a form that derives from System.Windows.Form, I add a CodeTypeReference object to the BaseTypes collection, which represents the base Form class.

BuildFormClass also demonstrates how to represent an object's type information in the CodeDOM. The CodeTypeReference class represents a scalar or array type in the CodeDOM and can be passed as an argument to most CodeDOM constructors and methods that require a type to be defined. There are four overloads for its constructor; the first two are for scalar datatypes, which take either a String or a System.Type argument. The safest way to go, of course, is to use the System.Type overload; however, this may not always be convenient or even possible depending on the type of code you are modeling. Regardless, it is recommended by the documentation (and is good coding practice) to use the fully qualified name of the data type you want to represent. The third and fourth overloads let you specify an array type and take either a String or a CodeTypeReference as parameters. The second parameter for both is an integer that represents the array rank. Consequently, most of the CodeDOM classes that take a CodeTypeReference as a parameter also have overloads that take a String or System.Type.

To complete the description of the HelloWorldForm class, I set the Attributes property. Attributes is a bit field that represents the access modifiers for the classes which are specified in the MemberAttributes enumeration, and thus multiple modifiers can be applied through the bitwise OR operator. Next, I need to add the declaration of the TextBox and the class constructor to the HelloWorldForm class through the Members collection of FormClass. This collection will contain the objects that derive from CodeTypeMember and represent entities that are declared within a Type, including constructors, member variables, and events, to name just a few. While this constructor overload sets the Type and Name properties of the member, there is also an Attributes property that can set its modifiers, as well as an InitExpression member that sets the initial value if desired. The constructor for the class is generated in the BuildFormConstructor function; however, I will continue by first examining the BuildAppClass function and representing the HelloApplication class in the CodeDOM before examining the constructor.

Figure 3 shows the source code for the BuildAppClass function. There are three new concepts in this class: representing the initial method in an assembly, setting the value of variables, and invoking methods on an object. The CodeEntryPointMethod object is responsible for representing the method where execution begins in an assembly. It inherits most of its properties for the CodeTypeMember class and adds an additional ReturnType property to represent the return type of the function.

Figure 3 Creating the HelloApplication Class (Close Figure 3)
private CodeTypeDeclaration BuildAppClass() {
    String TypeString = "HelloWorldForm";

    if(UseFullNamespace) {
        TypeString = "HelloWorld." + TypeString;
    }

    CodeTypeDeclaration hwAppClass =
        new CodeTypeDeclaration("HelloApplication");
    CodeEntryPointMethod MainMethod = new CodeEntryPointMethod();

    hwAppClass.IsClass = true;
    hwAppClass.Attributes = MemberAttributes.Public;

    MainMethod.Attributes = MemberAttributes.Public |
                            MemberAttributes.Static;
    MainMethod.ReturnType = new CodeTypeReference(typeof(void));
    CodeExpression[] prmRun = { new CodeObjectCreateExpression(
                                "HelloWorld", new CodeExpression[0]) };

    MainMethod.Statements.Add(new CodeVariableDeclarationStatement(
                          new CodeTypeReference(TypeString), "hwApp"));

    CodeExpression[] hwParams = new CodeExpression[0];
    MainMethod.Statements.Add(new CodeAssignStatement(
        new CodeVariableReferenceExpression("hwApp"),
        new CodeObjectCreateExpression(TypeString, hwParams)));

    MainMethod.Statements.Add( new CodeMethodInvokeExpression(
        new CodeVariableReferenceExpression("hwApp"),
        "ShowDialog", hwParams));

    hwAppClass.Members.Add(MainMethod);

    return hwAppClass;
}

CodeStatementCollections enable you to assemble groups of individual code statements to form the bodies of higher-level code constructs, such as methods, properties and try...catch blocks. In Figure 3, the CodeEntryPointMethod.Statements collection represents the code statements that collectively form the body of the Main function. Two other such examples are the CodeTryCatchFinallyStatement.TryStatements and CodeConditionStatement.TrueStatements, which are used to represent blocks of code in a try block and the True block of an if statement, respectively. Building the Main method simply requires adding objects derived from the CodeStatement class to the Statements collection.

After adding the declaration for the HelloWorldForm class, you must represent its assignment to a new instance of that class. This is done using the CodeAssignStatement class, which takes two arguments that derive from the CodeExpression class. As you can guess, this is the abstract base class for representing expressions in the CodeDOM. The two parameters represent the left-hand and the right-hand sides of the assignment. For this particular assignment statement, I reference the variable representing the HelloWorldForm class with the CodeVariableReferenceExpression class, passing in the variable name as the argument to the constructor. The creation expression is represented by the CodeObjectCreateExpression class, which takes the type of the object to create and the list of parameters to be used in the creation. This list of parameters is an array of CodeExpression objects and cannot be null.

As I have done in this example, you can send in a zero-length array to represent a call to the constructor with no arguments. Finally, the CodeMethodInvokeExpression class represents a method invocation. Note that if I wanted to invoke a static method of a class, I would have replaced the CodeVariableReferenceExpression with a CodeTypeReferenceExpression object.

By now you should be getting a pretty good grasp of the way that code is represented in the CodeDOM. There are only a few new System.CodeDom classes presented in the BuildFormConstructor function, partially represented in Figure 4, which is the final part of the template to investigate. This function's main job is to create the representation of the constructor which takes care of all of the property assignments needed to display the form correctly.

Figure 4 BuildFormConstructor Excerpt (Close Figure 4)
private CodeConstructor BuildFormConstructor() {
    CodeConstructor hwConstructor = new CodeConstructor();
    CodeThisReferenceExpression    oThis =
        new CodeThisReferenceExpression();
    CodeStatementCollection stCol = new CodeStatementCollection();

    hwConstructor.Attributes = MemberAttributes.Public;

    •••

    prmSizeArgs[0] = new CodePrimitiveExpression(700);
    prmSizeArgs[1] = new CodePrimitiveExpression(350);
    stCol.Add(new CodeAssignStatement(
        new CodePropertyReferenceExpression(oThis, "Size"),
        new CodeObjectCreateExpression(typeof(System.Drawing.Size),
                                       prmSizeArgs)));

    •••

    CodeTypeReferenceExpression oAnchor =
        new CodeTypeReferenceExpression("AnchorStyles");
    CodeBinaryOperatorExpression AnchorStyles =
        new CodeBinaryOperatorExpression(
          new CodeFieldReferenceExpression(oAnchor, "Bottom"),
          CodeBinaryOperatorType.BitwiseOr,
          new CodeBinaryOperatorExpression(
            new CodeFieldReferenceExpression(oAnchor, "Top"),
            CodeBinaryOperatorType.BitwiseOr,
            new CodeBinaryOperatorExpression(
              new CodeFieldReferenceExpression(oAnchor, "Left"),
              CodeBinaryOperatorType.BitwiseOr,
              new CodeFieldReferenceExpression(oAnchor, "Right"))));

    •••

    hwConstructor.Statements.AddRange(stCol);
    return hwConstructor;
}

Again, classes derived from CodeStatement are added to the Statements property of the CodeConstructor class much as they were to the CodeEntryPointMethod described previously. Note the use of the CodePrimitiveExpression class to represent primitives such as numbers and strings in the CodeDOM. Representing the Anchor property assignment of the TextBox named source in Figure 4 is cumbersome. In C#, the assignment is straightforward; however, the CodeDOM's CodeBinaryOperatorExpression takes only three arguments: the left and right sides of the expressions represented by CodeExpression classes and a CodeBinaryOperatorType enumeration to symbolize the operation. Therefore, it takes a composition of three of these objects to represent this in the CodeDOM. While admittedly cumbersome, it's not difficult.

Using the Object Graph

Creating the object graph for the Hello World application template serves the necessary evil of introducing some important classes in the object model and shows how they fit together to represent working code. Now let's look at how you can use that template to produce code. The System.CodeDom.Compiler namespace is the target for this examination. It defines three interfaces and a number of abstract base classes usable by language vendors as references for operating on CodeDOM object graphs to produce source and compiled assemblies. It can also be used to parse existing source code into CodeDOM object graphs. The HelloGen sample application gives you a framework to look into some of the properties of these classes as they pertain to individual languages and to make comparisons between them.

Figure 5 shows the main form of the application, which is composed of two sections. The top tab control allows you to set various options on the code compiler and code generator being used, as well as to pick the target language when compiling the HelloWorld application. The bottom tab control contains output source from each of the languages that are made available through the application's source code. By default, the downloadable source code is set to generate code and assemblies in C#, Visual Basic .NET, and JScript®. If you want to explore generating code in other languages that implement CodeProviders, there are comments in the source code explaining how to add these to the application. While writing this article, I explored creating the template in a number of additional languages, including ISE's Eiffel, Visual J# .NET, Mondrian, and Fujitsu COBOL.

Figure 5 Main Form Generator (Close Figure 5)

Generating Source Code

The ICodeGenerator interface of the System.CodeDom.Compiler namespace defines the methods used to generate code from a CodeDOM object graph, and the language-specific implementations of CodeDomProvider expose the CreateCodeGenerator function to return this interface. Clicking the Generate Preview button on the HelloGen main form initiates the preview process by calling the GeneratePreview method. This method begins by getting a CodeGeneratorOptions object that defines customizable parameters for the code generator output, which I will discuss next. Following this, it executes the loop in Figure 6, generating code from the object graph for each language.

Figure 6 Generating Preview Code (Close Figure 6)
// Create our source Graph for each language
foreach(LanguageItem Lang in cboTargetLanguage.Items) {
    if(Lang.LanguageID == Language.JScript) {
        HelloWorldApp.UseFullNamespace = true;
    } else {
        HelloWorldApp.UseFullNamespace = false;
    }
        hwCompileUnit = HelloWorldApp.GenerateCCU();
        hwGenerator = GetLanguageGenerator(Lang.LanguageID);

        CodeText = new StringWriter();
        hwGenerator.GenerateCodeFromCompileUnit(hwCompileUnit,
                                                CodeText,
                                                GeneratorOptions);
        ((TextBox)PreviewText[PT_PREFIX + Lang.LanguagePreviewID]).Text
            = CodeText.ToString();

        CodeText.Close();
}

The first step is to create the compile unit for the HelloWorld application by calling the GenerateCCU method of the AppConstructor class. While it would have been preferable to do this before the loop, the way CodeDOM handles type information appears to be a stumbling block. The problem is referencing the HelloWorldForm type in the BuildAppClass function (see Figure 3) in the following statement:

MainMethod.Statements.Add(new CodeVariableDeclarationStatement(
    new CodeTypeReference(TypeString), "hwApp"));
Here, TypeString is a string representing the HelloWorldForm type, but the CodeTypeReference object doesn't recognize the HelloWorldForm type, so it simply emits the string value of TypeString. For C# and Visual Basic .NET this must be "HelloWorldForm", however, JScript .NET requires it to be "HelloWorld.HelloWorldForm". I have added the UseFullNamespace flag in order to resolve this issue and I have created a new CodeCompileUnit on each iteration of the loop.

In creating templates with the CodeDOM, it is important to be wary of such shortcomings and handle them appropriately. In this case, I'm simply setting the UseFullNamespace of the AppCreator object to true when generating code for JScript .NET and leaving it out for the rest. In many cases, you can use the Supports method of the ICodeGenerator interface to ensure the appropriate language capabilities of the generator you intend to use. This method takes a GeneratorSupport enumeration value and returns a Boolean, indicating whether the language supports that particular construct. The enumeration covers a number of useful language features and constructs, but is by no means exhaustive. Figure 7 lists the GeneratorSupport enumeration values, which are self-descriptive.

Figure 7 GeneratorSupport Enumeration Values (Close Figure 7)

ArraysOfArrays | MultiDimensionalArrays
AssemblyAttributes | MultipleInterfaceMembers
ChainedConstructorArguments | NestedTypes
ComplexExpressions | ParameterAttributes
DeclareDelegates | PublicStaticMembers
DeclareEnums | ReferenceParameters
DeclareEvents | ReturnTypeAttributes
DeclareInterfaces | StaticConstructors
DeclareValueTypes | TryCatchStatements
EntryPointMethod | Win32Resources

Once you have a reference to the CodeCompileUnit, it's time to get hold of the code generator for the desired language. The GetLanguageGenerator function instantiates a new instance of a language-specific code provider class derived from CodeDomProvider and invokes its CreateGenerator method to return the ICodeGenerator interface for the specified language. The following example demonstrates this for C#:

case Language.CSharp:
      return new CSharpCodeProvider().CreateGenerator();

To create the language-specific source code, I invoke the GenerateCodeFromCompileUnit method (see Figure 6) of the ICodeGenerator interface returned by the CreateGenerator function. This takes as arguments the CodeCompileUnit representing my object graph, a StringWriter which will be filled with the source code, and the Generator options, which may be used to modify the style of the code created.

The output is displayed on the appropriate tab for quick comparison. At this point, the code from any preview window could be cut and pasted into a text file and compiled; however, later in this article I will explore the facilities in the HelloGen sample application to do this using objects in the System.CodeDom.Compiler namespace. By running the example, you can compare the C#, Visual Basic .NET, and JScript .NET output that was all compiled from the same template. Additionally, Figure 8 shows source generated for the HelloWorldForm class by a beta of ISE's EiffelCodeProvider. This code is a departure from the syntax of the other languages in the example, which tend to be quite similar, and underscores the language neutrality and flexibility of the CodeDOM.

Figure 8 Eiffel Source for HelloWorldForm (Close Figure 8)
class
  HELLO_WORLD_FORM

inherit
  FORM

create
  a_ctor

feature -- Initialization

  a_ctor is
    do
        set_auto_scale_base_size (
            create {SIZE}.make_from_width_and_height (5, 13))
        set_text (("Hello, World!").to_cil)
        set_size (create {SIZE}.make_from_width_and_height (700, 350))
        create source.make
        get_controls.add (source)
        txtSourceCode.set_multiline (True)
        txtSourceCode.set_scroll_bars (both)
        txtSourceCode.set_top (10)
        txtSourceCode.set_width (10)
        txtSourceCode.set_size (
            create {SIZE}.make_from_width_and_height (690, 300))
        txtSourceCode.set_anchor (bottom or top or left or right)
        txtSourceCode.set_text (("[Little Recursive Box]").to_cil)
    end

feature -- Access

    txtSourceCode: TEXT_BOX

end -- HELLOWORLDFORM

Formatting the Output

Earlier, I mentioned that the CodeGeneratorOptions class allows you to modify properties of the generated source, and the Generator Options tab provides controls that allow you to modify these properties. On the call to GetGeneratorOptions, a new object is created and its properties are set. There are four such properties: BlankLinesBetweenMembers, BracingStyle, ElseOnClosing, and IndentString. The first is a Boolean, indicating whether the generator inserts blank lines between code elements such as member variables and classes. BracingStyle can be set to either "Block" or "c". BlockStyle appends opening braces to the same line as the code construct they encapsulate; otherwise, they are placed on the line immediately following the declaration. Similarly, ElseOnClosing defines the placement of else, catch, and finally statements. Finally, the IndentString property is used to set the sequence of characters for each indentation level, usually best kept as spaces.

Compiling Object Graphs

While the straight generation of source code is generally as far as you would go in creating templates, my discussion of the System.CodeDom.Compiler namespace would be lacking if I didn't cover the ICodeCompiler interface. This interface defines methods for compiling a CodeDOM object graph to intermediate language. The HelloGen application's Create Application button triggers the compilation of the object graph to the language selected in the Target Language dropdown of the Application Output tab, which also contains textboxes to specify the target location of both the code and the executable. The Compiler Parameters tab allows you to set customizable properties of the code compiler, which I will describe later.

The GenerateProgram method of the example application does the bulk of the work preparing and compiling the code. Again, I begin by generating the CodeCompileUnit for the HelloWorld application. In this case, I'll need both a code generator and a code compiler, so I'll retrieve a reference to those next. The GetLanguageCompiler function is almost identical to the GetLanguageGenerator function described earlier with the exception that it returns an ICodeCompiler interface through a call to CreateCompiler rather than CreateGenerator. The next step is to update hwCompileUnit with the source for the application. For the purposes of the preview application, AppConstructor.GenerateCCU creates an object graph that sets the TextBox txtSourceText's Text property to "[Little Recursive Box]" by default. To stress the cross-language capabilities of the template, I'd like that text to be replaced by the language-specific source code for the Hello World app being generated. I do this by first generating the source in the currently selected language with hwGenerator and replacing the CodePrimitiveExpression through the CreateExtendedCompileUnit method (see Figure 9). After regenerating the compile unit with this new language-specific information, its source is written to a text file, and I prepare to compile the code.

Figure 9 CreateExtendedCompileUnit (Close Figure 9)
private void CreateExtendedCompileUnit(ICodeGenerator hwGenerator,
    ref CodeCompileUnit hwCompileUnit,
    CodeGeneratorOptions GeneratorOptions,
    TemplateConstructor HelloWorldApp) {

   StringWriter  CodeText = new StringWriter();

    // Generate code with text placeholder
    hwGenerator.GenerateCodeFromCompileUnit(hwCompileUnit, CodeText,
        GeneratorOptions);
    HelloWorldApp.SourceText = new
        CodePrimitiveExpression(CodeText.ToString());
    hwCompileUnit = HelloWorldApp.GenerateCCU();
}

Compiler Options

Just as ICodeGenerator.CreateCompileUnit takes a CodeGeneratorOptions object to modify code generation, the CompileAssemblyFromDom method accepts a CompilerParameters object that defines parameters for compiling that code. Some of these options can be set on the Compiler Parameters tab of the application and set on the object when it is created through the GetCompilerParameters function. The CompilerOptions takes a string of parameters to send to the compiler, simulating command-line execution. GenerateExecutable is a Boolean property that tells the compiler whether it should compile an executable or a DLL. Although it is not visible through the UI, there is a ReferencedAssemblies property, which is a collection of strings representing names of the assemblies referenced in the CodeCompileUnit. Since HelloWorld is a Windows Forms app, I have added references to System.Drawing.dll and System.Windows.Forms.dll. Although System.dll is referenced by default, I added it to the source to be complete. The output of the compile can be set to debug or release using the IncludeDebugInformation property, and the TreatWarningsAsErrors flag can be set to tell the compiler with what seriousness it should treat warnings in the code.

The CodeCompileUnit is compiled through a call to ICodeCompiler.CompileAssemblyFromDom with a CompilerParameters object and the CodeCompileUnit in question:

hwResults = hwCompiler.CompileAssemblyFromDom(hwCompilerParameters,
            hwCompileUnit);
The hwResults object is of type CompilerResults, which is defined in the System.CodeDom.Compiler namespace. This class contains a number of useful properties for retrieving information about the compile process, especially through the Errors collection, which will hold information on any issues that caused the compile process to fail. If you choose to compile the application to a file, the PathToAssembly property will reflect the location of the newly created file, while the CompiledAssembly property gets a reference to the in-memory Assembly object, if applicable. HelloGen tests the GenerateInMemory property of hwCompilerParameters and, if True, uses reflection to execute the program in the ExecuteCompiledAssembly function.

Class Templates and the CodeDOM

With this knowledge of the CodeDOM in hand, let's put it to good use in a real-world example. One of the things that the C# and Visual Basic .NET languages lack is support for parametric polymorphism, commonly referred to as generics. One such example of this is class templates in C++. Class templates allow you to define classes in which certain instances of types within the class are generalized until the class is declared in code. Upon declaration the class is given a type, and at compile time a class is created for each distinct type used in the application. While different compilers can optimize this process to a degree by factoring commonalities in the class, this general description suffices for the purposes of this article. Classically, perhaps the best and certainly the most noteworthy candidates for this type of an implementation are collection classes because they are general purpose in nature.

By creating templates for these classes, you benefit from having their implementation in one place; modifying the template changes the implementation for all instances. Since they are strongly typed, you don't need to use extra code casting variables when using them, which leads to cleaner code and fewer errors. One way of reaping some of the benefits of class templates in languages without generics is, of course, to use variables of type Object to achieve the results of a localized source base and universal usability. The drawback here is in type safety and in a reliance on descriptive variable names and code comments to convey type information to the programmer.

Generics will probably be coming to the C# language in a future release, and the current proposed implementation looks promising (see Generics for C# and .NET CLR and Future Features FAQ), but until then a useful way to build classes that offers some of the advantages of generics would be helpful. This can be accomplished by creating templates using the CodeDOM. By creating classes that define object graphs for your collections or other template based classes, you benefit from having the implementation in one place. If you want to change something, you need only make modifications to the template class; however, you will need to generate the source again for each type you want to represent.

Additionally, so that changes will not be lost, you must be careful that any template classes that you generated previously have not been modified. While this is a serious disadvantage, it is one shared by other template-based class generators. You do, however, gain type safety and cleaner code from reduced casting. The additional advantage to a CodeDOM implementation is the potential to generate these classes in a number of languages for which there are CodeProviders, assuming that the template has been designed within the constraints of those languages.

Creating the Application

NetTemplateGenerator is a general-purpose application for generating source code from CodeDOM object graph templates. These templates are set up in such a way that they can accept parameters that will change the properties of the object graphs, which in turn changes the properties of the code generated from them. If these parameters represent the types of certain variables within the object graph, then I am in effect mimicking the behavior of C++ class templates. The NetTemplateGenerator application itself is responsible only for generating source code from CodeCompileUnit objects and providing the user with a way to modify the parameters to the template. The TypedHashtableProvider project exposes an example template, the CTHTGenerator class, which provides an object graph representing a simple Hashtable whose Key and Value types can be modified through NetTemplateGenerator. Figure 10 shows the app after loading the TypedHashtableProvider assembly and modifying the parameters to the template.

Figure 10 Template Generator
Figure 10 Template Generator

Assemblies are loaded from the Source Assembly tab on the main form of the application. The default source assembly points to the TypedHashtableProvider.dll assembly, which contains the CTHTGenerator class for creating the CodeCompileUnit for this example. Clicking the Load Assembly button loads the chosen source assembly into memory and uses reflection to browse the classes in that assembly, as shown in the code snippet from PopulateClassesCBO in Figure 11.

Figure 11 Populating the Generator Class ComboBox (Close Figure 11)
foreach(Type curType in types) {
    GeneratorMethods CurGenMethods = new GeneratorMethods();
    if(curType.IsClass) {
        CurGenMethods.CCUGeneratorMethod = curType.GetMethod("GenerateCCU");
        CurGenMethods.QueryVarsMethod = curType.GetMethod("QueryVariables");
        CurGenMethods.SetVarsMethod = curType.GetMethod("SetVariables");
        CurGenMethods.GetNameMethod = curType.GetMethod("GetName");

        if(CurGenMethods.IsValid()) {
            CurGenMethods.ClassName = curType.FullName;
            if(CurGenMethods.GetNameMethod != null) {
                CurGenMethods.FriendlyName = GetProviderName(CurGenMethods);
            }
            cboClasses.Items.Add(CurGenMethods);
            bClassFound = true;
        }
    }
}

The GeneratorMethods class defined in the NetTemplateGenerator holds references to MethodInfo objects in the class; this class can be used later to invoke those methods to set and retrieve information on the template to be generated. The IsValid class checks the three MethodInfo objects to ensure that they conform to the expected interface. If so, the object is added to the Generator Class combobox, making it available to the application as a new template that may be generated.

If one or more acceptable classes are found in the assembly, you will notice that the Parameters panel is filled with replaceable parameters for the currently selected template (see Figure 10). The OnChange event of the combobox calls CreateTypeControls, which uses reflection and the GeneratorMethods class referenced by the currently selected combobox in order to invoke the QueryVars method of the selected class:

object AppInstance = GeneratorAssembly.CreateInstance(
    curMethods.ClassName);
GenClassTypesCol = (NameValueCollection)
    curMethods.QueryVarsMethod.Invoke(AppInstance, null);
This method returns a NameValueTypeCollection class that contains the names of the replaceable parameters and, optionally, default values for those parameters. The remainder of the method dynamically creates the TextBox controls for entry on the form.

The TypedHashtable template allows you to substitute values for the namespace, the type name of the class itself, and the type names of the values to be stored in the Hashtable and the associated keys. Since the types are being represented as strings, get in the habit of using the fully qualified name of the type to ensure it is represented correctly by the CodeDOM. In addition to replacing the type names with suitable values, you can modify the output parameters using the Class Output tab. Here, you select the language to output the source and a location to store the file—I've borrowed the GeneratorOptions group box and associated code from the HelloGen sample application.

Code for the class is generated in the GenerateCode method of the TemplateGenerator class. The method starts by creating an instance of the currently selected class of the loaded assembly and updating the GenClassTypesCol with the user-specified values. These values are then passed back to the generating class as an argument to SetVars, and the CodeCompileUnit is generated:

args[0] = GenClassTypesCol;
curMethods.SetVarsMethod.Invoke(AppInstance, args);
ccu = (CodeCompileUnit)curMethods.CCUGeneratorMethod.Invoke(
       AppInstance, null);

Using the CodeCompileUnit, the source code is generated as preview text and a text file, much the same as it was in the GeneratePreview method of the HelloGen sample:

codePreview = new StringWriter();

ntGenerator = GetLanguageGenerator(
    (Language)cboTargetLanguage.SelectedIndex);

ntGenerator.GenerateCodeFromCompileUnit(
    ccu, codePreview, ntGeneratorOptions);

codeFile = new StreamWriter(txtSourceDirectory.Text);
codeFile.Write(codePreview.ToString());
txtPreview.Text = codePreview.ToString();

Creating the Template

The TypedHashtableProvider project implements the CTHTGenerator, which is responsible for generating the CodeCompileUnit for the simple Hashtable template based on the user-selected parameters. The private member variable TemplateVariables is a NameValueCollection that stores this information and is passed between this assembly and the NetTemplateGenerator assembly via the QueryVariables and SetVariables functions:

public NameValueCollection QueryVariables() {
    return TemplateVariables;
}
public void SetVariables(NameValueCollection varCollection) {
    TemplateVariables = varCollection;
}

The CTHTGenerator constructor initializes this collection to values that describe the type expected, and the name for each item is used on the form to describe the parameters (see Figure 10).

Any class in an assembly designed for use by NetTemplateGenerator must implement three methods: QueryVariables, SetVariables, and GenerateCCU. QueryVariables simply returns the NameValueCollection of parameters to be set by the user. SetVariables takes one argument of type NameValueCollection, and this sets the internal collection to the values that the user selected. Finally, GenerateCCU generates the object graph using the user-supplied parameters and returns a CodeCompileUnit to the caller.

There is no magic involved in dynamically setting the types in the object graph. As you can see in Figure 12, I use the String overloads of the CodeDOM object constructors to set the type to the appropriate value from the NameValueCollection object.

Figure 12 Replacing Type Information in Templates (Close Figure 12)
CodeMemberProperty DefaultProperty = new CodeMemberProperty();
CodeExpression[] HashIndex =
    new CodeExpression[1] { new CodeArgumentReferenceExpression("Key") };

CodeIndexerExpression InternalDefaultReference =
    new CodeIndexerExpression(InternalHash, HashIndex);

DefaultProperty.Name = "Item";
DefaultProperty.Attributes = MemberAttributes.Public;
DefaultProperty.Parameters.Add(new
    CodeParameterDeclarationExpression(
    TemplateVariables[T_KEYTYPE], "Key"));
DefaultProperty.Type =
    new CodeTypeReference(TemplateVariables[T_VALUETYPE]);

// Get Statement
DefaultProperty.GetStatements.Add(new CodeMethodReturnStatement(
    new CodeCastExpression(TemplateVariables[T_VALUETYPE],
    InternalDefaultReference)));

// Set Statement
DefaultProperty.SetStatements.Add(
    new CodeAssignStatement(InternalDefaultReference,
    new CodePropertySetValueReferenceExpression()));

TypedHash.Members.Add(DefaultProperty);

I've chosen this section of code to illustrate the type replacement and to highlight the representation of default properties in the CodeDOM. The trick to creating this effect is giving the CodeMemberProperty object the special name "Item" and adding a single parameter to the Parameters collection. Aside from defining the default property, browsing the code download for the CTHTGenerator class will reveal that the template includes the following methods, which are a small subset of those available in the System.Collections.Hashtable class: Add, ContainsKey, ContainsValue, and Remove. There are a number of properties and methods that I have not added to the template, and you may want to experiment with the CodeDOM by implementing these.

Conclusion

The NetTemplateGen sample application serves as a good introductory example of generating template-based classes using the CodeDOM, but there are any number of extensions and improvements that could be made. A nice addition would be the ability to restrict the types that you could choose when setting individual parameters. For instance, it wouldn't make sense to generate a template class that used String variables as operands to mathematical functions. This could be done by passing the parameters and their values via XML, using XML Schemas to verify the types. Another excellent modification would be to create a Visual Studio .NET add-in that incorporated this functionality into the IDE.

For related articles see:
Generative Programming: Modern Techniques to Automate Repetitive Programming Tasks
Generics for C# and .NET CLR

For background information see:
CodeDOM Quick Reference
System.CodeDom Namespace

Adam J. Steinertis a consultant for Tara Software, a Division of Yahara Software LLC in Madison, WI. He can be reached at adams@tarasoftware.com.

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


BITS: Write Auto-Updating Apps with .NET and the Background Intelligent Transfer Service API

BITS

Write Auto-Updating Apps with .NET and the Background Intelligent Transfer Service API

Jason Clark
This article assumes you're familiar with C# and Visual Basic .NET
Level of Difficulty 1 2 3
Code download available at: BITS.exe (363KB)
SUMMARY

Both the .NET Framework and Windows have some very interesting APIs for creating applications that are capable of updating themselves automatically over a network. There are many advantages to writing your application to update itself like Windows Update does, including convenience for your users, from easier maintenance to network administration. Automatic updates require attention to factors such as discovery, security, and file replacement. In this article, the author covers the BITS API as well as a number of features of the .NET Framework that take care of these facets of auto-updating using the same facilities that the Windows Update uses.

Contents
The Challenge
BITS Basics
BITS, COM, and Managed Code
Using BITS with Auto-updating Apps
Some BITS Considerations
The .NET Framework and Security
Strong Names and Secure Binding
Update Packaging and Replacement
File Replacement and Extraction
The AutoUpdateApp.exe Sample App
The Future of Auto-updating Apps

I
have to admit, I love the Windows® Update feature. My computer is connected to the Internet about 85 percent of the time that it is turned on and yet, like most people, I certainly don't use the network that much. Windows XP takes advantage of this unused bandwidth by comparing the most recent service packs and hotfixes available online with those installed on my machine. If it finds I need updates, it downloads them in the background. Once completed, Windows notifies me of the arrival of new bits that need to be installed.

If I had my choice, every application on the client would allow auto-updating like Windows does. There are many benefits and the connectivity is in place now to make this a viable feature. If you're going to make your applications update themselves automatically, you'll have to write your code to handle discovery, downloading, security, and replacement.

For handling the actual downloading, I will cover a new feature of Windows called the Background Intelligent Transfer Service (BITS). Following that discussion, I will talk about features of the .NET Framework that let you solve the security and replacement problems for auto-updating applications.

Note that while the BITS 1.5 redistributable works fine on Windows 2000 and Windows XP, Microsoft does not plan to support the BITS API on Windows 9x or Windows Me. BITS is expected to ship as a component of future versions of Windows.

Before moving on, however, it is important that I point out that your application need not be managed under .NET to take advantage of the technologies covered in this article. BITS is part of the operating system itself, and the .NET Framework technologies covered here can be used for the auto-updating feature of your otherwise unmanaged application.

The Challenge

In order to find updates on a remote server, your application needs some means of querying the network. This requires networking code, as well as a simple protocol for the application and server to communicate over. I will come back to the topic of discovery later in this article.

Next you have to be able to download the bits. Downloading may not seem like much of a problem considering how simple networking is with .NET, but it's one thing to download files a user requested; it's another thing to download large files without the user's consent. The polite auto-updating application will download updates using only leftover bandwidth. This sounds great, but as you can see it poses a fairly difficult technical problem. Fortunately, a solution already exists.

Security is probably the most critical consideration. Think about the Windows Update feature for a moment. One of its primary purposes is to get security fixes. Imagine if Windows Update itself failed to verify that it was installing safe code. Obviously, any application that downloads code from the Internet and executes it must make security a top priority. Therefore, I will discuss how to make your auto-updating application secure.

The final consideration is the process of replacing your application with a new version of itself. This problem is an interesting one because it requires code to be able to run while removing itself from the system. There are a number of ways to manage this trick.

The great thing about these challenges is that between the .NET Framework and Windows itself, all the tools are in place right now to solve these problems.

BITS Basics

BITS is a cool new file transfer feature of Windows that asynchronously downloads files from a remote server over HTTP. BITS can manage multiple downloads from multiple users while making use of idle bandwidth exclusively. Although the use of BITS is not limited to auto-updating applications, it is the underlying API used by Windows Update. And since it is available to any application, it can be used to do much of the really tough work involved in creating an auto-updating application.

Here is the basic idea. An application asks BITS to manage the download of a file or set of files. BITS adds the job to its queue and associates the job with the user context under which the application is running. As long as the user is logged on, BITS will drizzle the files across the network using idle bandwidth. In fact, the code-name for the BITS technology is Drizzle, which, it turns out, is quite descriptive of what BITS does.

How does all of this work? The technology is actually fairly sophisticated. First, BITS is implemented as a Windows service that maintains a collection of jobs organized into a set of priority queues: foreground, high, normal, and low. Each job in the same priority level is given bandwidth via time slices of about five minutes, in a round-robin fashion. Once there are no jobs remaining in a queue, the next priority queue is inspected for jobs.

Jobs in the foreground queue use as much network bandwidth as they can, and for this reason the foreground priority should only be used by code that is responding to a user request. The remaining priorities—high, normal, and low—are much more interesting because they are all background priorities, which is to say that they only make use of network bandwidth that's not in use.

To achieve this background feature, BITS monitors network packets and disregards packets that it recognizes as its own. The remaining packets are considered the active load on the machine's bandwidth. BITS uses the active load information along with the connection speed and some other statistics to decide whether it should continue downloading files or back off in order to increase throughput for the active user. Because of this, the user doesn't experience bandwidth problems.

The ability to drop what it is doing at a moment's notice is very important for BITS. In many cases, only part of a file is downloaded before BITS must give up the network or even lose connection altogether. The partially downloaded file is saved, however, and when BITS gets another crack at the network, it picks up where it left off. This ability to recover does have some side effects.

Remember that BITS is used to transfer files from HTTP servers. A server should be HTTP 1.1-compliant or at least support the Range header in the GET method for BITS to work. This is because BITS needs to be able to request a portion of a file. In addition, the content being downloaded must be static content such as a markup file, code file, bitmap, or sound. A GET request including a Range header makes no sense when requesting dynamic content such as that produced by CGI, ISAPI, or ASP.NET.

Currently, there are two versions of BITS: 1.0 and 1.5. BITS 1.0 ships with Windows XP and has the following features: interruptible background downloading of files, download prioritization, optional notification of completed jobs and error situations, and optional progress notifications for use with dialog boxes and other UI elements. BITS 1.5 ships with Windows .NET Server. In addition to the features contained in BITS 1.0, version 1.5 has interruptible background uploading of files and authenticated connections using Basic, Digest, NTLM, Negotiate (Kerberos) or Passport. BITS 1.5 is available as a redistributable that is compatible with Windows 2000 and greater (see Background Intelligent Transfer Service).

The BITS 1.0 feature set is sufficient for writing auto-updating applications, but the BITS 1.5 features allow you to perform more complicated tasks like selling updates or handling interaction between the application and the server.

BITS, COM, and Managed Code

The BITS API is implemented as COM objects, and as of this writing a .NET Framework version of the API is not available. Fortunately, the BITS API is very straightforward and simple to use. The sample application included with this article is written in C#, so to use the BITS API I did have to mess around with the COM Interop features of the .NET Framework. Without going into too much detail about runtime callable wrappers (RCW) and such, I'll show you the process I followed to use the API.

If I had been using C++, my BITS code might have started out something like this:

IBackgroundCopyManager* pBCM = NULL;

hr = CoCreateInstance(__uuidof(BackgroundCopyManager), NULL,
         CLSCTX_LOCAL_SERVER, __uuidof(IBackgroundCopyManager),
         (void**) &pBCM);
if (SUCCEEDED(hr)) {
   // Party-on with the pBCM interface pointer
}

The C# equivalent creates the BackgroundCopyManager object using the new keyword and then gets a reference to the IBackgroundCopyManager interface through casting, rather than a call to a method such as CoCreateInstance or QueryInterface. The following code snippet is a C# example to acquire the IBackgroundCopyManager interface:

IBackgroundCopyManager bcm = null;
// Create BITS object
bcm = (IBackgroundCopyManager)new BackgroundCopyManager();

The simplicity of this code is somewhat misleading because more work needs to be done to associate the managed BackgroundCopyManager and IBackgroundCopyManager types with the underlying COM object and interface, respectively. The .NET Framework manages interop with COM objects through an RCW. To associate a managed type with an RCW, you must use attributes. The code in Figure 1 shows how to declare the BackgroundCopyManager class and IBackgroundCopyManager interface so that they represent the BITS COM objects.

Figure 1 Defining RCW Types (Close Figure 1)
[GuidAttribute("4991D34B-80A1-4291-83B6-3328366B9097")]
[ClassInterfaceAttribute(ClassInterfaceType.None)]
[ComImportAttribute()]
class BackgroundCopyManager{}

[InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
[GuidAttribute("5CE34C0D-0DC9-4C1F-897C-DAA1B78CEE7C")]
[ComImportAttribute()]
interface IBackgroundCopyManager {
   void CreateJob(
      [MarshalAs(UnmanagedType.LPWStr)] string DisplayName,
      BG_JOB_TYPE Type, out Guid pJobId,
      [MarshalAs(UnmanagedType.Interface)]
      out IBackgroundCopyJob ppJob);

   void GetJob(ref Guid jobID,
      [MarshalAs(UnmanagedType.Interface)]
      out IBackgroundCopyJob ppJob);

   void EnumJobs(uint dwFlags,
      [MarshalAs(UnmanagedType.Interface)]
      out IEnumBackgroundCopyJobs ppenum);

   void GetErrorDescription(
      [MarshalAs(UnmanagedType.Error)] int hResult, uint LanguageId,
      [MarshalAs(UnmanagedType.LPWStr)]
      out string pErrorDescription);
}

The code in Figure 1 is so riddled with attributes (ComImportAttribute, GuidAttribute, MarshalAsAttribute, and so on) that the real code seems obscured. In point of fact, there is no real code. The interface and class definition are little more than a marshaling placeholder in the form of metadata that the common language runtime (CLR) uses to call through to the BITS COM API.

The InteropBits.cs file included with the code sample (see the link at the top of this article) provides C# Interop code for all of the interfaces, enumerated types, and structures used with the BITS API. I included the full implementation even though the sample only makes use of a few methods because you'll find it useful if you decide to explore more features of the API. It is worth noting that the code in the InteropBits.cs file is not a managed API, but is rather an Interop API over the COM API implementation. Eventually Microsoft is expected to release an API that exposes BITS functionality to .NET Framework code in a way that is consistent with the rest of the .NET Framework Class Library.

The code in Figure 1, as well as the complete implementation in the InteropBits.cs sample file, was created by hand. The .NET Framework SDK ships with a tool called TlbImp.exe which will create and compile similar code for you into a managed assembly, if you have a TLB file describing the COM API that you are targeting. The BITS API does not ship with a TLB file; however, the Platform SDK does include an interface definition file called Bits.idl which describes the interfaces.

I used the MIDL.exe tool (which also ships with the Platform SDK) to create a TLB file from Bits.idl. I then used TlbImp.exe to create a managed assembly representing the TLB file. I used ILDasm.exe to disassemble the assembly produced by TlbImp.exe into intermediate language. Finally, I produced C# code using the intermediate language as a guideline, adjusting it in places to make it more usable and correct. This approach to COM Interop with C# is admittedly tedious, but it offers an exceptional degree of control over the eventual outcome.

Using BITS with Auto-updating Apps

The BITS service manages file downloads in terms of jobs. An application creates a transfer job and then adds one or more files to the job. Once the job's file list is established, the job is resumed because jobs start their lives in a suspended state. Jobs are used to manage details such as priority, authentication, and error management. At any time, a job can be cancelled by the application.

Once BITS has finished transferring all the files in a job, the application completes the job by calling a method. The Complete method copies all of the files to their final destinations. Although the BITS documentation refers to the job completion as "copying" files, in reality temporary hidden files are created in the destination directory, and the Complete method simply renames the hidden files and makes them viewable.

Even minimal use of the BITS API (using only the IBackgroundCopyManager and IBackgroundCopyJob interfaces) can achieve everything you need for an auto-updating application. If you need features such as job enumeration, or completion and error notification, then you will have to use additional interfaces.

The sample application, AutoUpdater.exe, uses BITS to achieve both update discovery and download. For this to work, the application defines a pattern for the successive names of updates: I chose Update1.dll, Update2.dll, and so on for the sample. Although this is by no means the only way of discovering updates, it works well with the BITS functionality. The sample application maintains an XML file for the purpose of storing update state. The two pertinent pieces of information in the file are the next update number and a GUID describing the current BITS download job, if there is one in progress.

Each time the application runs, it opens the XML file and checks to see if the next update has already been downloaded. If not, and there is no job GUID in the XML file, the application initiates a BITS job to download the next update in line. The following code shows the methods necessary to initiate a download job:

IBackgroundCopyJob job=null;

// Create a bits job to download the next expected update
bcm.CreateJob("Application Update",
   BG_JOB_TYPE.BG_JOB_TYPE_DOWNLOAD, out jobID, out job);

// Add the file to the job
job.AddFile(updateUrl, localLocation);
job.Resume(); // start the job in action

The call to job.AddFile passes the location of the file to be downloaded and the complete local path and file name where the final file should be stored upon job completion. Notice that the job starts in a suspended state and must be resumed before the BITS service will do any work with the job.

If there is a GUID in the XML file, then a BITS job has already been initiated by a previous launch of the auto-updating application. So the GUID is passed to the IBackgroundCopyManager.GetJob method to see if the previously arranged download job has produced results. The next step depends on the state of the job.

As you can see in Figure 2, if the job is in the error state, then the application completes the job (which clears the failed job from the queue) and then continues to create a new job for the same update file. The most likely reason for this error state is simply that an update with that name does not yet exist. This completes the discovery part of the sample application.

Figure 2 Checking Job State and Completing Jobs (Close Figure 2)
bcm.GetJob(ref jobID, out job); // Get the BITS job object
job.GetState(out state);        // check its state

switch(state){
case BG_JOB_STATE.BG_JOB_STATE_ERROR: // If it is an error
   job.Complete();
   xml.BitsJob = Guid.Empty;
   Marshal.ReleaseComObject(job);
   job = null;
   break; // continue on to create a new job
case BG_JOB_STATE.BG_JOB_STATE_TRANSFERRED: // If we got the file
   job.Complete();                          // then complete job
   xml.BitsJob = Guid.Empty;
   return; // all done, return
default:
   return;
}
... // Initiate a new job for update download

The great thing about using BITS to poll for updates is that BITS does its work in the background. So even though polling is not particularly elegant, the nonobtrusive nature of a BITS download makes it a very viable means of discovering updates.

If the job is in the transferred state, then the application completes the job by calling IBackgroundCopyJob.Complete. This causes the file to be put in the destination directory, making it ready for updating. Then the application returns because, at this point, the download piece of the puzzle has been solved.

Finally, in the default case in Figure 2 the sample application simply returns and ignores the job. The job could be in one of two states: the transferring state (which means that a job is in progress) or the transient error state. Both cases are considered to be potential success cases, so the application just leaves well enough alone. If a BITS job has been put in a transient error state, the BITS service thinks that the error may be recoverable, so it will try again. Eventually, a job that has been in transient error will either succeed or BITS puts the job in the error state.

Amazingly enough, aside from error recovery logic, this covers the whole discovery and downloading component of the AutoUpdater.exe sample application. Considering the advanced functionality of the download process, the implementation looks almost nothing like network communication and has more in common with a file copy. It is likely, however, that in a production application you will find a variety of uses for other features of the BITS API such as automatic transfer notifications, job priority manipulation, and upload jobs.

Some BITS Considerations

BITS is designed to never initiate a network connection. This is best because you don't want your non-connected application dialing up an ISP just to poll for an update. However, BITS 1.0 and 1.5 both suffer from a limitation with Internet connection sharing. If system A is sharing system B's Internet connection, an app that initiates a BITS job on system A may cause system B to initiate dial-up. This is because the current implementation of the BITS service does not take connection sharing into consideration.

This is a considerably tough problem to solve, but a fix may come with a future revision of BITS. The Windows Update feature is subject to this same problem. Your application that uses BITS will behave no differently than Windows does in terms of initiating dial-up on a shared connection.

A final concern with BITS is job file consistency. If your download job includes multiple files, BITS does not consider the job finished nor do the files arrive at their final destination until all the files are available. However, BITS has no way of knowing if a file on the server changes after other files in the job have already been downloaded. For this reason, updates made on the server can affect download consistency on the client.

There are two ways to manage this issue. The first is to limit BITS downloads to single-file jobs, which is the approach I take in the sample application. The second approach is to store all of the files for a single job in a single directory on the server. When an update is made to the files, create a new directory for the new set of files on the server. This way, clients that are in mid-transfer on an older job will not be affected by the new set of files. The problem with this solution is that the location of server-side files changes and the client needs a way to know where to find the new files.

When you find yourself needing to troubleshoot BITS code, there is no better helper than BitsAdmin.exe. This tool ships in the Support\Tools subdirectory of the Windows XP CD. To save time, rather than running the tools installation, I manually extracted the .exe from the Support.cab file.

The BitsAdmin.exe tool is very useful when you are first becoming familiar with BITS downloads. Nearly every feature of the API is exposed through switches on this command-line tool. You can also enumerate jobs, check job status, and view verbose job and error information. You can initiate, suspend, resume, and complete download jobs. Finally, you can change job priority and cancel jobs from BitsAdmin.exe.

It is rare that a Windows API is so accessible through a companion tool. In fact, scripts such as batch files can make complete use of BITS through the BitsAdmin.exe tool. BITS is a great solution for discovery and downloading updates, and for most applications the drawbacks are minimal. Now let's look at security.

The .NET Framework and Security

The .NET Framework introduces some very interesting security constructs for use in code. Code Access Security is one example. Another piece of security infrastructure is strong-name binding to managed assemblies. I chose to use this feature for more secure transfer of code updates from the server to the client. The problem here is that an auto-updating application is contacting a server across the Internet and downloading files, including code that it will install on the client. These new files must not be able to be manipulated by a malicious party somewhere on the wire between the client and the server.

One fix to this problem would be to conduct the entire exchange of data over an HTTPS connection. The BITS service is capable of targeting resources via the HTTPS protocol. HTTPS is just HTTP communication that occurs in an encrypted SSL/TLS channel. There are several drawbacks that are associated with this solution, however. The first is decreased performance.

Web servers excel at sending static content such as an HTML document or an EXE file. When Microsoft Internet Information Services (IIS) receives a request for a static file, it can make a single call to the TransmitFile Win32® API. TransmitFile copies the bits verbatim from the file system into the network stream at the driver level, providing excellent performance. The moment you introduce encryption through HTTPS, however, performance is drastically reduced because each byte of the file must be encrypted. This can increase the cost associated with update servers.

Another problem with using HTTPS is that it makes the Web server a very vulnerable node in the security infrastructure. If the Web server is compromised, then clients will blindly download and install the malicious content installed on the server.

Using a reasonably simple feature of the .NET Framework, however, you can achieve more secure transfer of update files without either of these drawbacks. First, an encrypted SSL/TLS channel is not necessary (so your Web server's throughput will not be affected). Second, with this solution, the big security threat from a compromised server is a denial of service to the client; the server will be prevented from running the malicious code on the client. This feature of the .NET Framework is known as strong-name binding and is part of the assembly loader.

Strong Names and Secure Binding

One of the longtime problems with DLLs which the .NET Framework has alleviated is the weak naming scheme of using only a file name to identify reusable code. This has led to all sorts of application problems that are collectively rolled into the term "DLL Hell." The solution is a strong name.

A strong name consists of four pieces of information: file name, version number, culture information, and a public key. The .NET Framework uses the strong name to enforce more strict binding between components. The framework also uses the public/private key pair to validate strongly named assemblies before loading. In the case of a strongly named assembly, the .NET Framework language compiler (C# or Visual Basic® .NET) builds the public key right into the DLL file. In addition to the public key, the compiler also hashes the contents of the file and encrypts it using the private key (which is tightly held by the build organization).

An encrypted hash is also known as a digital signature. To check the digital signature, the public key is extracted from the assembly file and used to decrypt the hash. Then the contents of the file are rehashed and the results are compared with the signature hash. If the contents of the assembly have changed in any way, the signature verification will fail because the hasher won't match.

If you guessed that using the public key in the assembly to confirm the very same assembly doesn't sound very secure, you're correct. However, if the code doing the verification had a way of making sure that the public key was a well known or expected public key, then you would have a more complete security solution. The .NET Framework does this by using a hash of the public key known as a public key token or originator.

When loading an assembly into your application, you can specify a public key token. The .NET Framework will only load the assembly if it is strongly named and if the public key of the strong name matches the public key token passed to the Assembly.Load method.

Now I'll walk through the process of strongly naming an assembly and loading it using an originator. The following C# code can be used to build a strongly named assembly. The assembly won't be able to do anything but demonstrate a strong name binding:

using System.Reflection;
[assembly:AssemblyKeyFile(@"Keys.snk")]

Before this code can be built, you must create the key file (Keys.snk) that contains the public/private key pair. You do this using the Sn.exe tool that ships with the .NET Framework SDK from the command line as follows:

>sn -k Keys.snk

Now you can compile the strongly named assembly into a .NET Framework assembly DLL using the C# compiler as follows:

>csc -t:library StrongName.cs
The resulting assembly will be named StrongName.dll. To discover the public key token that results from this strong name, you can use Sn.exe with the /T switch, as shown in Figure 3.

Figure 3 Extracting an Assembly's Public Key Token
Figure 3 Extracting an Assembly's Public Key Token

The example in Figure 3 shows a DLL whose public key token is d586e46fd39d13b5 in hexadecimal. To load this assembly with strong name verification, all you need is the following code:

// Load the update
String name = Path.GetFileNameWithoutExtension(patchName);
name = "StrongName.dll, PublicKeyToken=d586e46fd39d13b5");
Assembly update = Assembly.Load(name);
This code either verifies the signature and returns a reference to the assembly object or it fails the verification and the Assembly.Load method throws the FileLoadException. Either way, using Assembly.Load, the code is able to verify that the assembly has not been modified in the least since it was built.

An auto-updating application can download an update file using BITS without concern for server-side security. Then, before the update is installed and used on the client, the assembly can be verified using Assembly.Load. The only prerequisite is that the original application is installed with existing knowledge of a public/private key pair in terms of the public key token. Any future updates that are built using the matching key-pair can be verified on the client before they are installed and used. Meanwhile, the key-pair file, used for building updates, can be stored on a machine that is not even connected to a network.

This security mechanism is great, but it does raise a couple of questions. For example, can you update non-code files such as bitmaps and data files? Do you have to strong-name build all of the DLL and EXE files to use this mechanism? The answers to these questions are yes and no, respectively.

Update Packaging and Replacement

An auto-update solution wouldn't be that interesting to me if it didn't work for non-code files, or if it required all of my DLL and EXE files to be managed and strongly named. So I needed to come up with a way to use .NET Framework strong-named binding without constraining updates to .NET Framework assembly files. The answer is to embed the update files into an assembly file as assembly resources. This not only allows any file type to be protected by strong naming, but it also simplifies BITS job creation by making all update jobs single-file downloads. One big file is really a package of many files. In fact, the package could include CAB files or even MSI scripts—the possibilities are endless.

All of the .NET Framework language compilers let you embed arbitrary files into an assembly file. I will show you how to do it with the C# compiler, but the concept applies as easily to Visual Basic .NET or managed C++. The following command line will build the files Code.dll, Bitmap.gif, and Data.xml into an Update2.dll that includes each file as an embedded resource:

csc /res:Code.dll /res:Bitmap.gif /res:Data.xml /out:Update2.dll
    /t:library

Note that the C# compiler is flexible enough to build a DLL assembly without a .cs code file. The DLL contains nothing but embedded resources. However, if you want your DLL to include a strong name, you should also include a short .cs file that includes code similar to StrongName.cs.

The code necessary to discover embedded resources and copy them back into the file system is shown in Figure 4. The code makes use of the Assembly.GetManifestResourceName and Assembly.GetManifestResourceStream methods to find resources and copy bytes from them into files in the file system. The code in Figure 4 does not preserve the dates of the original files in the file system, but could be easily modified to do so. Note that the extraction process would only take place after Assembly.Load has verified the signature of the container assembly.

Figure 4 Extracting Assembly Resources (Close Figure 4)
Assembly update = Assembly.Load(...); // Load assembly

// Get resource names
String[] resources = update.GetManifestResourceNames();

// Enumerate resources
foreach(String s in resources){
   using(
      // Get stream the resource stream
      Stream res = update.GetManifestResourceStream(s),
      file = new FileStream(s, FileMode.CreateNew) // Create file
      ){
      Int32 pseudoByte;
      while((pseudoByte = res.ReadByte())!=-1){  // Copy the bytes
         file.WriteByte((Byte)pseudoByte);
      }
   }
}

File Replacement and Extraction

Extracting files into your application directory during an application update is a surprisingly nontrivial problem, even with strong names and assembly resources. The crux of the problem is that your application is executing while it is copying over its own files. To further complicate matters, the application you're running might be something like Notepad. With such applications, it is common for multiple instances to be running at the same time.

When you have such an application, a one-size-fits-all solution is nearly impossible. Rather than prescribing a single solution, it is best to look at the features that are available for managing the replacement problem. First, a simple possibility is to run your auto-update feature in a separate process from your main application. This is a particularly enticing approach when your application is unmanaged and you want to extend it to use the technologies covered in this article. The update process could handle all of the BITS logic as well as file extraction. The update process could even wait until all instances of the main application had terminated before attempting a file extraction.

There are three main disadvantages of running the extraction code in a separate process. First, it may make it difficult to patch the auto-update code itself. Second, this approach is less advantageous on the server side because the server is unlikely to shut down. Third, if your main process is managed by a Windows Job object (not to be confused with a BITS job), then when the main process exits, the auto-update process will be automatically terminated by Windows as well.

The AutoUpdateApp.exe sample application runs the auto-update logic in the same process as your main application logic. If you choose to do the same, you will run into the problem of deleting and replacing DLL and EXE files that are in use by the process. One little-publicized trick for deleting a DLL or EXE that is in use is to rename the file to a temporary file name, and then copy the new file over the old name. You can then use the MoveFileEx Win32 API to log the file for deletion when you reboot the system, or include logic in your application that will search for each of the temporary files and delete them.

Even when using the rename trick, you still have to consider the possibility that while an update is in progress the user will run another instance of your application and it will load with half of the old files and half of the new files. There are ways to address this possibility. One is to use the shadow copy feature of application domains in the CLR.

The CLR runs managed code in a logical application container called an application domain or AppDomain. Multiple AppDomains can run in a single Windows process. When an AppDomain is created, it is possible to mark the new domain to shadow-copy files. What this means is that as your application loads assemblies, the assembly files are copied into a hidden temporary directory and loaded from the shadowed location. This feature is used by the ASP.NET classes in the .NET Framework to get application files out of the way so that they can be updated by the ASP.NET code generator.

AppDomains are also useful for application reexecution. Ideally when an update is available, your application will prompt the user to see if they are interested in applying the update. After they say yes, the application should just run with the new features in place. For some applications, it is acceptable to relaunch the updated application in a new process. In other cases, it is preferable that the new code begin working right away in-process with the EXE that performed the update.

It is easy to use AppDomains for reexecution of a managed EXE. An example of code that does this can be found in the RelaunchExe method in the sample code included with this article (see the link at the top of this article). The RelaunchExe method creates a new AppDomain and then reexecutes the executable, passing it the same command-line arguments that were used when launching the application in the first place.

The AutoUpdateApp.exe Sample App

The code sample for this article not only includes auto-update logic, but also builds two updated versions of the application so that you can see the whole solution in action. Let's take a look at it now.

Even though Visual Studio® .NET is a powerful coding environment, there are times when you just have to create your own batch builds. My project is one example, a multiversioned build that was difficult to generate as a Visual Studio project. So I am publishing the sample as a collection of source files and a batch file to perform the build from the command line.

To try out the application, download the archive from the link at the top of this article and extract it into an empty directory. Then run Build.bat from the command line. For the batch file to succeed, it is necessary that csc.exe (the C# command-line compiler) and sn.exe (the strong-name utility) are both on the path environment variable. You will find csc.exe in the .NET Framework install in the following directory: C:\Windows\Microsoft.Net\Framework\. You will find the Sn.exe tool in the .NET Framework SDK directory which typically installs at one of the following two locations: C:\Program Files\Microsoft.NET or C:\Program Files\Microsoft Visual Studio .NET.

Once the project has been built using Build.bat, two important directories have been created: app and updates. Note that an sln file should appear so that you can easily edit the code files using Visual Studio .NET (you should continue to build the project using the batch file, however).

The first directory, app, contains the application install. Change the directory to the app directory and run AutoUpdateApp.exe to try it out. The actual functionality of the application is to display the first bitmap it finds in its launch directory (see Figure 5).

Figure 5 AutoUpdate Simple App
Figure 5 AutoUpdate Simple App

To see the update functionality in action, create an IIS virtual root directory called updates that grants anonymous download access. The goal is to make http://localhost/updates a working location for posting all of your updates. Then copy the contents of the second directory, updates, into your virtual root directory.

Assuming everything is set up properly, your application should now be able to find Update1.dll and Update2.dll at http://localhost/updates. From the command line, execute and shut down AutoUpdateApp.exe a couple of times. On about the second or third launch, the application should notify you that its first update is available to be applied. The first update just adds new GIF files to the directory. The second update actually installs a new copy of the AutoUpdateApp.exe, which will now have a feature that runs a slide show of all the GIF files in the launch directory.

The first time you launch AutoUpdateApp.exe the application creates a data file named UpdateState.xml. This file includes information that controls the functionality of the AutoUpdateApp.exe, including the network location where the application looks for updates. If you don't want to use a location on http://localhost/, you can change the URL in UpdateState.xml to use a different update server.

The Future of Auto-updating Apps

Today there are some great features available for writing automatically updating applications. That said, Microsoft developers are currently hard at work making auto-updating an even more simple and natural part of application development. Version 1.0 of the .NET Framework already provides some basic native functionality here with its no-touch deployment feature (see .NET Zero Deployment: Security and Versioning Models in the Windows Forms Engine Help You Create and Deploy Smart Clients). Ultimately, the easier it is for users to install and update the applications you write, the more successful your applications will be.

There are plans to introduce built-in services for update discovery and replacement in a future release of Windows. The BITS service will continue to be the underlying download mechanism, but additional API abstractions will be in place to simplify download and security. In addition to this, BITS will be extended to include an easy to use UI for managing application updates.

I look forward to the day when the line between launching and installing an application will be entirely blurred. But at least today I can write applications that, once installed, will update themselves. That's a pretty reasonable first step.

For related articles see:
Using Windows XP Background Intelligent Transfer Service (BITS) with Visual Studio .NET
.NET Interop: Get Ready for Microsoft .NET by Using Wrappers to Interact with COM-based Applications
Applied Microsoft .NET Framework Programming by Jeffrey Richter (Microsoft Press, 2002)

For background information see:
.NET and COM: The Complete Interoperability Guide by Adam Nathan (SAMS, 2002)

Jason Clarkprovides training and consulting for Microsoft and Wintellect (http://www.Wintellect.com) and is a former developer on the Windows NT and Windows 2000 Server team. He is the coauthor of Programming Server-Side Applications for Microsoft Windows 2000 (Microsoft Press, 2000). You can get in touch with Jason at JClark@Wintellect.com.


Printing: Preview and Print from Your Windows Forms App with the .NET Printing Namespace

Printing

Preview and Print from Your Windows Forms App with the .NET Printing Namespace

Alex Calvo
This article assumes you're familiar with C# and Windows Forms
Level of Difficulty 1 2 3
Code download available at: PrintinginNET.exe (134KB)
SUMMARY

Printing is an integral part of every complete Windows-based application. Providing robust printing capabilities in these applications has often proved to be a tedious chore. Now, printing from Windows Forms with the .NET Framework means you must adopt a document-centric approach, resulting in cleaner and more manageable code. While the System.Windows.Forms namespace provides seamless integration with all the standard print dialogs (such as Print Preview, Page Setup, and Print), the System.Drawing.Printing namespace offers numerous classes for extensibility and customization. These classes, and how they provide access to printing capabilities, are covered here. Other useful techniques, such as printing in the background to allow the user to continue other tasks, is also explained.

Contents
Using the PrintDocument Class
Implementing a Derived PrintDocument Class
Printing with GDI+
The PrintController Class
Using the Print Dialogs
Background Printing
Conclusion

M
icrosoft® .NET has changed just about everything from a development point of view. Some of these changes, such as Web Forms and ADO.NET, have required major shifts in how you get things done, while others have been more evolutionary in nature, merely improving upon existing technologies (such as System.Xml). For the traditional developer who uses Visual Basic® and Visual C++®, printing from Windows® Forms represents a major change. But, as is the case with so much of the .NET Framework, this change is definitely for the better.

Gone are the days of the Visual Basic Print object and its Printers collection. In the .NET Framework, there is no monolithic Print object and you won't find yourself setting CurrentX and CurrentY properties or issuing commands like EndDoc and NewPage either. If you're coming to .NET from Visual C++, you are probably aware that printing can be a tedious task. It requires, for example, that you carefully keep track of the printing process using the Win32® API, to ensure that pages are printed correctly. It's not so much that you don't have to do these kinds of things anymore. It's just that, with .NET, you end up with printing logic that is cleaner and much more maintainable. The .NET Framework classes allow your printing code to be fully encapsulated. Because you can derive your code from a set of base classes, you'll get all kinds of additional features for free. Hooking up to a Print Preview dialog is trivial in .NET, and that's just one example.

Most of the code examples for this article come from the sample printing application available for download at the link at the top of this article. This sample Windows Forms application, shown in Figure 1, demonstrates many of the new features and capabilities you'll have access to when printing in .NET. It allows you to choose any text document and send it to either the Print Preview dialog or to a specific printer. For demonstration purposes, I'll give you an option for selecting whether a watermark should be displayed on each page.

Figure 1 Windows Forms App
Figure 1 Windows Forms App

When printing using the Print Preview dialog, you can enable or disable anti-aliasing, a built-in feature that renders text and graphics on the screen with a smoother appearance. Keep in mind, however, that this incurs the cost of reduced output speed. In addition, the Print Preview dialog will automatically take advantage of any font smoothing provided by Windows (ClearType), thus reducing the need to use anti-aliasing. When sending output to the printer, the sample printing application lets you choose a variety of other options as well. Then you can decide whether or not to display a status dialog, show an animated printer in the status bar, or print in a background thread.

Let's take a tour of Windows Forms printing by first examining the quick and dirty way to send output to the printer. I will then look more closely at the right way to print from Windows Forms—using a derived PrintDocument class.

Using the PrintDocument Class

Printing from Windows Forms is a document-centric, event-driven process. The bulk of your effort will go into either using a generic PrintDocument object or implementing a derived PrintDocument class. Inheriting from the base PrintDocument class is the better way to go—for reasons which I'll go into shortly. Nevertheless, at times it may be quicker and simpler to use an instance of the base PrintDocument class.

Printing with a base PrintDocument class requires you to wire the class's PrintPage event to a handler method (static or instance) whose signature matches the PrintPageEventHandler delegate. This event will fire when your code calls the Print method on an instance of a PrintDocument object. To actually draw your page, you use the Graphics property of the PrintPageEventArgs object. An instance of a PrintPageEventArgs class is passed as an argument to the PrintPage event handler. The Graphics property of the PrintPageEventArgs object exposes a GDI+ object that encapsulates the drawing surface onto which you paint your page. (I'll cover some of the basic GDI+ commands later in the article.) To print more than one page, you notify the underlying print controller that you have more to print. You can do this using the HasMorePages property of the PrintPageEventArgs object. Setting the HasMorePages property to true will ensure that your PrintPage event handler gets called again.

In addition, you can set up event handlers for other common print events such as BeginPrint, EndPrint, and QueryPageSettings. BeginPrint is a good place to initialize any objects (such as Fonts) that your PrintPage routine may rely on. The QueryPageSettings event fires immediately before each PrintPage event. It allows you to print each page using different page settings, which you can do by modifying the QueryPageSettingsEventArgs.PageSettings property. In order to modify page settings for the entire document, you can use the DefaultPageSettings property of the PrintDocument class.

Here's an example that illustrates how to initiate a print job using the base PrintDocument class:

PrintDocument printDoc = new PrintDocument();
printDoc.PrintPage += new PrintPageEventHandler(this.printDoc_PrintPage);
printDoc.Print();

// The PrintPage event is raised for each page to be printed.
private void printDoc_PrintPage(object sender, PrintPageEventArgs e)
{
    // TODO: Print your page using the e.Graphics GDI+ object
    // Notify the PrintController whether there are any more pages
    e.HasMorePages = false;
}
As you can see, there are numerous drawbacks to this approach. The biggest drawback is that you must maintain state-aware objects between subsequent calls to the PrintPage event handler. For example, if you're printing a text document you will need to hold onto an open StreamReader object. You could initialize the StreamReader during the BeginPrint event and then close it during the EndPrint event. No matter how you slice it, however, the StreamReader variable will need to be scoped outside of the PrintPage event handler along with your other variables. When this happens, your printing code is exposed and vulnerable and can muddy up the rest of your code.

Implementing a Derived PrintDocument Class

When printing from Windows Forms, a better approach is to implement a class that inherits from the generic PrintDocument class. Doing so, you'll quickly reap the rewards of encapsulation. Instead of implementing event handlers for the BeginPrint, EndPrint, and PrintPage events, you override the OnBeginPrint, OnEndPrint, and OnPrintPage methods of the underlying PrintDocument base class. Any state-aware objects used by the OnPrintPage method can now be kept in private class fields. This completely does away with the potential code problems I just mentioned. In addition, you are now free to add custom properties, methods, events, and constructors to your derived PrintDocument class.

The sample printing application uses a derived PrintDocument class of type TextPrintDocument, as shown in Figure 2. The TextPrintDocument class exposes an overloaded constructor that takes a file name as an argument. Alternatively, the file name can be set and read using a custom FileToPrint property. This property throws an exception when set to a nonexistent file. This class also exposes a public Boolean field, named Watermark, which enables or disables a page background graphic. (The page background graphic is stored in the assembly as an embedded resource called Watermark.gif.) Finally, the derived TextPrintDocument class exposes a Font property that specifies which font is the correct one to use when rendering the page.

Figure 2 Derived PrintDocument Class (Close Figure 2)
using System;
using System.Drawing;
using System.Drawing.Printing;
using System.IO;
using System.Windows.Forms;

namespace PrintingDemo
{
    public class TextPrintDocument : PrintDocument
    {
        private Font printFont;
        private TextReader printStream;
        private string fileToPrint;
        private Bitmap imgWatermark;

        public bool Watermark = false;

        public TextPrintDocument()
        {
            imgWatermark = new Bitmap(GetType(), "Watermark.gif");
        }

        public TextPrintDocument(string fileName) : this()
        {
            this.FileToPrint = fileName;
        }

        public string FileToPrint
        {
            get
            {
                return fileToPrint;
            }
            set
            {
                if (File.Exists(value))
                {
                    fileToPrint = value;
                    this.DocumentName = value;
                }
                else
                    throw(new Exception("File not found."));
            }
        }

        public Font Font
        {
            get { return printFont; }
            set { printFont = value; }
        }

        protected override void OnBeginPrint(PrintEventArgs e)
        {
            base.OnBeginPrint(e);
            printFont = new Font("Verdana", 10);
            printStream = new StreamReader(fileToPrint);
        }

        protected override void OnEndPrint(PrintEventArgs e)
        {
            base.OnEndPrint(e);
            printFont.Dispose();
            printStream.Close();
        }

        protected override void OnPrintPage(PrintPageEventArgs e)
        {
            base.OnPrintPage(e);

            // Slow down printing for demo.
            System.Threading.Thread.Sleep(200);

            Graphics gdiPage = e.Graphics;
            float leftMargin = e.MarginBounds.Left;
            float topMargin = e.MarginBounds.Top;
            float lineHeight = printFont.GetHeight(gdiPage);
            float linesPerPage = e.MarginBounds.Height / lineHeight;
            int lineCount = 0;
            string lineText = null;

            // Watermark?
            if (this.Watermark)
            {
                int top = Math.Max(0,
                         (e.PageBounds.Height - imgWatermark.Height) / 2);
                int left = Math.Max(0,
                         (e.PageBounds.Width - imgWatermark.Width) / 2);
                gdiPage.DrawImage(imgWatermark, left, top,
                         imgWatermark.Width, imgWatermark.Height);
            }

            // Print each line of the file.
            while (lineCount < linesPerPage &&
                  ((lineText = printStream.ReadLine()) != null))
            {
                gdiPage.DrawString(lineText, printFont, Brushes.Black,
                leftMargin, (topMargin + (lineCount++ * lineHeight)));
            }

            // If more lines exist, print another page.
            if(lineText != null)
                e.HasMorePages = true;
            else
                e.HasMorePages = false;
        }
    }
}

The guts of the TextPrintDocument class can be found in the OnPrintPage method. This is where you paint your page using the GDI+ drawing surface provided by the PrintPageEventArgs Graphics property. In addition, the PrintPageEventArgs object contains the following properties: Cancel, HasMorePages, MarginBounds, PageBounds, and PageSettings. Cancel allows you to cancel the print job. The MarginBounds property returns a Rectangle object representing the portion of the page within the margins. You can use this rectangle to determine where to start and stop your printing on each page. PageBounds, on the other hand, represents the total area of the page, including the margins.

Printing with GDI+

The particulars of GDI+ would require an article in and of itself, so for the purposes of this article, I will only cover GDI+ from the perspective of how the TextPrintDocument class renders each page. I will then discuss some of the GDI+ calls you're most likely to make during the typical printing process.

First, the OnPrintPage method determines the height of the current font using the GetHeight method of the Font class. The GetHeight method can determine the dots per inch (dpi) of the current page by taking a Graphics object as an argument. Once the height of the current font is determined, the number of lines per page is calculated using the current MarginBounds.Height. Next, the text file is read, one line at a time, and printed using the DrawString method. If the end of the page is reached before the end of file, the HasMorePages property is set to true.

As you can see, basic printing can be accomplished simply by using DrawString. GDI+, however, arms you with more than 15 draw methods, each with numerous overloads. You can print using both vector graphics (such as DrawBezier, DrawEllipse) and raster graphics (such as DrawImage, DrawIcon). Notice how the OnPrintPage method uses DrawImage to display the watermark. You can also take advantage of the many advanced features of GDI+, such as clipping and transformations. Try doing that with the Visual Basic Print object!

The PrintController Class

Earlier, I mentioned how the sample printing application (shown in Figure 1) allows you to display an optional status dialog and/or animated status bar icon (the kind that spits out pages while printing). Both of these features are implemented using derived print controller classes. PrintController is an abstract class that is implemented by three different concrete classes within the .NET Framework: StandardPrintController, PrintControllerWithStatusDialog, and PreviewPrintController.

The print controller is responsible for how a print document is printed. The PrintDocument class exposes its underlying print controller as a property. Calling a print document's Print method triggers the underlying print controller's OnStartPrint, OnEndPrint, OnStartPage, and OnEndPage methods. Figure 3 shows the sequence of events that occur between the print document and the print controller. OnStartPage is the only method that returns a value. The return value is of type Graphics and, as you may have already guessed, is the GDI+ drawing surface that is passed to the print document via the PrintPageEventArgs argument.

Figure 3 Print Flow
Figure 3 Print Flow

The default print controller is of type PrintControllerWithStatusDialog. So, if you want to turn off the print status dialog, you'll need to use the StandardPrintController. The PreviewPrintController is used by the PrintPreviewDialog and PrintPreviewControl classes. PrintControllerWithStatusDialog can be found in the System.Windows.Forms namespace, while StandardPrintController and PreviewPrintController are located under the System.Drawing.Printing namespace. The PrintControllerWithStatusDialog provides an overloaded constructor that takes another print controller as an argument. This allows you to combine the PrintControllerWithStatusDialog with any additional features that you might add to your own print controller. When running the sample printing application, try checking both the PrintControllerWithStatusDialog and PrintControllerWithStatusBar checkboxes to see this in action. Here is a snippet of how the code works:

   CustomPrintDocument printDoc = new
      CustomPrintDocument();
   CustomPrintController printCtl = new
      CustomPrintController();
   printDoc.PrintController = new
      PrintControllerWithStatusDialog(
         printCtl, "Dialog Caption");
   printDoc.Print();

The sample printing application uses a custom print controller of type PrintControllerWithStatusBar (see Figure 4). PrintControllerWithStatusBar exposes a StatusBarPanel property that determines which status bar panel should display the animated printer icon. I used a timer of type System.Timers.Timer in order to do the actual animation. The System.Timers.Timer class works very well in a multithreaded application, as is the case when doing background printing.

Figure 4 PrintControllerWithStatusBar (Close Figure 4)
using System;
using System.Drawing;
using System.Drawing.Printing;
using System.Timers;
using System.Windows.Forms;

namespace PrintingDemo
{
    public class PrintControllerWithStatusBar : StandardPrintController
    {
        private int iCurrentPage;
        private int iCurrentIconFrame;
        private string strPriorStatus;
        private Icon icoPriorIcon;
        private Icon[] icoPrinters;
        private StatusBarPanel statusBarPanel;
        private System.Timers.Timer tmrIconAnimation;

        public bool ShowPrinterIcon = false;

        public PrintControllerWithStatusBar()
        {
            Icon icoPrinter1 = new Icon(GetType(), "Printer1.ico");
            Icon icoPrinter2 = new Icon(GetType(), "Printer2.ico");
            Icon icoPrinter3 = new Icon(GetType(), "Printer3.ico");

            icoPrinters = new Icon[3];
            icoPrinters[0] = icoPrinter1;
            icoPrinters[1] = icoPrinter2;
            icoPrinters[2] = icoPrinter3;

            tmrIconAnimation = new System.Timers.Timer();
            tmrIconAnimation.Enabled = false;
            tmrIconAnimation.Interval = 200;
            tmrIconAnimation.Elapsed += new
                             ElapsedEventHandler(tmrIconAnimation_Elapsed);
        }

        public PrintControllerWithStatusBar(StatusBarPanel sbp) : this()
        {
            statusBarPanel = sbp;
        }

        private void tmrIconAnimation_Elapsed(object sender,
                                              ElapsedEventArgs e)
        {
            if (statusBarPanel != null)
            {
                // Animate printer icon...
                statusBarPanel.Icon = icoPrinters[iCurrentIconFrame++];
                if (iCurrentIconFrame > 2)
                    iCurrentIconFrame = 0;
            }
        }

        public StatusBarPanel StatusBarPanel
        {
            get { return statusBarPanel; }
            set { statusBarPanel = value; }
        }

        public override void OnStartPrint(PrintDocument printDoc,
                                          PrintEventArgs e)
        {
            iCurrentPage = 1;
            iCurrentIconFrame = 0;
            if (statusBarPanel != null)
            {
                // Save prior settings...
                strPriorStatus = statusBarPanel.Text;
                icoPriorIcon = statusBarPanel.Icon;
                statusBarPanel.Text = "Printing...";
            }
            if (this.ShowPrinterIcon)
            {
                tmrIconAnimation.Start();
            }
            base.OnStartPrint(printDoc, e);
        }

        public override Graphics OnStartPage(PrintDocument printDoc,
                                             PrintPageEventArgs e)
        {
            if (statusBarPanel != null)
            {
                statusBarPanel.Text = "Printing page " + iCurrentPage++;
            }
            return base.OnStartPage(printDoc, e);
        }

        public override void OnEndPage(PrintDocument printDoc,
                                       PrintPageEventArgs e)
        {
            base.OnEndPage(printDoc, e);
        }

        public override void OnEndPrint(PrintDocument printDoc,
                                        PrintEventArgs e)
        {
            tmrIconAnimation.Stop();

            // DoEvents is required here, when not printing in a
            // background thread. It allows any pending messages to be
            // processed prior to restoring the StatusBar back to its
            // original settings.
            Application.DoEvents();

            if (statusBarPanel != null)
            {
                // Restore original panel settings...
                statusBarPanel.Icon = icoPriorIcon;
                statusBarPanel.Text = strPriorStatus;
            }
            base.OnEndPrint(printDoc, e);
        }
    }
}

Using the Print Dialogs

The beauty of printing in .NET is the way that the print document so elegantly fits together with the print dialogs. There are three different print dialogs in the System.Windows.Forms namespace: PrintDialog, PrintPreviewDialog, and PageSetupDialog. In addition, there is a PrintPreviewControl class that doesn't include the surrounding dialog, providing you with greater UI design flexibility. For simplicity, the sample printing application uses the PrintPreviewDialog class (see Figure 5).

Figure 5 Print Preview
Figure 5 Print Preview

The PrintDialog class can be used to display the standard Print dialog in Windows and gives the user the ability to select a printer, specify which pages to print, and determine the number of copies. Using it is as simple as this:

CustomPrintDocument printDoc = new CustomPrintDocument();
PrintDialog dlgPrint = new PrintDialog();

dlgPrint.Document = printDoc;

if (dlgPrint.ShowDialog() == DialogResult.OK)
{
    printDoc.Print();
}

Using the PrintPreviewDialog class is even easier. I found the Print Preview dialog to be extremely useful during the development process. It can save you a lot of paper when debugging your print documents. Here's how it works:

CustomPrintDocument printDoc = new CustomPrintDocument();
PrintPreviewDialog dlgPrintPreview = new PrintPreviewDialog();
// Set any optional properties of dlgPrintPreview here...
dlgPrintPreview.Document = printDoc;
dlgPrintPreview.ShowDialog();
After you create an instance of the PrintPreviewDialog class, you set its Document property to an instance of any class that derives from PrintDocument. The ShowDialog method of the PrintPreviewClass will automatically take care of rendering a print preview of your document (see Figure 5). As you know, it doesn't get much simpler than that.

The PageSetupDialog class works in a similar fashion. However, in addition to supporting a Document property, you can opt to set the PageSettings or PrinterSettings property to an instance of a PageSettings or PrinterSettings class. The PageSettings class defines settings that apply to the actual printed page, such as Margins and PaperSize, while the PrinterSettings class specifies information about how a document is printed, such as FromPage, ToPage, and PrinterName. The PrinterSettings class also allows you to obtain a list of available printers using the InstalledPrinters static method, which returns a PrinterSettings.StringCollection object. Upon returning from the Page Setup dialog, the Document, PageSettings, or PrinterSettings object will be modified accordingly.

Background Printing

Threading is a tricky thing, and by no means do I intend to deny the tendency for issues to arise when writing a multithreaded application. The truth is, though, that the use of a background thread really improves the print experience for users. It allows your users to continue to use the rest of your application while the print job is processing in the background. To experiment with background printing, it's best to use a large document and to make sure that your printer is paused. Why waste the paper? You can also slow down printing using the static Sleep method of the Thread class. The sample printing application lets you try this out when sending output to the printer. It doesn't make sense to use background printing with the Print Preview dialog. In a nutshell, here is how it works:

private void cmdBackgroundPrint_Click(object sender, System.EventArgs e)
{
    Thread t = new Thread(new ThreadStart(PrintInBackground));
    t.IsBackground = true;
    t.Start();
}

private void PrintInBackground()
{
    printDoc.Print();
}

You can also use a delegate to accomplish the same thing. Initially, when I wrote the sample printing application, I used the Thread class. However, be-cause I wanted to be informed when the printing was complete, I decided to use a delegate instead. Using a delegate's BeginInvoke method allows you to specify a callback function so that your code can be notified when an asynchronous operation has completed. I needed to do this in order to re-enable the Print button in a thread-safe manner after printing was completed (see Figure 6).

Figure 6 Sample Printing Application Code (Close Figure 6)
using System;
using System.Data;
using System.Drawing;
using System.Drawing.Printing;
using System.ComponentModel;
using System.Threading;
using System.Windows.Forms;

namespace PrintingDemo
{
  public class PrintDialog : System.Windows.Forms.Form
  {
    private System.Windows.Forms.Button cmdPrint;
    private System.Windows.Forms.Label lblFile;
    private System.Windows.Forms.TextBox txtFileName;
    private System.Windows.Forms.Button cmdBrowse;
    private System.Windows.Forms.OpenFileDialog dlgOpenFile;
    private System.Windows.Forms.PrintPreviewDialog dlgPrintPreview;
    private System.ComponentModel.Container components = null;

    private System.Windows.Forms.PrintDialog dlgPrint;
    private System.Windows.Forms.CheckBox chkBackgroundThread;
    private System.Windows.Forms.RadioButton optPrinter;
    private System.Windows.Forms.RadioButton optPrintPreview;

    private System.Windows.Forms.Button cmdPageSettings;
    private System.Windows.Forms.PageSetupDialog dlgPageSettings;
    private System.Windows.Forms.CheckBox chkWatermark;
    private System.Windows.Forms.GroupBox fraSettings;
    private System.Windows.Forms.CheckBox chkAntiAlias;
    private System.Windows.Forms.StatusBar sbStatus;
    private System.Windows.Forms.StatusBarPanel simpleTextPanel;
    private System.Windows.Forms.CheckBox chkPrintControllerWithStatusBar;
    private System.Windows.Forms.CheckBox
        chkPrintControllerWithStatusDialog;
    private TextPrintDocument printDoc = new TextPrintDocument();

    delegate void PrintInBackgroundDelegate();

    public PrintDialog()
    {
      InitializeComponent();
      dlgPrintPreview.Icon = this.Icon;
    }

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

    #region Windows Form Designer generated code

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

    private void cmdBrowse_Click(object sender, System.EventArgs e)
    {
      if (dlgOpenFile.ShowDialog(this) == DialogResult.OK)
          txtFileName.Text = dlgOpenFile.FileName.ToString();
    }

    private void optPrintPreview_CheckedChanged(object sender,
                                                System.EventArgs e)
    {
      EnableDisableCheckBoxes();
    }

    private void optPrinter_CheckedChanged(object sender,
                                           System.EventArgs e)
    {
      EnableDisableCheckBoxes();
    }

    private void EnableDisableCheckBoxes()
    {
      chkAntiAlias.Enabled = optPrintPreview.Checked;
      chkPrintControllerWithStatusDialog.Enabled = optPrinter.Checked;
      chkPrintControllerWithStatusBar.Enabled = optPrinter.Checked;
      chkBackgroundThread.Enabled = optPrinter.Checked;
    }

    private void cmdPageSettings_Click(object sender, System.EventArgs e)
    {
      dlgPageSettings.Document = this.printDoc;
      dlgPageSettings.ShowDialog(this);
    }

    private void cmdPrint_Click(object sender, System.EventArgs e)
    {
      try
      {
        printDoc.FileToPrint = txtFileName.Text;
        printDoc.Watermark = chkWatermark.Checked;

        if (optPrintPreview.Checked)
        {
          dlgPrintPreview.UseAntiAlias = chkAntiAlias.Checked;
          dlgPrintPreview.Document = printDoc;
          dlgPrintPreview.ShowDialog(this);
        }
        else if (optPrinter.Checked)
        {
          dlgPrint.Document = printDoc;

          if (dlgPrint.ShowDialog(this) == DialogResult.OK)
          {
            this.Refresh();

            PrintController printController;

            if (chkPrintControllerWithStatusBar.Checked)
            {
              printController = new PrintControllerWithStatusBar();
              ((PrintControllerWithStatusBar) printController)
                  .StatusBarPanel = sbStatus.Panels[0];
              ((PrintControllerWithStatusBar) printController)
                  .ShowPrinterIcon = true;
            }
            else
              printController = new StandardPrintController();

            if (chkPrintControllerWithStatusDialog.Checked)
              printDoc.PrintController = new
                  PrintControllerWithStatusDialog(
                  printController, "Please wait...");
            else
              printDoc.PrintController = printController;

            if (chkBackgroundThread.Checked)
            {
              cmdPrint.Enabled = false;
              cmdPageSettings.Enabled = false;
              fraSettings.Enabled = false;

              PrintInBackgroundDelegate d = new
                  PrintInBackgroundDelegate(PrintInBackground);
              d.BeginInvoke(new AsyncCallback(PrintInBackgroundComplete),
                            null);
            }
            else
            {
              this.Cursor = Cursors.WaitCursor;
              printDoc.Print();
              this.Cursor = Cursors.Default;
            }
          }
        }
      }
      catch (Exception ex)
      {
        MessageBox.Show(ex.Message, Application.ProductName,
                        MessageBoxButtons.OK, MessageBoxIcon.Error);
      }
    }

    private void PrintInBackground()
    {
      try
      {
        printDoc.Print();
      }
      catch (Exception e)
      {
        MessageBox.Show(e.Message, Application.ProductName,
                        MessageBoxButtons.OK, MessageBoxIcon.Error);
      }
    }

    private void PrintInBackgroundComplete(IAsyncResult r)
    {
      cmdPrint.Enabled = true;
      cmdPageSettings.Enabled = true;
      fraSettings.Enabled = true;
    }
  }
}

Figure 6 contains most of the code used to drive the sample printing application. In particular, it contains a delegate called PrintInBackgroundDelegate. The delegate's BeginInvoke method is called inside the Print button's Click event handler if the Background thread checkbox is checked. Otherwise, the print document's Print method is called directly from the main UI thread.

Any pitfalls you may encounter will most likely involve state-aware objects your print document class may rely upon. For instance, using the prior example, what happens if the user clicks cmdBackgroundPrint more than once? The results would be unpredictable at best. Because the PrintDocument.PrintPage event will often rely on state-aware class fields, things can get really messy in a case like this. An easy way to deal with these types of issues is by simply disabling some UI controls to prevent the user from reissuing the same command. In the meantime, they can continue to use the rest of the app. This is the approach the sample printing app uses. Before you tackle background printing, I recommend that you read Ian Griffiths' article on page 68 in this issue.

Conclusion

While this article covered native Windows Forms printing in .NET, there are other printer output methods available for .NET, such as Crystal Reports. In certain situations, a reporting tool may be more appropriate than native printing. However, if you don't want the overhead of an off-the-shelf product and your application requires custom or proprietary printing capabilities—like those found in many desktop Windows-based apps—native printing is definitely the way to go.

For related articles see:
Safe, Simple Multithreading in Windows Forms
http://samples.gotdotnet.com/quickstart/winforms

Alex Calvois an MCSD and MCAD for Microsoft .NET. He is the co-founder of Developer Box LLC (http://www.developerbox.com), a Conn. consulting company specializing in .NET, Visual Basic, COM, and SQL Server. You can reach Alex at acalvo@hotmail.com.


Windows Forms: Give Your .NET-based Application a Fast and Responsive UI with Multiple Threads

New information has been added to this article since publication.
Refer to the Editor's Update below.

Windows Forms

Give Your .NET-based Application a Fast and Responsive UI with Multiple Threads

Ian Griffiths
This article assumes you're familiar with .NET, C#, and Windows Forms
Level of Difficulty 1 2 3
Code download available at: Multithreading.exe (106KB)
SUMMARY

If your application performs any non-UI processing on the thread that controls the user interface, it may make the app seem slow and sluggish, frustrating users. But writing multithreaded apps for Windows has long been restricted to C++ developers. Now with the .NET Framework, you can take advantage of multiple threads in C# to control the flow of instructions in your programs and isolate the UI thread for a fast and snappy user interface. This article shows you how. Also, it discusses the pitfalls of multiple threads and presents a framework for safe concurrent thread execution.

Contents
Why Multiple Threads?
Asynchronous Delegate Invocation
Threads and Controls
Calling a Control on the Right Thread
Wrapping Control.Invoke
Locking
Deadlock
Keep it Simple
Cancellation
Program Shutdown
Error Handling
Conclusion

U
sers hate slow programs. The more sluggish a program is, the less a user will like it. Judicious use of multiple threads can improve the responsiveness of a program's UI when performing lengthy operations, making everything seem faster. Multithreaded programming in Windows® was once the domain of C++ developers, but now it is available in all Microsoft® .NET-compliant languages, including Visual Basic® .NET. Yet Windows Forms imposes some significant restrictions on the use of threads. This article explains those restrictions and how to work with them to provide a snappy, high-quality UI experience even when the program is performing tasks that are inherently slow.

Why Multiple Threads?

Multithreaded programs are much harder to write than single-threaded ones, and the indiscriminate use of threads is a big reason subtle bugs can take weeks to locate. This raises two obvious questions: why not stick to single-threaded code? And if you must use multiple threads, how can you avoid the pitfalls? Most of this article answers the second question, but first I will explain why you really need multiple threads.

Multithreading can enable you to keep the UI responsive by ensuring that the program never "goes to sleep" on you. Most programs have moments where they pay no attention to the user: they are too busy doing some work on your behalf to respond to further requests. Perhaps the most widely known example of this is the combobox that appears at the top of the Open File dialog. If you happen to have a disk in your CD-ROM drive when you expand this combobox, the computer will often spin this disk before showing the list. This can take several seconds, during which time the program will not respond to any input nor let you cancel the operation, which is particularly infuriating if you didn't actually plan to use the CD-ROM.

The reason that the UI freezes during such operations is that in a single-threaded program, it is not possible for the single thread to wait for the CD-ROM drive to spin up while simultaneously handling incoming user input, as Figure 1 illustrates. The Open File dialog will be calling certain blocking APIs to determine the title of the CD-ROM. A blocking API is one which does not return until it has completed its work and therefore prevents the thread from doing anything else in the meantime.

Figure 1 Single Thread
Figure 1 Single Thread

With multithreading, long-winded tasks like this can be run on their own thread, often called a worker thread. Blocking operations no longer cause the user interface to freeze because only the worker thread is blocked, as you can see in Figure 2. The application's main thread can continue dealing with the user's mouse and keyboard input while the other thread is blocked waiting for the CD-ROM to spin up, or for whatever else the worker thread may be doing.

Figure 2 Multiple Threads
Figure 2 Multiple Threads

The general principle is that the thread that's responsible for responding to the user and keeping the user interface up to date (usually referred to as the UI thread) should never be used to perform any lengthy operation. As a rule of thumb, anything that could take longer than about 30ms is a candidate for removal from the UI thread. This is a little aggressive—30ms is about the shortest interval that most people will perceive as being anything other than instantaneous and it's actually slightly less than the interval between successive frames shown on a movie screen.

A delay of more than 30ms between a mouse click and the corresponding UI cue (such as a button being redrawn) will be perceptible as a slight disconnection between the action and the display and will be disconcerting in the way that loss of lip-sync on film can be. For absolute top-quality responsiveness, 30ms has to be the limit. If, on the other hand, you really don't mind a slight feeling of disconnection but you don't want a hiatus to be long enough to actively annoy the user, then 100ms is widely thought to be about as much as you can reasonably get away with.

What this means is that any blocking operation that either has to wait for something mechanical to happen (waiting for the CD-ROM to start or the hard disk to locate the data, for example) or for a response to come in over the network should be performed on a worker thread if you want the user interface to remain responsive.

Asynchronous Delegate Invocation

The simplest way to run code on a worker thread is to use asynchronous delegate invocation (all delegates provide this facility). Delegates are normally invoked synchronously—that is, when you call through a delegate, the call does not return until the wrapped method returns. To invoke a delegate asynchronously, call the BeginInvoke method, which will queue the method to be run on a thread from the system thread pool. The calling thread returns immediately without waiting for the method to complete. This makes it ideal for UI programs because you can use it in order to launch lengthy jobs without slowing down the user interface.

For example, in the following code, the System.Windows.Forms.MethodInvoker type is a system-defined delegate for calling methods that take no parameters.

private void StartSomeWorkFromUIThread () {
    // The work we want to do is too slow for the UI
    // thread, so let's farm it out to a worker thread.

    MethodInvoker mi = new MethodInvoker(
        RunsOnWorkerThread);
    mi.BeginInvoke(null, null); // This will not block.
}

// The slow work is done here, on a thread
// from the system thread pool.
private void RunsOnWorkerThread() {
    DoSomethingSlow();
}

If you wanted to pass parameters, you would either select an appropriate system-defined delegate type or define your own. There is nothing magic about the MethodInvoker delegate. As with any delegate, calling BeginInvoke will cause the method to be run on a thread from the system thread pool, leaving the UI thread free to do other work. In this case the method returns no data, so you can just launch it and let it do its stuff. If you cared about any of the results from the method, then the return value from BeginInvoke would have been significant, and you would probably not be passing null parameters. However, for most UI applications, this "launch and leave" style turns out to be the most effective for reasons that will be discussed shortly. You should note that BeginInvoke returns an IAsyncResult. This can be used in conjunction with the delegate's EndInvoke method to retrieve the results of the method call once it has completed.

Other techniques for running methods on other threads are available, such as using the thread pool API directly or creating your own thread. For the majority of user interface applications, however, asynchronous delegate invocation will suffice. Not only does this make for easier code, it also avoids creating more threads than is strictly necessary because you will be using the shared threads in the thread pool, improving your application's overall performance.

Threads and Controls

The Windows Forms architecture has strict rules about the use of threads. If you are writing only single-threaded app, it is safe to know absolutely nothing about these rules because it is not possible for single-threaded code to break them. As soon as you introduce multiple threads, however, you need to understand the single most important rule of threading in Windows Forms: with very few exceptions, you must never use any member of a Control on any thread other than the one that created it.

There are documented exceptions to this rule, but only a few. This applies to any object whose class derives from System.Windows.Forms.Control, which includes more or less everything in the UI. All UI elements, including forms themselves, are objects that derive from the Control class. Furthermore, the upshot of this rule is that a contained Control, such as a button contained by a form, must live on the same thread as its containing control. That is, all of the controls on a window belong to the same UI thread. In practice, most Windows Forms applications end up having a single thread on which all UI activity happens. This thread is usually referred to as the UI thread. This means that you cannot call any methods on anything in the user interface unless the documentation for that method says that it's OK. The exceptions to the rule, which are always documented, are few and far between. Note that the following code is illegal:

// Created on UI thread
private Label lblStatus;
•••
// Doesn't run on UI thread
private void RunsOnWorkerThread() {
    DoSomethingSlow();
    lblStatus.Text = "Finished!";    // BAD!!
}

If you try this with version 1.0 of the .NET Framework, you will probably get away with it—or so it will seem at first. This is the main problem with multithreading bugs—they tend not to show themselves immediately. Even when you do something wrong, everything appears to work right up until the first time you demo your program. But make no mistake—the code I just showed you definitely breaks the rules, and anyone relying on the "well, it worked when I tried it" style of proof can look forward to some painful debugging sessions in the near future.

Be aware that this problem can arise without your ever having created a thread explicitly. Any code which uses a delegate's asynchronous invocation facility (calling its BeginInvoke method) can run into the same problem. Delegates provide a very attractive solution for dealing with slow, blocking operations in UI applications because they make it very easy for you to get such work off the UI thread without having to create a new thread yourself. But since code run with asynchronous delegate invocation will run on a thread from the thread pool, it cannot access any UI elements. The same restrictions apply to both threads in the thread pool and to manually created worker threads.

Calling a Control on the Right Thread

The restrictions on controls seem disastrous for multithreaded programs. How can some slow operation running on a worker thread keep the user informed of its progress if it can't do anything to the UI? Indeed, how will the user even find out when the work is complete or if an error occurs? Fortunately, although this restriction is inconvenient, it is not insurmountable. There are a variety of ways to get a message from a worker thread to the UI thread. In theory, you could produce your own mechanism using low-level synchronization primitives and polling, but fortunately there is no need to resort to this type of low-level work because a solution exists in the form of the Control class's Invoke method.

The Invoke method is one of the Control class's few documented exceptions to the threading rule: it is always OK to call Invoke on a Control from any thread. The Invoke method itself simply takes a delegate and an optional parameter list and calls the delegate for you on the UI thread, regardless of which thread issued the Invoke call. In fact, it is simple to get any method to run on the right thread for the control. You should note, though, that this mechanism only works if the UI thread is not currently blocked—invocations can only get through when the UI thread is ready to handle user input. This is another good reason never to block the UI thread. The Invoke method tests to see whether the calling thread is the UI thread. If so, it calls the delegate directly. Otherwise, it arranges for a thread switch and calls the delegate on the UI thread. In either case, the method wrapped by the delegate will run on the UI thread, and Invoke will not return until the method has completed.

The Control class also supplies an asynchronous version of Invoke, which returns immediately and arranges for the method to run on the UI thread at some point in the future. This is called BeginInvoke and looks very similar to asynchronous delegate invocation, the obvious difference being that with a delegate, the call runs asynchronously on some thread in the thread pool, whereas here it runs asynchronously on the UI thread. In fact, the Control's Invoke, BeginInvoke, and EndInvoke methods, as well as the InvokeRequired property, are all members of the ISynchronizeInvoke interface. This interface can be implemented by any class that needs to control the way events are delivered to it.

You should use BeginInvoke rather than Invoke wherever possible because it is less prone to deadlock. Because Invoke is synchronous, it will block the worker thread until the UI thread becomes available. But what if the UI thread was waiting for the worker thread to do something? The application will deadlock. BeginInvoke avoids this since it never waits for the UI thread.

Now I'll review a legal version of the code snippet I presented earlier. First, I must pass a delegate to the Control's BeginInvoke method so that I can run my thread-sensitive code on the UI thread. This means placing that code in its own method, as shown in Figure 3. Once the worker thread has finished the slow work, it calls BeginInvoke on the Label in order to run some code on its UI thread. From there, it can update the user interface.

Figure 3 Proper Thread Use (Close Figure 3)
// Created on UI thread
private Label lblStatus;
•••
// Doesn't run on UI thread
private void RunsOnWorkerThread() {
    DoSomethingSlow();
    // Do UI update on UI thread
    object[] pList = { this, System.EventArgs.Empty };
    lblStatus.BeginInvoke(
      new System.EventHandler(UpdateUI), pList);
}
•••
// Code to be run back on the UI thread
// (using System.EventHandler signature
// so we don't need to define a new
// delegate type here)
private void UpdateUI(object o, System.EventArgs e) {
    // Now OK - this method will be called via
    // Control.Invoke, so we are allowed to do
    // things to the UI.
    lblStatus.Text = "Finished!";
}

Wrapping Control.Invoke

Although the code in Figure 3 solves the problem, it is rather verbose. If the worker thread wanted to provide more feedback than the single Finished! message at the end, the extra complexity of using BeginInvoke would start to become more obtrusive. You would need to deal with getting a parameter to the UpdateUI function in order to accommodate other messages such as "I'm working on it," "Be right with you," and so on. You might also want to improve the feedback by adding a progress bar. The complexity of having so many calls to BeginInvoke might cause this code to dominate the worker thread. Not only is this inconvenient, it's also bad design for the worker thread to concern itself with UI minutiae. Abstracting these behind a wrapper function would fix both of these problems, as shown in Figure 4. [ Editor's Update - 4/27/2005: The code in Figure 4 has been updated. When passing a System.EventHandler to Control.Invoke or Control.BeginInvoke, EventArgs.Empty will be used as the EventArgs argument regardless of the actual EventArgs value specified in the arguments array.]

Figure 4 Wrapper Functions (Close Figure 4)
public class MyForm : System.Windows.Forms.Form {
    ...
    public void ShowProgress(string msg, int percentDone) {
        // Wrap the parameters in some EventArgs-derived custom class:
        System.EventArgs e = new MyProgressEvents(msg, percentDone);
        object[] pList = { this, e };

        // Invoke the method. This class is derived
        // from Form, so we can just call BeginInvoke
        // to get to the UI thread.
        BeginInvoke(new MyProgressEventsHandler(UpdateUI), pList);
    }

    private delegate void MyProgressEventsHandler(
        object sender, MyProgressEvents e);

    private void UpdateUI(object sender, MyProgressEvents e) {
        lblStatus.Text = e.Msg;
        myProgressControl.Value = e.PercentDone;
    }
}

The ShowProgress method encapsulates the work of handing the call over to the correct thread. This means the worker thread code would no longer have to worry itself too much over the UI details—it would just call ShowProgress at regular intervals. Notice that I have defined my own method which is exempt from the "must be called on the UI thread" rule because it, in turn, only calls other methods that are exempt. This technique suggests a more general idiom: why not write public methods on the control which are documented exceptions to the UI thread rule?

It turns out that the Control class provides a useful facility for such methods. If I am providing a public method which is designed to be callable from any thread, it is entirely possible that someone will call this method from the UI thread. In this case, calling BeginInvoke is not necessary because I am already on the right thread. It would be a waste of time and resources to call Invoke instead of calling the appropriate method directly. To help avoid this, the Control class exposes a property called InvokeRequired. This is another exception to the "UI thread only" rule. It can be read from any thread, will return false if the calling thread is the UI thread, and will return true for any other thread. This means I can modify the wrapper in the following way:

public void ShowProgress(string msg, int percentDone) {
    if (InvokeRequired) {
        // As before
        •••
    } else {
        // We're already on the UI thread just
        // call straight through.
        UpdateUI(this, new MyProgressEvents(msg,
            PercentDone));
    }
}

ShowProgress can now be documented as a public method which can be called from any thread. This doesn't get rid of the complexity—the code to perform BeginInvoke is still here, it's just in one place. Unfortunately, there is no easy way to get rid of it entirely.

Locking

Any concurrent system must deal with the fact that two threads might try to use a single piece of data simultaneously. Sometimes this might not be a problem—if multiple threads attempt to read a field in an object at the same time, no problems will arise. If any thread wants to modify the data, however, you do have a problem. It is possible for the reading threads to see bogus values if their read coincides with a write on another thread. If two threads write to the same place at the same time, it is possible that any thread that reads from this place will see garbage at any time after the simultaneous writes occurred. Though this behavior only arises in certain circumstances, the read doesn't even have to collide with the writes—the data could be a mix of the two writes and would remain bad until the next time a value is written. In order to avoid this problem, you must take steps to ensure that only one thread at a time can read or write an object's state.

The way you usually protect yourself from these problems is to use the runtime's locking facilities. C# lets you exploit these to protect your code with the lock keyword (Visual Basic has a similar construct called SyncLock). The rule is that any object that expects to have its methods called on multiple threads should use a lock construct every time it accesses its fields, whether it is reading or writing them. For example, take a look at Figure 5.

Figure 5 Locking Threads (Close Figure 5)
// This field could be modified and read on any thread, so all access
// must be protected by locking the object.

private double myPosition;
•••
public double Position {
    get {
        // Position could be modified from any thread, so we need to lock
        // this object to make sure we get a consistent value.
        lock (this) {
            return myPosition;
        }
    }
    set {
        lock (this) {
            myPosition = value;
        }
    }
}

public void MoveBy(double offset) {
    // Here we are reading, checking and then modifying the value. It is
    // vitally important that this entire sequence is protected by a
    // single lock block.
    lock (this) {
        double newPos = Position + offset;
        // Check within range - MINPOS and MAXPOS
        // will be const values defined somewhere in
        // this class
        if (newPos > MAXPOS) newPos = MAXPOS;
        else if (newPos < MINPOS) newPos = MINPOS;
        Position = newPos;
    }
}

The way this works is that each object in the common language runtime (CLR) has a lock associated with it which any thread can acquire, but which can only be owned by one thread at a time. If a thread attempts to acquire the lock when another already owns it, it will have to wait until the owning thread releases the lock. The C# lock construct acquires the object lock (waiting for another thread to finish with it, if necessary), and holds it until the code inside the braces exits. The lock will be released if the execution runs to the end of the block, returns from the middle of the block, or throws an exception which is not caught in the block.

Note that the logic in the MoveBy method is protected by a single lock statement. When making modifications that are more complex than a simple read or write, the whole process must be protected by a single lock statement. This also applies when making updates to multiple fields—the lock must not be released until the object is in a consistent state. If the lock is released partway through updating the state, other threads may be able to get in and see this inconsistency. If you already hold a lock and you call a method that attempts to acquire the same lock, this does not cause a problem—a single thread is allowed to acquire the same lock several times over. This is important for code where locking is needed to protect both low-level access to the field and also higher-level operations performed on the field. MoveBy uses the Position property, and they both acquire the lock. The lock is not properly released until the outermost lock block finishes.

You must be thorough with your locking code. All it takes is one omission to spoil things for everyone. If just one method modifies the state without acquiring the object lock, the remaining code that carefully locks the object before using it does so in vain. Likewise, if a thread tries to read the state without first acquiring the lock, it may read incorrect values. The runtime cannot check to make sure that multithreaded code behaves properly.

Deadlock

Locks are essential for ensuring that multithreaded code behaves correctly, even though they can introduce new risks of their own. The easiest way of running code on another thread is to use asynchronous delegate invocation (see Figure 6).

Figure 6 Asynchronous Delegate Invocation (Close Figure 6)
public class Foo {
    public void CallBar() {
        lock (this) {
            Bar myBar = new Bar ();
            myBar.BarWork(this);
        }
    }

    // This will be called back on a worker thread
    public void FooWork() {
        lock (this) {
            // do some work
            •••
        }
    }
}

public class Bar {
    public void BarWork(Foo myFoo) {
        // Call Foo on different thread via delegate.
        MethodInvoker mi = new MethodInvoker(
            myFoo.FooWork);
        IAsyncResult ar = mi.BeginInvoke(null, null);
        // do some work
        •••
        // Now wait for delegate call to complete (DEADLOCK!)
        mi.EndInvoke(ar);
    }
}

This code will grind to a halt if Foo's CallBar method is ever called. The CallBar method acquires the lock on the Foo object and will not release it until BarWork returns. BarWork then uses asynchronous delegate invocation to call the Foo object's FooWork method on a thread pool thread. It then does some other work before calling the delegate's EndInvoke method. EndInvoke will wait for the worker thread to complete, but the worker thread will be blocked in FooWork. It also tries to acquire the Foo object's lock, but that lock is already held in the CallBar method. So, FooWork is waiting for CallBar to release the lock, but CallBar is waiting for BarWork to return. Unfortunately, BarWork is waiting for FooWork to complete, so FooWork must complete before it can start. As a result, none of the threads can proceed.

This is an example of deadlock, which is where two or more threads are both blocked waiting for the other to proceed. The behavior here is slightly different from the standard deadlock scenario, which usually involves two locks. This illustrates that if a causality (a chain of procedure calls) crosses a thread boundary, you can get a deadlock even when only one lock is involved! This is an important fact given that Control.Invoke is a way of calling a procedure across a thread boundary. BeginInvoke does not suffer from this problem because it doesn't make the causality cross threads. In effect it causes a brand new causality to start on a thread pool thread, allowing the original one to proceed independently. However, if you keep the IAsyncResult that BeginInvoke returns and use this to call EndInvoke, then you are in trouble again because EndInvoke effectively merges the two causalities back into one. The simplest way to avoid this situation is to never wait for a cross-thread call to complete while you are holding an object lock. To ensure this, avoid calling either Invoke or EndInvoke inside a lock statement. As a result, you won't be waiting for some other thread to do something while you are holding an object lock. Sticking to this rule is easier said than done.

When examining the code for BarWork, it is far from obvious that it is scoped by a lock statement because there is no lock statement in this method. The problem arises only because BarWork is called from within a lock statement in the Foo.CallBar method. This means that it is only safe to call Control.Invoke or EndIn-voke if you are sure that there are no functions calling you that own locks! For non-private methods, there is no easy way to guarantee this, so for these, the best rule would be to never call Control.Invoke or EndInvoke at all. This is why the "launch and leave" style of programming is preferable wherever possible and why Control.BeginInvoke is usually a much better solution than Control.Invoke.

Sometimes there is no option but to break this rule, in which case very careful and thorough analysis is the only answer. But if at all possible, you should avoid blocking while holding locks because if you do, deadlock is difficult to eliminate.

Keep it Simple

How can you best reap the benefits of multithreading without suffering from the tricky bugs that can plague concurrent code? If your improved UI responsiveness merely allows the program to crash at a moment's notice, you can hardly claim to have improved the user experience. Most of the problems common in multithreaded code arise from the inherent complexity of having lots of things occurring at once—most people are better at thinking about sequential processes than concurrent ones. Often, the best solution is to keep things as simple as possible.

The nature of UI code is that it receives events from external sources like user input. It handles these as they occur, but it spends most of its time waiting for things to happen. If you can structure the communication between the worker thread and the UI to fit into this model, you are unlikely to see many problems because no new idioms will have been introduced. I am keeping things simple by treating the worker thread as another source of asynchronous events. Just as the Button control delivers events such as Click and MouseEnter, you can treat the worker thread as something that delivers events such as ProgressUpdate and WorkComplete. It's up to you whether to treat this simply as an analogy or to actually encapsulate your worker object in a class which exposes proper events in this way. The latter will probably require more code, but will leave your user interface code looking pleasingly uniform. In either case, you will need Control.BeginInvoke to deliver these events on the correct thread.

For the worker thread, the simplest approach is to write the code as a normal sequential piece of code. But what if you want to use the "worker thread as event source" model I've just described? This model is desirable, but it places a constraint on the interaction this code has with the user interface: the thread can only send information to the UI, not request it.

For example, it would be difficult for your worker thread to bring up a dialog halfway through to request information needed to complete the result. If you do need to do this, it is probably better to open such a dialog on the worker thread, not the main UI thread. This constraint is a good thing, as it ensures a very simple model for communication between the two threads—and simplicity is the key to success here. The great thing about this style of development is that neither thread is ever blocking while waiting for the other one. This is a good strategy for avoiding deadlock.

Figure 7 shows the use of asynchronous delegate invocation to perform a potentially slow operation (reading the contents of a directory) on a worker thread, and then displaying the results back in the UI. It doesn't go as far as using the high-level event syntax, but it does handle the completion of the worker code in much the same way that it would handle events such as clicks.

Figure 7 A Slow Operation on a Worker Thread (Close Figure 7)
private void btnRead_Click(object o, EventArgs e)
{
    btnRead.Enabled = false;
    txtDir.Enabled = false;
    Cursor = Cursors.AppStarting;

    // Clear the list view
    lvwDirList.Clear();

    // This could be slow, so do this via an async delegate.
    ReadDelegate dlg = new ReadDelegate(ReadDirectory);
    dlg.BeginInvoke(txtDir.Text, null, null);
}

// Worker function (and associated delegate)
private delegate void ReadDelegate(string dirName);
private void ReadDirectory(string dirName)
{
    try
    {
        string[] names = Directory.GetFileSystemEntries(dirName);

        // Marshal the results back onto the right thread.
        object[] pList = { names };
        BeginInvoke(new DirRead(readDir_DirEntries), pList);
    }
    catch (DirectoryNotFoundException)
    {
        // Raise the directory not found 'event'
        BeginInvoke(new MethodInvoker(readDir_DirNotFound));
    }
    catch (Exception e)
    {
        // Raise the miscellaneous error 'event'.
        object[] pList = { e };
        BeginInvoke(new DirError(readDir_Error), pList);
    }

}

// Functions called via Invoke by worker thread.

private delegate void DirRead(string[] names);
private void readDir_DirEntries(string[] names)
{
    Cursor = Cursors.WaitCursor;
    foreach(string name in names)
    {
        lvwDirList.Items.Add(name);
    }

    ResetCtls();
}

private void readDir_DirNotFound()
{
    MessageBox.Show(this, "Directory not found", "Error");
    ResetCtls();
}

private delegate void DirError(Exception e);
private void readDir_Error(Exception e)
{
    MessageBox.Show(this, string.Format("Error reading directory:\n{0}",
        e.Message), "Error");
    ResetCtls();
}

// Set the relevant controls and cursors back to their default
// 'ready' states after the operation has completed.
private void ResetCtls()
{
    // Re-enable the Read button and put the cursor back to normal.
    btnRead.Enabled = true;
    txtDir.Enabled = true;
    Cursor = Cursors.Default;
}

Cancellation

The problem with the previous example is that the only way to cancel the operation is to quit the entire application. Although the UI remains responsive while reading a directory, the user cannot view another directory, since the program disables the relevant buttons until the current operation completes. It would be unfortunate if you attempted to read a directory on a remote machine that turned out to be unresponsive, since it can take a long time for such an operation to time out.

The ability to cancel an operation can be hard to arrange, although it depends on precisely what is meant by cancel. One possible interpretation is "stop waiting for this operation to complete and let me get on with a different operation." This really amounts to abandoning the operation in progress and ignoring whatever results come in when it does eventually complete. For the present example, this is the best you can do, since the operation in question (reading the contents of a directory) is done with a single blocking API call for which there is no means of cancellation. But even this fairly loose pseudo-cancelling requires a certain amount of effort on your part. If you just decided to launch a new read without waiting for the old one to complete, there would be no way of knowing which of the two outstanding requests the next notification you received came from.

The only way to support cancellation of requests running on a worker thread is to provide some kind of call object associated with each request. At its simplest, this would just act as a cookie, which the worker thread passes back in with each notification, allowing the UI thread to correlate events with requests. Through a simple identity comparison (see Figure 8), the UI code can work out whether an event was from the current request or one that has long since been abandoned.

Figure 8 Which Request is This? (Close Figure 8)
// Member variable holding the current request—gets updated with the
// current call object every time we start a new operation. (This call
// object would also be made available to the worker function—either as a
// parameter, or possibly by making the worker function a member of the
// call object.)
private object currReq;
•••
private void workerNotify(object callObj) {
    // Only process this event if it's from the last request we made.
    // (I.e. ignore belated notifications from requests we've long since
    // abandoned).

    if (object.ReferenceEquals(callObj, currReq)) {
        // This is for an operation we still care about, so handle it
        •••
    }
}

This is all very well if simple abandonment is sufficient, but you might want to do better. If the worker thread is performing a complex operation that consists of a series of blocking operations in a row, you would probably want the worker thread to stop at the earliest opportunity. Otherwise, it could be continuing for minutes with work that is no longer needed. In this case, the call object will need to do a little more than just act as a passive cookie. At the very least, it will need to maintain a flag indicating whether the request has been cancelled. The UI could set this flag at any point, and the worker thread code would test the flag regularly during its execution to see if it should abandon its work.

Given this approach, there are some decisions to make: if the UI cancels the operation, does it then wait until the worker thread notices the cancellation? If not, there is a race condition you need to consider: it is possible that the UI thread might cancel the operation, but before being able to set the control flag the worker thread had already decided to deliver a notification. Because the UI thread decided not to wait until the worker thread dealt with the cancellation, it is possible that the UI thread will continue to receive notifications from the worker thread. It might even receive several if the worker thread was delivering them asynchronously using BeginInvoke. The UI thread can always deal with this in the same way as the "abandoning" approach—by checking the identity of the call object and ignoring notifications for operations it no longer cares about. Alternatively, I could try to fix this by using locking in the call object and never calling BeginInvoke from the worker thread. But since it is simpler for the UI thread to simply check that it's still interested in an event before handling it, you are likely to have fewer problems using that approach.

See the code download (at the link at the top of this article) for AsyncUtils, a useful base class that provides cancellation functionality for worker thread-based operations. Figure 9 shows a derived class that implements a recursive directory search with cancellation support. These classes illustrate some interesting techniques. They both use the C# event syntax to provide notifications. The base class exposes events that are raised when the operation completes successfully, when it is cancelled, and when it throws an exception. The derived class extends this with events that notify the client of search matches and also of its progress, showing which directory it is on at the moment. These events are always delivered on the UI thread. In fact, these classes are not restricted to the Control class—they can deliver events to anything that implements the ISynchronizeInvoke interface. Figure 10 is an example Windows Forms application which provides a user interface to the search class. It allows searches to be cancelled and shows their progress and results.

Figure 10 A Search UI (Close Figure 10)
public class TestHarness : System.Windows.Forms.Form
{
    private DirSearchWorker currSearch;

    //. . . Usual Windows Forms boilerplate . . .

    private void btnSearch_Click(object sender, EventArgs e)
    {
        if (currSearch == null)
        {
            lstResults.Items.Clear();
            currSearch = new DirSearchWorker(this, txtDir.Text,
                                             txtRegExp.Text);
            currSearch.Completed += new EventHandler(Search_Finished);
            currSearch.Cancelled += new EventHandler(Search_Cancelled);
            currSearch.Match += new
                DirSearchWorker.MatchEvent(Search_Result);
            currSearch.Dir += new DirSearchWorker.MatchEvent(Search_Dir);
            currSearch.Failed += new
                System.Threading.ThreadExceptionEventHandler(Search_Failed);
            btnSearch.Text = "&Cancel";
            lblResult.Text = "Searching...";
            Cursor = Cursors.AppStarting;
            currSearch.Start();
        }
        else
        {
            lblResult.Text = "Cancelling...";
            lblResult.Update();
            // Doing synchronous cancel, so the UI will be inactive
            // until it completes. For certain weird cases like the
            // Internet Explorer cache this can take several seconds, so
            // bring up the wait cursor.
            Cursor = Cursors.WaitCursor;
            currSearch.CancelAndWait();
            Cursor = Cursors.Default;
        }
    }

    private void btnExit_Click(object sender, System.EventArgs e)
    {
        if (currSearch != null)
        {
            currSearch.CancelAndWait();
        }
        Close();
    }

    private void Search_Finished(object sender, EventArgs e)
    {
        ResetCtls();
        lblResult.Text = "Finished";
    }
    private void Search_Cancelled(object sender, EventArgs e)
    {
        ResetCtls();
        lblResult.Text = "Cancelled";
    }
    private void Search_Failed(object sender,
        System.Threading.ThreadExceptionEventArgs e)
    {
        ResetCtls();
        lblResult.Text = string.Format("Error ({0}): {1}",
            e.Exception.GetType().ToString(), e.Exception.Message);
    }
    private void ResetCtls()
    {
        btnSearch.Text = "&Search";
        currSearch = null;
        Cursor = Cursors.Default;
    }

    private void Search_Result(string name)
    {
        lstResults.Items.Add(name);
    }
    private void Search_Dir(string name)
Figure 9 A Cancellable Search (Close Figure 9)
public sealed class DirSearchWorker : AsyncOperation
{
    public delegate void MatchEvent(string name);
    public event MatchEvent Match;
    public event MatchEvent Dir;

    private string searchDir;
    private Regex searchExpr;

    public DirSearchWorker(ISynchronizeInvoke isi, string dir,
                           string regexp)
        : base(isi)
    {
        searchDir = dir;
        searchExpr = new Regex(regexp, RegexOptions.Compiled);
    }

    protected override void DoWork()
    {
        DoSearch(searchDir);
        // When a cancel occurs, the recursive DoSearch drops back
        // here asap, so we'd better acknowledge cancellation.
        if (CancelRequested)
        {
            AcknowledgeCancel();
        }
    }

    private void DoSearch(string dir)
    {
        // Stop if cancellation was requested

        if (!CancelRequested)
        {
            OnDir(dir);

            foreach(string name in Directory.GetFileSystemEntries(dir))
            {
                // Check for cancellation here too, otherwise for really
                // large directories (such as those in the Internet
                // Explorer cache) the user might have to wait for
                // several seconds before the cancellation happens.
                if (CancelRequested) return;
                if (searchExpr.IsMatch(name))
                {
                    OnMatch(name);
                }
            }

            foreach(string name in Directory.GetDirectories(dir))
            {
                DoSearch(name);
            }
        }
    }

    private void OnMatch(string name)
    {
        lock(this)
        {
            FireAsync(Match, name);
        }
    }
    private void OnDir(string name)
    {
        lock(this)
        {
            FireAsync(Dir, name);
        }
    }

}

Program Shutdown

In some cases, it will be acceptable to "launch and leave" asynchronous operations without the added complexity required to make them cancellable. However, even though your user interface might not require cancellation, it is possible that you will still need to implement this facility to let your program shut down cleanly.

If any worker threads created by the thread pool are still running when your application exits, they will be terminated. This termination is fairly violent—the shutdown will even bypass any finally blocks that are still in scope. If any of your asynchronous operations perform work that should not be interrupted in that way, you must make sure that these operations have completed before shutting down. Such operations might include writing to a file, since the file will probably be corrupted after an abrupt halt.

One way to do this is to create your own thread, instead of one from the worker thread pool, which would of course rule out the use of asynchronous delegate invocation. Then even if the main thread shuts down, the application will not terminate until your thread exits. The System.Threading.Thread class has an IsBackground property which controls this behavior. It is false by default, and the CLR will not normally terminate the application until all non-background threads have exited. This can lead to a different problem, however, since your application may now hang around much longer than you intended. The windows will all be closed, but the processes will still be running. This might not be a problem. If the application simply hangs around a bit longer than normal while it tidies up, that's fine. If, on the other hand, the application hangs around for several more minutes or hours doing work after its user interface has gone away, this won't be acceptable. It might, for example, prevent the user from restarting the app later on if it holds some files open.

The best approach is usually to write your asynchronous operations so that they can be cancelled promptly, if possible, and wait for any outstanding operations to complete before letting the application shut down. This means you can carry on using asynchronous delegates while ensuring a clean and timely shutdown.

Error Handling

Errors that arise on the worker thread can typically be handled by raising an event on the UI thread so that errors are dealt with in exactly the same way as completion and progress updates. The simplest strategy is to make all errors fatal because it will be hard to arrange for error recovery on the worker thread. The best strategy for error recovery is to allow the operation to fail completely and perform the retry logic on the UI thread. If user intervention is required to fix the problem causing the error, it will be easy to bring up the appropriate prompts.

The AsyncUtils class handles errors as well as cancellation. If the operation throws an exception, the base class catches this and delivers it via a Failed event to the UI.

Conclusion

Careful use of multithreaded code can significantly improve the perceived quality of an application by preventing the UI from going to sleep when it performs a lengthy task. Asynchronous delegate invocation is the easiest way to migrate slow code out of the UI thread, avoiding such narcoleptic episodes.

The Windows Forms Control architecture is essentially single threaded, but it provides facilities to marshal calls from worker threads back onto the UI thread. The simplest strategy for dealing with notifications from the worker thread—whether they are indications of success, failure, or progress—is to treat them in much the same way as you treat events from normal controls such as mouse clicks or keyboard inputs. This avoids introducing any new idioms into the UI code, and the one-way nature of the communication makes deadlocks less likely to occur.

Sometimes it will be necessary for the UI to send a message to an operation already in progress. The most common use for this is to cancel an operation. This can be achieved by having an object that represents the call in progress and also maintains a cancellation flag which is periodically checked by the worker thread. If the user interface thread needs to wait for the cancellation to be acknowledged (either because the user needs to know that work has definitely halted or because a clean program exit is required), it gets a little more complicated, but the supplied sample code includes a base class which encapsulates all of this complexity. Deriving classes simply need to do the necessary work, periodically test for cancellation, and then inform the base class if it stopped work due to a cancellation request.

For related articles see:
Applied Microsoft .NET Framework Programming by Jeffrey Richter (Microsoft Press, 2002)
Essential .NET, Volume 1: The Common Language Runtime by Don Box (Addison Wesley Professional, 2002)

For background information see:
Safe, Simple Multithreading in Windows Forms
A Second Look at Windows Forms Multithreading

Ian Griffithsis an independent consultant in the UK specializing in .NET Windows Forms applications. He is an instructor at DevelopMentor, and is coauthor of .NET Windows Forms in a Nutshell (O'Reilly, 2002) and Mastering Visual Studio .NET (O'Reilly, February 2003).


Delegates in .NET: A Primer on Creating Type-Safe References to Methods in Visual Basic .NET

Delegates in .NET

A Primer on Creating Type-Safe References to Methods in Visual Basic .NET

Jesse Liberty
This article assumes you're familiar with Visual Basic .NET
Level of Difficulty 1 2 3
Code download available at: Delegates.exe (36KB)
SUMMARY

Delegates, new in Visual Basic .NET, are type-safe, object-oriented references to methods. By using delegates, you make your methods accessible to others and therefore more extensible. This article provides an introduction to delegates, their benefits, and how they are used.

Here the author shows you how to declare, create, and use delegates to invoke instance and shared methods at run time, and how to implement delegates as properties. He then goes on to provide examples of how delegates are used for callback functionality and explains the relationship between delegates and events. Finally, the article provides a glimpse at how delegates are implemented in intermediate language code by the Visual Basic .NET compiler.

Contents
Getting Started
Instantiating the Delegate
Instance Methods and Delegates
Shared Delegates
Multicasting
Delegates and Callback Mechanisms
Delegates and Events
Delegates Under the Covers
Delegate and Multicast Delegate
Conclusion

S
uppose you are drawing up a business plan for a new computer company and you face the question of how to handle the distribution of your products. Certainly you would never consider hand-delivering each new gadget to every customer. Instead, you would delegate this responsibility to a shipping service like FedEx or UPS. There are three determinations you must make in advance: what the action is (deliver gadget), what the parameters are (customer address), and what the return value is (payment). When you actually have a package to deliver, you can then delegate the responsibility to a particular delivery service.

In Microsoft® .NET programming, you face situations in which you need to execute a particular action, but there's no way to know in advance which method you'll call upon to execute that action. For example, a button might need to call some method when it is clicked, but at the time the button is designed it's impossible to know which method it will call. You need a way to describe the kind of method you want to call. This is where delegates come in handy. In this article, I'll explore delegate use in Visual Basic® .NET.

Getting Started

A delegate is a reference type that represents a method with a specific signature and return type. You can encapsulate any matching method in that delegate. In addition, you can think of a delegate as a reference to a method.

To see how this works, let's look at a problem that delegates solve. As you will see in Figure 1, I'll create a Pair class—a simple collection that holds two objects. The Pair class creates a private array of two members called thePair:

Public Class Pair

    Private thePair(2) As Object

Figure 1 Using Delegates (Close Figure 1)
Option Strict On

Imports System

Namespace DelegatesAndEvents

    ' a simple collection to hold two items
    Public Class Pair

        ' enumeration to indicate which value is smaller
        Public Enum comparison
            theFirst = 1
            theSecond = 2
        End Enum

        ' private array to hold the two objects
        Private thePair(2) As Object

        ' the delegate declaration
        Public Delegate Function WhichIsSmaller( _
          ByVal obj1 As Object, ByVal obj2 As Object) As comparison

        ' constructor take two objects,
        ' added to array in order received
        Public Sub New( _
           ByVal firstObject As Object, _
           ByVal secondObject As Object)
            thePair(0) = firstObject
            thePair(1) = secondObject
        End Sub

        ' public method that orders the two objects
        ' by whatever criteria the objects like
        Public Sub Sort(ByVal theDelegatedFunc As WhichIsSmaller)
            If theDelegatedFunc(thePair(0), thePair(1)) = _
               comparison.theSecond Then
                Dim temp As Object = thePair(0)
                thePair(0) = thePair(1)
                thePair(1) = temp
            End If
        End Sub

        ' public method that orders the two objects
        ' by the reverse of whatever criteria the objects like
        Public Sub ReverseSort(ByVal theDelegatedFunc As WhichIsSmaller)
            If theDelegatedFunc(thePair(0), thePair(1)) = _
                    comparison.theFirst Then
                Dim temp As Object = thePair(0)
                thePair(0) = thePair(1)
                thePair(1) = temp
            End If
        End Sub

        ' ask the two objects to give their string value
        Public Overrides Function ToString() As String
            Return thePair(0).ToString() & ", " & thePair(1).ToString()
        End Function
    End Class

    Public Class Dog

        Private weight As Integer

        Public Sub New(ByVal weight As Integer)
            Me.weight = weight
        End Sub

        ' dogs are ordered by weight
        Public Shared Function WhichDogIsSmaller( _
          ByVal o1 As Object, ByVal o2 As Object) As Pair.comparison
            Dim d1 As Dog = DirectCast(o1, Dog)
            Dim d2 As Dog = DirectCast(o2, Dog)
            If d1.weight > d2.weight Then
                Return Pair.comparison.theSecond
            Else
                Return Pair.comparison.theFirst
            End If
        End Function

        Public Overrides Function ToString() As String
            Return weight.ToString()
        End Function
    End Class

    Public Class Student

        Private name As String

        Public Sub New(ByVal name As String)
            Me.name = name
        End Sub

        ' students are ordered alphabetically
        Public Shared Function WhichStudentIsSmaller( _
          ByVal o1 As Object, ByVal o2 As Object) As Pair.comparison
            Dim s1 As Student = DirectCast(o1, Student)
            Dim s2 As Student = DirectCast(o2, Student)
            If String.Compare(s1.name, s2.name) < 0 Then
                Return Pair.comparison.theFirst
            Else
                Return Pair.comparison.theSecond
            End If
        End Function

        Public Overrides Function ToString() As String
            Return name
        End Function
    End Class

    Class Tester

        Public Sub Run()
            ' create two students and two dogs
            ' and add them to Pair objects
            Dim Jesse As New Student("Jesse")
            Dim Stacey As New Student("Stacey")
            Dim Milo As New Dog(65)
            Dim Fred As New Dog(12)

            Dim studentPair As New Pair(Jesse, Stacey)
            Dim dogPair As New Pair(Milo, Fred)
            Console.WriteLine("studentPair: {0}", _
                studentPair.ToString())
            Console.WriteLine("dogPair: {0}", _
                dogPair.ToString())

            ' Instantiate  the delegates
            Dim theStudentDelegate As New _
              Pair.WhichIsSmaller(AddressOf Student.WhichStudentIsSmaller)
            Dim theDogDelegate As New _
              Pair.WhichIsSmaller(AddressOf Dog.WhichDogIsSmaller)

            ' sort using the delegates
            studentPair.Sort(theStudentDelegate)
            Console.WriteLine("After Sort studentPair: {0}", _
                studentPair.ToString())
            studentPair.ReverseSort(theStudentDelegate)
            Console.WriteLine("After ReverseSort studentPair: {0}", _
                studentPair.ToString())

            dogPair.Sort(theDogDelegate)
            Console.WriteLine("After Sort dogPair: {0}", _
                dogPair.ToString())
            dogPair.ReverseSort(theDogDelegate)
            Console.WriteLine("After ReverseSort dogPair: {0}", _
                dogPair.ToString())
        End Sub

        Public Shared Sub Main()
            Dim t As New Tester()
            t.Run()
        End Sub
    End Class
End Namespace
Output:
studentPair: Jesse, Stacey
dogPair: 65, 12
After Sort studentPair: Jesse, Stacey
After ReverseSort studentPair: Stacey, Jesse
After Sort dogPair: 12, 65
After ReverseSort dogPair: 65, 12

The constructor takes two objects, and adds them to this internal array in the order in which they are received:

Public Sub New(ByVal firstObject As Object, ByVal secondObject As Object)
    thePair(0) = firstObject
    thePair(1) = secondObject
End Sub

Pair offers three other methods: Sort, ReverseSort, and an override of ToString. The Sort method will sort the two objects in the internal array. Of course, you do not want to make the Pair class know the test for sorting the two objects since you might store virtually any type of object in the pair (Students, Dogs, Employees, Buttons, and so on). How can Pair possibly know how to sort all these different types of objects?

The solution is to delegate the responsibility for determining which object is smallest to the objects themselves. How does Pair accomplish this? With a delegate (see Figure 1).

In the Pair class, I've defined a delegate to encapsulate (refer to) a method that compares two objects and then determines which is smaller (by whatever definition of smaller the compared class determines is appropriate):

Public Delegate Function WhichIsSmaller( _
  ByVal obj1 As Object, ByVal obj2 As Object) As Comparison

This is a pretty complicated definition. Let's review this definition one piece at a time:

  • The keyword Public declares the delegate to be a public member of the Pair class.
  • The keyword Delegate signals that you are creating a delegate (rather than a method or a property).
  • The keyword Function indicates that the delegate will be used to encapsulate a function (rather than a sub).
  • The identifier WhichIsSmaller is the name of this delegate.
  • The values within the parentheses are the signature of the methods that this delegate will encapsulate. That is, this delegate may encapsulate any function that takes two objects as parameters.
  • The final keywords, As Comparison, are the return type of the methods that may be encapsulated by this delegate. Comparison is an enumeration that's defined in the Pair class:
    Public Enum Comparison
    theFirst = 1
    theSecond = 2
End Enum
    The method that's encapsulated with this delegate must return either Comparison.theFirst or Comparison.theSecond.

In total, the statement I've just shown defines a public delegate in the Pair class named WhichIsSmaller that encapsulates functions that take two objects as parameters and that return an instance of the enumerated type Comparison.

You can encapsulate any matching method in an instance of this delegate. For example, perhaps your Pair collection will hold two Student objects, as shown here:

Public Class Student

    Private name As String

    Public Sub New(ByVal name As String)
        Me.name = name
    End Sub

    ' other Student methods here

End Class
Your Student class must create a method that matches the WhichIsSmaller delegate. For example, you might create a method WhichStudentIsSmaller (see the code in Figure 2). This method matches the required signature; it takes two Objects as parameters and it returns a Comparison value.

Figure 2 WhichStudentIsSmaller (Close Figure 2)
Public Shared Function WhichStudentIsSmaller( _
           ByVal o1 As Object, ByVal o2 As Object) As Pair.Comparison
    Dim s1 As Student = DirectCast(o1, Student)
    Dim s2 As Student = DirectCast(o2, Student)
    If String.Compare(s1.name, s2.name) < 0 Then
        Return Pair.Comparison.theFirst
    Else
        Return Pair.Comparison.theSecond
    End If
End Function

Because my WhichStudentIsSmaller method wants to work with the parameters as Student objects and not as the more general Object type, I'll cast the parameters to Student. This is type-safe because I'll never call this method with any other type of parameter.

Once I've cast the two objects, I can compare them. In this case, I'll compare their name values alphabetically and return the appropriate enumerated value: either Pair.Comparison.theFirst or Pair.Comparison.theSecond.

The alphabetic comparison is accomplished by calling the shared Compare method of the String class. Compare returns a negative integer value if the first string comes earlier in the alphabet than the second (s1.name comes before s2.name alphabetically). It returns a positive integer value if the second string comes earlier in the alphabet, and it returns 0 if they are equal.

Other classes can also create methods that match the WhichIsSmaller delegate. For example, I might also create a Dog class:

Public Class Dog

    Private weight As Integer

    Public Sub New(ByVal weight As Integer)
        Me.weight = weight
    End Sub

    ' other Dog methods here

End Class
This Dog class will implement a method to compare two Dog instances based on their weight:
Public Shared Function WhichDogIsSmaller( _
  ByVal o1 As Object, ByVal o2 As Object) As Pair.comparison
    Dim d1 As Dog = DirectCast(o1, Dog)
    Dim d2 As Dog = DirectCast(o2, Dog)
    If d1.weight > d2.weight Then
        Return Pair.Comparison.theSecond
    Else
        Return Pair.Comparison.theFirst
    End If
End Function

The Pair class is now ready to create its Sort method. As a parameter, it will take a WhichIsSmaller delegate:

Public Sub Sort(ByVal theDelegatedFunc As WhichIsSmaller)
    If theDelegatedFunc(thePair(0), thePair(1)) = _
       Comparison.theSecond Then
        Dim temp As Object = thePair(0)
        thePair(0) = thePair(1)
        thePair(1) = temp
    End If
End Sub
The Sort method calls the delegated method through the delegate, passing in the two members of the Pair's array in return for an enumerated value. If that value is Comparison.theSecond, then it knows that the second object is smaller than the first type. It knows this without even knowing what type the two objects are! It can then reverse the two objects. If the delegated method returns Comparison.theFirst, then no swap is necessary.

Instantiating the Delegate

To test this, I can create two Student objects, like so:

Dim Jesse As New Student("Jesse")
Dim Stacey As New Student("Stacey")
I then add them to a new Pair object:
Dim studentPair As New Pair(Jesse, Stacey)
Next, I can instantiate a WhichIsSmaller delegate, passing in the matching method of Student that knows how to do a comparison of the two student objects:
Dim theStudentDelegate As New _
  Pair.WhichIsSmaller(AddressOf Student.WhichStudentIsSmaller)
I can now pass this delegate to the Sort method in order to sort the two students:
studentPair.Sort(theStudentDelegate)

Similarly, I could create two Dog objects, add them to a pair, instantiate a delegate based on the Dog method for comparing two Dogs, and then pass that delegate to the dog pair's sort method, as shown in the following lines:

' make two dogs
Dim Milo As New Dog(65)
Dim Fred As New Dog(12)

' store the two dogs in a Pair
Dim dogPair As New Pair(Milo, Fred)

' instantiate a delegate
Dim theDogDelegate As New _
  Pair.WhichIsSmaller(AddressOf Dog.WhichDogIsSmaller)

' invoke Sort, pass in the delegate
dogPair.Sort(theDogDelegate)

The Pair class has a ReverseSort that takes the same delegate as Sort. I can pass the Student and Dog delegates you just created to ReverseSort as well:

studentPair.ReverseSort(theStudentDelegate)
dogPair.ReverseSort(theDogDelegate)
As you might have guessed, the logic of ReverseSort is the reverse to that of Sort. That is, you only swap the two items in the Pair if the comparison method returns a value indicating that the first object is smaller than the second. The complete source code for this example is shown in Figure 1.

Instance Methods and Delegates

In the example shown in Figure 1, you encapsulated shared methods of the Dog and Student class. I can just as easily declare the encapsulated methods to be instance methods (that is, nonshared methods) instead:

Public Comparison
    WhichStudentIsSmaller(o1 as Object, o2 as Object)
Although I can encapsulate this method in a delegate, I must refer to it through an instance rather than through the class, like this:
Dim theStudentDelegate As _
  New Pair.WhichIsSmaller(AddressOf Jesse.WhichStudentIsSmaller)

While it is more efficient to make repeated calls to a delegate encapsulating an instance method than a static method, you do need an instance on which to invoke the method.

Shared Delegates

One disadvantage to the way the delegates are declared in Figure 1 is that the calling class (Tester) must instantiate the delegates it needs in order to sort the objects in the Pair. For example, within the Run method of Tester in Figure 1 you see the following lines of code:

Dim theStudentDelegate As New _
  Pair.WhichIsSmaller(AddressOf Student.WhichStudentIsSmaller)
The Tester object is required to know that the method it wants is WhichStudentIsSmaller, but this should be an implementation detail of the Student class, invisible to the Tester object.

You can solve this problem by making the delegate instance a shared member of the Student class. This way, rather than knowing which method will handle the sort for the Student (or the Dog), the Tester class need only know that the Student class has a shared delegate named, for example, OrderStudents. The run method can then be rewritten like this:

studentPair.Sort(Student.OrderStudents)
Within the student class, the shared delegate is declared as follows:
Public Shared ReadOnly OrderStudents As _
  New Pair.WhichIsSmaller(AddressOf Student.WhichStudentIsSmaller)
Similarly, I can declare a static delegate within the Dog class:
Public Shared ReadOnly OrderDogs As New Pair.WhichIsSmaller( _
              AddressOf Dog.WhichDogIsSmaller)
The Run method of Tester can now order the Dog pair by writing:
dogPair.Sort(Dog.OrderDogs)
Delegates As Properties

The problem with shared delegates is that they must be instantiated, whether or not they are ever used, as with Student.OrderStudents and Dog.OrderDogs in the previous example. These classes can be improved by changing the shared delegate fields to properties, as shown here:

Public Shared ReadOnly Property OrderStudents As Pair.WhichIsFirst
    Get
       Return New Pair.WhichIsFirst(AddressOf WhichStudentComesFirst)
    End Get
End Property
The assignment of the delegate is unchanged:
  studentPair.Sort(Student.OrderStudents)
The key difference, however, is that now the delegate is only instantiated when you actually access the property.

Multicasting

In some cases you may want to call not just one, but two or more methods through a single delegate. This is known as multicasting. You accomplish multicasting by encapsulating the various methods in delegates, and then you combine the delegates using the Delegate.Combine shared method. The Combine method takes an array of delegates as a parameter and returns a new delegate that represents the combination of all the delegates in the array.

To see how this works, in the following code, I'll create a simplistic class that declares a delegate:

Public Class MyClassWithDelegate
    ' the delegate declaration
    Public Delegate Sub StringDelegate(ByVal s As String)
End Class
I can then create a class that implements a number of methods that match the StringDelegate:
Public Class MyImplementingClass

    Public Shared Sub WriteString(ByVal s As String)
        Console.WriteLine("Writing string {0}", s)
    End Sub

    Public Shared Sub LogString(ByVal s As String)
        Console.WriteLine("Logging string {0}", s)
    End Sub

    Public Shared Sub TransmitString(ByVal s As String)
        Console.WriteLine("Transmitting string {0}", s)
    End Sub
End Class

Within the Run method of the Tester class, I'll instantiate three StringDelegate objects:

Dim Writer, Logger, Transmitter As MyClassWithDelegate.StringDelegate
I instantiate these delegates by passing in the address of the methods I want to encapsulate, like this:
Writer = New MyClassWithDelegate.StringDelegate( _
     AddressOf MyImplementingClass.WriteString)
Logger = New MyClassWithDelegate.StringDelegate( _
     AddressOf MyImplementingClass.LogString)
Transmitter = New MyClassWithDelegate.StringDelegate( _
      AddressOf MyImplementingClass.TransmitString)

Next, I instantiate a multicast delegate that I'll use to combine the three other delegates:

Dim myMulticastDelegate As MyClassWithDelegate.StringDelegate
I create an array of the first two delegates:
Dim arr() As MyClassWithDelegate.StringDelegate = {Writer, Logger}
Then I use that array to instantiate the multicast delegate:
myMulticastDelegate = _
  DirectCast(System.Delegate.Combine(arr), _
  MyClassWithDelegate.StringDelegate)
DirectCast is used to cast the result of calling Combine to the specialized type MyClassWithDelegate.StringDelegate because Combine returns an object of the more general type Delegate. The complete code for this example can be found in the download at the link at the top of this article.

I can add the third delegate to the collection by calling the overloaded Combine method, this time passing in the existing multicast delegate and the new delegate to add:

myMulticastDelegate = _
   DirectCast(System.Delegate.Combine(myMulticastDelegate, Transmitter), _
   MyClassWithDelegate.StringDelegate)

I can remove just the Logger delegate by calling the static method Remove, passing in the multicast delegate and the delegate to be removed. The return value is a Delegate that's cast to a StringDelegate and assigned back to the multicast delegate, like this:

myMulticastDelegate = _
   DirectCast(System.Delegate.Remove(myMulticastDelegate, Logger), _
   MyClassWithDelegate.StringDelegate)

Delegates and Callback Mechanisms

There are two ways to get your laundry done. The first way is to put your laundry into the washing machine, put in a few quarters, and then wait for the machine to run. You wait, and then you wait some more. About 30 minutes later, the machine stops and you take your laundry back.

The second way to get your laundry done is to take it to the laundromat and say "Here, please clean this clothing and call me back when you are finished. Here's my cell number." The person in the laundromat does the work while you go off and do something else. When your laundry is ready, they call you and say "Your clothes are clean. Your pick-up number is 123." When you return, you give the person at the desk the number 123, and you get back your clothes. This strategy is known as a callback.

The .NET Framework supports the notion of an asynchronous callback. The idea of a callback is that you say to a method "Do this work, and call me back when you are finished." It is a simple and clean mechanism for multitasking. For instance, opening a file and reading data from it is time consuming. You'd like to tell the object responsible for reading from the file to "go get information from this file and let me know when you have it," while you go off and do something completely different.

The .NET Framework provides the FileStream class for this kind of service. This class will open and read a file for you asynchronously, and it will also call back a method that you designate when it has data from the file.

In the next example, shown in Figure 3, I open a FileStream object, passing in the name of the file, the fileMode (Open), the FileAccess flag (Read), and the FileShare mode (ReadWrite). I also pass in an integer signifying the buffer size and a Boolean indicating whether the FileStream should be opened asynchronously:

inputStream = New FileStream( _
    "C:\temp\streams.txt", _
    FileMode.Open, _
    FileAccess.Read, _
    FileShare.ReadWrite, _
    1024, _
    True)

Figure 3 Performing File Operations (Close Figure 3)
Imports System.IO
Imports System.Text

Public Class AsynchIOTester
    Private inputStream As Stream

    ' delegated method
    Private myCallBack As AsyncCallback

    ' buffer to hold the read data
    Private buffer() As Byte

    ' the size of the buffer
    Private Const BufferSize As Integer = 256

    ' constructor
    Sub New()
        ' open the input stream
        inputStream = New FileStream( _
         "C:\temp\streams.txt", _
        FileMode.Open, _
        FileAccess.Read, _
        FileShare.ReadWrite, _
        1024, _
        True)

        ' allocate a buffer
        Buffer = New Byte(BufferSize) {}

        ' assign the call back
        myCallBack = AddressOf OnCompletedRead

    End Sub 'New

    Public Shared Sub Main()
        ' create an instance of AsynchIOTester
        ' which invokes the constructor
        Dim theApp As New AsynchIOTester()

        ' call the instance method
        theApp.Run()
    End Sub 'Main

    Sub Run()
        inputStream.BeginRead( _
        Buffer, _
        0, _
        Buffer.Length, _
        myCallBack, _
        Nothing)

        Dim i As Long
        For i = 0 To 49999999
            If i Mod 1000 = 0 Then
                Console.WriteLine("i: {0}", i)
            End If
        Next i
    End Sub 'Run

    ' call back method
    Sub OnCompletedRead(ByVal asyncResult As IAsyncResult)
        Dim bytesRead As Integer = inputStream.EndRead(asyncResult)

        ' if we got bytes, make them a string
        ' and display them, then start up again.
        ' Otherwise, we're done.
        If bytesRead > 0 Then
            Dim s As String = _
              Encoding.ASCII.GetString(Buffer, 0, bytesRead)
            Console.WriteLine(s)
            inputStream.BeginRead( _
               Buffer, 0, Buffer.Length, myCallBack, Nothing)
        End If
    End Sub 'OnCompletedRead
End Class

In the example shown in Figure 3, the name of the file is hardwired as streams.txt in the c:\temp subdirectory. In production code, you will probably ask the user to specify a file. If you are testing this program, you should replace this file path with the name of a text file already on your disk.

Now that I have a FileStream object, I can call its BeginRead method, which provides asynchronous reading of the file—by reading a block of text into memory while my other code does its work. You must pass in a buffer so it has a place to put your data in, along with the offset into that buffer where it should begin reading. You must also pass in the length of the buffer and tell BeginRead the method you want to call back to.

You designate the method that you want to call back to by passing in a delegate. I will create that delegate in the next example as a member of my class:

Private myCallBack As AsyncCallback

The type of the delegate was determined by the author of the FileStream class, which designated that you must pass in a delegate of type AsyncCallback. The AsyncCallback delegate is defined in the documentation as follows:

Public Delegate Sub AsyncCallback( _
   ByVal asyncResult As IAsyncResult)
The AsyncCallback delegate is a subroutine (and thus returns no value) and takes as its single parameter an object that implements the IAsyncResult interface. You do not have to implement that interface yourself. All you need to do is create a method that declares a parameter of type IAsyncResult. Such an object will be passed to you by the FileStream's BeginRead method, and you'll need to return it as a token to the FileStream by calling EndRead. Here is the declaration of the method I've encapsulated in my AsyncCallback delegate:
Sub OnCompletedRead(ByVal asyncResult As IAsyncResult)
 •••
End Sub 'OnCompletedRead
I instantiate the delegate in the constructor to my class:
myCallBack = New AsyncCallback(AddressOf OnCompletedRead)
As an alternative, you can simply write:
myCallBack = AddressOf OnCompletedRead
The compiler will figure out that you are instantiating an AsyncCallback delegate based on the declared type of myCallBack.

I'm now ready to start the callback process. Begin in the test class's Run method by calling BeginRead:

Sub Run()
    inputStream.BeginRead( _
    buffer, _
    0, _
    buffer.Length, _
    myCallBack, _
    Nothing)
The first parameter is a buffer, declared in this case as a member variable, as shown here:
Private buffer() As Byte
The second parameter is the offset into that buffer. By entering 0, the data read from the disk will be written to the buffer starting at offset 0. The third parameter is the length of the buffer. The fourth parameter is the AsyncCallBack delegate you declared and instantiated earlier. The fifth and final parameter can be any object you like (or nothing) and is typically used to hold the current state of the calling object. In the case shown, you pass Nothing, a Visual Basic .NET keyword that indicates that you have no state object.

After calling BeginRead, the program can go on with its other work. In the example shown in Figure 3 that work is simulated by counting to half a million:

Dim i As Long
For i = 0 To 499999
    If i Mod 1000 = 0 Then
        Console.WriteLine("i: {0}", i)
    End If
Next i

The FileStream will go off and open the file on your behalf. It then reads from the file and fills up your buffer. When it is ready for you to process the data, the method you encapsulated with the delegate will be called. Recall that the delegated method is called OnCompletedRead:

Sub OnCompletedRead(ByVal asyncResult As IAsyncResult)
    Dim bytesRead As Integer = inputStream.EndRead(asyncResult)

    ' if we got bytes, make them a string
    ' and display them, then start up again.
    ' Otherwise, we're finished.
    If bytesRead > 0 Then
        Dim s As String = Encoding.ASCII.GetString(buffer, 0, bytesRead)
        Console.WriteLine(s)
        inputStream.BeginRead(buffer, 0, buffer.Length, myCallBack,
                              Nothing)
    End If

When the FileStream calls my method, it will pass in an instance of a class that implements the IAsyncResult interface. The first thing this method does is pass the IAsyncResult object to the FileStream's EndRead method. EndRead returns an integer indicating the number of bytes successfully read from the file. If that value is greater than 0, the buffer has data in it.

The buffer contains bytes, but you need a string to display. The buffer can be converted to a string with Encoding.ASCII.GetString—a static method that takes a buffer, an offset, and the number of bytes read and returns an ASCII string. You can then display that string to the console.

Finally, I'll call BeginRead again, passing back the buffer, the offset (again 0), the length of the buffer, and the delegate, as well as Nothing for the state object. This begins another round. Control will then return to the Run method, and the code will continue counting bytes.

I have achieved multitasking without instantiating or managing any threads on my own; I only wrote the callback mechanism and let the FileStream do the thread management.

Delegates and Events

When a user presses a button in your application, you want to be notified. When the user closes a window, changes text in a textbox, makes a choice in a listbox, and so on, you want to be notified. These happenings are called events. Windows®-based programs are event-driven, and programmers developing for Windows have been dealing with events since...well, since Windows.

The .NET environment makes events first-class objects, and implements events with delegates. Visual Basic .NET hides many of the details of implementing events, but the delegates are still there under the covers. Consider the following:

AddHandler myButton.Click, AddressOf MyButton_Click
That's really shorthand for this:
AddHandler myButton.Click, New EventHandler(AddressOf MyButton_Click)
EventHandler is the name of the implicitly defined delegate. Usually event-handling delegates in .NET are in the following form:
Public Delegate Event (sender as Object, e as EventArgs)
This delegate encapsulates a method that takes two parameters. The first, sender, represents the object that raises the event. The second parameter, e, is an object of type EventArgs or of a class derived from EventArgs. The EventArgs object will typically contain additional information that may be of use to the method handling the event. For example, you might have a clock object that fires an event every time the second changes. You might then create a ClockEventArgs class and use that to pass in the elapsed time since the clock was originally started or reset.

When you call RaiseEvent, you are calling Invoke on EventHandler (the implicitly created delegate).

Delegates Under the Covers

It turns out that the common language runtime (CLR) and the compiler are working hard together to make your delegates work. For example, in Figure 1, when I declared a delegate

Public Delegate Function WhichIsSmaller( _
  ByVal obj1 As Object, ByVal obj2 As Object) As comparison
the compiler actually created a complete class definition to support this delegate, that looks more or less like the code in Figure 4.

Figure 4 Class Definition (Close Figure 4)
Public Class WhichIsSmaller
    Inherits System.MulticastDelegate

    Public Sub New( _
      target as object, _
      pointerToMethod as int32)
    End Sub

    Public Overridable Sub Invoke( _
        ByVal firstObject As Object, _
        ByVal secondObject As Object) _
    End Sub

    Public Overridable Function BeginInvoke( _
        ByVal firstObject As Object, _
        ByVal secondObject as Object, _
        ByVal theCallback As AsyncCallback, _
        ByVal theObject As Object) As AsyncCallback
    End Function

    Public Overridable Sub EndInvoke(ByVal result As AsyncCallback)
    End Sub

You can see this class when you examine the Pair application in ILDASM, which is shown in Figure 5. ILDASM is the intermediate language disassembler that is provided with the .NET Framework SDK as a tool for examining the intermediate language code produced by the compiler.

Figure 5 Class in ILDASM (Close Figure 5)

The nested class is named for the delegate and has the access type (public) of the delegate you declared in the outer class (Pair). The class, its constructor, and its three methods are created by the compiler on your behalf. WhichIsSmaller is derived from MulticastDelegate and as such it inherits the methods and properties of its base class.

The class that was created contains a constructor that takes two parameters. The first is a reference to an object, and the second is an integer that represents the callback method. When I created the delegate, I wrote:

Dim theStudentDelegate As New _
  Pair.WhichIsSmaller(AddressOf Student.WhichStudentIsSmaller)
You may be wondering, how can this single argument (the address of Student.WhichStudentIsSmaller) be matched to the two arguments to the delegate's constructor? The compiler knows that I am creating a delegate, and it parses the delegate's arguments into the object (Student) and the method (whichStudentIsSmaller), then the compiler does the conversion.

Examine the invocation of the delegated function. In the Pair class, you'll find a Sort method that takes a Delegate as an argument, as shown here:

Public Sub Sort(ByVal theDelegatedFunc As WhichIsSmaller)
Within that method, I can invoke the delegated method, passing in the parameters it expects:
theDelegatedFunc(thePair(0), thePair(1))
When the compiler sees this invocation of the delegate, it generates code like the following:
WhichIsSmaller.Invoke(thePair(0), thePair(1))
That is, the compiler calls the Invoke method on the nested delegate class. If you double-click the Sort method of the Pair object in ILDASM and then open the disassembler, you'll find the following line of code:
IL_001c: callvirt instance valuetype Pair.DelegatesAndEvents.comparison
Pair.DelegatesAndEvents.Pair/WhichIsSmaller::Invoke(object,object)

Delegate and Multicast Delegate

Originally, the design guidelines were that single-cast delegates would derive from the Delegate class and could be Subs or Functions, while multicast delegates would derive from the MulticastDelegate class, and would only be Subs. This made sense because if you're invoking multiple methods, it makes no sense to return a value. During beta testing, Microsoft decided that this was too confusing, and so all delegates now derive from MulticastDelegate. A multicast delegate used for multicasting will typically encapsulate a Sub (not a function). You can encapsulate functions, but you must either ignore their returned values or call the delegate's GetInvocationList method and invoke each delegate manually.

Conclusion

If you're using Visual Basic .NET, you can pretty much ignore delegates when creating events. You use the With Events and AddHandler keywords and let Visual Basic .NET take care of the details for you. However, if you're creating and using callback mechanisms, delegates will be very important. And it always pays to know what a language is doing behind the scenes.

For related articles see:
Visual Basic .NET: New Programming Model and Language Enhancements Boost Development Power
Basic Instincts: New Features in Visual Basic .NET: Variables, Types, Arrays, and Properties
Basic Instincts: Exploiting New Language Features in Visual Basic .NET, Part 2
Basic Instincts: Programming with Events Using .NET

Jesse Libertyis the author of a dozen books on .NET and object-oriented software development. He is the president of Liberty Associates Inc. (http://www.LibertyAssociates.com), where he provides contract programming and training in .NET technology. Portions of this article were adapted from his books, Learning VB.NET and Programming Visual Basic .NET, 2nd Edition, both published by O'Reilly & Associates.


Web-Aware Apps: Build Hyperlinks into Your Client App with the Windows Forms LinkLabel Control

Web-Aware Apps

Build Hyperlinks into Your Client App with the Windows Forms LinkLabel Control

Dan Hurwitz
This article assumes you're familiar with C# or Visual Basic .NET
Level of Difficulty 1 2 3
Code download available at: LinkLabel.exe (49KB)
SUMMARY

LinkLabels are Windows Forms controls that enable a user to hyperlink to a URL that points to either the Web or the local directory system. While the SDK documentation discusses the control, it does not demonstrate how linking is accomplished, nor does it outline the power and flexibility the control provides. This article fills those gaps, showing how to link using the LinkLabel control. It also shows the flexibility of this control for the programmer using the .NET Framework. In addition, the author covers the large number of properties that allow you to customize your controls and accurately place them. Their built-in behaviors are also discussed, along with their use in both Visual Basic .NET and C#.

Contents
Label
LinkLabel

W
indows® Forms are the classes in the Microsoft® .NET Framework that provide functionality for building smart client applications that run on a user's desktop. They are comprised of various controls and components that provide both the user interface for and the functionality of the application. One control common to all Windows Forms applications is the Form object itself. The Form is a container control; it can be a parent to other controls that live on the surface of the form.

There are many different controls that can be contained by the form: listboxes, buttons, checkboxes, textboxes, and so forth. One of the most commonly used controls is the Label control, which displays either a read-only text string or an image. Another commonly used Windows Forms control, the LinkLabel control, is closely related to and derived from the Label control.

The LinkLabel control provides read-only text as a hyperlink, offering a user the ability to navigate to a URL. Although LinkLabels may seem simple and one dimensional, it turns out that they offer the developer surprising depth and versatility.

Label

A Label control (such as System.Windows.Forms.Label) is used in order to display read-only text on a form. If you need to display text with read/write capability, you'll need to use the TextBox or RichTextBox control, which allow users to enter text.

Labels serve several different functions. At the most basic level, they are used to display text on the client area of a form—text such as titles, paragraphs, or captions for other controls. The text displayed by a Label is contained in its Text property, which can either be set at design time or programmatically at run time.

In addition to the Text property, the Label control has many other properties. The most commonly used properties are listed in Figure 1 and Figure 2. Figure 1 lists the properties inherited from the Control class. In addition to all these properties, the Label control also has the properties described in Figure 2. Valid values of several of these properties are contained in an appropriate enumeration; these include the BorderStyle and FlatStyle enumerations (see Figure 3), as well as the ContentAlignment enumeration (see Figure 4).

Figure 4 ContentAlignment Enumeration Values (Close Figure 4)

Value | Vertical Alignment | Horizontal Alignment
BottomCenter | Bottom | Center
BottomLeft | Bottom | Left
BottomRight | Bottom | Right
MiddleCenter | Middle | Center
MiddleLeft | Middle | Left
MiddleRight | Middle | Right
TopCenter | Top | Center
TopLeft | Top | Left
TopRight | Top | Right
Figure 3 BorderStyle and FlatStyle Enumeration Values (Close Figure 3)

BorderStyle Value | Description
Fixed3D | 3D border.
FixedSingle | Single line border.
None | No border.
FlatStyle Value | Description
Flat | Control appears flat.
Popup | Control appears 3D when the mouse cursor is over it, flat otherwise. The 3D appearance is the same as Standard.
Standard | Control appears 3D.
System | Control appearance controlled by user's operating system. All image and alignment-related properties of the control are ignored, as well as Button.BackColor.
Figure 2 Label Properties (Close Figure 2)

Property | Value Type | Description
AutoSize | Boolean | Read/write. Indicates that the label will automatically resize to display the entire Text property. Default is false. Yields the same size label as using both PreferredWidth and PreferredHeight.
BackgroundImage | Image | Read/write. The background image displayed on the label. Translucent or transparent colors are not supported as background images.
BorderStyle | BorderStyle | Read/write. The border style for the label. Values are members of the BorderStyle enumeration. Default is BorderStyle.None.
FlatStyle | FlatStyle | Read/write. The flat style appearance of the label. Values are members of the FlatStyle enumeration. Default is FlatStyle.Standard.
Image | Image | Read/write. The image displayed on the control. Cannot be used for the same control at the same time as the ImageList or ImageIndex properties.
ImageAlign | ContentAlignment | Read/write. Aligns image displayed in the label. Values are members of the ContentAlignment enumeration. Default is ContentAlignment.MiddleCenter.
ImageIndex | Integer | Read/write. Zero-based index value of the Image object contained in the ImageList.
ImageList | ImageList | Read/write. The image list which contains the images available for display in the label control. One image is displayed at a time, selected by the ImageIndex property. The ImageList stores a collection of Image objects.
PreferredHeight | Integer | Returns the height of the label, in pixels, if a single line of text was displayed. The value returned reflects the current font for the label.
PreferredWidth | Integer | Returns the width of the label, in pixels, if a single line of text was displayed, assuming no line wrapping. The value returned is the current font for the label.
TextAlign | ContentAlignment | Read/write. Aligns text displayed in the label. Values are members of the ContentAlignment enumeration. Default is ContentAlignment.TopLeft.
UseMnemonic | Boolean | Read/write. Value indicating if an ampersand (&) character in the label's Text property will be interpreted as an access key character. If true (the default), the user can move focus to the control following in the tab order by pressing the ALT key in combination with the access key.
Figure 1 Properties Inherited from Control (Close Figure 1)

BackColor | ForeColor | Name | Text
Bottom | Height | Parent | Top
Enabled | Left | Right | Visible
Font | Location | Size | Width

LinkLabel

Closely related to and derived from the Label control is the LinkLabel control (System.Windows.Forms.LinkLabel), which displays one or more hyperlinks. In addition to the properties and features I discussed for the Label control, the LinkLabel has the additional properties listed in Figure 5. The behavior of the link is controlled by the LinkBehavior property, which has values from the LinkBehavior enumeration, listed in Figure 6.

Figure 6 LinkBehavior Enumeration Values (Close Figure 6)

Value | Description
AlwaysUnderline | The link text is always underlined.
HoverUnderline | The link text is underlined only while the mouse is hovering over the link.
NeverUnderline | The link text is never underlined. It can be distinguished using the LinkColor property.
SystemDefault | The link text is displayed using the system default.
Figure 5 LinkLabel Properties (Close Figure 5)

Property | Value Type | Description
ActiveLinkColor | Color | Read/write. The color to display an active link. A link is active while it is being clicked. The default color is specified by the system, typically Color.Red.
DisabledLinkColor | Color | Read/write. The color to display a disabled link. Disabled links do not raise the LinkClicked event.
LinkArea | LinkArea | Read/write. The characters within the LinkLabel control that represents a hyperlink. The Start property of the LinkArea structure is the zero-based index of the character in the LinkLabel Text property representing the start of the link. The Length property of the LinkArea structure is the number of characters, including the starting character, comprising the link.
LinkBehavior | LinkBehavior | Read/write. The behavior of the link. The values must be a member of the LinkBehavior enumeration, listed in Figure 6. The default is LinkBehavior.SystemDefault.
LinkColor | Color | Read/write. The color to display a normal link. The default color is specified by the system, typically Color.Blue.
Links | LinkLabel.LinkCollection | Read-only. Returns the collection of links contained within the LinkLabel.
LinkVisited | Boolean | Read/write. Value indicating if the link should display as though it were visited. Default is false. Does not automatically get set to true; this must be done by code in the LinkClicked event handler. Only applies to the first link defined for the control. Use the Visited property of the LinkLabel.Link class for multiple links in a control.
VisitedLinkColor | Color | Read/write. The color to display a link that has previously been visited. The default color is specified by the system, typically Color.Purple.

The LinkLabel control does not automatically provide an actual link to anything, nor (by default) does it change the color of the link once it has been visited, as most browsers do. It is easy to add these features, however, using the properties and events of the LinkLabel class (see Figure 5 and Figure 6).

When a LinkLabel control is clicked, it raises an event called LinkClicked, which has an event argument of the LinkLabelLinkClickedEventArgs. This event argument exposes a single read-only property called Link, which returns the link that was clicked. Several different ways of handling this event will be demonstrated later using both C# and Visual Basic® .NET.

The LinkLabel control exposes a Links property (listed in Figure 5), which is of the LinkLabel.LinkCollection type, a subclass of LinkLabel. This is a collection of all the links contained by the LinkLabel control. As a class in itself, this collection exposes some useful properties and methods (see Figure 7). Each link within a LinkLabel control is represented by a member of the LinkLabel.Link class. Each Link has the properties that are listed in Figure 8.

Figure 8 LinkLabel.Link Properties (Close Figure 8)

Property | Value Type | Description
Enabled | Boolean | Read/write. Value indicating if the link is enabled. If disabled, then clicking on the link does not raise the LinkClicked event and the link will appear in the color specified by the DisabledLinkColor property.
Length | Integer | Read/write. The number of characters in the link text.
LinkData | Object | Read/write. Data associated with the link.
Start | Integer | Read/write. The zero-based starting location of the link within the text of the LinkLabel.
Visited | Boolean | Read/write. Value indicating if the link should display as though it were visited. Default is false. Does not automatically get set to true; this must be done by code in the LinkClicked event handler. Used for multiple links in a control.
Figure 7 LinkLabel.LinkCollection Properties and Methods (Close Figure 7)

Property | Value Type | Description
Count | Integer | Read-only. The number of links in the collection.
Item | LinkLabel.Link | Read/write. The link at the specified index. In C#, this is the indexer for the class.
Method | Return Value | Description
Add | Void / None | Adds a link to the collection. Overloaded.
Clear | Void / None | Removes all the links from the collection.
Contains | Boolean | Returns true if the specified link is contained in the collection.
GetEnumerator | IEnumerator | Returns an enumerator used to iterate through the collection.
IndexOf | Integer | Returns index of specified link in the collection.
Remove | Void / None | Removes the specified link from the collection.
RemoveAt | Void / None | Removes the link with the specified zero-based index from the collection.

Figure 9 demonstrates many of the features of the LinkLabel control in Visual Basic .NET. The code download for this article contains a C# version as well (see the link at the top of this article). In these examples, there are five LinkLabel controls, each implemented differently. The first three LinkLabels are single links to Web pages, the fourth is a LinkLabel containing four different links, and the last links to a text file, which automatically opens up Notepad (or whichever application on the user's system is the default for files with a .txt extension).

Figure 9 LinkLabels in Visual Basic .NET (LinkLabels.vb) (Close Figure 9)
Option Strict On
imports System
imports System.Drawing
imports System.Windows.Forms

namespace ProgrammingWinApps
   public class LinkLabels : inherits Form
      Dim lnkMsft as LinkLabel
      Dim lnkLA as LinkLabel

      public sub New()
         Text = "LinkLabels"
         Size = new Size(300,250)

         '  use Text property & LinkArea
         lnkMsft = new LinkLabel()
         lnkMsft.Parent = me
         lnkMsft.Text = "www.microsoft.com"
         lnkMsft.Location = new Point(0,0)
         lnkMsft.AutoSize = true
         lnkMsft.BorderStyle = BorderStyle.None
         lnkMsft.LinkArea = new LinkArea(4,9)
         AddHandler lnkMsft.LinkClicked, AddressOf lnkMsft_LinkClicked

         '  use Add
         lnkLA = new LinkLabel()
         lnkLA.Parent = me
         lnkLA.Text = "Liberty Associates"
         lnkLA.Links.Add(0,7,"www.LibertyAssociates.com")
         'set other properties here
         AddHandler lnkLA.LinkClicked, AddressOf lnkLA_LinkClicked

         '  use generic Add & generic handler
         Dim lnkSterSol as LinkLabel = new LinkLabel()
         lnkSterSol.Parent = me
         linkSterSol.Text = "Sterling Solutions"
         linkStersol.Links.Add(0.lnkSterSol.Text.ToString().Length, _
                                "www.SterSol.com")
         'set other properties here
         AddHandler lnkSterSol.LinkClicked, _
                               AddressOf lnkGeneric_LinkClicked

         '  multiple links & generic handler
         Dim lnkMulti as LinkLabel = new LinkLabel()
         lnkMulti.Parent = me
         lnkMulti.Text = "Ford Chevy VW Porsche"
         linkMulti.Links.Add(0,4,"www.Ford.com")
         'set other properties here
         AddHandler lnkMulti.LinkClicked, AddressOf lnkGeneric_LinkClicked

         '  link to text file
         Dim lnkTxtFile as LinkLabel = new LinkLabel()
         lnkTxtFile.Parent = me
         linkTxtFile.Text = "Gettysburg Address"
         linkTxtFile.Links.Add(0, lnkTxtFile.Text.ToString().Length, _
                                "c:\\GettysburgAddress.txt")
         'set other properties here
         AddHandler lnkTxtFile.LinkClicked, _
                          AddressOf lnkGeneric_LinkClicked

      end sub

      public shared sub Main()
         Application.Run(new LinkLabels())
      end sub

      private sub lnkMsft_LinkClicked(ByVal sender as object, _
                              ByVal e as LinkLabelLinkClickedEventArgs)
         lnkMsft.Links(lnkMsft.Links.IndexOf(e.Link)).Visited = true
         System.Diagnostics.Process.Start(lnkMsft.Text)
      end sub

      private sub lnkLA_LinkClicked(ByVal sender as object, _
                           ByVal e as LinkLabelLinkClickedEventArgs)
         lnkLA.LinkVisited = true
         System.Diagnostics.Process.Start(e.Link.LinkData.ToString())
      end sub

      private sub lnkGeneric_LinkClicked(ByVal sender as object, _
                              ByVal e as LinkLabelLinkClickedEventArgs)
         dim lnk as LinkLabel = new LinkLabel()
         lnk = CType(sender, LinkLabel)
         lnk.Links(lnk.Links.IndexOf(e.Link)).Visited = true
         System.Diagnostics.Process.Start(e.Link.LinkData.ToString())
      end sub
   end class
end namespace

When either of these programs are compiled and run, you will get the form shown in Figure 10. The underlined links are blue, as is the line showing the four automobile brands. The Gettysburg Address line is red.

Figure 10 LinkLabels
Figure 10 LinkLabels

The LinkLabels program starts by declaring two LinkLabel controls as form member variables. These two controls need to be declared this way because they are referenced outside the constructor; the other controls aren't, and instead can be local to the constructor method. In C#, this would look like the following:

LinkLabel lnkMsft;
LinkLabel lnkMsdnMag;
In Visual Basic .NET it would look like this:
dim lnkMsft as LinkLabel
dim lnkMsdnMag as LinkLabel

The first LinkLabel control to be created, lnkMsft, provides a link to the Microsoft Web site. As you can see in Figure 10, the Text property of the control is the full URL of the Web site, but the actual link is comprised only of the word Microsoft. The implementation technique used here requires that the Text property contains the full URL, but the limited characters comprising the link just demonstrates how that is done. In C#, you'd do the following:

lnkMsft = new LinkLabel();
lnkMsft.Parent = this;
lnkMsft.Text = "www.microsoft.com";
lnkMsft.Location = new Point(0,0);
lnkMsft.AutoSize = true;
lnkMsft.BorderStyle = BorderStyle.None;
lnkMsft.LinkArea = new LinkArea(4,9);
lnkMsft.LinkClicked +=
   new System.Windows.Forms.LinkLabelLinkClickedEventHandler(
      lnkMsft_LinkClicked);

The Visual Basic .NET code is almost identical, except for the typical language differences (like no trailing semicolons). The last statement at the end of the snippet, which adds the event handler method to the LinkClicked event delegate, is written differently. The Visual Basic .NET equivalent for adding the handler to the delegate is as follows:

AddHandler lnkMsft.LinkClicked, AddressOf lnkMsft_LinkClicked
In this snippet, all the lines of code are similar to those used for a normal Label control except for the last two statements, which set a value for the LinkArea property and the event handler for the LinkClicked event.

The LinkArea property is used to specify which characters in the Text property comprise the actual link. The first argument specifies the zero-based starting character and the second argument specifies the number of characters, including the starting character, to include in the link.

This control has its own event handler method. As you will see shortly, some of the LinkLabel controls on this form use a generic event handler. In C#, the event handler method looks like this:

private void lnkMsft_LinkClicked(object sender,
                                 LinkLabelLinkClickedEventArgs e)
{
     lnkMsft.Links[lnkMsft.Links.IndexOf(e.Link)].Visited = true;
     System.Diagnostics.Process.Start(lnkMsft.Text);
}
The Visual Basic .NET version is similar:
private sub lnkMsft_LinkClicked(ByVal sender as object, _
                                ByVal e as LinkLabelLinkClickedEventArgs)
     lnkMsft.Links(lnkMsft.Links.IndexOf(e.Link)).Visited = true
     System.Diagnostics.Process.Start(lnkMsft.Text)
end sub
This code uses the Link property exposed by the event args to index the proper link. The code then uses this link in order to set the Visited property of the LinkLabel.Link to true. This has the effect of changing the color of the link to the VisitedLinkColor. Since the VisitedLinkColor property has not been set for this control, it defaults to purple.

The second line in the method invokes a static form of the System.Diagnostics.Process.Start method. This method takes a file name (at the minimum) and starts the default application associated with files with that file extension. Notice that in this method, the file name passed in is the Text property of the control. It is also possible to pass in a file name which is not displayed as the Text property of the control, as you will see on the other LinkLabels in this example. Since the file name passed in is actually a URL, the default application that runs is a Web browser.

The next LinkLabel control on the form, lnkMsdnMag, displays a link to msdn.microsoft.com/msdnmag. This is very similar to the Microsoft link, except that it adds a link to the Links collection rather than specifying the LinkArea property. This is done with the following line of code:

lnkMsdnMag.Links.Add(0,4,"msdn.microsoft.com/msdnmag")
The Add method takes three arguments. The first two are the same as for LinkArea—the zero-based starting character and the length of the link. The third argument is the URL to which you want to link. Notice that this LinkLabel displays one text string, specified in the Text property for the control, but links to a URL different from that text string, specified in the Links property.

This LinkLabel control also has its own two-line event handler for the LinkClicked event, reproduced here in C#:

private void lnkMsdnMag_LinkClicked(object sender,
                         LinkLabelLinkClickedEventArgs e)
{
   lnkMsdnMag.LinkVisited = true;
   System.Diagnostics.Process.Start(e.Link.LinkData.ToString());
}
And here's the same code in Visual Basic .NET:
private sub lnkMsdnMag_LinkClicked(ByVal sender as object, _
                     ByVal e as LinkLabelLinkClickedEventArgs)
   lnkMsdnMag.LinkVisited = true
   System.Diagnostics.Process.Start(e.Link.LinkData.ToString())
end sub

Similar to the previous event handler, the first line sets the LinkVisited property to true so that it will display with the color specified by the VisitedLinkColor. It's not set for this control, so it defaults to purple. The LinkVisited property should only be used for a LinkLabel that has a single link. The Microsoft.com LinkLabel could have also used the simpler LinkVisited property rather than indexing into the Links collection and setting the Visited property; in this case they are equivalent. Later, I will present a situation where the indexing syntax is required.

The MsdnMag LinkLabel links to the URL specified. This is accomplished with the third argument of the Add method that I just mentioned in conjunction with the argument passed to the Process.Start method, as shown here:

System.Diagnostics.Process.Start(e.Link.LinkData.ToString())
Here, the LinkData property contains the URL. This is converted to a string, using the ToString method, and that is given to the Start method. Since this is a valid URL, a browser will be instantiated.

The next LinkLabel control on the form, lnkGotDotNet, displays a hyperlink to GotDotNet. This is very similar to the MsdnMag hyperlink, but with two changes. First, the Add method dynamically calculates the length of the Text property to specify the length of the linked characters:

lnkGotDotNet.Links.Add(0,lnkGotDotNet.Text.ToString().Length, _
                     "www.GotDotNet.com")
This ensures that the entire Text property will properly display as a link without having to manually count the characters and hardcode the literal number in the arguments.

The second change is a move to a generic event handler, namely, one that can be used with any number of LinkLabel controls. The line that adds this generic event handler to the LinkClicked event delegate in C# is:

lnkGotDotNet.LinkClicked +=
   new System.Windows.Forms.LinkLabelLinkClickedEventHandler(
      lnkGeneric_LinkClicked);
In Visual Basic .NET it is:
AddHandler lnkGotDotNet.LinkClicked, AddressOf lnkGeneric_LinkClicked

The generic event handler method is called lnkGeneric_LinkClicked. It is shown here in C#:

private void lnkGeneric_LinkClicked(object sender,
                                    LinkLabelLinkClickedEventArgs e)
{
   LinkLabel lnk = new LinkLabel();
   lnk = (LinkLabel)sender;
   lnk.Links[lnk.Links.IndexOf(e.Link)].Visited = true;
   System.Diagnostics.Process.Start(e.Link.LinkData.ToString());
}
In Visual Basic .NET, it looks like this:
private sub lnkGeneric_LinkClicked(ByVal sender as object, _
                             ByVal e as LinkLabelLinkClickedEventArgs)
   dim lnk as LinkLabel = new LinkLabel()
   lnk = CType(sender, LinkLabel)
   lnk.Links(lnk.Links.IndexOf(e.Link)).Visited = true
   System.Diagnostics.Process.Start(e.Link.LinkData.ToString())
end sub
The first two lines in the method declare a LinkLabel variable, lnk, and instantiates it with the object called sender, which raised the event. That object is cast to the LinkLabel type in the second line in the method. Then the Visited property of the Link in the collection is set to true, just as was done for the Microsoft.com event handler. Finally, the Process.Start method is called, just as was done for the MsdnMag event handler.

The next LinkLabel example on the form demonstrates a control with multiple links in a single control. The Text property contains the text to display for four different links. There are four separate Add statements, each of which specifies a starting character, the number of characters in the link, and the URL to link to:

lnkMulti.Text = "Ford  Chevy  VW  Porsche"
lnkMulti.LinkBehavior = LinkBehavior.HoverUnderline
lnkMulti.Links.Add(0,4,"www.Ford.com")
lnkMulti.Links.Add(6,5,"www.chevrolet.com")
lnkMulti.Links.Add(13,2, "www.vw.com")
lnkMulti.Links.Add(17,7,"www.porsche.com")
This LinkLabel control also demonstrates the use of the LinkBehavior property, which is why these links did not display as underlined, as shown in Figure 10. These do display as underlined when the mouse hovers over each individual link.

The lnkMulti control uses the generic LinkClicked event handler, which uses the indexed Visited property rather than the unitary LinkVisited property. Consequently, each individual link correctly displays if it has been visited. If you had substituted the following line of code into the generic event handler

lnk.LinkVisited = true
then the first link in the collection (Ford), would have displayed as visited, irrespective of which of the four links was actually clicked.

The final LinkLabel control in the application links to a file on the local hard drive rather than a URL. In this case, it is a text file containing Lincoln's Gettysburg Address, located in the root of the C drive. The generic event handler is again used. The file name has a .txt extension, so the default application for that extension is used to open it (typically, Notepad).

This LinkLabel also sets several of the display properties that change the default colors and behavior of the link. The code that creates the control in C# is reproduced in Figure 11. The code for the same function in Visual Basic .NET is shown in Figure 12.

Figure 12 Default LinkLabel Behavior (Visual Basic) (Close Figure 12)
dim lnkTxtFile as LinkLabel = new LinkLabel()
lnkTxtFile.Parent = me
lnkTxtFile.Text = "Gettysburg Address"
lnkTxtFile.Location = new Point(0,100)
lnkTxtFile.AutoSize = true
lnkTxtFile.BorderStyle = BorderStyle.None
lnkTxtFile.LinkBehavior = LinkBehavior.NeverUnderline
lnkTxtFile.ActiveLinkColor = Color.Green
lnkTxtFile.LinkColor = Color.Red
lnkTxtFile.VisitedLinkColor = Color.Orange
lnkTxtFile.Links.Add(0,lnkTxtFile.Text.ToString().Length, _
   "c:\\GettysburgAddress.txt")
AddHandler lnkTxtFile.LinkClicked, AddressOf lnkGeneric_LinkClicked
Figure 11 Default LinkLabel Behavior (C#) (Close Figure 11)
LinkLabel lnkTxtFile = new LinkLabel();
lnkTxtFile.Parent = this;
lnkTxtFile.Text = "Gettysburg Address";
lnkTxtFile.Location = new Point(0,100);
lnkTxtFile.AutoSize = true;
lnkTxtFile.BorderStyle = BorderStyle.None;
lnkTxtFile.LinkBehavior = LinkBehavior.NeverUnderline;
lnkTxtFile.ActiveLinkColor = Color.Green;
lnkTxtFile.LinkColor = Color.Red;
lnkTxtFile.VisitedLinkColor = Color.Orange;
lnkTxtFile.Links.Add(0,lnkTxtFile.Text.ToString().Length,
                     @"c:\GettysburgAddress.txt");
lnkTxtFile.LinkClicked +=
   new System.Windows.Forms.LinkLabelLinkClickedEventHandler(
      lnkGeneric_LinkClicked);

The LinkVisited and Visited properties are reset every time the application is run, unlike the default behavior of most browsers. If you want those properties to persist from one program execution to the next, you must save the information somewhere, such as in an XML configuration file.

For related articles see:
LinkLabel Control
Introduction to the Windows Forms LinkLabel Control
Linking to an Object or Web Page with the Windows Forms LinkLabel Control

Dan Hurwitzis the coauthor, with Jesse Liberty, of Programming ASP.NET (O'Reilly, 2002). He specializes in the design and development of Web and desktop applications using the Microsoft .NET Framework. This article was adapted from the upcoming book, Programming .NET Windows Applications by Dan Hurwitz and Jesse Liberty, to be published shortly by O'Reilly.


Editor's Note: Rich Clients Count Too

Editor's Note
Rich Clients Count Too

It's the dead of winter here at the MSDN Magazine offices, and we're having a blast. Temperatures are making their daily struggle to stay above zero. The tempting aroma of chestnuts roasting on an open subway ventilation grate fills the city with a wintry mix of excitement and anticipation. The last of the rampaging hordes of malevolent Santas have finally been brought to justice, and the city is looking to move past the Kris Kringle incidents into a new spirit of community.

Since we're sharing in this new spirit, we felt it was time for a heart-to-heart with our readers. For over two years now, we've listed a few of the swell things that the Microsoft .NET Framework can bring to your coding efforts. (Okay, so "a few" has turned into over 100 articles.) There's a reason the name ".NET" was coined for these services—.NET is packed with features designed to make it easier to write interconnected, networked code. Whether you're writing a program that connects to the Internet reliably or you're creating an XML Web Service, the classes in the .NET Framework have been designed to make formerly daunting tasks easier.

However, there's still a universe of programs that aren't about to become Web Services and which aren't centered around the Internet. Maybe these apps only feature a networked connection for sending help requests. Perhaps they never really need a connection at all. These are the types of applications that people have designed for the past 15 years for the Windows platform—there are probably tens of thousands of apps out there now that take advantage of the underlying operating system.

As we've discussed in previous Editor's Notes, the world of development has moved back and forth for years, from dumb clients and servers to PC-centric computing, then back to the client/server model with the rise of the Internet. We're now in the age of two-way computing, where machines can interact with each other as peers, as well as acting as standalones. With functionality that wouldn't have been dreamed of even 20 years ago, these desktop-centric products are often called "smart clients" or "rich clients."

If you're looking at your app's design, it's relatively easy to figure out what sort of model you should adopt during the design phase. Just ask what your app is going to be doing. Is its functionality based around the messages returned by another service? Are the only initial menu options going to be File | Connect and File | Exit? If so, you're looking at a networked thin client.

But much as the pundits (including us, at times) would have you believe, there's plenty of room for rich clients in a thin-client world. Be proud of what your program is! Celebrate your diversity! Desktop-centric apps are back, and with Windows Forms they're better than ever! A couple of months ago in Redmond, we talked to several people who were focused on making the client-side user experience as fabulous as possible. These chats inspired us to investigate some topics that might be helpful to the myriad programmers who are working on programs that might not need to be connected to the Internet at all times.

For instance, printing is a sometimes unglamorous, yet always important topic. The ability to print polished output is one of those little touches that turns a project into a real application. Threading, on the other hand, is never going to be a "feature" of an app, but at the same time it affects what users think of your program more than any other factor except UI design. Of course, threading is a big concern when you're working with data that's carried over the Internet. With multisecond latency, even the best-coded apps will appear to be frozen if you don't take advantage of asynchronous features like BeginRead/EndRead.

This concern isn't unique to networked client/server apps. When you're reading large disk files, you run into the same problems—but most programs treat disk access and other lengthy tasks as synchronous actions. This freezes the UI while a file loads. In this issue, we'll show you how to use multiple threads with Windows Forms to make your app snappier and more responsive.

Even rich desktop apps can benefit from behind-the-scenes networking, however. One of the grooviest features of Windows is Windows Update. Without bothering the user, it automatically checks for new downloadable patches and updates, and it's smart enough to figure out the machine's connection status first. What's the magic behind this feature? It's the little-known Background Intelligent Transfer Service API—a.k.a. BITS. In this issue, we'll show you how to give your application the auto-update feature you've always dreamed of implementing. Yes, in the spirit of the season, we're making your auto-update dreams come true.

—J.T.

ActiveX, JScript, Microsoft, MSDN, PowerPoint, Visual Basic, Visual C++, Visual Studio, Windows, Windows NT, and Win32 are registered trademarks of Microsoft Corporation. Visual J# is a trademark 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
Theresa W. Carey

Contents
Develop IVI-COM-compliant Instrument Drivers
Make a Movie from Your Desktop
Combobox Controls
Subscribe to ASP.NET Components
XML Visual Code Generator
Audit that Database
Check Out the Changes
The Bookshelf

Develop IVI-COM-compliant Instrument Drivers

Vektrex Electronic Systems Inc. has announced the launch of a new version of VIVID, a software development toolkit for the generation of IVI-COM-compliant instrument drivers. VIVID links the many IVI Foundation specifications into a full lifecycle environment and produces IVI-COM drivers that are compliant with the latest specifications approved by the IVI Foundation.

VIVID allows the driver designer to graphically view the driver hierarchy, automate the code generation, provide control of instruments, and construct context-sensitive HTML-based help. VIVID is designed for use by test equipment manufacturers, systems integrators, and users of automated functional test equipment. The program operates on a standard PC under the Windows® environment.

Make a Movie from Your Desktop

TechSmith Corporation recently announced an update to Camtasia Studio, its software suite used to create videos of PC desktop activity. Using Camtasia Studio, you can turn these screen recordings into Flash presentations or industry-standard video files. The program enables users to record the activity on a Windows desktop, add narration, insert call-outs or annotations, and add other production effects.

The output can be used for demonstrating solutions to software problems, creating employee or customer training materials, and explaining concepts visually.

There are five components in Camtasia Studio. Camtasia Recorder records videos in real time, including cursor movements or any on-screen activity. Voice narration and text annotations can be added during the recording.

Camtasia Producer edits a video and provides you with production capabilities such as transition effects, audio editing, and storyboard layouts. A second audio track can be added for additional voice-overs or instructions. Many multimedia formats are supported, including Macromedia Flash.

Camtasia Effects enables you to add elements to a video with frame-by-frame precision. These include call-outs, text boxes, images, and free-hand drawings.

Finally, Camtasia MenuMaker creates a CD-ROM interface to allow users to index Camtasia Studio videos, Microsoft® PowerPoint® files, Microsoft Word documents, and more.

Camtasia Player is a compact video player that requires 320KB of hard drive storage space. It allows you to play back Camtasia Studio videos.

Combobox Controls

Softel vdm Inc. has released an upgrade of SftBox/ATL 3.0, its combobox control. This ActiveX® user interface control offers complete support for Windows XP, including themes, right-to-left reading support for Hebrew and Arabic versions of Windows, icons and metafiles support, tooltips, and scrolltips.

SftBox/ATL can be used with all major languages and development tools, such as the Microsoft .NET Framework, C#, Visual Basic®, Visual C++®, Delphi, and C++Builder. SftBox/ATL 3.0 also supports all current versions of Windows.

Use of the product with Microsoft Internet Explorer is fully supported, so the control can be used directly on Web pages. A digitally signed cabinet file is also included for this purpose.

Subscribe to ASP.NET Components

ComponentOne LLC has released ComponentOne Studio for ASP.NET, an ASP.NET Web Forms components subscription that delivers ComponentOne ASP.NET Web Forms components, updates and upgrades, select product betas, new ComponentOne Web Forms releases, and e-mail support for one year.

This release includes ComponentOne WebGrid for ASP.NET, an enterprise grid control; ComponentOne WebChart for ASP.NET, a charting tool; ComponentOne WebDataObjects for ASP.NET, data components; ComponentOne WebMenus and WebBars for ASP.NET, menu and toolbar components; and ComponentOne PayPal eCommerce for ASP.NET, a PayPal eCommerce solution.

ComponentOne Studio for ASP.NET is also available as part of ComponentOne Studio Enterprise, a components subscription for ActiveX, .NET, and ASP.NET that combines three individual component subscriptions: ComponentOne Studio for ActiveX, ComponentOne Studio for .NET, and ComponentOne Studio for ASP.NET.

XML Visual Code Generator

Canam Software Labs has released XML Thunder, a visual XML designer and code generator specifically designed for creating XML data handling program code. Using XML Thunder, developers can create XML processing program code, called XML handlers, in a visual environment.

XML Thunder can be used to design and generate two distinct types of XML handling program code: XML Writers and XML Readers. An XML Writer is a subprogram that uses data passed to it to create an XML document and writes it to a data buffer. Conversely, an XML Reader is a subprogram that parses XML data that's passed to it in a buffer and then populates the corresponding program data structures.

XML Thunder's visual designer allows you to map XML elements and attributes to program-specific data structures. The XML document structure can be manipulated in a visual design environment, allowing developers to design XML document-handling subprograms without the burden of having to perform low-level programming or to learn using XML parsers and writing parsing logic. The appropriate source code is generated to implement the mapping between the desired XML elements and program-specific data structures.

Audit that Database

Lumigent Technologies Inc. has introduced Entegra, which is designed to create a complete audit trail of a file's data access, including changes to database structure and permissions. With a record of who viewed or changed data or database structures, companies can protect the privacy and integrity of data such as customer, patient, or employee records and be certain that there has been no unauthorized access. With real-time notification of database changes, firms can facilitate remedial action or protect the database from potential damage.

Entegra is an extension of proprietary technology for analyzing database transaction logs that Lumigent offers in its Log Explorer for Microsoft SQL Server™ and Oracle databases. Entegra uses nontrigger audit agents to collect data from multiple servers. A centralized management console enables configuration of target servers for audit, including specifying types of database activities to be tracked. Entegra supports SQL Server 2000 and SQL Server 7.0.

Check Out the Changes

SlickEdit Inc. announced the availability of the DIFFzilla Software Developer Kit as part of the enhanced Visual SlickEdit OEM Edition product. The DIFFzilla file differencing system, previously only available as part of the Visual SlickEdit editing environment, is now available as a standalone SDK. DIFFzilla allows developers to view changes between two files, directories, or directory trees with side-by-side editing and color-coding capabilities enabled for more than 30 programming languages. Supported languages include C/C++, C#, HTML, XML, Unicode, and COBOL.

DIFFzilla gives developers the ability to view which symbols and functions were changed or added in new versions, view what changed on each line, and merge three versions of a file into one. DIFFzilla allows developers to edit and update files being compared in real time. It can also work with other software development tools such as network management products, performance testing and debugging tools, embedded systems tools and applications, hardware configuration and programming tools, and manufacturing automation products.

The Slick-C macro capability allows OEMs to customize commands, dialogs, configurations, and settings. The SDK provides public APIs and libraries along with sample applications in order to assist OEMs in integrating the DIFFzilla differencing engine into their own products.

The Bookshelf

No Starch Press has released Flash: The Future, a guide to developing Flash applications for handheld computers, wireless devices, and other platforms. Written by a team of developers headed by Jon Warren Lentz, Flash: The Future is aimed at intermediate to advanced Flash developers. The book shows readers how to work with either Flash 5 or Flash MX in order to design Flash applications and games for Pocket PCs, manage sound, button, file size, and other limitations that developers encounter when creating content for devices.

In addition, you'll learn how to work with color and optimize for different screen sizes, memory, and CPUs. The book will also show you how to work within the limitations of the browser, Pocket Internet Explorer, or build custom standalone players; use advanced ActionScript techniques optimized for mobile devices; integrate with Microsoft eMbedded Visual C++ or eMbedded Visual Basic for business applications, and prepare Flash animation for video or DVD; and build interfaces for interactive TV.

The CD-ROM contains code and examples from the book plus bonus developer tools for Pocket PCs. Find out more at http://www.nostarch.com .

Contacts

Canam Software Labs Inc 90 Matheson Boulevard West, Suite 101, Mississauga, Ontario L5R 3R3, 905-712-3840, http://www.canamsoftware.com

ComponentOne LLC 4516 Henry Street, Suite 500, Pittsburgh, PA 15213, 800-858-2739, 412-681-4343, http://www.componentone.com

Lumigent Technologies Inc. 289 Great Road, Acton, MA 01720, 866-586-4436, http://www.lumigent.com

SlickEdit Inc. 3000 Aerial Center Parkway, Suite 120, Morrisville, NC 27560, 800-934-EDIT, 919-473-0070, http://www.slickedit.com

Softel vdm Inc. 1436 Kinglet Drive, Punta Gorda, FL 33950, 941-505-8600, http://www.softelvdm.com

TechSmith Corporation 2405 Woodlake Drive, Okemos, MI 48864, 800-517-3001, 517-381-2300, http://www.techsmith.com

Vektrex 10225 Barnes Canyon Road, Suite A213, San Diego, CA 92121, 877-835-8739, 858-558-8282, http://www.vektrex.com

Send your New Stuff to Theresa at newstuff@microsoft.com.

Theresa W. Careyis a freelance writer who lives in California. Her byline has appeared in Barron's and PC World.


Web Q&amp;A: Data Shredding, Updating the Status Bar, and More

Web Q&A
Data Shredding, Updating the Status Bar, and More
Edited by Nancy Michell

QLast month you mentioned data shredding in a question about converting from relational data formats to XML. What exactly is data shredding and what do I need to know about it?

AHere shredding refers to extracting relevant data out of hierarchical XML and putting it into flat relational storage.

Shredding is an open problem with many different solutions, but in general, the more hierarchical the XML is (that is, the more deeply nested the data), the more joins are required to turn the flat, shredded data back into XML. Joins are expensive, so the more deeply nested the original XML, the more expensive the data's reconstitution becomes. XML also has other context information, like in-scope namespaces, that accumulates on the stack during parsing and therefore also contributes to performance slowdown.

For example, given very simple, flat XML like this

<x y="a" z="b c"/>
you could choose several different relational layouts. Here are three examples. A single X table with y and z columns:
X    y     z
     a     b c
Separate X, Y, and Z tables joined together:
X    ykey zkey
      0    0

Y    xkey value
      0    a

Z    xkey value
      0    b
      0    c
Separate tables for attributes, elements, and names, and a pivot table to describe which attributes go with which elements:
Elements    pk  namekey
             0   0

Attributes  pk  namekey     value
             0   1           a
             1   2           b c

Names       pk  local-name  namespace-uri
             0   x           NULL
             1   y           NULL
             2   z           NULL

AttributeList  elementkey  attributekey
                0           0
                0           1

The problem with the first two approaches is that you have to know the structure of the XML beforehand (to describe the tables or columns that are used). The third approach is much more general, but requires many joins even for simple XML data. And all three approaches invariably require joins the minute that the XML becomes nested (when the x element can contain other elements); you'll generally need at least one join per level of depth in the XML. The XML-to-relational mapping problem is something that could fill a whole book. Microsoft, and lots of other companies are working on this problem.

The previous examples illustrate the variety of solutions. They also show that no matter what method you choose, the shredded model becomes complex and expensive to reconstruct. Many companies use hybrid approaches in which they shred the data to query it efficiently (using Microsoft® SQL Server™) but never reconstruct it; instead, they also store a copy of the original XML. This solution introduces its own problems (like keeping the two copies in sync), but performs better than pure XML or pure relational data alone.

QMy program, written in Visual Basic® .NET, does file copying inside a loop. For each file copied, I update the status bar text. This works fine as long as my form has the focus. The minute a user brings another window into focus, the text no longer updates. I've tried using me.update and mystatusbar.update without any luck. Even if I return focus to the form, the updating continues to not work. What's the secret? Unfortunately, these don't work:

Application.DoEvents()
Statusbar.Update()
Statusbar.Refresh()

AAll you really need is a call to Application.DoEvents right after you update the status bar. This can really bog down the code though, so you might prefer to set up a Timer control that monitors a custom property's value. If it is anything other than "Ready" then the code displays and updates the status bar. This allows it to be multithreaded and not slow down other running programs. Here's a quick and dirty, but slow-running, example:

Statusbar.value = myvalue
Application.doevents()
And the following is an example using a timer monitor:
Private Sub tmStatusMonitor_Tick(ByVal sender As System.Object, _
    ByVal e As System.EventArgs) Handles tmStatusMonitor.Tick
    If strStatus <> "Ready" Then 'strStatus is the custom property
         sbStatus.Text = strStatus
         Application.Doevents()
    End If
End Sub
In the main code, the strStatus string is updated to state whatever text is appropriate and then set back to "Ready" when finished.

QI need some help using resources from within Visual Studio® .NET. I understand the resource story from the command line, but I have had no luck from within the IDE.

AFirst of all, have a look at the SDK tutorial installed in the Framework SDK's tutorials\ResourcesAndLocalization folder. The key points you need to know are that you can easily localize a Windows® Form by selecting the form in Visual Studio .NET, then changing the localize property to True. When you do, Visual Studio .NET automatically pushes all the localizable properties of your form (including the controls on it) into a .resx file and adds the necessary code to your form to load those dynamically. These are your default resources. If you then change the language property of your form, Visual Studio .NET makes a new .resx file for that culture, and any changes you make for that language will be written to it. If you change language back to default, you will notice that the form reverts to its original state. You can add as many cultures as you like; just remember to save your work regularly. There will be one .resx file per language.

Note that you need to add a line of code for the other languages to get picked up:

Thread.CurrentThread.CurrentUICulture =
   Thread.CurrentThread.CurrentCulture
Add this line just before the ResourceManager object is created in the Windows Forms-generated code section. The construction of the ResourceManager was added by the designer when you set the Localized property of the form to True.

This will ensure that if you change your language in Windows, it will affect the resources available inside the project. You shouldn't add this line of code for MUI versions of Windows. (MUI is the Windows Multilingual User Interface, which supports user-activated language switching of the UI. The system can have resources for several languages at once and the user can switch among languages from the Control Panel.) Once you have added a valid .resx file to a project, you can double-click the file in the project window (if you are viewing all files) to edit the entries. This is how you can add new values or alter existing ones.

QI have a question about JScript® performance when doing string concatenation. I realize that, generally speaking, doing a lot of concatenation with large strings is a performance killer. However, I'm concerned about seeing different behavior with different browsers. My current test is tracing the problem to string-handling code. Are there better ways to handle large string concatenation in JScript?

AThe code you sent (see Figure 1) works much slower than the modified script in Figure 2. When concatenating normal length strings, performance is not great but it's reasonable. ASP pages tend to build up many long strings using the += operator (where "long" means tens of kilobytes, not ridiculously long, as in the example.)

Figure 2 Modified Script (Close Figure 2)
<HTML>
<BODY>
<SCRIPT LANGUAGE="JSCRIPT">
<!--
function runtest()
{
var oldTime = new Date();
var tempStr = new String();
var tempArr = new Array(10000);

alert("Starting test");

for(var i=0; i<10000;++i)
{
    tempArr[i] = "This is big String that we are using for
                  manipulation.";
}

tempStr = tempArr.join("");

var newTime = new Date();
alert("string size="+tempStr.length+ " time taken = "+ (newTime-
      oldTime));
}
-->
</SCRIPT>
<input type="button" value="start test" onclick="runtest()">
</BODY>
</HTML>
Figure 1 Original String Handling Code (Close Figure 1)
<BODY>
<SCRIPT LANGUAGE="JSCRIPT">
<!--
function runtest()
{
var oldTime = new Date();
var tempStr = new String();

alert("Starting test");

for(var i=0; i<10000;++i)
{
    tempStr += "This is big String that we are using for manipulation.";
}

var newTime = new Date();

alert("string size="+tempStr.length+ " time taken = "+ (newTime-
      oldTime));
}
-->
</SCRIPT>
<input type="button" value="start test" onclick="runtest()">
</BODY>
</HTML>

It is often true that algorithms, which in theory ought to greatly speed up string concatenation (through lazy concatenation), in fact make performance much worse; the cost of maintaining the extra memory for the lazy concatenation trees eat into the savings.

Rather than attempting to eliminate contention by eliminating allocations, you could make contention less likely by implementing a better heap algorithm for small allocations. This will lead to a huge increase in performance.

That said, doing lots of += on strings is considered bad programming. It is an O(n2)operation—every time you double the number of strings concatenated, you quadruple the runtime. It does not matter how fast the heap is—the problem here is one of memory moves, rather than contention.

No one should use a language like JScript to do manipulation of multi-megabyte strings. The fact that JScript performs poorly when asked to manipulate these strings is unfortunate, but the only people who will notice are benchmark writers.

You should try not to waste time worrying about the performance of unrealistic programs. The time you spend tracking down and optimizing performance problems ought to be focused on making things faster for real-world programs.

QIf you have to concatenate large strings in JScript, what would be the best approach to take?

AWell, the join method on arrays gets around the O(n2) nature of the naïve string concatenation algorithm.

As for other techniques, suppose you want to build up the string "XXXXXXXX". The naïve algorithm shown here would build up:

X + X =
XX + X =
XXX + X =
XXXX + X =
XXXXX + X =
XXXXXX + X =
XXXXXXX + X =
XXXXXXXX
Notice that this allocates 1 + 2 + 3 + ... + 8 = 36 characters to build an eight-character string, or (n × n + n) / 2 characters for an n character final result. A better method would be
X + X =
XX + XX =
XXXX + XXXX =
XXXXXXXX
which allocates 2 × n - 1 characters for an n-character final result. If you try this program
tempStr = "This is big String that we are using for manipulation.";
for(var i=0; i<18;++i)
{
    tempStr += tempstr;
}
you'll see that it builds up a much longer string in much less time. The key is to eliminate as many memory allocations as possible. Concatenating large strings is really not much more expensive than concatenating small strings—but doing 10,000 allocations of large strings will be extremely costly.

QI'm thinking about adding tooltips to all the links on my Web site's navigation menu. My thought is that this will help users get a better understanding of what they're about to click on without having to go to the actual page. This seems like a good idea to me, but I don't see this too often on other sites. It doesn't take DHTML or anything fancy to do this; in fact, it's simply an attribute in the anchor tag. Why aren't more Web developers using it?

AYou should ask yourself if you're convinced that people aren't understanding the links. If you're not convinced, you could be trying to solve a problem that doesn't exist.

Assuming you're certain that there is a problem, there are a few reasons not to solve it through tooltips. First, the links themselves should be self-sufficient and make sense in the context of your Web site. There shouldn't be a need for any description at all. Think of stop or yield signs on the street—if you have to read a sentence to understand what the sign is telling you to do, the sign has failed in its purpose. With menus of any kind, you're aiming for a similar level of direct communication. If your Web site needs a user's manual, then you have a problem.

Second, tooltips do not stay visible for very long—about six seconds. For many people, this isn't quite enough time to read a paragraph of text, assuming they were truly interested in doing so. Because the tooltip is auto-generated, it appears on every mouseover. If you move your mouse across the list, you'll see tooltips appearing and disappearing for each one, and this makes for lots of visual noise. Compared to just letting users click on the link to see what it actually is, the overhead of trying to explain everything beforehand can create more problems than it solves. By the way, there are two attributes that you can use with the anchor (<A>) tag: TITLE, which has spotty support across browsers, and ALT, which is intended to provide information for screen readers, should you go this route. Typically, ALT makes sense only for images.

QIn the December 2002 Web Q&A, you address a reader's problem concerning catching a C# exception in JScript. The reader did not want to have to change the JScript code, but the only methods you were able to suggest to access the C# exception properties worked "on the catch side, not on the throw side."

Wouldn't it be possible in the C# code to catch the C# exception, create a new instance of a JScriptException, assign properties appropriately to reflect the relevant information from the C# exception, and then throw the JScriptException? Or would the newly thrown JScriptException then be wrapped in a further instance of JScriptException?

AThere isn't a JScriptException class in .NET. This "class" is specific to the JScript .NET language and is not accessible by any C# component. With ActiveX® components you can implement IErrorInfo and JScript will use this information to raise the exception. Accessing the InnerException property is the only way to get to the original exception.

Got a question? Send questions and comments to WebQA@microsoft.com.

Thanks to the following Microsoft developers for their technical expertise: Mike Blaszczak, Scott Berkun, Michael Brundage, Mark Davis, Seth Demsey, Kit George, Francois Liger, Eric Lippert, Sanjay Manghnani, Gray McDonald, Bill Metters, Eric Smith, and Maxim Stepin


The XML Files: XPath Selections and Custom Functions, and More

XML Files
XPath Selections and Custom Functions, and More
Aaron Skonnard

Code download available at: XMLFiles0302.exe (147KB)

QUsing the Microsoft® .NET Framework, is it possible for me to evaluate XPath expressions without having to load the entire document into memory?

AIn the .NET Framework, XPath evaluation is exposed through the XPathNavigator abstract class. The .NET Framework ships only two implementations of XPathNavigator: one that operates on the standard W3C DOM implementation (XmlNode) and another that operates on an in-memory tree representation called XPathDocument. Both implementations load the entire tree into memory before you can begin evaluating XPath expressions.

For instance, the following example uses the .NET Framework DOM implementation to sum the invoice Price elements:

const string EXPRESSION =
  "/Invoices/Invoice/LineItems/LineItem/Price/text()";

static void UseDOM()
{
    XmlDocument doc = new XmlDocument();
    doc.Load("invoices.xml");
    XmlNodeList selection = doc.SelectNodes(EXPRESSION);
    double total = 0;
    foreach(XmlNode n in selection)
        total += XmlConvert.ToDouble(n.InnerText);
    Console.WriteLine("sum: {0}", total);
}
The call to Load parses through the stream of XML (using an XmlTextReader) and builds an in-memory DOM tree whose nodes can be traversed.

The following example, on the other hand, uses XPathDocument and XPathNavigator to accomplish the same task:

static void UseNavigator()
{
    XPathDocument doc = new XPathDocument("invoices.xml");
    XPathNavigator nav = doc.CreateNavigator();
    XPathNodeIterator it = nav.Select(EXPRESSION);
    double total = 0;
    while (it.MoveNext())
        total += XmlConvert.ToDouble(it.Current.Value);
    Console.WriteLine("sum: {0}", total);
}
Here, when you instantiate the XPathDocument class, you must provide an XML stream that is parsed and loaded into an in-memory tree optimized for XPath and XSLT evaluation. Then you call the Select method of XPathNavigator to evaluate the expression and XPathNodeIterator to iterate over the selection.

This cursor-based programming model and the DOM node-oriented approach are quite different. However, if you check out the implementation of XmlNode's SelectNodes method, you'll see that it's also using XPathNavigator, but exposing the results as a collection of nodes over an XPathNavigator cursor. Hence, both models use the same XPath evaluation interface; the difference is in the implementation of the trees and traversal logic (I'll provide more details in the next question).

As a quick side note, one advantage of XPathNavigator is that it also comes with a method (also named Evaluate) for evaluating XPath expressions that return strings, numbers, or Booleans—something other than a node-set (XPathNodeIterator). This allows you to greatly simplify the summing logic through XPath's sum function as shown here:

const string SUMEXPRESSION =
  "sum(/Invoices/Invoice/LineItems/LineItem/Price/text())";

static void UseNavigatorEvaluate()
{
    XPathDocument doc = new XPathDocument("invoices.xml");
    XPathNavigator nav = doc.CreateNavigator();
    Console.WriteLine("sum: {0}", nav.Evaluate(SUMEXPRESSION));
  );
}

These are the only two mechanisms for evaluating XPath expressions in the .NET Framework out of the box today. So if you're looking for a solution supported by Microsoft, you won't find it. You must load the tree into memory in order to evaluate XPath expressions. Typically, you're better off using XPathDocument since it has been optimized for XPath/XSLT evaluation. However, many developers still prefer the DOM programming model because they're already familiar with it. (And if you're targeting the .NET Compact Framework, it's your only choice.)

There is another alternative if you're willing to write a little code. Thanks to the extensible design of System.Xml, it's possible to write an extension class that provides XPath support in a streaming fashion. What you really want is the ability to evaluate XPath expressions over an underlying XmlTextReader object. To do this, you need to write a new class that derives from XPathNavigator and that uses an XmlTextReader object internally as the data source (see the code in Figure 1).

Figure 1 Extension Class (Close Figure 1)
public class XPathReader: XPathNavigator
{
    public XmlTextReader Node;

    public XPathReader( String url )
    {
        Node = new XmlTextReader(url);
    }

    public XPathReader( Stream stream )
    {
        Node = new XmlTextReader(stream);
    }

    ... // implementations of the various
        // XPathNavigator overrides
}

Then you need to override all of XPathNavigator's abstract members and translate the calls to the underlying XmlTextReader object. I've provided a sample implementation with this column (thanks to Mark Fussell at Microsoft) that you can use to experiment. Figure 2 shows the equivalent functions for evaluating the invoice total using XPathReader.

Figure 2 Evaluating Invoice Total (Close Figure 2)
static void UseReader()
{
    XPathReader r = new XPathReader("invoices.xml");
    XPathNodeIterator it = r.Select(EXPRESSION);
    double total = 0;
    while (it.MoveNext())
        total += XmlConvert.ToDouble(it.Current.Value);
    Console.WriteLine("sum: {0}", total);
}
static void UseReaderEvaluate()
{
    XPathReader r = new XPathReader("invoices.xml");
    Console.WriteLine("sum: {0}", r.Evaluate(SUMEXPRESSION));
}

You have to be careful with your XPath expressions when using XPathReader because it's limited to the capabilities of the underlying XmlTextReader; for instance, XmlTextReader can only move forward through the stream. So if you use an XPath expression that needs to move backwards (such as "parent::node()"), the implementation will throw an exception. Regardless of the limitations, however, it does make it possible to evaluate XPath expressions without incurring the cost of loading the entire document into memory. The complete sample code for this column is available from the link at the top of this article.

QIs it more efficient to use XmlNode's SelectSingleNode than SelectNodes? Or should I use XPathNavigator's Select?

AFirst of all, let's think about which of the following two lines of code is more efficient:

XmlNode n1 = doc.SelectSingleNode("//Price");
XmlNode n2 = doc.SelectNodes("//Price")[0];
You'd think that it would be more efficient to use XmlNode's SelectSingleNode than SelectNodes since the latter returns an XmlNodeList while the former returns only the first XmlNode that matches. Due to constraints in the DOM specification, the XmlNodeList implementation should come with more overhead than just accessing the first matching node. However, thanks to the Microsoft implementation, there isn't a significant difference between the two techniques.

For instance, consider the internal code for the call to SelectSingleNode, which (using ILDasm or anakrino) looks something like this:

public XmlNode SelectSingleNode(string xpath) {
    XmlNodeList local0;
    XmlNode local1;

    try {
        local0 = this.SelectNodes(xpath);
        local1 = local0.get_ItemOf(0);
    }
    catch (ArgumentOutOfRangeException) {
        local1 = null;
    }
    return local1;
}
Notice that the code simply delegates to SelectNodes and returns the first item in the local XmlNodeList object. You may be wondering why they did something so seemingly foolish, but before judging too quickly let's take a close look at the actual implementation of SelectNodes:
public XmlNodeList SelectNodes(string xpath) {
    XPathNavigator local0;

    local0 = this.CreateNavigator();
    return new XPathNodeList(local0.Select(xpath));
}

Note that the code creates an XPathNavigator for the current DOM tree, calls Select, and returns an XPathNodeList object (which happens to derive from XmlNodeList). The implementation of XPathNodeList uses XPathNavigator's traversal methods to move through the selection as they're requested, and along the way it caches the encountered XmlNode references just in case they're asked for again. Hence, ultimately both of those lines of code are equivalent.

Looking at these implementation details also answers the other question about using XPathNavigator's Select. Since both methods use XPathNavigator's Select internally, it's not any more efficient to use XPathNavigator directly. If you're really concerned with performance, however, you may want to move away from the DOM implementation altogether and use XPathDocument instead. XPathDocument has been optimized for evaluating XPath expressions, but you're forced to use XPathNavigator's Select method since XPathDocument only supportsXPathNavigator traversal. These conclusions are completely based on current (version 1.0) implementation details, which may change over time.

QIs it possible to extend the .NET Framework XPath implementation by creating custom functions?

AYes, it's possible to hook custom functions into the .NET Framework XPath processor's context at run time, although you'll have to write a few helper classes along the way. Specifically, you have to write a class that derives from IXsltContextFunction and another that derives from XsltContext. The class you derive from IXsltContextFunction acts as the dispatcher to your custom functions. You can build your custom functions right into that class or dispatch to another class if you like. The XsltContext-derived class is called by the XPath engine to determine when your IXsltContextFunction-derived class is needed.

For example, let's say that you want to extend XPath with a custom C# function that calculates the distance between two coordinates as shown in the following code:

double Distance(double x1, double y1, double x2, double y2)
{
  return Math.Sqrt(Math.Pow(x1-x2, 2) + Math.Pow(y1-y2, 2));
}
First you need to write a class that derives from IXsltContextFunction—let's call it DistanceFunction—and override the required members (see Figure 3). The Minargs and Maxargs properties get the minimum and maximum number of arguments supported by the function. In this case I expect exactly four arguments, but you could use these properties to distinguish between overloaded functions. The ReturnType property specifies the type of the return value, in this case XPathResultType.Number. And finally, the ArgTypes property gets the types of the supplied arguments.

Figure 3 Custom XPath Function Classes (Close Figure 3)
class DistanceFunction : IXsltContextFunction
{
    private XPathResultType[] _argTypes;

    public DistanceFunction(XPathResultType[] argTypes)
    {
       _argTypes = argTypes;
        foreach(XPathResultType t in argTypes)
        if (t != XPathResultType.Number)
          throw new Exception(
              "incorrect argument type: number expected");
    }
    public int Minargs
    {
      get { return 4; }
    }
    public int Maxargs
    {
      get { return 4; }
    }
    public XPathResultType ReturnType
    {
      get { return XPathResultType.Number; }
    }
    public XPathResultType[] ArgTypes
    {
      get { return _argTypes; }
    }

    public object Invoke(XsltContext xsltContext,
      object[] args, XPathNavigator docContext)
    {
      return Distance((double)args[0], (double)args[1],
        (double)args[2], (double)args[3]);
    }

    private double Distance(double x1, double y1,
     double x2, double y2)
    {
      return Math.Sqrt(Math.Pow(x1-x2, 2) +
        Math.Pow(y1-y2, 2));
    }
}

class DistanceFunctionContext : XsltContext
{
    public override IXsltContextFunction ResolveFunction(
      string prefix, string name, XPathResultType[] ArgTypes)
    {
      if (name == "distance")
         return new DistanceFunction(ArgTypes);
      else
         return null;
    }
    ... // remainder of class omitted
}

When the class is constructed, I expect the argument types to be provided. Within the constructor implementation, I validate that all of the arguments are number values before I cache the array in a member variable for future use; otherwise, I throw an exception. If you want to support automatic coercions when the wrong type is supplied, you can do so within Invoke.

Invoke will be called by the XPath engine at run time if it encounters a function that's handled by this class. Notice that Invoke receives an array of arguments that you can use to dispatch to the function, in this case named Distance. You need to handle any necessary coercion at this point before dispatching. Then you need to return an object of the expected type from Invoke.

The other class you have to write is one that derives from XsltContext—let's call it DistanceFunctionContext in this case (see Figure 3). This class has a few required members that you have to provide, but the most important and interesting one is ResolveFunction. This is called by the XPath engine at run time when it encounters an unrecognized function. In my implementation if the function name matches the word "distance," I return a new DistanceFunction object which will in turn be used in order to invoke the function.

To piece it all together you need to associate the custom context, DistanceFunctionContext, with the compiled XPath expression object before evaluating the expression (via SetContext), as shown in the code in Figure 4.

Figure 4 Using SetContext (Close Figure 4)
XmlDocument doc = new XmlDocument();
doc.Load("line.xml");
XPathNavigator nav = doc.CreateNavigator();

DistanceFunctionContext ctx =
  new DistanceFunctionContext(new NameTable());
XPathExpression expr =
  nav.Compile("distance(number(/line/point[1]/x),
  number(/line/point[1]/y), number(/line/point[2]/x),
  number(/line/point[2]/y))");
expr.SetContext(ctx);

Console.WriteLine("distance: {0}", nav.Evaluate(expr));

Now you can evaluate XPath expressions that contain my custom distance function. For example, assume that line.xml contains the following data:

<line>
  <point>
    <x>0</x>
    <y>0</y>
  </point>
  <point>
    <x>3</x>
    <y>4</y>
  </point>
</line>
Using this file, the code in Figure 4 will produce this output:
distance: 5

This is yet another example of System.Xml's powerful extensibility model that enables you to customize your XML processing framework. The complete sample is available for download from the MSDN Magazine Web site.

QIs it possible to remove the default XML Schema namespaces that are inserted by XmlSerializer's Serialize method?

AYes, you can control the serialized namespaces through an XmlSerializerNamespaces object. Assume you have written the following employee class

public class Employee
{
    public Employee() {}
    public Employee(string n, string id, string p)
    {
        this.name = n;
        this.id = id;
        this.position = p;
    }
    public string name;
    public string id;
    public string position;
}
and also assume that you use XmlSerializer to serialize an instance, as shown here:
Employee e = new Employee("Michelle", "342-33-8390",
                          "Manager");
XmlSerializer ser = new XmlSerializer(typeof(Employee));
ser.Serialize(Console.Out, e);
The previous code produces the following XML instance:
<Employee xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi=
  "http://www.w3.org/2001/XMLSchema-instance">
  <name>Michelle</name>
  <id>342-33-8390</id>
  <position>Manager</position>
</Employee>

To get rid of the XML Schema namespaces you need to construct an XmlSerializerNamespaces object, add at least one namespace (you can use Add("", "")), and then pass it during the call to Serialize:

•••
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
ser.Serialize(Console.Out, e, ns);
In this case Serialize produces the following XML document, which I believe is what you're after:
<Employee>
  <name>Michelle</name>
  <id>342-33-8390</id>
  <position>Manager</position>
</Employee>

QI'm using values of type QName in some of my elements while using XmlSerializer to generate XML from an object. Here's the problem: I can't get XmlSerializer to inject the namespace declarations required by the QName values. Is there any way that I can make this happen?

AIf I understand the situation correctly, you need to generate an XML document (using XmlSerializer) that looks something like the following document:

<Employee xmlns:pos="http://example.org/positions">
  <name>Michelle</name>
  <id>342-33-8390</id>
  <position>pos:Manager</position>
</Employee>
As you probably know, you can use XmlRoot and XmlType to specify the namespace for the root element and XmlSchema complexType, respectively:
[XmlRoot(Namespace="http://example.org/employee")]
[XmlType(Namespace="http://example.org/employee")]
public class Employee { ... }
If you do this, and assuming you're using an empty XmlSerializerNamespaces object (see the previous question), you'll get the following document:
<q1:Employee xmlns:q1="http://example.org/employee">
  <q1:name>Michelle</q1:name>
  <q1:id>342-33-8390</q1:id>
  <q1:position>pos:Manager</q1:position>
</q1:Employee>

But that doesn't help with the "pos" prefix used in the position element. If you have control over the XmlSerializer directly, you can always use the technique described in the previous section and simply add the desired namespace binding to the XmlSerializerNamespaces collection before calling Serialize:

•••
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("pos", "http://example.org/positions");
ser.Serialize(Console.Out, e, ns);

This call to Serialize produces the following XML document with a namespace declaration for "pos":

<Employee xmlns:pos="http://example.org/positions"
  xmlns="http://example.org/employee">
  <name>Michelle</name>
  <id>342-33-8390</id>
  <position>pos:Manager</position>
</Employee>
However, you may not always have direct access to the XmlSerializer used to serialize your objects, as is the case with WebMethods. In that type of situation, you need to use the XmlNamespaceDeclarations attribute on a class member of type XmlSerializerNamespaces. Then you just need to add namespaces to the member prior to the object serialization, and XmlSerializer will inject them into the XML document for you. For example, here is a new version of Employee that uses XmlNamespaceDeclarations:
[XmlRoot(Namespace="http://example.org/employee")]
[XmlType(Namespace="http://example.org/employee")]
public class Employee
{
    [XmlNamespaceDeclarations]
    public XmlSerializerNamespaces namespaces =
      new XmlSerializerNamespaces();

    public string name;
    public string id;
    public string position;
    •••
}

Now you can write a WebMethod that injects the right namespace binding into the XmlSerializerNamespaces object at run time, before the infrastructure serializes the returned Employee object as shown here:

[WebMethod]
public Employee GetEmployee()
{
    Employee e = new Employee("Monica", "555-23-3421",
      "p:President");
    e.namespaces.Add("p", "http://example.org/positions");
    return e;
}

In this case the WebMethod produces the following XML document, which contains a namespace declaration for the value used in position:

<Employee xmlns:xsd="http://www.w3.org/2001/XMLSchema"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:p="http://example.org/positions"
  xmlns="http://example.org/employee">
  <name>Monica</name>
  <id>555-23-3421</id>
  <position>p:President</position>
</Employee>
The sample code illustrated in this question, as well as the previous question, can also be found in the code download.

QIs it possible to disable the stack trace from appearing within the SOAP Fault that gets produced by throwing a SoapException?

AYou can configure this in your web.config file using the customErrors element (/configuration/system.web/customErrors):

<configuration>
  <system.web>
    <customErrors mode="On"/>
  </system.web>
</configuration>

The customErrors element has a mode attribute that takes three values: On, Off, and RemoteOnly (default). If you set it to On, the stack trace will no longer appear. If you set it to Off, the stack trace will always appear. And if you set it to RemoteOnly, the stack trace will appear if the endpoint was invoked from the same machine (for example, when the developer tests it), but it will not appear if it was invoked from a remote client—this is typically the behavior you want. Consider the following WebMethod and assume I have mode set to On or RemoteOnly:

[WebMethod]
public double Divide(double x, double y)
{
  if (y == 0)
    throw new SoapException("Cannot divide by 0",
      SoapException.ClientFaultCode);
  return x/y;
}

If someone attempts to invoke this WebMethod using 0 for the value of y, they'll receive the following SOAP Fault:

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <soap:Fault>
      <faultcode>soap:Client</faultcode>
      <faultstring>Cannot divide by 0</faultstring>
      <detail />
    </soap:Fault>
  </soap:Body>
</soap:Envelope>
If mode had been set to Off, the faultstring element would have also contained the stack trace.

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

Aaron Skonnardis an instructor/researcher at DevelopMentor, where he develops the XML and Web Service-related curriculum. Aaron coauthored Essential XML Quick Reference (Addison-Wesley, 2001) and Essential XML (Addison-Wesley, 2000). Reach him at http://staff.develop.com/aarons.


Cutting Edge: The ASP.NET View State

Cutting Edge
The ASP.NET View State
Dino Esposito

Code download available at: CuttingEdge0302.exe (39KB)
Contents
View State Pros and Cons
The StateBag Class
Security Issues
Performance Issues
Programming Without View State
Keeping View State on the Server
Conclusion

In ASP.NET pages, the view state represents the state of the page when it was last processed on the server. It's used to build a call context and retain values across two successive requests for the same page. By default, the state is persisted on the client using a hidden field added to the page and is restored on the server before the page request is processed. The view state travels back and forth with the page itself, but does not represent or contain any information that's relevant to client-side page display. In this column, I'll take a tour of the view state implementation in ASP.NET and show how to keep your Web pages fit and trim and a bit more secure.

View State Pros and Cons

View state has pros and cons that you might want to weigh carefully before making a decision to use it. First, view state does not require any server state resources and is simple to implement. View state, though, does require computational effort on the server and the cost of sending the extra data over the wire. Since it's a physical part of the page, it's fast to retrieve and use; for this same reason, though, it can become a weakness that can be exploited.

Because the view state is packed with the page, it inevitably adds a few extra kilobytes of data to the payload. A complex, real-world page—especially one that does not optimize and restrict the use of the view state—can easily find 10KB of extra stuff packed in the HTML code sent to the browser. Because it's composed of plain text, the view state could be tampered with. Although programmers are not supposed to store sensitive data in the view state (credit card numbers, passwords, or connection strings, for example), it goes without saying that the view state can be used to carry out attacks on the server. View state is not a security hole per se, but just like query strings and other hidden fields you may have used in the past, it's a potential vehicle for malicious code. Since the view state is encoded, protected, and validated, it does provide more security features than other hidden fields that you may use for your own programming purposes.

View state is one of the most important features of ASP.NET, not so much because of its technical relevance, but more because it makes the magic of the Web Forms model possible. However, if used carelessly, view state can easily become a burden.

The StateBag Class

StateBag implements the view state and manages the information that ASP.NET pages and embedded controls persist across successive posts of the same page instance. The class works like a dictionary object and implements the IStateManager interface. The Page and the Control base classes expose the view state through the ViewState property. So you can add or remove items from StateBag as you would with any dictionary object:

ViewState["FontSize"] = value;

You should start writing to the view state only after the Init event is fired for a page request. You can read from the view state during any stage of the page lifecycle, but not after the page enters rendering mode—that is, after the PreRender event is fired. Figure 1 lists the properties and the methods defined for StateBag.

Figure 1 StateBag Properties and Methods (Close Figure 1)

Property | Description
Count | Returns the number of elements in the object
Item | Indexer property; gets or sets the value of an item stored in the class
Keys | Gets a collection object containing the keys defined in the object
Values | Gets a collection object containing all the values stored in the object
Method | Description
Add | Adds a new StateItem object to the collection; if the item already exists, it gets updated
Clear | Removes all items from the current view state
GetEnumerator | Returns an object that scrolls over all the elements in the StateBag
IsItemDirty | Indicates whether the element with the specified key has been modified during the request processing
Remove | Removes the specified object from the StateBag object

Each item in the StateBag class is represented by a StateItem object. An instance of the object is implicitly created when you set the Item indexer property with a value or when you call the Add method. Items added to the StateBag object are tracked until the view state is saved to the storage medium using the page's SaveViewState method. If a state item hasn't been saved, IsDirty property will be True. The IsItemDirty method represents an indirect way to call into the IsDirty method of a StateItem object.

The contents of the StateBag collection are first serialized to a string, then Base64 encoded, and finally assigned to a hidden field in the page that is served to the client. The view state for the page is a cumulative property that results from the contents of the ViewState property of the page plus the view state of all the controls hosted in the page.

Security Issues

Architecturally speaking, the importance of the view state cannot be denied since it is key to setting up the automatic state management feature of ASP.NET. A couple of hot issues surround its use, however. Most of the frequently asked questions about it are related to security and performance. Let's tackle security first.

Many developers are hesitant to use view state because it's stored in a hidden field and left on the client where it has the potential for compromise. As Figure 2 shows, the information can be read by viewing the source of the page. While this action alone is not a threat to the application, having the view state on the client is a potential security issue because it is information that all ASP.NET pages contain and transmit to the Web server for processing.

Figure 2 ViewState is Visible in Source
Figure 2 ViewState is Visible in Source

Although freely accessible in a hidden field called __VIEWSTATE, the view state information is not clear text. By default, a machine-specific authentication code is calculated on the data and appended to the view state string. The resulting text is then Base64 encoded only, but not encrypted. If data confidentiality is desired, however, then SSL is the only solution since it protects not only view state, but all the data that travels to and from the page. Decoding view state is still possible, but a number of steps must be accomplished; not only must several undocumented and internal structures be disassembled, but a number of circumstances must also occur. In addition, consider that a tampered view state is normally detected on the server and a security exception is thrown. Finally, and most important of all, the view state contains data, not code. Unless you explicitly lessen the default security settings for the page, there's not much a hacker can do to modify the view state. If you change the default security settings, though, you should be careful about the view state. A hacker could modify the data that represents the state of the page. This is not a bug per se and opens holes for attacks only if the basic rules of data validation and data checking are not enforced. But this, you understand, is a more general problem when you're trying to write secure code.

The view state internal implementation is quite complex and layered enough to discourage attacks. Encryption is the most important element in protecting view state information.

In order to make the view state more secure, the ASP.NET @Page directive supports an attribute called EnableViewStateMac whose only purpose is detecting any possible attempt at corrupting original data. (The "Mac" in EnableViewStateMac stands for machine authentication check and, despite what some documentation claims, it is enabled by default.) When serialized, and if EnableViewStateMac is set to True, the view state is appended with a validator hash string based on the algorithm and the key defined in the <machineKey> section of the machine.config file. By default, the encryption algorithm is SHA1 and the encryption and decryption keys are auto-generated and stored in the Web server machine's Local Security Authority (LSA) subsystem. The LSA is a protected component of Windows NT®, Windows® 2000, and Windows XP. It provides security services and maintains information about all aspects of local security on a system.

If EnableViewStateMac is True, then when the page posts back the encrypted view state is algorithmically checked to verify that it has not been tampered with on the client. The net effect is that you might be able to read the contents of the view state, but to replace it you need the encryption key, which is in the Web server's LSA.

The EnableViewStateMac directive attribute corresponds to a protected page member with the same name. The EnableViewStateMac property defaults to True. The following excerpt comes from the source code that the ASP.NET runtime generates for a running .aspx page. (You can obtain it by adding the debug=true attribute to the @Page directive and searching for the source code in the temporary system folder of your ASP.NET application.)

protected override void FrameworkInitialize() {
        this.__BuildControlTree(this);
        this.FileDependencies = __fileDependencies;
        this.EnableViewStateMac = true;
}
If you're particularly concerned about attacks through the view state, simply make sure that EnableViewStateMac is on.

You should look carefully at your own view state. If you maintain the default settings you should be OK. Later, after discussing performance issues, I'll return to security. At that time I'll discuss a possible alternative implementation of the view state that addresses both security concerns and bandwidth overhead.

Performance Issues

Keeping the default view state settings is fine from a security perspective, but not in terms of performance. The view state can reach a considerable size, especially for feature-rich pages that make use of plenty of controls. This extra burden can cause serious overhead for the application as a whole.

By default, the view state is enabled for all server controls, but this doesn't mean that you need it all the time. View state saves you a lot of coding and, more importantly, makes your job simpler. But if you find you're paying too much for this feature, drop view state altogether and reinitialize the state of the server controls at every postback. In this case, disabling view state saves processing time and speeds up the download process.

You can disable the view state for an entire page by setting the EnableViewState attribute of the @Page directive to false:

<% @Page EnableViewState="false" %>
While this is not usually recommended, you should definitely consider it for read-only pages that either don't post back or don't need state to be maintained.

A better approach entails disabling the view state only for some of the server controls hosted in the page. To disable view state on a per-control basis, set the EnableViewState property of the control to false, as shown here:

<asp:datagrid runat="server" EnableViewState="false">
    •••
</asp:datagrid>

While developing the page, you can keep the size of the view state under control by enabling tracing on the page. Once you have set the @Page's trace attribute to true, look under the ViewState column of the control tree, as shown in Figure 3.

Figure 3 Control Tree (Close Figure 3)

The tracer doesn't show the total size of the view state for the page, but it gives you a precise idea of what each control does. In Figure 3, the page is made of a relatively simple DataGrid control. As you can see, the cells of the grid take up a large part of the view state. The TableCell control, in particular, saves complete user interface information in the view state, including text, column and row span, and style attributes.

Contrary to what many developers think, the DataGrid server control doesn't store its data source in the view state. It caches almost all of its public properties, but the amount of space required is in no way related to the data source. However, the DataGrid uses child controls, like the TableCell, which store their text and style in view state, too. The text displayed in the cells is a subset of the DataGrid's data source.

Figure 4 Retrieve ViewState String
Figure 4 Retrieve ViewState String

In Figure 4, I've reworked the page in Figure 3 to add a client-side button that retrieves the view state string and calculates its length. The JavaScript code for this is pretty simple:

<script language="javascript">
    function ShowViewStateSize()
    {
        var buf = document.forms[0]["__VIEWSTATE"].value;
        alert("View state is " + buf.length + " bytes");
    }
</script>

Programming Without View State

As I've discussed here, the view state represents the state of the page and its controls just before the page is rendered in HTML. When the page posts back, the view state is recovered from the hidden field, deserialized, and used to initialize the server controls in the page and the page itself. However, this is only half the story.

After loading the view state, the page reads client-side information through the Request object and uses those values to override most of the settings for the server controls. In general, the two operations are neatly separated and take place independently. In particular, though, the second operation—reading from Request.Form—in many situations ends up just overriding the settings read out of the view state. In this particular case the view state is only an extra burden.

Let's examine a typical case. Suppose you have a page with a textbox server control. You expect that, when the page posts back, the textbox server control is automatically assigned the value set on the client. To meet this rather common requirement, you don't need view state. Let's consider the page in Figure 5. In this case, the behavior of the page is state-aware even if view state is disabled. The reason is that you are using two server controls—TextBox and CheckBox—whose key properties are updated according to the values set by the user. These values will override any setting that view state may have set. In short, as long as you're simply interested in persisting properties such as Text or Checked you don't have any need for view state.

Figure 5 State-aware Page Without View State (Close Figure 5)
<% @Page language="c#" EnableViewState="false" %>
<script runat="server">
void OnPost(object sender, EventArgs e) {
    Response.Write(theInput.Text);
}
</script>

<form runat="server">
    <asp:textbox runat="server"
        id="theInput" readonly="false" text="Type here" />
    <asp:checkbox runat="server" id="theCheck" text="Check me" />
    <asp:linkbutton runat="server" text="Click" onclick="OnPost" />
</form>

All control properties declared as attributes in <asp:xxx> tags are automatically restored to their default values during the initialization of the control. As long as these extra properties are not expected to change during the session, you don't need view state. So when is view state really necessary?

Suppose that your textbox is only editable if the user has certain privileges. You could declare the textbox as read-only to turn the attribute off the first time the page is loaded and after checking the user's credentials. The code would look like Figure 6.

Figure 6 Making Textbox Read-only (Close Figure 6)
<% @Page language="C#" %>
<script runat="server">
void Page_Load(object sender, EventArgs e) {
    if (!IsPostBack)
        theInput.ReadOnly = !IsUserAdmin();
}
bool IsUserAdmin() {
    return true;
}
void OnPost(object sender, EventArgs e) {
    Response.Write(theInput.Text);
}
</script>

<form runat="server">
    <asp:textbox runat="server" id="theInput" readonly="true"
        text="Type your UID here" />
    <asp:linkbutton runat="server" text="Click" onclick="OnPost" />
</form>

This programming style is inherently state-aware and works as expected as long as you have view state enabled. Try setting the EnableViewState attribute to false and then load the page again. In the OnInit page event, the textbox is instantiated as declared in the page layout—that is, with a ReadOnly property of true. The first time the page is processed (in this case, IsPostBack returns false), the attribute is programmatically turned on according to the user's role. So far, so good.

The code in Figure 6 is state-aware because it assumes that after the first access to the page the runtime can remember all settings. This is no longer the case if you turn view state off. In this case, since IsPostBack now returns true, the Page_Load event won't have a chance to modify the textbox's ReadOnly property. By choosing an alternative programming style you can get the same functionality without the view state, thus saving 40 bytes of HTML for each instance of a TextBox control. Obviously, this sort of savings can add up. The code in Figure 7 shows the few changes needed to make the page work the same, but without view state.

Figure 7 Textbox Without View State (Close Figure 7)
<% @Page language="c#" EnableViewState="false" %>
<script runat="server">
void Page_Load(object sender, EventArgs e) {
    if (IsPostBack)
        theInput.ReadOnly = !IsUserAdmin();
}
bool IsUserAdmin() {
    return true;
}
void OnPost(object sender, EventArgs e) {
    Response.Write(theInput.Text);
}
</script>

<form runat="server">
    <asp:textbox runat="server" id="theInput" readonly="false"
        text="Type your UID here" />
    <asp:linkbutton runat="server" text="Click" onclick="OnPost" />
</form>

In some cases, you can disable view state and have the page still run unchanged. In other cases, some minor changes must be made to the code to make sure that all properties of all controls are correctly initialized. In general, you can do without view state whenever the state can be deduced either from the client or from the runtime environment (if the user has certain privileges, for example). By contrast, doing without view state is difficult whenever state information can't be dynamically inferred. For example, if you want to track the sort order of a pageable DataGrid you can cache it only in the view state.

In my experience, any combination of default ASP.NET controls can be adapted to work in pages in which the view state has been disabled. The more you use custom controls—both yours and those from third-parties—the more disabling view state can be a problem. Keep this in mind if you're going to write custom ASP.NET controls, and don't overload the view state bag.

Keeping View State on the Server

As I said, the view state is stored on the client in a hidden field. However, this is simply the default storage medium. Let's see how to save the view state in a file on the Web server instead.

To design an alternative storage scheme for view state, you need the string that ASP.NET stores in the hidden field. You can then save it in a server-side file or a database table and read the view state whenever that page is being processed. Be aware that there are benefits and drawbacks to this alternative.

The bad news is that the Base64 view state string is not publicly exposed to the code running in the page. The good news is that the class that the Microsoft® .NET Framework actually uses to serialize and deserialize the view state is configured as a public type (as opposed to an internal type) and as such can be called from user applications. This class, LosFormatter, is not yet fully documented.

The LosFormatter class has quite a simple programming interface made of only two publicly callable methods—Serialize and Deserialize. The Serialize method writes the final Base64 representation of the view state to a Stream or a TextWriter object:

public void Serialize(Stream stream, object viewState);
public void Serialize(TextWriter output, object viewState);
The Deserialize method builds a StateBag object out of a stream, a TextReader object, or a plain Base64 string, as shown here:
public object Deserialize(Stream stream);
public object Deserialize(TextReader input);
public object Deserialize(string input);
The LosFormatter class is the entrypoint in the view state internal mechanism; by using it, everything should happen exactly as in the default case.

The LosFormatter class also features a read/write property called EnableViewStateMac whose role is enabling encryption/decryption on the Base64 string. The variable cannot be accessed programmatically because it is marked as internal, meaning that only classes in the same assembly can work with it. However, since I'm simply plugging my code into the ASP.NET view state pipeline, I can control the machine authentication check using the high-level tools I discussed earlier.

Fortunately, the Page class can support alternative storage schemes. The class contains a couple of protected virtual methods that the runtime uses to deserialize or serialize the view state. LoadPageStateFromPersistenceMedium is used to restore the view state at the beginning of the page lifecycle. By contrast, a method named SavePageStateToPersistenceMedium is used to persist the view state just before the page is rendered:

protected virtual void SavePageStateToPersistenceMedium
    (object viewState);
protected virtual object LoadPageStateFromPersistenceMedium();
Override both methods and you can load and save view state information to and from virtually any storage medium other than a hidden field. (Note that you cannot override only one method; you have to override both.) Since the methods are defined as protected members, the only way to redefine them is to create a new class that derives from Page. The following code gives you an idea of the default behavior of the loading method:
string m_viewState = Request.Form["__VIEWSTATE"];
LosFormatter m_formatter = new LosFormatter();
object viewStateBag = m_formatter.Deserialize(m_viewState);

Notice that the object returned by the LosFormatter's Deserialize method is not the StateBag object that the programmers actually work with. What the Deserialize method returns is just one of the intermediate internal objects that I mentioned earlier. The structure of the page I'm going to create is shown here:

public class ServerViewStatePage : Page
{
  protected override object LoadPageStateFromPersistenceMedium()
    { ... }
  protected override void SavePageStateToPersistenceMedium(object
      viewState) { ... }
}

The tasks accomplished by the SavePageStateToPersistenceMedium method are very easy to understand. The method takes the string as an argument, opens the output stream, and calls into the LosFormatter serializer:

protected override void SavePageStateToPersistenceMedium
    (object viewStateBag)
{
    string file = GetFileName();
    StreamWriter sw = new StreamWriter(file);
    LosFormatter m_formatter = new LosFormatter();
    m_formatter.Serialize(sw, viewStateBag);
    sw.Close();
    return;
}
In this code snippet the view state is saved in a server-side file. With minimal changes, you could save it to a database as well. How should you choose the name of the file to ensure that no conflicts arise? The view state is specific to a page request made within a particular session. So the session ID and the request URL are unique pieces of information that can be used to associate the request with the right file. Alternatively, you could give the view state file a randomly generated name and persist the file name in a custom hidden field within the page. Note that in this case you should create the hidden field manually and not rely on the __VIEWSTATE field because in overriding the methods you alter the internal procedure that would have created it.

The GetFileName function in the code I just showed gives the file a name according to the following pattern:

SessionID_URL.viewstate
Figure 8 shows some of these files created in a temporary directory. Note that for an ASP.NET application to create a local file you must give the ASP.NET account special permissions on a file or a folder. I suggest you create a new subfolder to contain all the view state files. Deleting files for expired sessions can be a bit tricky; you could write a Windows NT service that would periodically scavenge the temp directory to delete useless files. This would be better than just deleting files from within the Session_OnEnd event.

Figure 8 ViewState Files in Temporary Directory (Close Figure 8)

The LoadPageStateFromPersistenceMedium method determines the name of the file to read from, extracts the Base64 string, and calls LosFormatter to deserialize (see Figure 9).

Figure 9 LoadPageStateFromPersistenceMedium (Close Figure 9)
protected override object LoadPageStateFromPersistenceMedium()
{
    object viewStateBag;
    string file = GetFileName();
    StreamReader sr = new StreamReader(file);
    string m_viewState = sr.ReadToEnd();
    sr.Close();
    LosFormatter m_formatter = new LosFormatter();

    try {
        viewStateBag = m_formatter.Deserialize(m_viewState); }
    catch {
        throw new HttpException("The View State is invalid."); }
    return viewStateBag;
}

To be able to create the view state on the server, a page only needs to inherit from the ServerViewStatePage class that I described a few code snippets earlier. Here's how it's done:

<% @Page Language="C#" Inherits="MsdnMag.ServerViewStatePage" %>

Note, though, that session IDs (or any state mechanism) is also subject to sniffing and replay. So some of the same caveats discussed earlier apply here as well. Again, SSL is the best tool for ensuring the integrity of data on the wire. In addition, consider creating files outside the Web application space and give them random names. The actual file name can then be encoded and stored in a hidden field.

Conclusion

The view state is a key element of an ASP.NET page because it is the primary means to persist the state of the Web server controls. Whenever the page posts back, the state is restored, updated using the current form parameters, then used to run the postback event handler. Normally, the view state is a hashed string encoded as Base64 and stored in a hidden field called __VIEWSTATE. In this way, the view state is not cached on the client, but simply transported back and forth with potential issues both for security and performance. As I said, you could keep the view state on the server, which requires only minimal coding. You will pay extra overhead because you're accessing a file off the disk or a database, but you'll be returning much slimmer pages while taking less of a security risk. Although in this column I considered persistent storage on the server (file or database), nothing would really prevent you from using the Cache object, thus combining server-side storage and speed of execution.

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

Dino Espositois an instructor and consultant based in Rome, Italy. Author of Building Web Solutions with ASP.NET and ADO.NET and Applied XML Programming for .NET, both from Microsoft Press, he spends his time teaching ASP.NET. Get in touch with Dino at dinoe@wintellect.com.


Advanced Basics: Automatically Generating Proxy Classes

Advanced Basics
Automatically Generating Proxy Classes
Ken Spencer

Code download available at: AdvancedBasics0302.exe (76KB)

QThe Web Services generator shown in the January 2003 Advanced Basics column left me wondering if I could create a tool that automatically generates proxy classes for an assembly. Is there any support in the Microsoft® .NET Framework for me to do this directly without using brute-force string concatenation for the entire process?

AYes, you can and as you'll see, it's a good example of the flexibility and power of the .NET Framework. Not only can you dynamically load applications (including those that use Windows® Forms), but you can generate code on the fly, compile it, then load it while the application is running.

This functionality is provided by the Code Document Object Model (CodeDOM) namespace in the .NET Framework. The CodeDOM allows you to create various elements that represent the code you want to generate and then links them together in what is known as a code graph. The CodeDOM generates the code from this code graph. Let's walk through an example to see how.

The new application I'll create allows you to point at an assembly and then automatically generate a proxy class from that assembly. I used the Web Services generator from last month's column as the basis for this application, changing the code generation routines. You will notice that there's less code in this new application than there was in the previous column and that I concatenate many fewer strings.

In the previous application, the user would press a button, which would execute a call to GenerateWebService to generate the code. This button has been rewired to call the GenerateProxyClass subroutine shown in Figure 1. This code loads the assembly specified in sFileName, then loads the public types found in it by calling GetExportedTypes. Then the For Each loop processes each class by calling GenerateClassCode. Note that to this procedure I pass not only the type that references the assembly, but also the folder path for the generated code. This allows the GenerateClassCode function to actually write the output files, making this class quite reusable. For instance, you can call this function and pass in a reference to a class and a folder path, and it will generate the new proxy class for you.

Figure 1 GenerateProxyClass Subroutine (Close Figure 1)
Sub GenerateProxyClass()
    Dim t As Type
    Dim othis As Object
    Dim oAssembly As [Assembly]
    Dim arrayOfTypes() As Type

    StatusBar1.Text = ""

    If sFileName = "" Then
        Exit Sub
    End If

    Try
        oAssembly = [Assembly].LoadFrom(sFileName)

        arrayOfTypes = oAssembly.GetExportedTypes()

        For Each t In arrayOfTypes
            If t Is Nothing Then
                MsgBox("Type was not found — exiting")
                Exit Sub
            End If

            txtClassesGenerated.Text &= t.Name & vbCrLf

            'Generate a class
            If GenerateClassCode(t, txtOutputPath.Text) Then
                StatusBar1.Text = _
                    "Class Generation Complete"
            End If
        Next

    Catch exc As Exception
        StatusBar1.Text = "File " & sFileName & _
            "error occurred. " & exc.Message
    Finally
        oAssembly = Nothing
        GC.Collect()
    End Try
End Sub

Now let's look at the code in the GenRoutines module in Figure 2. The first step in creating this module is to define the Imports. In addition to standard imports like System and System.IO, there are three key imports you need to take advantage of for the CodeDOM features, plus an import for the Reflection namespace: System.CodeDom, System.CodeDom.Compiler, Microsoft.VisualBasic, and System.Reflection.

Figure 2 GenRoutines Module (Close Figure 2)
Imports System
Imports System.IO
Imports System.Reflection
Imports System.CodeDom
Imports System.CodeDom.Compiler
Imports Microsoft.VisualBasic

Module GenRoutines

    Public Function GenerateClassCode(ByVal t As Type, _
        ByVal CodePath As String) As Boolean

    Dim localCompileUnit As New _
        CodeCompileUnit()
    Dim localNamespace As New _
        CodeNamespace(t.Namespace & "Client")
    Dim localNewType As New _
        CodeTypeDeclaration(t.Name & "Client")
    Dim localNewConstructor As New CodeConstructor()
    Dim localMethodInfo As MethodInfo
    Dim localSnippet As CodeSnippetStatement
    Dim localVBcodeProvider As New VBCodeProvider()
    Dim gen As ICodeGenerator = _
        localVBcodeProvider.CreateGenerator()
    Dim localMethodCall As String
    Dim localObjectName As String
    Dim bReturn As Boolean
    Dim localAssemblyName As String
    Dim localOutput, localObjectRef As String
    Dim i As Integer
    Dim localTempString As String
    Dim bAtLeastOne As Boolean = False
    Dim tabLevel1 As String = vbTab
    Dim tabLevel2 As String = vbTab & vbTab
    Dim tabLevel3 As String = vbTab & vbTab & vbTab
    Dim tabLevel4 As String = vbTab & vbTab & vbTab & vbTab

    Dim sw As New _
        StreamWriter(CodePath & "\" & t.Name & _
        "Client.vb", False)

    localObjectName = "o" & t.Name

    localAssemblyName = t.AssemblyQualifiedName.ToString

    i = InStr(localAssemblyName, ",")

    localAssemblyName = Left(localAssemblyName, i - 1)

    localObjectRef &= tabLevel1 & _
        "Dim " & localObjectName & " as New " & _
        localAssemblyName & "()" & vbCrLf

    Try

        localNamespace.Imports.Add(New _
            CodeNamespaceImport("System.IO"))
        localCompileUnit.Namespaces.Add(localNamespace)

        localNewConstructor.Attributes = _
            MemberAttributes.Public
        localNewConstructor.Name = _
            t.GetConstructor(System.Type.EmptyTypes).Name

        localNewType.Members.Add(localNewConstructor)

        '
        For Each localMethodInfo In _
            t.GetMethods(( _
            BindingFlags.Public Or BindingFlags.Instance _
            Or BindingFlags.DeclaredOnly Or _
                BindingFlags.InvokeMethod))

        localMethodCall = ""
        localOutput = ""
        If localMethodInfo.Attributes = _
            MethodAttributes.Public Then

            If localMethodInfo.ReturnType.ToString = _
                "System.Void" Then
                bReturn = False
            End If

            Dim localNewMethod As New CodeMemberMethod()
            Dim localMethodParams As String = ""

            localNewMethod.Name = localMethodInfo.Name

            localNewMethod.Attributes = _
                MemberAttributes.Public

            localNewMethod.ReturnType = New _
                CodeTypeReference( _
                localMethodInfo.ReturnType.FullName)

            localTempString = ""

            localMethodCall = "Try" & vbCrLf

            If Not bReturn Then
            localMethodCall &= tabLevel3 & _
                localObjectName & "." & _
                localMethodInfo.Name
            Else
            localMethodCall &= tabLevel3 & _
                "localReturn = " & localObjectName _
                & "." & localMethodInfo.Name
            End If

            bAtLeastOne = False

            Dim locaParameter As ParameterInfo
            For Each locaParameter In _
                localMethodInfo.GetParameters()
            localNewMethod.Parameters.Add(New _
                CodeParameterDeclarationExpression( _
                locaParameter.ParameterType, _
                locaParameter.Name))
            'Build string for method call output

            If bAtLeastOne Then
                localTempString = ","
                localTempString &= " _ " & vbCrLf _
                    & tabLevel4
            Else
                localTempString = "("
            End If

            localTempString &= locaParameter.Name

            If localTempString <> "" Then
                bAtLeastOne = True
                localMethodParams &= localTempString
            End If
            Next locaParameter

            localNewType.Members.Add(localNewMethod)

            localSnippet = New _
                CodeSnippetStatement(localObjectRef _
                & vbCrLf)

            localNewMethod.Statements.Add(localSnippet)

            localSnippet = Nothing

            localMethodCall &= localMethodParams

            If bReturn Then
            localSnippet = New _
                CodeSnippetStatement(_
                "Dim localReturn as " & _
                localMethodInfo.ReturnType.ToString _
                & vbCrLf)

            localNewMethod.Statements.Add(localSnippet)

            localSnippet = Nothing

            If localMethodParams = "" Then
                localMethodCall &= "()" & vbCrLf & _
                    vbCrLf
            Else
                localMethodCall &= ") as _
                    " & localMethodInfo.ReturnType.ToString _
                    & vbCrLf & vbCrLf
            End If
            Else
            If localMethodParams = "" Then
                localMethodCall &= "()" & vbCrLf & vbCrLf
            Else

                localMethodCall &= ")" & vbCrLf & vbCrLf
            End If
            End If

            localMethodCall &= tabLevel1 & _
                "Catch Exc as Exception" & vbCrLf
            localMethodCall &= tabLevel2 & _
                 "Throw New Exception( _
                 ""Error in "", _
                 Exc.InnerException)" & vbCrLf

            localMethodCall &= tabLevel1 & "End Try" _
                & vbCrLf & vbCrLf

            If bReturn Then
            localMethodCall &= tabLevel1 & "Return " _
                & "localReturn" & vbCrLf
            End If

            localOutput = localMethodCall & vbCrLf

            localSnippet = New _
                CodeSnippetStatement(localOutput)

            localNewMethod.Statements.Add(localSnippet)
        End If
        Next localMethodInfo

        localNamespace.Types.Add(localNewType)

        gen.GenerateCodeFromCompileUnit( _
            localCompileUnit, sw, _
            New CodeGeneratorOptions())

        sw.Close()
    Catch exc As Exception

        Throw New Exception(exc.Message)

    End Try

    Return True

    End Function

End Module

The GenerateClassCode function starts out by defining a number of variables. Let's take a look at the variables that are directly part of the CodeDOM namespace. The first is localCompileUnit, which represents the new class I'm going to create. This is the only element that's used during code generation. The next variable is localNamespace and, as you might guess, it represents a namespace. Its name is created by using the name of the namespace of the class being generated and appending "Client" or "Proxy" to it.

Next, I define localNewType (of type CodeTypeDeclaration); this represents the class I'm going to create. The constructor to CodeTypeDeclaration takes the name of the class which, like the namespace, has "Client" appended to it.

The next three variables define elements of the code I will generate. The localNewConstructor variable (of type CodeConstructor) represents the constructor for the class. The localMethodInfo variable (of type MethodInfo) represents the detail attributes of the method. The localSnippet variable (of type CodeSnippetStatement) represents code that you want to add to the generated class. This variable is used to build strings of code that can be dropped into the output by adding the snippet to a method.

The variable localVBcodeProvider (of type VBCodeProvider) defines a reference to an instance of the VBCodeProvider that allows you to generate code in Visual Basic®. (There is also a C# provider for those who are so inclined.) Finally, the variable gen (of type ICodeGenerator) provides a reference that will be used later to actually generate the code. The other variables are standard types that are used for strings or other generic purposes.

Also in Figure 2, the six statements just before the Try block create a StreamWriter object that will be used to write the code file later and to create a string (localObjectRef) that defines the reference original class.

The first two statements in the Try block add the Imports statement to the Imports collection, then add the namespace to localCompileUnit.Namespace. You could add any number of Imports statements in this manner. The next three statements create the constructor and then add it to the Members collection of the new type (the new class).

The bulk of the work is performed in the For Each loop that loops through each of the methods. The first If statement makes sure you're looking at a Public method. The second checks to see if the return value is System.Void, which indicates that the method is a subroutine and not a function. If this is true, a Boolean variable is set to restrict generation of the output code.

The next statement is a reference to a new object of type CodeMemberMethod, which represents the method I'm going to generate. The statement that follows sets the method's name by pulling the name of the method referenced by localMethodInfo. Then this statement sets the attributes of the method to Public:

localNewMethod.Attributes = MemberAttributes.Public
You can set more than one attribute if necessary by anding them together. Next, the return type of the method is set by accessing the ReturnType property of localMethodInfo.

Now it's time to generate the code within the class using one of two approaches. You can either determine elements of the CodeDOM to use and construct everything this way or you can create and add code snippets, which are simply strings representing your code. The advantage of the first approach is that it works across different Microsoft .NET-compliant languages. The second approach is usually language-specific, but it allows you to quickly reuse code you may already have in your arsenal. This is the approach I used most frequently in my example.

The localMethodCall variable stores the code that will be output within the method. The If block sets up the correct call to the original classes method, which will depend on whether or not the method returns data.

The inner For Each block loops through the method's parameters, performing two tasks. The first statement in the block adds the current parameter to the method definition (localNewMethod). The rest of the code adds the parameters to the original method call of the class. When the loop has finally completed, localMethodParams will contain all of the parameters for the method call, properly delimited.

After the inner loop ends, the localNewMethod object is added to the Members collection of the new type. Next, a new instance of the CodeSnippetStatement class is created with the string required to instantiate the original class. Then the snippet is added to the Statements collection of the new type. The rest of the code in the outer loop builds the balance of code for the method and adds the code snippet(s) to the new type.

After this loop ends, the new type is added to the Types collection of the namespace with this statement:

localNamespace.Types.Add(localNewType)

Now comes the fun part. The next statement takes the compile unit containing your namespace and the string writer, and then generates this code:

gen.GenerateCodeFromCompileUnit(localCompileUnit, sw, _
    New CodeGeneratorOptions())
Pretty sweet. The only bits of code generated with strings are the code snippets for the code's method. If I didn't use any code snippets, I could have allowed an option to generate code for different languages. Note that this sample application is extensible. For instance, right now it does not add the public properties of the original class to the proxy class, but it could.

As you can see, it's easy to automate many tasks using the .NET Framework. In fact, automating various parts of the application creation process will allow you to build applications in record time, yet still build the types of user interface, middle tier, and other customizations that you require.

Send your questions and comments for Ken to basics@microsoft.com.

Ken Spencerworks for 32X Tech (http://www.32X.com), where he provides software development, consulting services, and training on Microsoft technologies.


Basic Instincts: Programming with Events Using .NET

Basic Instincts
Programming with Events Using .NET
Ted Pattison

Contents
What Exactly is an Event?
Programming with Events
Raising an Event
Creating and Registering an Event Handler
Conclusion

This month's Basic Instincts column builds upon my last two columns in which I talked about concepts and programming techniques associated with delegates. I will assume you have read the last two installments of this column and that you understand the role that delegates play within the Microsoft® .NET Framework. If you haven't read the last two columns, see Implementing Callback Notifications Using Delegates and Implementing Callbacks with a Multicast Delegate. You should also know how to design and write a simple application that uses multicast delegates to send callback notifications to a set of handler methods.

While you have probably been programming with events for years, migrating to the .NET Framework requires you to reexamine their inner workings because events in the .NET Framework are layered on top of delegates. The more you know about delegates, the more power you can harness when you program events. Your understanding of how events work at a lower level is critical when you start to work with one of the event-driven frameworks of the common language runtime (CLR) such as Windows® Forms or ASP.NET. My goal this month is to give you an understanding of how events work at a lower level.

What Exactly is an Event?

An event is just a formalized software pattern in which a notification source makes callbacks to one or more handler methods. Events are therefore similar to interfaces and delegates because they provide a means to design applications that use callback methods. However, events add a valuable degree of productivity because they are easier to use than interfaces or delegates. Events allow the compiler and the Visual Studio® .NET IDE to do much of the work for you behind the scenes.

A design that involves events is based on an event source and one or more event handlers. An event source can be either a class or an object. An event handler is a delegate object that's bound to a handler method. Figure 1 shows a high-level view of an event source wired up to its handler methods.

Figure 1 Event Source and Handlers
Figure 1 Event Source and Handlers

Every event is defined in terms of a particular delegate type. For each event defined by an event source, there is a private field that is based on the event's underlying delegate type. This field is used to track a multicast delegate object. An event source also provides a public registration method that allows you to register as many event handlers as you'd like.

When you create an event handler (a delegate object) and register it with an event source, the event source simply appends the new event handler to the end of the list. An event source can then use the private field to call Invoke on the multicast delegate which, in turn, will execute all the registered event handlers.

What's really nice about events is that much of the work to set them up is already done for you. As you will soon see, the Visual Basic® .NET compiler assists you by automatically adding a private delegate field and a public registration method whenever you define an event. You will also see that Visual Studio .NET provides even more assistance with a code generator that can automatically emit the skeleton definitions for your handler methods.

Programming with Events

Because events in .NET are built on top of delegates, their underlying plumbing details are very different from the way things used to work in previous versions of Visual Basic. However, the language designers of Visual Basic .NET did a good job in keeping the syntax for programming events consistent with earlier versions of Visual Basic. In many cases, programming events involves the same old familiar syntax you're used to using. For example, you will use keywords such as Event, RaiseEvent, and WithEvents, and they will behave almost identically to the way they have behaved in previous versions of Visual Basic.

Let's start by creating a simple callback design based on an event. First, I need to define an event within a class definition by using the Event keyword. Every event must be defined in terms of a specific delegate type. Here's an example of defining both a custom delegate type and a class that uses it to define an event:

Delegate Sub LargeWithdrawHandler(ByVal Amount As Decimal)

Class BankAccount
  Public Event LargeWithdraw As LargeWithdrawHandler
  '*** other members omitted
End Class
In this example, the LargeWithdraw event has been defined as an instance member. In this design, a BankAccount object will act as the event source. If you want a class instead of an object to act as an event source, you should define events as shared members using the Shared keyword.

When you program with events, it's important to acknowledge that the compiler is doing a good deal of extra work for you behind the scenes. For example, what do you think the compiler does when you compile the definition of the BankAccount class that I just showed you into an assembly? Figure 2 shows what the resulting class definition would look like when inspected with ILDasm.exe, the intermediate language disassembler. This view provides a revealing look at how much the Visual Basic .NET compiler is doing behind the scenes to assist you.

Figure 2 Class Definition in ILDasm
Figure 2 Class Definition in ILDasm

When you define an event, the compiler generates four members inside the class definition. The first member is a private field based on the delegate type. This field is used to track a reference to a delegate object. The compiler generates the name for this private field by taking the name of the event itself and adding the suffix "Event". This means that creating an event named LargeWithdraw results in the creation of a private field named LargeWithdrawEvent.

The compiler also generates two methods to assist with the registration and unregistration of delegate objects that are to serve as event handlers. These two methods are named using a standard naming convention. The method for registering an event handler is named after the event along with a prefix of "add_". The method for unregistering an event handler is named after the event along with a prefix of "remove_". Therefore, the two methods created for the LargeWithdraw event are named add_LargeWithdraw and remove_LargeWithdraw.

The Visual Basic .NET compiler generates an implementation for add_LargeWithdraw that accepts a delegate object as a parameter and adds it to the list of handlers by calling the Combine method of the Delegate class. The compiler generates an implementation for remove_LargeWithdraw that removes a handler method from the list by calling the Remove method in the Delegate class.

The fourth and final member that is added to the class definition is one that represents the event itself. You should be able to locate the event member named LargeWithdraw in Figure 2. It is the member with an upside-down triangle next to it. However, you should note that this event member isn't really a physical member like the other three. Instead, it's a metadata-only member.

This metadata-only event member is valuable because it can inform compilers and other development tools that the class supports the standard pattern for event registration in the .NET Framework. The event member also contains the names of the registration method and the unregistration method. This allows compilers for managed languages such as Visual Basic .NET and C# to discover the name of the registration method at compile time.

Visual Studio .NET is another good example of a development tool that looks for this metadata-only event member. When Visual Studio .NET sees that a class definition contains events, it automatically generates the skeleton definitions for handler methods as well as the code to register them as event handlers.

Before I move on to a discussion of raising events, I'd like to cover a restriction involved with creating a delegate type that's to be used for defining events. A delegate type used to define an event cannot have a return value. You must define the delegate type using the Sub keyword instead of the Function keyword, as shown here:

'*** can be used for events
Delegate Sub BaggageHandler()
Delegate Sub MailHandler(ItemID As Integer)

'*** cannot be used for events
Delegate Function QuoteOfTheDayHandler(Funny As Boolean) As String
There's a good reason for this restriction. It's far more difficult to work with return values in a case involving a multicast delegate that's bound to several handler methods. A call to Invoke on a multicast delegate returns the same value as the last handler method in the invocation list. However, capturing the return value of handler methods that appear earlier in the list isn't so straightforward. Eliminating the need to capture multiple return values simply makes events easier to use.

Raising an Event

Now let's modify the BankAccount class so that it's able to raise an event when a withdrawal is made for an amount that exceeds a $5000 threshold. The easiest way to fire the LargeWithdraw event is to use the RaiseEvent keyword within the implementation of a method, property, or constructor. This syntax is probably familiar because it's similar to what you have used in earlier versions of Visual Basic. Here's an example of firing the LargeWithdraw event from the Withdraw method:

Class BankAccount
  Public Event LargeWithdraw As LargeWithdrawHandler
  Sub Withdraw(ByVal Amount As Decimal)
    '*** send notifications if required
    If (Amount > 5000) Then
      RaiseEvent LargeWithdraw(Amount)
    End If
    '*** perform withdrawal
  End Sub
End Class

While the syntax remains the same from previous versions of Visual Basic, what happens when you raise an event is very different now. When you use the RaiseEvent keyword to fire an event, the Visual Basic .NET compiler generates the code required to execute each and every event handler. For example, what do you think happens when you compile the following code?

RaiseEvent LargeWithdraw(Amount)
The Visual Basic .NET compiler expands this expression to code that calls Invoke on the private field that holds the multicast delegate object. In other words, using the RaiseEvent keyword has the very same effect as writing the code in the following snippet:
If (Not LargeWithdrawEvent Is Nothing) Then
  LargeWithdrawEvent.Invoke(Amount)
End If

Note that the code generated by the Visual Basic .NET compiler conducts a check to make sure that the LargeWithdrawEvent field contains a valid reference to an object. That's because the LargeWithdrawEvent field will have a value of Nothing until the first handler method is registered. Therefore, the generated code doesn't attempt to call Invoke unless at least one handler method is currently registered.

You should be able to make an observation about raising an event. It usually doesn't matter whether you use the RaiseEvent keyword or you program directly against the private LargeWithdrawEvent field that's automatically generated by the compiler. Both approaches produce equivalent code:

'*** this code
RaiseEvent LargeWithdraw(Amount)

'*** is the same as this code
If (Not LargeWithdrawEvent Is Nothing) Then
  LargeWithdrawEvent.Invoke(Amount)
End If

In many cases, it's likely that you will prefer the syntax of the RaiseEvent keyword because it requires less typing and it results in code that is more concise. However, in certain situations where you need more control, it might make sense to explicitly program against the private LargeWithdrawEvent field. Let's look at an example of when this would be the case.

Imagine a scenario in which a BankAccount object has three event handlers that have been registered to receive notifications for the LargeWithdraw event. What would happen if you triggered the event using the RaiseEvent keyword and the second event handler in the invocation list threw an exception? The line of code containing the RaiseEvent statement would receive a runtime exception, but you would have no way to determine which event handler threw it. Furthermore, there would be no way to handle the exception thrown by the second event handler and to continue where the third event handler is executed as expected.

However, if you are willing to program in terms of the private LargeWithdrawEvent field, you can deal with an exception thrown by an event handler in a more graceful manner. Examine the code in Figure 3. As you can see, dropping down to a lower level and programming against the private delegate field provides an extra degree of control. You can gracefully handle an exception and then go on to execute event handlers that appear later in the list. This technique has obvious benefits over the RaiseEvent syntax in which an exception thrown by an event handler prevents the execution of any event handlers that appear later in the invocation list.

Figure 3 Using the Private Delegate Field (Close Figure 3)
Sub Withdraw(ByVal Amount As Decimal)
  '*** send notifications if required
  If (Amount > 5000) AndAslo (Not LargeWithdrawEvent Is Nothing) Then
    Dim handler As LargeWithdrawHandler
    For Each handler In LargeWithdrawEvent.GetInvocationList()
      Try
        handler.Invoke(Amount)
      Catch ex As Exception
        '*** deal with exceptions as they occur
      End Try
    Next
  End If
  '*** perform withdrawal
End Sub

Creating and Registering an Event Handler

Now that you've seen how to define and raise an event, it's time to discuss how to create an event handler and register it with a given source. There are two different ways to accomplish this in Visual Basic .NET. The first technique is known as dynamic event binding and involves the use of the AddHandler keyword. The second technique is called static event binding and involves the use of the familiar Visual Basic keyword WithEvents. I plan to cover static event binding in a future column. So for now, let's examine how dynamic event binding works.

Remember that an event handler is a delegate object. Therefore, you create one by instantiating a delegate object from the delegate type on which the event is based. When you create this delegate object, you must bind it to a target handler method that you want to serve as an event handler.

Once you have created an event handler, you must register it with a specific event by calling the special registration method on the event source. Recall that the registration method for the LargeWithdraw event is named add_LargeWithdraw. When you call the add_LargeWithdraw method and pass a delegate object as a parameter, the event source adds the delegate object to the list of event handlers that are to receive event notifications.

What's confusing about event registration is that you never directly call a registration method such as add_LargeWithdraw. In fact, the Visual Basic .NET compiler will raise a compile-time error if you try to access an event registration method by name. Instead, you use an alternate syntax involving the AddHandler statement. When you use the AddHandler statement, the Visual Basic .NET compiler generates the code to call the event registration method for you.

Let's look at an example of wiring up a few event handlers using dynamic event registration. Imagine you have written the following set of shared methods in the AccountHandlers class:

Class AccountHandlers
  Shared Sub LogWithdraw(ByVal Amount As Decimal)
    '*** write withdrawal info to log file
  End Sub

  Shared Sub GetApproval(ByVal Amount As Decimal)
    '*** block until manager approval
  End Sub
End Class

What should you do if you'd like to employ these methods as event handlers for the LargeWithdraw event of the BankAccount class? Let's start by creating an event handler that's bound to the handler LogWithdraw. First, you must create the delegate object that's going to serve as an event handler:

Dim handler1 As LargeWithdrawHandler
handler1 = AddressOf AccountHandlers.LogWithdraw

Next, you must register this new delegate object with an event source using an AddHandler statement. When you register an event handler using the AddHandler statement, you are required to pass two parameters, like this:

AddHandler <event>, <delegate object>
The first parameter required by AddHandler is an expression that evaluates to an event of a class or object. The second parameter is a reference to the delegate object that is going to be wired up as an event handler. Here's an example of using an AddHandler statement to register an event handler with the LargeWithdraw event of a BankAccount object:
'*** create bank account object
Dim account1 As New BankAccount()

'*** create and register event handler
Dim handler1 As LargeWithdrawHandler
handler1 = AddressOf AccountHandlers.LogWithdraw
AddHandler account1.LargeWithdraw, handler1

When you use the AddHandler keyword to register an event handler for the LargeWithdraw event, the Visual Basic .NET compiler expands this code to call the registration method add_LargeWithdraw. Once the code containing the AddHandler statement has been executed, your event handler is in place and ready for notifications. Therefore, the LogWithdraw method will execute whenever the BankAccount object raises a LargeWithdraw event.

In the last example, I used a longer form of syntax to illustrate exactly what happens when you create and register an event handler. However, once you understand how things work, you might appreciate using a more concise syntax to accomplish the same goal, as shown here:

'*** create bank account object
Dim account1 As New BankAccount()

'*** register event handlers
AddHandler account1.LargeWithdraw, AddressOf AccountHandlers.LogWithdraw
AddHandler account1.LargeWithdraw, AddressOf AccountHandlers.GetApproval
Since the AddHandler statement expects a reference to a delegate object as the second parameter, you can use the shorthand syntax of the AddressOf operator followed by the name of the target handler method. When it sees this, the Visual Basic .NET compiler then generates the extra code to create the delegate object that is going to serve as the event handler.

The AddHandler statement of the Visual Basic .NET language is complemented by the RemoveHandler statement. RemoveHandler requires the same two parameters as AddHandler, yet it has the opposite effect. It removes the target handler method from the list of registered handlers by calling the remove_LargeWithdraw method supplied by the event source:

Dim account1 As New BankAccount()

'*** register event handler
AddHandler account1.LargeWithdraw, AddressOf AccountHandlers.LogWithdraw

'*** unregister event handler
RemoveHandler account1.LargeWithdraw, AddressOf AccountHandlers.LogWithdraw

Now you have seen all the steps required to implement a callback design using events. The code in Figure 4 shows a complete application in which two event handlers have been registered to receive callback notifications from the LargeWithdraw event of a BankAccount object.

Figure 4 An Event-based Design for Callback Notifications (Close Figure 4)
Delegate Sub LargeWithdrawHandler(ByVal Amount As Decimal)

Class BankAccount
  Public Event LargeWithdraw As LargeWithdrawHandler
  Sub Withdraw(ByVal Amount As Decimal)
    '*** send notifications if required
    If (Amount > 5000) Then
      RaiseEvent LargeWithdraw(Amount)
    End If
    '*** perform withdrawal
  End Sub
End Class

Class AccountHandlers
  Shared Sub LogWithdraw(ByVal Amount As Decimal)
    '*** write withdrawal info to log file
  End Sub
  Shared Sub GetApproval(ByVal Amount As Decimal)
    '*** block until manager approval
  End Sub
End Class

Module MyApp
  Sub Main()
    '*** create bank account object
    Dim account1 As New BankAccount()
    '*** register event handlers
    AddHandler account1.LargeWithdraw, _
               AddressOf AccountHandlers.LogWithdraw
    AddHandler account1.LargeWithdraw, _
               AddressOf AccountHandlers.GetApproval
    '*** do something that triggers callback
    account1.Withdraw(5001)
  End Sub
End Module

Conclusion

While the motivation for using events and some of the syntax remains unchanged from previous versions of Visual Basic, you have to admit that things are rather different now. As you can see, you have far more control over how you respond to events than you've ever had before. This is especially true if you're willing to drop down and program in terms of delegates.

In the next installment of the Basic Instincts column I plan to continue this discussion of events. I'll show you how Visual Basic .NET supports static event binding through the familiar syntax of the WithEvents keyword, and I'll discuss the Handles clause. In order to really master events, you must be comfortable with both dynamic event registration and static event registration.

Send your questions and comments for Ted to instinct@microsoft.com.

Ted Pattisonis an instructor and researcher at DevelopMentor (http://www.develop.com), where he co-manages the Visual Basic curriculum. He is the author of Programming Distributed Applications with COM and Microsoft Visual Basic 6.0 (Microsoft Press, 2000).


The ASP Column: Using the Internet Explorer WebControls

The ASP Column
Using the Internet Explorer WebControls
George Shepherd

Contents
Getting Started
TabStrip, MultiView, and PageView
MultiPage and TabStrip
PageView
Getting Data into the PageViews
What is Rendered
Conclusion

During the lifespan of ASP, there have been many technologies developed for making browser-based user interface development easier. For example, during the early days of classic ASP development, keeping a browser-based UI consistent required many conditional statements into the ASP script. Server-side controls eliminate much of that hassle by pushing the details of managing UI state down into a set of runtime classes.

Web Forms and server-side controls manage their own view states, relieving you of that requirement (see the Cutting Edge column in this issue). There's also built-in designer support for working with server-side controls. In some cases, developing the browser-based UI is as simple as dragging the controls onto the form and wiring up the events to be handled by the page.

This month I'm going to start looking at an auxiliary set of server-side controls: the Microsoft® Internet Explorer WebControls. These controls can really help you dress up your Web site. This suite of controls includes three UI components normally associated with desktop-style applications: a hierarchical tree control (called a TreeView), a toolbar (called ToolBar), and a component similar to a tabbed dialog box (the TabStrip, the MultiPage, and the PageView controls). This month I'll take a look at the TabStrip and the MultiPage control. I'll look a the TreeView and the Toolbar in a future column.

Getting Started

Before going any further, you'll probably want to download a copy of the controls from ASP.NET. To get the whole suite (including the controls and the DHTML behaviors), select "Automatic Install: WebControls Version 1.0 Client DHTML Behaviors and Server Controls" as the downloading option. It's a small download—only 660KB. Once the file called IEWebControls.MSI is on your machine, run it. The installation process adds the assemblies to the global assembly cache.

To make the controls available to Visual Studio®, go to the Tools menu and select Customize Toolbox.

Choose the .NET Framework Components tab and you'll see all the components that are installed on your machine. Take a quick look through the list. You'll see two entries for ToolBar and TreeView. If you examine the namespace for each component, you'll see that there's a ToolBar for Windows Forms and another for Web Forms. For developing Web Forms with the Internet Explorer WebControls, make sure the ToolBar, TreeView, TabStrip, and MultiPage components are all checked. Then they'll appear in the designer toolbox while you're working on a form.

TabStrip, MultiView, and PageView

Tabbed dialog boxes are straightforward to implement within a desktop environment. The entire state of the tabbed dialog box can be held in one place—inside the application. However, Web development is different. When developing a Web-based UI, you're concerned with making the user interface look as much as possible like a desktop user interface even though the UI is coded using a markup language over a connectionless protocol. You would certainly think that tabbed dialog boxes are out of the question. After all, there's a lot of state to keep track of when managing multiple dialog-style interfaces.

The Internet Explorer WebControls make this sort of interface possible through TabStrip, MultiView, and PageView. In mapping the Web components to the components in a standard Windows® UI, TabStrip represents the tabs across the top of the dialogs. The MultiPage control is somewhat akin to the property sheet, collecting separate property pages. In the Internet Explorer WebControls scenario, PageView represents each of the property pages.

MultiPage and TabStrip

In my December 2001 column I compared the DataGrid and the DataList as different ways of generating a user interface based upon a shopping cart-style application. This month I'll use a MultiPage and some PageViews to display the details of the same collection. A DataGrid on the left-hand side of the page shows a list of products (guitars). Pressing a "Select" link in the first column places the current selection's detail in a MultiPage control. The detail categories include information about a particular guitar, information about the seller, and other related information.

Using a TabStrip and a MultiPage is easy: just drag them from the toolbox onto the designer, as Figure 1 shows. When you drag a TabStrip onto the designer, it comes with three tabs by default. To change them or add new tabs, highlight the TabStrip and go to the Properties pane. When you press the button in the Items property, you'll see the dialog shown in Figure 2.

Figure 1 DataGrid (Close Figure 1)

Figure 2 Modifying Tabs in TabStrip
Figure 2 Modifying Tabs in TabStrip

The dialog box for modifying the Tab properties is straightforward and very similar to the one used for most other controls. Press the Add button to add tabs and tab separators, then set the properties for each element in the tab bar.

Putting a MultiPage onto the Web Form is also easy. The MultiPage also appears in the Visual Studio toolbox—you can just drag it onto the form. Like TabStrip, MultiPage has properties such as the control ID, foreground and background color, and border options.

After you drag one of these controls onto a Web Form, Visual Studio automatically generates the following registration directive at the top of the ASPX page:

<%@ Register TagPrefix="iewc"
    Namespace="Microsoft.Web.UI.WebControls"
    Assembly="Microsoft.Web.UI.WebControls,
    Version=1.0.2.226, Culture=neutral,
    PublicKeyToken=31bf3856ad364e35" %>
The Register directive helps the runtime map the controls declared on the page to a specific assembly and namespace.

Among the properties for a TabStrip is the TargetID, which points to the MultiPage associated with the TabStrip. When you have a TabStrip and a MultiPage on the Web Form, associating the TabStrip TargetID with the name of the MultiPage control enables the user to click on a tab to show the separate PageViews within a MultiPage.

PageView

Placing TabStrips and MultiPages on a Web Form involves dragging and dropping from the toolbox. Putting different PageViews in the MultiPage is a different matter. If you look at Figure 1 you'll see that the MultiPage on the form says "To add PageViews to this MultiPage, please switch to HTML view." PageViews need to be typed by hand. Figure 3 shows the ASPX code for both the MultiPage and PageViews.

Figure 3 Adding PageViews to a MultiPage (Close Figure 3)
<iewc:multipage id="MultiPageProductInfo"
    style="Z-INDEX: 101; LEFT: 468px; POSITION: absolute; TOP: 93px"
    runat="server" BorderWidth="1px" BorderColor="Black"
    Height="365px" Width="407px">
    <iewc:PageView id="ProductInformationPage">
        <h3>Product Information</h3>
            <table>
                <tr>
                <td valign="top" align="left">
                    <asp:Label Runat="server"
                    text="GuitarName:" Font-Bold />
                    <asp:Label runat="Server"
                    ID="LabelGuitarName"
                    text="Hamer Artist" /><br>
                    <asp:Label Runat="server"
                    text="Pickups: " Font-Bold />
                    <asp:Label Runat="server"
                    ID="LabelPickups"
                    text="2 Seymour Duncans" /><br>
                    <asp:Label Runat="server"
                    text="Scale: " Font-Bold />
                    <asp:Label Runat="server"
                    ID="LabelScale"
                    text="24 3/4 in." /><br>
                    <asp:Label Runat="server"
                    text="Price: " Font-Bold />
                    <asp:Label Runat="server"
                    ID="LabelPrice" text="444.55" />
                </td>
                </tr>
                <td>
                    <asp:Image Runat="server"
                    ImageUrl="images\HamerArtist.jpg"
                    id="ProductImage" />
                </td>
                <tr>
                </tr>
            </table>
    </iewc:PageView>
    <iewc:PageView id="SellerInformationPage">
        <h3>Seller Information</h3>
            <table>
                <tr>
                <td valign="top" align="left">
                    <asp:Label Runat="server"
                    text="Seller Name:" Font-Bold
                    ID="Label1" />
                    <asp:Label runat="Server"
                    ID="LabelSellerName" />
                    <br>
                    <asp:Label Runat="server"
                    text="Address:" Font-Bold />
                    <asp:Label Runat="server"
                    ID="LabelSellerAddress" />
                    <br>
                    <asp:Label Runat="server"
                    text="Comment:" Font-Bold
                    ID="Label5" />
                    <asp:Label Runat="server"
                    ID="LabelSellerComment" />
                </td>
                </tr>
                <td>
                    <asp:Image Runat="server"
                    ImageUrl=""
                    id="SellerImage" />
                </td>
                <tr>
                </tr>
            </table>
    </iewc:PageView>
    <iewc:PageView id="RelatedInformationPage">
        <h3>Related Information</h3>
            <asp:Label Runat="server"
            text="Other items for sale"
            ID="Label2"></asp:Label>
            <br>
            <asp:Label Runat="server"
            ID="LabelRelatedItem1"></asp:Label>
            <br>
            <asp:Label Runat="server"
            ID="LabelRelatedItem2"></asp:Label>
            <br>
            <asp:Label Runat="server"
            ID="LabelRelatedItem3"></asp:Label>
            <br>
    </iewc:PageView>
</iewc:multipage>

Figure 3 lists a MultiPage added by the designer. Sandwiched between the MultiPage opening and closing tags are three separate PageViews. You can think of PageViews as convenient frames for holding server-side controls (or plain HTML) elements. The MultiPage in this example shows three PageViews: product information, seller information, and a list of related links. Each element within the PageViews has a distinct ID. You'll need each of those IDs when putting data into the PageViews.

Getting Data into the PageViews

The collection is represented in a database as a DataTable. The table for this example has the schema shown in Figure 4. The ProductName and the ProductNumber appear in the DataGrid along with the selection link. The Web Form's job is to display all the other fields in the PageViews within the MultiView. That requires some code-behind. Figure 5 shows a method named ShowSelectedItemsInfo that populates the elements within each of the PageViews.

Figure 5 Updating Each Element within PageViews (Close Figure 5)
public class WebForm1 : System.Web.UI.Page
{
    protected PageView ProductInformationPage;
    protected PageView SellerInformationPage;
    protected PageView RelatedInformationPage;

public void ShowSelectedItemsInfo(DataRow dr)
{
    // Show Product Page
    PageView pvProductInfo = this.ProductInformationPage;
    Label GuitarName =
        (Label)pvProductInfo.FindControl("LabelGuitarName");
    GuitarName.Text = dr["ProductName"].ToString();

    Label Pickups =
        (Label)pvProductInfo.FindControl("LabelPickups");
    Pickups.Text = dr["Pickups"].ToString();

    System.Web.UI.WebControls.Image ProductImage =
        (System.Web.UI.WebControls.Image)
        pvProductInfo.FindControl("ProductImage");
    ProductImage.ImageUrl = dr["Image"].ToString();

    Label Scale =
        (Label)pvProductInfo.FindControl("LabelScale");
    Scale.Text = dr["Scale"].ToString();

    Label Price =
        (Label)pvProductInfo.FindControl("LabelPrice");
    Price.Text = "$" + dr["Price"].ToString();

    // Show Seller Page
    PageView pvSellerInfo = SellerInformationPage;
    Label SellerName =
        (Label)pvSellerInfo.FindControl("LabelSellerName");
    SellerName.Text = dr["SellerName"].ToString();

    Label SellerAddress =
        (Label) pvSellerInfo.FindControl("LabelSellerAddress");
    SellerAddress.Text =
        dr["SellerAddress"].ToString();

    Label SellerComment =
        (Label) pvSellerInfo.FindControl("LabelSellerComment");
    SellerComment.Text = dr["SellerComment"].ToString();

    System.Web.UI.WebControls.Image SellerImage =
        (System.Web.UI.WebControls.Image)
        pvSellerInfo.FindControl("SellerImage");
    SellerImage.ImageUrl = dr["SellerImage"].ToString();

    // Show Related Info Page
    PageView pvRelatedInfo = RelatedInformationPage;
    Label LabelRelatedItem1 =
        (Label) pvSellerInfo.FindControl("LabelRelatedItem1");
    LabelRelatedItem1.Text =
        dr["RelatedItem1"].ToString();

    Label LabelRelatedItem2 =
        (Label) pvSellerInfo.FindControl("LabelRelatedItem2");
    LabelRelatedItem2.Text =
        dr["RelatedItem2"].ToString();

    Label LabelRelatedItem3 =
        (Label) pvSellerInfo.FindControl("LabelRelatedItem3");
    LabelRelatedItem3.Text = dr["RelatedItem3"].ToString();
}
}
Figure 4 DataTable Schema (Close Figure 4)

Column Name | Data Type
ProductNumber | String
ProductName | String
Price | Double
Image | String
SellerName | String
Scale | Double
Pickups | String
SellerAddress | String
SellerComment | String
SellerImage | String
RelatedItem1 | String
RelatedItem2 | String
RelatedItem3 | String

Each of the PageViews declared in the ASPX page is listed as a member of the WebForm1 class ("WebForm1" was assigned by the wizard). To set the properties for a specific element within the PageView, use the FindControl method. The code that is listed in Figure 5 uses FindControl to get each element within each PageView. The data source for setting up the PageViews is a DataRow, presumably retrieved from a database by using a SQL command. (The actual data source is less important—this example happens to build a table in memory.)

ShowSelectedItemsInfo should be called the first time the page loads as well as on subsequent reposts caused by the user selecting a product. Figure 6 shows the code necessary for filling the PageViews during the page load event and a postback. The first row of the table is used when the page loads, and the selected item is used when responding to one of the selection links. After selecting a product, the user browses the PageViews by clicking on the various tabs to show the information. Figure 7 shows the state of the Web page after selecting one of the tables.

Figure 6 Filling PageViews (Close Figure 6)
private void Page_Load(object sender, System.EventArgs e)
{
    // ProductDataGrid and ProductDataTable are class members
    ProductDataTable = GetDataSource();
    ProductDataGrid.DataSource = ProductDataTable;
    this.DataBind();

    if(!this.IsPostBack)
    {
        DataRow dr = ProductDataTable.Rows[0];
        ShowSelectedItemsInfo(dr);
    }
}

protected void Grid_OnItemCommand(Object sender,
    DataGridCommandEventArgs e)
{
    //TableRow tr = (TableRow)e.Item.Cells;
    int nDataSetIndex = e.Item.DataSetIndex;
    DataTable dt = (DataTable)ProductDataGrid.DataSource;
    DataRow dr = dt.Rows[nDataSetIndex];
    ShowSelectedItemsInfo(dr);
}
Figure 7 Selecting a Tab
Figure 7 Selecting a Tab

What is Rendered

Besides helping you with the process of creating rich browser-based UIs, these controls are made flexible by the markup code they return to the browser. When the control detects that an uplevel browser made the last request, the controls emit Dynamic HTML (DHTML). DHTML uses the browser's inner object model and client-side script to make the pages within a MultiPage respond to the TabStrip without making a round-trip back to the server. The Internet Explorer WebControls consider Internet Explorer 5.5 and higher to be uplevel browsers. If the browser is a downlevel browser (that is, anything else other than Internet Explorer 5.5 and higher), then the controls render HTML 3.2 markup language.

Conclusion

This month I showed how to use the TabStrip, MultiPage, and PageView controls for creating a multiple-page tabbed user interface element very similar to those found in standard Windows-based applications. In a future column I'll look at the two other controls in the package: TreeView and ToolBar.

Send your questions and comments for George to asp-net@microsoft.com.

George Shepherdwrites .NET development tools with SyncFusion (http://www.syncfusion.com) and teaches at Developmentor. He is the author of a number of programming books, including Programming with Microsoft Visual C++.NET (Microsoft Press, 2002), and Applied .NET (Addison-Wesley, 2001). George may be reached at georges@syncfusion.com or georges@develop.com.


C++ Q&amp;A: Singleton Class Private Constructor, C# Singleton Class, and More

C++ Q&A
Singleton Class Private Constructor, C# Singleton Class, and More
Paul DiLascia

Code download available at: CQA0302.exe (40KB)

QI have a C++ singleton class, and I have done the preliminaries of making the constructor private. I wrote a static function that returns a reference to the one and only object, restricting all means of creating the object except the following:

// This should not compile
CSingleton temp = CSingleton::GetObjectInstance();
How can I prevent this line from compiling?

Nilesh Prakash Padbidri

AWhen the C++ compiler encounters the statement

CSingleton temp = CSingleton::GetInstance();
it looks for a copy constructor, which is a constructor with the signature CSingleton(const CSingleton&). If you don't provide a copy constructor, C++ will provide one for you. The default copy constructor does a simple flat copy of the bytes from one object to the other. If you want something else, you'll have to implement your own copy constructor. In this case, to disallow copying, you can make it private.
class CSingleton {
private:
  // private copy constructor
  CSingleton(const CSingleton& obj)
  { ASSERT(FALSE); } // should never happen
};

If you do this, you'll also have to implement the default (no-argument) constructor, if you haven't already, because C++ only generates a default constructor if no others are defined. Without the default constructor, you won't be able to instantiate the singleton. You can make the default constructor protected or private, depending on whether you want to let other classes derive from CSingleton. Figure 1 shows the details. So the short answer to your question is: implement a private copy constructor.

Figure 1 Singleton.cpp (Close Figure 1)
// This program illustrates how to write a singleton class (a class that
// can have only one instance) in C++. The trick is to make the default
// constructor, copy constructor and assignment operator all private. A
// static function GetInstance returns the one and only object instance.
//
// If you attempt to compile this program, it will generate errors.
// (See main function below.)
//
class CSingleton {
public:
   static CSingleton& GetInstance() {
      static CSingleton theInstance; // one and only instance
      return theInstance;
   }

protected:
   // need default ctor for GetInstance.
   // ctor is protected, not private in case you want to derive.
   CSingleton() { }

private:
   CSingleton(const CSingleton& o) { }
   CSingleton& operator=(const CSingleton& o) { }
};

main()
{
    // These lines will not compile:
    CSingleton x = CSingleton::GetInstance(); // error: private
                                              // copy ctor!
    CSingleton y = CSingleton::GetInstance(); // error: private
                                              // copy ctor!
    x = y;                                    // error: private
                                              // assignment!
}

QI've been programming in C++ for a long time, and I'm familiar with a number of patterns like the singleton (a global object with a private constructor). Now I'm starting a project in C# using Microsoft® .NET and I'd like to know the best way to create a singleton class in C#.

Marianne Simone

ASingleton classes are even easier in C# than they are in C++ because the .NET Framework has the notion of singletons built in. Here's the C# Singleton pattern distilled:

sealed class Singleton
{
  private Singleton() { }
  public static readonly Singleton
    TheInstance = new Singleton();
}

As in C++, you can use a private constructor to prevent programmers from creating instances of Singleton. To prohibit inheritance, declare your class sealed. (In C++ you can do this by making all constructors private.) Instead of a static object inside a static GetInstance function, as shown in Figure 1, C# lets you create a read-only static property (in this case, Singleton.TheInstance) initialized to a new instance of the class. This accomplishes the same thing as the C++ pattern with less typing: the framework won't actually create and initialize TheInstance until someone attempts to use it, which you can do like so:

Singleton s = Singleton.TheInstance;

The framework even takes care of synchronization problems that can arise in multithreaded situations. Figure 2 shows a C# Singleton you can actually compile and run. For more on singletons, see the article titled "Exploring the Singleton Design Pattern" in the MSDN® library.

Figure 2 Singleton.cs (Close Figure 2)
// Singleton — list top-level visible windows
//
using System;

sealed class Singleton
{
   private Singleton() { }
   public static readonly Singleton TheInstance = new Singleton();
   public void SayHello() {
      Console.WriteLine("hello,world");
   }
}

class MyApp {
   // global command-line switches

   [STAThread]
   // main entry point
   static int Main(string[] args) {
//    Singleton s = new Singleton(); // error!
      Singleton s = Singleton.TheInstance;
      s.SayHello();
      return 0;
   }
}

QI read your column in the August 2002 issue of MSDN Magazine and found it very helpful. My problem is similar to your delegates example. I pass a C# delegate into unmanaged C++, and C++ makes the callback whenever it needs to. However, one of the members in the signature of the callback is an int array, and when it does get back into C#-land, C# thinks the array is of length one and only contains the first member of the array, no matter how big the array is. I've looked through many books and Web sites, but not many give help on interop issues between C# and unmanaged C++. I would be grateful if you could give me some ideas.

Vince Kwok

AWhile Microsoft .NET makes mixing managed and unmanaged code as painless as possible, the finer details of interop can sometimes be intimidating and elusive. But the designers were actually quite thorough in this area, so when it comes to interop, it's usually safe to assume that if you can't make things work, it's because you haven't yet divined the magic voodoo. Life can be even more confusing when callbacks are involved because of the added indirection. Even certified C++ gurus sometimes have to scratch their heads to recall the proper typedef syntax for callbacks. Never fear, I'm here to help.

I wrote a little program called TestArray that shows how to pass int arrays between managed and unmanaged code in a variety of situations. TestArray actually comprises two pieces: an unmanaged DLL (MyLib.dll) written in C++ and a .NET-centric console application written in C#. Figure 3 shows the code and makefile; Figure 4 shows its results. TestArray runs three tests. The first passes a .NET Array of ints from C# to an unmanaged C function, ArrayTest in MyLib.dll:

extern "C" __declspec( dllexport )
int ArrayTest(int ar[], int count)
{
  ••• // printf to print the array
  return count;
}

Figure 4 Running TestArray (Close Figure 4)

Figure 3 TestArray (Close Figure 3)

MyLib.cpp
// To build:
//
//    cl /LD MyLib.cpp
//
#include <stdio.h>

// This is normally defined in windows.h:
#define CALLBACK __stdcall

// callback function takes array of ints and length
typedef void (CALLBACK* ARRAYCB)(int ar[], int len);

// fwd ref
static void PrintArray(int* ar, int len);

///////////////////
// Simple array test: unmanaged C function receives array, length and
// displays it on console.
//
extern "C" __declspec( dllexport ) void TestArray(int ar[], int len)
{
   // display array on stdout
   printf(" [c++] inside TestArray: ar=");
   PrintArray(ar, len);
}

///////////////////
// Callback test: unmanaged C function receives a callback that takes an
// array and count.
//
extern "C" __declspec( dllexport ) void TestArrayCallback(ARRAYCB cbfn)
{
   // create an array
   int len = 10;
   int* parray = new int[len];
   for (int i=0; i<len; i++) {
      parray[i] = len-i;
   }

   // pass it to callback
   printf(" [c++] inside TestArrayCallback, cbfn=%p, calling it
      now...\n", cbfn);
   (*cbfn)(parray, len);
}

///////////////////
// Modify array test: unmanaged C function receives an array, modifies
// it, and returns to .NET caller. The array must be passed as a C# ref,
// so it's declared here as a ptr-to-array. The length is also passed as
// ref so it's a ptr-to-int. The function replaces each item in the
// array with its value squared. Its function doesn't alter the size of
// the array, but it could.
//
extern "C" __declspec( dllexport ) void TestModifyArray(int** pArray,
   int* pLen)
{
   int len = *pLen;
   int* ar = *pArray;

   printf(" [c++] inside TestModifyArray, *pArray=");
   PrintArray(ar, len);
   printf(" [c++] squaring elements...\n");

   // modify array: square each element
   for (int i=0; i<len; i++) {
      ar[i] *= ar[i];
   }
}

//////////////////
// Helper fn to print array on stdout.
//
static void PrintArray(int* ar, int len)
{
   printf("[");
   for (int i=0; i<len; i++) {
      if (i>0)
         printf(",");
      printf("%d", ar[i]);
   }
   printf("], len=%d\n", len);
}
TestArray.cs
// This program illustrates how to pass integer arrays between C# and
// C++ in various ways, and in particular how to pass an array from C++
// to a C# callback without having the array length collapse to 1. The
// trick is to use MarshalAs with SizeParamIndex to tell the framework
// which callback parameter contains the array size.
//
// To build:
//
//    csc TestArray.cs
//
using System;
using System.Runtime.InteropServices;

//////////////////
// This class wraps the unmanaged C++ functions in MyLib.dll.
//
class LibWrap {
   [DllImport("MyLib.dll")]
   public static extern void TestArray(int[] ar, int len);

   // callback delegate declaration. SizeParamIndex = 1 tells .NET that
   // the length of the array is in the 1st (2nd from 0-offset) param.
   //
   public delegate void ArrayCB(
      [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] int[] ar, int
         len);

   [DllImport("MyLib.dll")]
   public static extern void TestArrayCallback(ArrayCB cb);

   [DllImport("MyLib.dll")]
   public static extern void TestModifyArray( ref IntPtr array, ref
      int len );
}
//////////////////
// App class with Main entry
//
class MyApp {

   // callback function simply displays array contents
   private static void MyCallbackFn(int[] ar, int len) {
      Console.Write(" [c# ] inside MyCallbackFn with array=");
      WriteArray(ar);
   }

   // main entry point
   [STAThread]
   static int Main(string[] args) {

      // create array
      int len = 7;
      int[] array = new int[len];
      for (int i=0; i<len; i++) {
         array[i] = i+1;
      }

      // Run simple array test.
      Console.WriteLine("TEST 1 — Pass array from .NET to C++");
      Console.Write(" [c# ] calling TestArray with array=");
      WriteArray(array);
      LibWrap.TestArray(array, len);

      // Run callback test
      Console.WriteLine("\nTEST 2 — Pass array callback from .NET
                        to C++:");
      Console.WriteLine(" [c# ] calling TestArrayCallback...");
      LibWrap.ArrayCB mycb = new LibWrap.ArrayCB(MyCallbackFn);
      LibWrap.TestArrayCallback(mycb);

      // Run modify array test. This requires copying the array to an
      // InPtr buffer allocated with Marshal.AllocCoTaskMem.
      //
      Console.WriteLine("\nTEST 3 — Pass modifiable array from .NET
                        to C++");
      IntPtr buf =
         Marshal.AllocCoTaskMem(Marshal.SizeOf(array[0]) * array.Length);
      Marshal.Copy(array, 0, buf, array.Length );
      Console.Write(" [c# ] calling TestModifyArray with array=");
      WriteArray(array);
      LibWrap.TestModifyArray(ref buf, ref len); // call DLL
      array = new int[len];                  // new array w/returned length
      Marshal.Copy(buf, array, 0, len);      // copy buffer to array
      Marshal.FreeCoTaskMem(buf);            // free buffer
      Console.Write(" [c# ] TestModifyArray returns array=");
      WriteArray(array);

      return 0;
   }

   // helper to print array on console
   static private void WriteArray(int[] ar)
   {
      Console.Write("[");
      for (int i=0; i<ar.Length; i++) {
         if (i>0)
            Console.Write(",");
         Console.Write(ar[i]);
      }
      Console.WriteLine("], len={0}", ar.Length);
   }
makefile
################################################################
# MSDN Magazine — February 2003
# If this code works, it was written by Paul DiLascia.
# If not, I don't know who wrote it.
# Compiles with Visual Studio .NET on Windows XP. Tab size=3.
#
# To build, type "NMAKE" in a console window. Make sure your LIB and
# INCLUDE variables are set up as per the Visual Studio vsvars32.bat.
#

all: MyLib.dll TestArray.exe

MyLib.dll: MyLib.cpp
   cl /LD MyLib.cpp

TestArray.exe: MyLib.dll TestArray.cs
   csc TestArray.cs

TestArray takes an ordinary C array of ints and its length and displays them on stdout. Extern "C" is required to suppress C++ name-mangling and __declspec(dllexport) exports the function. This is C 101; MyLib.dll could have been written 10 years ago.

To make TestArray known to C#, you have to wrap it the .NET interop way, like this:

using System.Runtime.InteropServices;
class LibWrap {
  [DllImport("MyLib.dll")]
  public static extern void TestArray(int[]
      ar, int len);
}
Now you can call TestArray with a .NET Array and the framework will automagically marshal it properly:
int[] array = // create, initialize
LibWrap.TestArray(array, array.Length);

The interop services in the common language runtime (CLR) convert the .NET Array into a C array of ints. Remember, in .NET an array is not a block of memory stuffed with ints one after another, as in C. In .NET, System.Array is a full-blown class with rank, length, and lower bound, as well as data. To pass the length to C++, you simply pass Array.Length as a separate parameter.

But what about going the other way? That is, from unmanaged to managed code. In particular, how can you pass an array to a callback method? The second test shows how. This is where life gets a little tricky because if you declare your callback parameter as int[], you'll discover, as did Vince, that the array arrives with a length of 1 every time. C-style arrays don't have their lengths built in, whereas .NET Arrays do. So how do you tell .NET the array length when going from C++ to C#? The answer lies in the magic MarshalAs attribute and special field SizeParamIndex:

class LibWrap {
  public delegate void ArrayCB(
    [MarshalAs(UnmanagedType.LPArray,
      SizeParamIndex=1)] int[] ar, int len);
}

In general, MarshalAs lets you tell .NET how to marshal parameters when you don't like the default. Here, marshaling as LPArray (C-style array) is fine, but you need SizeParamIndex set to 1 to tell .NET that the length is the second parameter (the zero-based index is 1). If you have a fixed-length array, you can use SizeConst to specify a fixed size—for example, SizeConst = 50 if the array always has 50 elements. You only need SizeParamIndex in the delegate declaration, not the actual callback:

// don't need MarshalAs here
private static void MyCallbackFn(int[] ar, int len) {
  // ... ar.Length is same as len
}

The C++ side of things is straightforward. The only challenge is remembering how to declare a pointer-to-function in C. Here it is:

// ptr-to-fn
typedef void (*ARRAYCB)(int ar[], int len);
So now the C function that runs the callback test looks like this:
extern "C" __declspec( dllexport )
void TestArrayCallback(ARRAYCB cbfn)
{
  int len =    // length of array
  int* array = // create, initialize
  (*cbfn)(array, len); // call it
}
Now if you write the following lines everything works fine, courtesy of .NET:
LibWrap.ArrayCB mycb = new LibWrap.ArrayCB(MyCallbackFn);
LibWrap.TestArrayCallback(mycb);
When TestArrayCallback calls cbfn, the interop services convert the C-style array into a .NET Array upon arrival, with Array.Length set to whatever you pass as the len parameter. Amazing! To see for yourself, download the code, compile, and go (for full source code, see the link at the top of this article). So the short answer to your question is: you must declare the array parameter of your delegate with MarshalAs and SizeParamIndex equal to the index of the parameter that holds the size.

I've answered Vince's question, but there's one more situation I should mention briefly before saying bye-bye for today. The first test shows how to pass an array from C# to C++, and the second test shows how to pass an array the other way through a callback. Curious minds might wonder if you can pass an array from managed to unmanaged code in a way that lets the unmanaged code modify the array directly. In fact, you can, and that's what the third test in my sample program does.

You might think all you have to do is declare and pass the array as a reference (ref), which makes sense but, alas, doesn't work. If you try, you'll discover the array goes from C# to C++ just fine, but always comes back with the length set to 1. Sigh. So then you might think to try the SizeParamIndex trick again, but this time the compiler complains, muttering something like, "can't use SizeParamIndex with reference array..." Double sigh.

So how can you pass a modifiable array from managed to unmanaged code? TestArray shows the way: you must pass the array as an IntPtr. This requires manually copying the array to and from a buffer that you previously allocate with Marshal.AllocCoTaskMem. It's ugly—but hey, it works! Figure 3 shows the gory details.

Like I said at the outset, the .NET interop services have everything you need to marshal all kinds of parameters any way you like. For more details see "Default Marshaling Behavior" and in particular, "Default Marshaling for Arrays" in the .NET documentation. And make sure to bring along your universal nerdspeak de-crypto translator!

Minor correction: In my recent article, ".NET GUI Bliss," which appeared in the November 2002 issue, I stated that "XUL was developed by the Java language folks for Mozilla." While there are no doubt many Java folks who've contributed to XUL, XUL is, properly speaking, a Mozilla effort. Sorry for the confusion.

Send your questions and comments for Paul to cppqa@microsoft.com.

Paul DiLasciais 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 askpd@pobox.com or http://www.dilascia.com.


Resource File: Technical Support Options

Resource File
Technical Support Options

Contents

On this page we provide information on the most current Microsoft developer resources. This month we discuss sources for technical support, as well as additional resources related to the topics covered in this issue of MSDN Magazine.

Development can have its frustrating moments. How many times have you been in the middle of a project and encountered a problem you just couldn't solve alone? In order to help you through this, Microsoft provides quite a wide variety of support options from Web-based chats and newsgroups to paid premier customer support. The support Web site, http://support.microsoft.com, is the place to begin exploring all of your options.

Some products come with support. For example, Visual Studio .NET Professional and higher includes free online and telephone support incidents. The number of incidents allocated depends on the product, so visit http://support.microsoft.com, select "Contact Microsoft | Phone Numbers, Support Options, Pricing" for details on the number of support incidents allocated to your specific product.

When you call technical support to report a problem, you'll often discover that yours is a known issue and a set of workaround steps already exists. There may even be a Quick Fix Engineering (QFE) patch available that solves the problem. However, if your problem is entirely new, support engineers will do their best to help you work around the issue.

There are also a number of free support options such as MSDN Online Support, MSDN Communities, Developer Newsgroups the Knowledge Base, and officially sanctioned third-party communities. Chances are that any problem you encounter has been experienced by others as well, so you should be able to get what you need from one of these resources.

The Visual Studio product team at Microsoft has embarked on a program known as Community Room where team members spend several hours each month answering questions posed by customers in online forums. Primarily, teams closely monitor the microsoft.public.* newsgroups for any Visual Studio .NET questions that have gone unanswered after 24 hours. This means they answer the most difficult questions, and they also look here to find problems that need to be fixed for the currently shipping version of a product. (To get to the newsgroups, point your newsreader to msnews.microsoft.com.)

MSDN subscribers have one additional newsgroup option. The MSDN team recently committed to respond to customer questions on over 200 managed newsgroups within 72 hours of the original posting. Read more about this program at http://msdn.microsoft.com/newsgroups/managed.

Additional Resources

For additional information on the topics covered in this issue, see the following:

.NET Framework Class Library: LinkLabel.Link Class

Visual Basic and Visual C# Concepts: Changing the Appearance of the Windows Forms LinkLabel Control

Using Windows XP Background Intelligent Transfer Service [BITS] with Visual Studio .NET

.NET Framework Class Library: System.Threading Namespace

Safe, Simple Multithreading in Windows Forms

.NET Framework Developer's Guide: Delegates

Visual Basic and Visual C# Concepts: Event Handling in Windows Forms

.NET Framework Developer's Guide: Using the CodeDOM

.NET Framework Class Library: System.Drawing.Printing Namespace